Testing
How DropTerm is verified: headless unit tests for everything that needs no compositor, and this manual checklist for everything that does - runtime behavior is only testable on real COSMIC hardware. After building, check:
- First launch shows the overlay at the configured edge, focused and ready for input
- The bound shortcut hides and re-shows it
- Caret on show: every toggle-show paints the block cursor at the prompt in the first shown frame - before any keystroke; the same after a focus-loss hide (click another app with
hide_on_focus_losson, then re-toggle) and in a bash tab (no focus reporting - nothing else repaints the caret), and switching tabs paints the newly active tab’s caret the same way - A background job (
sleep 100 &, shell history) survives a hide/show cycle - Shell presence - hidden (the default): while DropTerm runs, shown or hidden, the window switcher and the dock list no DropTerm entry at all (the anchor is a layer surface no toplevel listing can see); the shortcut, the panel applet, and the CLI each summon it, and it never takes focus or input by itself
- Shell presence - dock mode: with
show_in_dock: true(file or the Advanced toggle), the switcher and dock list one DropTerm window carrying the DropTerm name and icon, never an “unknown” label, and selecting it neither disturbs the running surface nor drops the clipboard binding - Shell presence - the clipboard across both modes: copy in DropTerm, hide, focus another app, paste - the selection survives in hidden mode and in dock mode alike (the anchor’s whole duty), and a mid-session flip (file or toggle, no restart) swaps the entry at the flip itself, costs exactly the selection on the clipboard at flip time, and copies made after the flip persist across hide/show again
- Shell presence - the anchor outlives its output: in hidden mode, with DropTerm hidden, make the output vanish and return (monitor sleep that drops the link, unplug/replug, or disable/enable it with
cosmic-randron a multi-monitor setup); the log shows onethe compositor closed the clipboard anchorwarn; show DropTerm, copy, hide, paste into another app - the copy lands, and keeps landing across further hide/show cycles (withcosmic-ext-droptermhidden,/proc/<pid>/task/*/commstill lists asmithay-clipboathread) - Rapid repeated toggles (double key-press) don’t crash or duplicate the surface
- It appears correctly above a fullscreen window
Ctrl+Shift+C/Ctrl+Shift+Vcopy/paste, and middle-click /Shift+Insertpaste the primary selection- Shift escape hatch under a mouse-grabbing program: in vim with
set mouse=a(or tmux withmouse on), a plain drag stays the program’s own selection while Shift+drag paints DropTerm’s highlight andCtrl+Shift+Ccopies it; Shift+right-click opens the context menu with Copy live; releasing Shift mid-drag keeps the gesture whole - the selection ends where the pointer stopped and the program sees no stray release; the wheel stays the program’s even with Shift held (Shift+scroll scrolls vim, not the scrollback) Shift+PageUp/PageDownscroll the scrollback- Numbered tab switching: with three tabs,
Ctrl+Shift+1/2/3activate the first/second/third;Ctrl+Shift+9activates the last tab;Ctrl+Shift+5with three tabs does nothing (no wrap, no crash); bareCtrl+2reaches the shell (only theCtrl+=/-/0zoom cluster and its Shift-held forms bind) - Zoom chords - premise then pairs: on a current build, unshifted
Ctrl+=zooms in andCtrl+-zooms out first (if either fails, stop and debug the event path - a binds fix cannot repair it); then the Shift-held forms -Ctrl+Shift+=andCtrl+Shift++zoom in,Ctrl+Shift+-zooms out where the layout keeps-- and numpadCtrl+Plusstill zooms; on a US layout a Shift-heldCtrl+-arrives as_and still sends the shell 0x1F (readline undo keeps working, nothing stolen);Ctrl+0resets, and the status band’s zoom segment shows only while zoomed - Tab moves:
Ctrl+Shift+PageUp/PageDownshift the active tab one slot, clamping at the list edges (no wrap); the moved tab stays active and focused, andsession.ronrecords the new order - Attention markers: with two tabs,
printf '\a'in the inactive tab marks it🔔;echo hiin a third (inactive) tab marks it•; activating a marked tab clears its marker; the active tab never gains one; awhile true; printf '\a'; donebell storm in an inactive tab leaves one steady🔔(a state, not a counter); markers follow their tabs through drag-reorder and survive the closing of neighbors - Bell notifications: hide the terminal,
printf '\a'inside it - exactly one desktop notification appears, naming the tab; a bell storm while hidden still yields exactly one; two different tabs belling while hidden yield two; showing and re-hiding re-arms the latch (a new bell notifies again); with the terminal visible a bell posts no notification (marker only);bell_notification_enabled: falsesilences them and the Behavior toggle round-trips to the config file - Remote new-tab: with the app running,
cosmic-ext-dropterm new-tabopens a tab in the running instance, places it pernew_tab_after_current(default: after the active tab; see the placement item below), activates it, and shows the overlay; with the app not running, the first launch starts with that tab (alongside any restored session tabs) - New-tab placement: with
new_tab_after_current: false(written to the config file or flipped via the Behavior toggle, which round-trips to the file),Ctrl+Shift+T,Ctrl+Shift+H, the tab bar’s+, andcosmic-ext-dropterm new-tabappend the new tab at the end and activate it (test with a middle tab active - from the last tab, append-at-end and after-current are indistinguishable) - including a first-launchnew-tabalongside restored session tabs;duplicate-tabstill lands after the active tab; a restart keeps session order; with the key absent ortrue, every new tab lands after the active tab exactly as the default always did - New-tab directory - the mode: with
new_tab_cwd: Default(Behavior’s dropdown or the file) and a livedefault_cwd,Ctrl+Shift+T, the tab bar+, the terminal and per-tab menus’ plain New Tab, a barenew-tab, the startup tab, and the last-close respawn all open in that directory (pwdshows it); withInherit(or both keys absent) every one of those surfaces behaves exactly as before (inheritance);Ctrl+Shift+H,duplicate-tab, profile launches, and session restore keep their own directories in both modes; an explicit--cwdwins in both - New-tab directory - degrade and interplay: a dead
default_cwdlogs onewarnper spawn and the tab opens in the pty default, whileInheritnever warns (the key is not consulted); the Terminal card’s “Default directory” row round-trips (Enter a path to set, empty to clear) and reopens showing the stored path;default_profileset → the profile’s own live cwd owns every plain spawn in both modes and the fresh-tab respawn runs the profile - Shell picker: the Terminal card’s Shell dropdown lists this system’s
/etc/shellsshells by basename (duplicates like/bin/bash+/usr/bin/bashcollapsed to one row; an unreadable file offers the static bash/zsh/fish/sh quartet); a pick writes the full path and the next plain spawn runs it (echo $0names it) while a running tab is never re-launched; the stored value selects its row, a custom path typed in the free-text row below still works and selects no dropdown row; the row’s description carries thedefault_profilenote on the picker, the mode, and the directory new-tab --cwd /tmpopens the shell in/tmp;new-tab --cwd /nonexistentlogs awarnand opens in the fallback directory; with the active tab cd’d somewhere, plainnew-tabinherits that cwdnew-tab --cmd htopruns htop instead of the shell (no config shell arguments inherited), and closing htop closes the tab; the--cmdtab is absent fromsession.ronafter the next debounced save, while plainnew-tabtabs are saved like any other- Unrunnable-program degradation: a profile whose command names a program that is gone (e.g.
asdf) - submenu pick,+dropdown row,default_profilespawn, and session restore alike - runs the command-then-shell wrap, so the shell reports the miss in-band (“command not found”) above an interactive prompt and the tab keeps the profile’s whole identity (colors, env, name on the band); a blank stored command line (hand-edited) logs onewarnand opens a plain-shell tab (no profile name on the band; the preset’s live cwd still applies);new-tab --cmd asdfdegrades to a plain-shell tab as before; a configshellvalue that cannot exec skips to$SHELLthen/bin/shon the next plain spawn, and a session-recorded shell the system no longer carries restores onto the configured shell - Profile submenu: with at least one profile configured, the terminal context menu’s “New Tab” is a submenu - plain New Tab first, then one row per profile in config order - and each profile row launches that profile’s command in its
cwd; with zero profiles the menu renders flat exactly as before - Profile command-then-shell (ruled 2026-10-12): a profile’s command runs as a preamble of the resolved shell with an interactive tail (
sh -c "<command>; exec sh") every way it launches - submenu pick,+dropdown,new-tab --profile,default_profileplain spawns, session restore, and Switch Profile - so a one-shot command (ls) leaves its output visible above a prompt in the profile’s colors and env, the band carries the profile name, and the session records the profile id; the tab never flash-dies on a finishing command, quitting a long-running profile program (q in htop) lands on the same prompt, and exiting that tail shell closes the tab exactly like a plain shell’s exit (the last tab’s close still respawns the never-empty default) - Remote profile tab:
new-tab --profile web-prodopens a tab running that profile, both in the running instance and at first launch;--profile ghostlogs awarnand degrades to a visible plain-shell tab;--profile+--cmdis rejected by the CLI; an explicit--cwdbeats the profile’s owncwd - Default profile: with
default_profileset,Ctrl+Shift+Tand a fresh startup run it as full profile tabs; a restart restores the profile tab by id, and a profile edited between save and restart launches the edited command;duplicate-tabstill spawns a fresh shell - Profile session semantics: a profile tab is saved to
session.ronwith its id and cwd while a--cmdtab stays absent; a saved id since deleted from the config restores a plain shell in the saved cwd - Tab bar
+visibility: the+shows beside the gear at the strip’s right corner in every tab state (the 1-tab strip carries+and gear alone), on both edge placements and both themes; glyph,New Tabtooltip, and hover treatment read on light and dark panes alike - Tab bar
+click parity: a plain click on the+behaves exactly likeCtrl+Shift+T- withdefault_profileset it runs as a full profile tab,cd /tmpthen click spawns in/tmp, and placement followsnew_tab_after_currentboth ways (the placement item above covers the shared funnel) - Tab bar
+profile dropdown: with at least one profile configured, right-click on the+opens a dropdown listingNew Tabfirst then one row per profile in config order (the context-menu submenu’s composition); every profile row launches that profile, the plain row runs the default spawn; rows highlight on hover and the dropdown never clips at the surface’s right corner - Tab bar
+zero profiles: with an empty profile list, right-click on the+no-ops (no visual change beyond the+itself) while the plain click still spawns - Tab bar
+dropdown dismissal: while open, a left press on a row picks it and closes; any other press - on the+itself (toggle), the strip, or away over the terminal - closes it with no side effects (no spawn, no tab select, no drag armed, nothing reaching the terminal) - Tab bar
+and strip geometry: a many-tab squeeze never underlaps the+or the gear (the row clips at the widened corner zone), drag reorder stays clear of both targets, and a+press during an inline rename commits the rename - Tab bar
+dropdown lifecycle: hide/show while the dropdown is open returns with it closed (the open state dies with the strip’s tree) - Status band - disc states and process name: with the default config the 28 px band renders at the free edge beside the grab bar; an idle tab shows the hollow ring plus the recorded shell’s basename,
sleep 30fills the disc and namessleep,vimnamesvim, and returning to the prompt restores the ring and basename; a wrapper-launched shell whose comm cannot be read falls back to the basename (honest, never blank) - Status band - path and right cluster:
cd ~/src/ossshows the~-collapsed path; squeeze it (a narrowwidth_percent, a long path, or profile + zoom set together) and the path elides from its left keeping the tail; the profile name shows only on profile tabs - a profile renamed or aswitch-profile-tabrespawn reflects on the band at the next event - and the signed zoom percent shows only while zoomed (Ctrl+=thenCtrl+0hides it) - Status band - all four stackings: flip
edgeandtab_positionthrough every combination - the band stays adjacent to the grab bar in each, the terminal’s pixel height never changes across the flips, and the defaultAutokeeps the band between the tab strip’s side and the grab bar - Status band - colors: a scheme flip (settings drawer or config edit) recolors the band and its text on the next render through the shared pane derivation; text stays luminance-readable against both light and dark schemes, the right cluster reading dimmer than the path
- Status band - toggle and keybind: the Behavior page’s Status bar card (“Show the status band”) flips the band instantly with the terminal re-pinning its height the same frame;
Ctrl+Shift+Bflips identically (bareCtrl+Bstill reaches the shell); a restart restores the persisted state; hand-editingstatus_bar_enabledwhile the app runs applies on the echo; a config directory from before the key loads with the band on - Status band - font resolution: the band’s text renders in the terminal’s configured font at 12 px - compare its glyphs against the terminal’s own cells; if the family did not resolve (fallback glyphs or a silent default), the sanctioned fallback is the default UI font at 12 px with every other token standing
- Status band - the grown segments: the grid segment (
⊞ 80×24) tracks a live resize (narrow the window and watch the columns count down, never dipping below one); the tab position (▤ 2/5) follows switches, new tabs, and closes; the branch segment reads a plain repo (⎇ main), a worktree checkout (its.gitfile indirection), a detached checkout (the commit’s first seven characters), and stays absent outside repositories; the clock shows local◷ HH:MMand ticks exactly on minute boundaries - Status band - click-to-copy: click each segment in turn (path, branch, profile, grid, tab, zoom, clock, and the disc-and-name cluster) and paste elsewhere - every one lands the segment’s full value (a path squeezed to
…/ced2still copies~/src/oss/l1br3/ced2); the copy lands on release and the✓flash replaces the segment for ~800 ms before the value returns; the pointer cursor rides the segments and idles over the gaps; in-terminal copy binds (Ctrl+Shift+C/V) still reach the shell - Status band - text selection (user-ruled 2026-10-11): press and drag across the band - the covered characters highlight (pane foreground at quarter alpha, following the cursor both directions) and the release copies exactly the selected display text (WYSIWYG: the elided path’s
…included; segments joined by single spaces; Ctrl+Shift+V into the terminal or middle-click paste both serve it - drag-selection also claims the primary selection); the highlight persists after release and clears on the next press; a drag held within 4 px stays a click (the full-value verb above); a drag confined to the gaps selects nothing - Status band - per-segment toggles: each Behavior “Show …” row hides exactly its segment on the next render (flip every one in turn); hiding the branch stops the
.gitreads and hiding the clock stops its tick (toggle the clock off mid-minute - the segment vanishes immediately and no further ticks land; on again it returns within a minute); a restart restores every persisted row; a config directory from before the keys loads with the full band - Bar borders - placement through the four stackings: with both toggles on (the default), each bar shows exactly one 1 px hairline on its terminal-facing edge - walk every
edge×tab_positioncombination: the strip’s line follows the terminal (below a top strip, above a bottom strip), the band’s stays on its side away from the grab bar; where the strip and the band adjoin (e.g. edge Top with tabs Follow edge, edge Bottom with tabs Top) the seam carries only the band’s line - never a doubled line; the drag-reorder’s lifted button may ride over the strip’s line mid-drag (transient, verdict it here) - Bar borders - live toggles and derived color: the Behavior page’s “Show the tab bar border” (Tabs card) and “Show the status band border” (Status bar card) each flip their line on the very next frame - nothing resizes, the terminal’s pixel height never changes; both lines read as subtle contrast lines on light and dark schemes (the 0.3-alpha verdict: retune
BORDER_ALPHAinsrc/tab_bar.rsif either scheme reads too faint or too harsh) and a scheme flip recolors them on the next render; atopacitybelow 100 the lines still render; a fresh config directory loads with both on, and a config directory from before the keys gains both lines - Status band - squeeze with the branch: at narrow widths the path elides first (tail kept); keep narrowing until the branch drops entirely - the priority is deterministic, path over branch, and the band never wraps or overflows
- Status band - grid on resize: at spawn dimensions the grid segment is absent (silent when default); drag the surface’s height or narrow it and the segment appears showing the live
cols×rows; resize back to the spawn dimensions and it vanishes; respawn the tab (Switch Profile on it) and it vanishes again - a fresh process starts default - Status band - narrow-surface drop order: squeeze
width_percentdown and watch the band fit as one pass - the path elides first (tail kept), then segments drop one at a time in order (grid, tab position, zoom, profile, branch, clock), each drop buying the path back room (it re-expands between drops); the disc and process name are last standing, survivors keep their seats, and nothing wraps or overflows however narrow - Status band - inline reveal: rest the cursor on an elided path - its display expands in place, the branch lending its seat (the full value when it fits, else the longest elision that does); the pointer cursor rides the displayed strip and a press copies the full value with the usual
✓flash; hover any other segment or an un-elided path → pointer cursor only; leaving collapses instantly; walk it at both anchor edges (the reveal is pure in-band text - nothing floats, nothing clips) - Status band - fresh on landing: run
sleep 30, hide the surface, let the sleep exit, re-show - the disc is hollow on the first visible frame (the landing refresh read the exit), not one title-report later; the clock reads the current minute on landing too - Status band - context menu surface and composition: right-click anywhere on the band - over a segment or a gap alike - opens the menu at the cursor on the terminal’s side (walk both anchor edges; the bubble never overflows either end of the band); the composition is Copy · Copy All · Paste │ Zoom In · Zoom Out · Reset Zoom │ Hide Status Bar · Settings… - Copy muted on a gap press, Reset Zoom muted at base size and live once zoomed (
Ctrl+=first); enabled rows highlight on hover, muted rows stay flat - Status band - context menu copy and action semantics (status-menu criteria 2-3): Copy on a pressed segment lands that segment’s full value (a path squeezed to
…/ced2still copies~/src/oss/l1br3/ced2) and flashes✓in place; Copy All lands every present segment’s full value space-joined (paste elsewhere and compare against the band, absent segments skipped); both write the clipboard only (middle-click paste serves the drag selection, not the menu); Paste, the zoom trio, Hide Status Bar, and Settings… behave exactly like their keybinds (Ctrl+Shift+V,Ctrl+=/Ctrl+-/Ctrl+0,Ctrl+Shift+B,Ctrl+Shift+I) - Status band - context menu dismissal, lifecycle, and the unified press (status-menu criteria 4-5): while open, any press closes it with no side effects unless it picks an enabled row (nothing reaches the terminal under it; a keybind still passes through -
Ctrl+Shift+Twhile open spawns and the rows re-derive); hide/show while open returns with it closed; and the press unification - a click under 4 px of travel on any segment copies its full value (before, every click read as a drag once the band sat mid-column), a crossing drag still selects WYSIWYG, and the pointer cursor rides the segments again - Status band (command-lifecycle) - live elapsed: run
sleep 30and watch the duration segment beside the process name count once per second (0:04→0:05→ …) - the tick alone drives it, nothing prints;cargo buildcounts the same way; past the hour the shape is1:00:00 - Status band (command-lifecycle) - frozen pair and freshness: when the command finishes the reading freezes as
cargo 2:41(the finished command’s name beside its duration - the process segment already shows the shell again) and stands until the next command starts; clicking the segment copies the same string with the usual✓flash; a second command freezes fresh - Status band (command-lifecycle) - honesty: a profile tab whose initial command wraps into its shell at spawn shows no duration for that command (start unwitnessed); a terminal switched to mid-run shows no live elapsed until its own next command; a respawned tab (Switch Profile) starts clean
- Status band (command-lifecycle) - lifecycle: hide the surface mid-run and re-show - the elapsed resumes honestly (no jump past the wall clock, no reset); after a finish, a hide, or a toggle-off the tick stands down within a second (no further counts land); the “Show the command duration” toggle off returns exactly the pre-segment band, on mid-command shows the live elapsed immediately - and both persist across a restart
- Status band (status-scrolled) - the chip appears and counts: in a busy terminal (output still flowing) scroll up with the wheel - the dim
↑Nchip appears in the right cluster between the zoom and the clock and its count tracks the distance scrolled; scroll back down and the count shrinks, reaching nothing at the bottom - Status band (status-scrolled) - the jump click: click the chip - the viewport lands at the bottom and the chip vanishes the same frame; no clipboard write lands for it (paste elsewhere: the previous clipboard stands) and no
✓flash - the jump is the whole verb; the context menu opened over the chip shows its Copy row muted (no value to copy) and carries no chip row of its own - Status band - hover tips on every segment: hover each segment in turn - a tip pops to the terminal’s side of the band naming the segment and its verb (
Working directory. Click to copy the full path.,Terminal size: 120×30 cells. Click to copy.,Tab 1 of 3. Click to copy., the tab strip’s own pane-inverted bubble look, instantly, no dwell) and vanishes when the cursor leaves; the scrolled chip’s tip names the jump (Scrolled back 12 lines. Click to jump to bottom.) and the pasted chip’s the panel (1 pasted image. Click to open the images panel.), clicking still doing those verbs; hovering a gap tips nothing, and the tips stay suppressed while the band’s context menu is open - Status band - separators and leading glyphs: thin low-alpha vertical hairlines render between adjacent segments within each cluster (never between the clusters, never beside an absent segment - hide segments and confirm no orphan hairline stands) on light and dark schemes alike; each hairline sits centered in a full 12 px gap on both clusters - the right cluster long rendered flush, its segments touching seam to seam, so confirm profile, grid, tab, zoom, the chips, and the clock all breathe the left cluster’s own spacing; every segment carries its leading mark - the ten text glyphs (branch
⎇ main, grid⊞ 80×24, tab position▤ 2/5, profile◉ dev, duration⧗ 2:41, progress◐ 42%, path⌂ ~/src, zoom⇕ +120%, pasted▦ 3 images, clock◷ 14:32) riding their displays’ measured widths so the gaps and hairlines stay exactly the twelve-px rhythm, beside the three owned marks that never double (the process disc, the exit✗, the scrolled↑) - monochrome text shapes in the terminal’s own font, the text system’s fallback answering a family that lacks one (a mismatched style is the worst case; retune the constants insrc/status_bar.rsif any glyph reads wrong); under squeeze the path’s glyph is eaten first by the eliding…- a fully-shown path carries⌂, a heavily elided one does not, and both are correct; the click copy stays the bare value (paste80×24, never⊞ 80×24) and the✓flash replaces glyph and value together - Status band (status-scrolled) - absent at bottom and under pressure: an at-bottom terminal shows nothing (a fresh terminal, a respawned tab, and the moment after a jump); squeeze
width_percentdown - the chip drops after the command duration and before the clock - Status band (status-scrolled) - the toggle: the Behavior page’s “Show the scrolled-back lines” row hides the chip outright (scroll up: nothing appears; on again it returns at the next event) and persists across a restart; a config directory from before the key loads with the chip on
- Status band (status-remote) - the stand-in shows:
sshto a real host from a tab inside a git repo - the path’s seat shows the command’s destination token exactly as typed (user@host, a bare alias), the branch disappears (both stale the moment ssh started), and the process segment keeps its honest● ssh - Status band (status-remote) - flagged ssh and the copy verb:
ssh -p 2222 box(or-i key, any option with a value) shows the host token, the option values skipped; click the stood-in path - the token copies whole with the usual✓flash (the token is the value) - Status band (status-remote) - exit restores: exit ssh - the local path and branch return at the same snapshot event (the next prompt redraw), never mid-session; an argv that cannot certify a destination (a wrapper, a mangled invocation) leaves the local path standing rather than a guessed host
- Status band (status-select) - double-click select: double-click a rendered segment (the path, the branch, the clock) - its whole display text highlights (persisting until the next press) and the release copies exactly it to clipboard and primary, WYSIWYG (an elided path selects its elided display); no
✓flashes - the highlight is the feedback - Status band (status-select) - the single click is untouched and the verbs compose: click the same segments once - each still copies its full un-elided value with the usual
✓flash; then click-then-double-click over one segment - the first release copies the full value, the second press-and-release selects the display (the terminal’s own click-then-word-select rhythm) - Status band (status-select) - middle-click paste: middle-click anywhere on the band - over a segment and over a gap - the clipboard pastes into the narrated terminal (the context menu’s own Paste verb); a prior drag or double-click selection still serves middle-click paste as before, and the band’s hidden state kills both idioms with the tree (no keys added)
- Status band (status-hover) - the tint: rest the cursor on any rendered segment and a quiet fill covers exactly that segment’s strip - the same rect clicking copies from, rounded like the context menu’s own rows - following the cursor segment to segment and vanishing with it (no fade, no layout motion); it reads at the menu’s hover weight on light and dark schemes alike (the pane foreground’s own hue, never a new one)
- Status band (status-hover) - gaps and absences tint nothing: the gaps between segments (the cursor already idles over them), a segment hidden by its toggle or squeezed out by a drop, and the cursor away from the band each leave the strip unpainted
- Status band (status-hover) - a selection owns the visual: press and drag across the band - the selection highlight paints and no hover tint does, through the drag and the persisting highlight after release; the next press (which clears the selection) hands the visual back
- Status band (status-hover) - the scrolled chip hovers like any segment: rest the cursor on the
↑Nchip - the tint covers its strip exactly as any segment’s (its cursor already says Pointer) and the jump verb stands unchanged: the click still lands at the bottom with no clipboard write - Status band (status-hover) - the open menu owns hover: open the context menu and rest the cursor on the bubble’s rows - the bubble’s own hover highlight is the only one; no band segment tints while the cursor rests over the bubble
- Status band (status-activity) - the hidden echo arms the halo: run
echo hiin the active tab, hide the surface before it lands, then show it - a thin red ring circles the process disc; it survives the show and persists through further shown output (no timed expiry, no pulse - the toast expires, the halo does not) - Status band (status-activity) - the touch clears: with the halo showing, a keypress into the terminal clears it that frame; arm it again and each of a paste, a wheel scroll, and a click in the terminal pane clears it in turn; output alone (another hidden echo) never clears it
- Status band (status-activity) - the chip’s jump is a touch and the switch clears both: arm the mark, scroll up, then click the
↑Nchip - the jump clears the halo; arm it again and switch tabs by keybind while hidden, then switch back and show - the mark is gone (the switch cleared it; the show never does; both tabs’ latches clear at a switch) - Status band (status-activity) - composition and geometry: the ring composes with the filled disc (a running command) and the hollow disc (idle) without covering either; with the process segment toggled off it stands alone at the band’s left edge, and no segment rect ever moves - the halo is not a click target (the disc’s click still copies the process name)
- Status band (status-activity) - the toggle and the red: the Behavior page’s “Show the missed-activity halo” row hides it outright (key off means never rendered) and persists across a restart; a config directory from before the key loads with the halo on; the red tracks the scheme - the bright red on a dark strip, the plain red on a light one (flip schemes to watch it flip) - and a scheme that defines no red degrades to the pane foreground’s monochrome, never an invented hue
- Status band (status-exit) - the verdict lands beside its cost: with the fish-4 integration active (fish ≥ 4 emits the 133;D mark natively), run
false- the chip✗1appears beside the frozen duration (cargo 2:41 ✗1shape); atruefinish shows nothing (success is the unmarked default); the next command’s start clears the chip with the frozen pair - Status band (status-exit) - without integration nothing changes: a shell with no 133;D emission (plain bash before any rc hook) renders the band exactly as before - no seat, no gap, no verdict guessed from a bell or a title
- Status band (status-exit) - the red and the copy: the chip’s red matches the scheme’s own red - bright on a dark strip, plain on a light one - and tracks a scheme flip on the next render; clicking the chip copies the bare code (
1) and flashes its✓; the context menu’s Copy row does the same - Status band (status-exit) - the chip drops after its duration: squeeze the band (narrow surface, many segments) - the exit chip yields after the duration and before the scrolled chip, survivors keeping their seats
- Status band (status-exit) - the toggle: the Behavior page’s “Show the exit status” row hides the chip and its sampling on the echo and persists across a restart; a config directory from before the key loads with the chip armed
- Status band (osc7-cwd) - the report outranks the guess: with an OSC 7 integration active (the fish/zsh/bash snippets in the status-bar page),
cdaround - the path segment tracks the reported cwd live,~-collapsed on this machine, and clicking it copies the displayed value whole - Status band (osc7-cwd) - the remote truth: ssh into a host whose shell reports OSC 7 and
cdthere - the path rendershost:pathverbatim (no~collapse - no remote home is known) and the git branch hides while the report stands; exiting the ssh leaves the last report standing until a newer one or a respawn replaces it (the documented peer lifetime) - Status band (osc7-cwd) - without integration nothing changes: a shell with no OSC 7 emission (plain bash before any rc hook) renders the path exactly as before -
/proccwd, ssh stand-in, branch - byte-identical - Status band (osc94-progress) - every state by hand: run the printf driver block from the status-bar page in a shell -
42%, the moving84%,…(indeterminate owns the chip), the red42%on error, the plain paused42%, the!after a clear-then-error, and the final clear’s absence each render in order, beside the exit chip - Status band (osc94-progress) - the three deaths: a
state 0report clears the chip instantly; a report mid-command dies at the command’s finish ((printf '\e]9;4;1;42\e\\'; sleep 2)- the chip stands while the job runs and vanishes at its prompt); a stalled reporter’s last number expires ~15 s after its final refresh (emit1;42, emit nothing, wait) - Status band (osc94-progress) - the red and the copy: the error state’s value (or
!) paints in the scheme’s own red - bright on a dark strip, plain on a light one, tracking a scheme flip; clicking the chip copies the bare percent (42) and flashes its✓; a…chip copies nothing - Status band (osc94-progress) - the seat and the drop: the chip sits after the exit chip; squeezed (narrow surface, many segments) it yields before the exit chip, survivors keeping their seats; the Behavior page’s “Show the progress chip” row hides chip and sampling on the echo and persists across a restart, and a config directory from before the key loads with the chip on
- Status band (osc94-progress) - without integration nothing changes: a shell with no OSC 9;4 emission renders the band exactly as before - no seat, no gap, no percent guessed
- Status band (osc7501-status) - every state by hand: run the printf driver block from the status-bar page in a shell - the
cargo 40%name with an unchanged disc, the red-filled disc onblocked, the red fill with70%onerror, the disc and name alone again ondoneandidle(no second hue, ever), and the clear’s absence each render in order - Status band (osc7501-status) - the lifetime edges:
working/blockeddie at the command’s finish ((printf '\e]7501;state=blocked\e\\'; sleep 2)- the red fill stands while the job runs and leaves at its prompt);done/errorpersist past the finish until a newer report,state=clear, or a respawn; a full reset (printf '\e c') clears even the persistent pair - Status band (osc7501-status) - root-only and the ignored set: a report with an
id(\e]7501;id=us-east;state=blocked\e\\) changes nothing;app,kind,title, andmsgkeys ride harmlessly; a mangled report (state=bogus) disturbs nothing standing; theblockedtint matches the missed-activity halo’s red across a light and a dark scheme (one token, both strips) - Status band (osc7501-status) - the seat and the toggle: the percent rides the name seat (
cargo 40%) with the process copy still copying the bare name; the Behavior page’s “Show the program status” row hides tint, percent, and gathering on the echo and persists across a restart, and a config directory from before the key loads with the report on - Inline images - imgcat both forms: with imgcat installed (iterm2.com/utilities),
imgcat photo.png(the chunked MultipartFile/FilePart/FileEnd session) andimgcat -l photo.png(the legacy single-shot) both render inline at the resolved size, the cursor landing below with the emitter’s trailing newline not overlapping the image;chafa -f iterm photo.pngrenders too - Inline images - the grid’s own bookkeeping: scroll an image through scrollback (it rides the lines like text), resize the window over a placed image (reflow carries its cells), and select across image cells (no text grabbed, no panic);
inline=0renders nothing - Inline images - the kill switch: turning “Inline images” off (Behavior → Terminal, applies live) makes the next
imgcatsilent - nothing decodes, nothing places, no error - while already-placed images keep rendering; re-enabling renders the next sequence again; the row persists across a restart and a config directory from before the key loads with images on - Inline images (kitty) - the namesake round-trip:
kitten icat photo.pngdetects DropTerm through its live query probe (no env vars, nothing configured) and renders inline, scrolling away like text;chafa -f kitty photo.pngrenders the same; both respect the kill switch - Inline images (kitty) - a placement scrolled and re-placed: scroll a rendered image through scrollback (its cells ride the lines like text), resize the window over a
kitten icatimage (the re-place moves its own cells with no flicker and no duplicated upload), and run two images’ outputs interleaved (each delivers its own); place two overlappingz>=0images (az=1then az=2transmission, botha=T) and the higherzcomposites on top; re-place one with a differentzand the next frame draws it at the new layer - Inline images (kitty) - the DA1 fence and the replies: kitten icat’s detection resolves on the first try (the graphics reply lands before the DA1 answer - no retry, no fallback to text); a refused transmission (
printf '\e_Gt=f,i=1\e\\'after transmitting image 1) answers the one uniformEBADF:Failed to read image file;a=q,i=1answersi=1;OKwhilea=q,i=1,q=2answers nothing - Status band (osc7501-status) - without integration nothing changes: a shell with no OSC 7501 emission renders the disc and the name exactly as before - no tint, no percent, no state guessed
- Notifications (command-notify) - the post: run
sleep 6in the active tab, hide before it finishes - one desktop notification lands naming the tab, its body “sleep finished in 0:06” (the band’s own duration shape); a command that ran in a background tab - never selected while it ran - posts the honest fallback “Command finished in …” with no name - Notifications (command-notify) - threshold and visible silence: a hidden
sleep 1finishes with no notification (under the 5 s threshold; a finish exactly at 5 s counts); a finish while the terminal is visible never posts - the active tab’s frozen duration segment and the inactive tab’s activity marker carry the signal - Notifications (command-notify) - the latch: within one hide period a second hidden long command posts nothing (one per tab per hide); re-show the terminal and the next hidden long command posts again; each tab latches on its own (two tabs’ hidden finishes while one is shown yield one notification each)
- Notifications (command-notify) - the controls: the Behavior page’s “Command finish notifications” toggle silences the next finish and re-enables at the next trigger; the “Command finish threshold (seconds)” row applies at the next finish (
30makes a hiddensleep 10silent); both persist across a restart, and a config directory from before the keys loads with the notification on at 5 s - Autosuggestions - ghost and accept: run a command twice (
git statusthen Home) and re-type its prefix (git sta) - the remainder shows dim after the cursor, tracking each keystroke; Right accepts it whole (the bytes land at the prompt, nothing executes), and on a fresh prefix Alt+Right accepts exactly one shell-word (leading spaces included) with the remainder re-suggested - Autosuggestions - the history seed: a fresh bash/zsh/fish tab suggests from a prefix that only exists in the shell’s own history file (run it in another terminal first); a command run in the tab outranks the seed from then on; a tab spawned with the toggle off seeds nothing until a respawn;
sh/dash/kshtabs stay scrape-only - Autosuggestions - paths and escapes: a path prefix with one matching entry ghosts the full completion (directories slashed); two matches sharing a prefix ghost only the shared part (
src+srvpresent:~/sghostsr,~/srnothing); a spaced name ghosts backslashed (my\ file.txt), a printable non-ASCII name ghosts single-quoted ('café'), a control-character name ghosts nothing - Autosuggestions - legibility and shells: the ghost reads dim-but-clear against stock light and dark schemes (a scheme flip recolors it on the echo; it is the scheme foreground blended into its background, never palette gray); the same walk works under bash, zsh, fish, and over ssh - no integration installed anywhere
- Autosuggestions - honest silence: inside vim/htop nothing suggests (a child holds the foreground), a
read -pprompt stays silent (anchor mismatch), a multi-line or right-side prompt stays silent, and scrolling back suppresses the ghost until the view returns to the bottom - Autosuggestions - dismissal and the kill switch: any other keystroke, a resize, and a focus loss each clear the ghost on the next update; the Behavior page’s Autosuggestions toggle removes every visual trace live (a showing suggestion disappears the moment it lands; a restart restores the persisted state; a config directory from before the key loads with suggestions on); with nothing showing, Right and Alt+Right reach the shell exactly as before
- Autosuggestions - subcommands and flags:
git cheghostsckoutfrom the curated table (the subcommand seat),git --drandgit checkout --drghost the same long flag (the any-seat flag rule), anddocker pghostsull(the tie rule);python3 whaghosts nothing (outside the five), and a tab spawned as ssh stays silent at both seats while its scraped history keeps suggesting - Autosuggestions - the spec walker, per-subcommand flags: with the generated core tier copied to
completions.toml(configuration; a new tab reads the file at spawn),git commit --amghostsendfrom commit’s own flag list, andCtrl+Spaceatgit commit --lists commit’s own flags - the root’s persistent flag riding with them, every other root flag absent - Autosuggestions - the spec walker, a flag’s argument seat: with the core tier seated,
rustc --emit=aghostssmand accepting writes the bare value (never an=; the=and space forms are the same seat),mkdir -mghosts nothing (the mode seat has no vocabulary) whilemkdir -m 755 src/completes from the directory listing (the folder argument hands the seat to paths), andgit add a b --ghosts add’s own flags again - Autosuggestions - the spec walker, a nested subcommand: with the core tier seated,
docker compose ughostspandCtrl+Spaceatdocker compose ucarriesup’s description into the dim second column;gh pr creghostsate(a second nesting level) - Autosuggestions - the spec walker, the sudo shape: with the core tier seated,
sudo apt insghoststall- the command argument walks apt’s own tree, andsudo <unknown-command>stays silent (never a guess) - Autosuggestions - the spec walker, the unknown-flag tolerance:
git --whatever ststill ghostsashandCtrl+Spacelistsstashandstatus(an unknown flag mid-line never silences the seat; a typed short flag likemkdir -mis understood butCtrl+Spaceatmkdir -offers the long table only) - Completions tiers - the generous default: after
sudo just install(which stagesshare/<appid>/completions-extended.toml;just uninstallremoves it), a fresh tab with no user file completes across both tiers at once -git commit --amghostsendandrustc --emit=aghostssmfrom the embedded 92-command core floor, and an extended-tier command (bat --lafrom the 532-command installed file) ghostsnguagewith the description in the menu’s dim column - the settings caption (Behavior → Terminal, “Extended completions”) reading “applies live - 532 more commands - found at /usr/share/io.github.l1br3_io.CosmicExtDropterm/completions-extended.toml”; with the file uninstalled the extended command goes silent (the floor keeps answering) and the caption reads “applies live - not installed”;just dev-install && just devfinds the file too (both data-dir seats written) - Completions tiers - the kill switch and the override: turning “Extended completions” off (applies live, no restart) silences
bat --laat the next typed prefix whilegit commit --amkeeps answering from the floor, and the caption still reports the file (it is the file’s state, not the toggle’s); re-enabling adds the tier back at the next prefix (the already-parsed file reused, no re-read); a usercompletions.tomlre-declaringgitwith one subcommand of its own wins over both tiers whole -git cheghosts only what the user’s tree seats - Autosuggestions - the clip-to-fit ghost: in a narrow pane a ghost wider than the remaining columns clips with a trailing
…instead of disappearing (withgit statusin history and five columns free,gighostst s…), and with fewer than three columns free nothing is drawn; either way Right accepts the full command, Alt+Right one full word of it, and the remainder re-suggests - while a ghost that fits fully renders unchanged, marker-free - Autosuggestions - the delay, default unchanged: with
autosuggestions_delay_msunset and at0, every ghost behavior is exactly as the rows above - the ghost appears on the update it always did, per keystroke, nothing withheld - Autosuggestions - the delay at 250 ms: set
autosuggestions_delay_msto 250 on the Terminal card (the change applies live, no restart) - typing a prefix continuously shows no ghost at all; stopping shows the ghost about a quarter second later with no keystroke in between; the next keystroke dismisses it instantly (no grace period) and the restarted wait re-shows at the next pause; the moment it shows, Right accepts whole and Alt+Right one word exactly as at0 - Autosuggestions - the fresh tab: a brand-new tab (first app start after a fresh install,
Ctrl+Shift+T, or a respawn) suggests at its very first prompt with no prior output - with the prompt near the top of the empty grid, type a prefix that exists in the shell’s own history file and the ghost appears immediately (before the fresh-tab fix a fresh terminal stayed silent until its first command finished and output pushed the prompt to the bottom row) - Autosuggestions - the one-character floor: on a fresh tab (no matching history)
gghosts nothing whilegighostsit(togit); on a lived-in tab - after runninggit statusonce -gstill ghostsit statusfrom history (the floor quiets the catalog alone, never history) - Autosuggestions - the bare-
--floor:gitthen--ghosts nothing (two dashes alone match every long flag equally), while--aghosts a flag’s remainder andgit astill ghostsdd(the curated subcommand seat stays strong at one character) - Autosuggestions - aliases: declare
gc = "git checkout"in the config file (autosuggestions_aliases; the change applies live at the next typed prefix - no respawn) and typegc --dr- the ghost isy-run(git’s--dry-runre-targeted through the expansion);gc cheghostsckout; accepting writes only the suffix (the line keeps your typedgc- the expansion is never sent); after one realgc checkout main,gc cheghosts the history remainder instead (history outranks the map); an undeclared alias and an emptied map ghost exactly as before, and a tab spawned as ssh stays silent at the alias tier while its scraped history keeps suggesting - Completions menu - open and contents: at an idle prompt with a typed prefix (
git stafter runninggit statusonce),Ctrl+Spaceopens a panel anchored at the cursor listing every provider’s matches - the scoped rows leading (the curated subcommandsstashandstatusabove the history line; the seat’s own scope outranks history in the list, where the single-choice ghost keeps history first), a baregit(space echoed, nothing after) listing git’s whole curated subcommand table while the ghost stays silent there, andgit checkoutlisting none (the seat rules hold at the boundary) - ≤8 rows with scrolling past it; the panel reads against stock light and dark themes (the terminal scheme’s own foreground, never a palette gray), sits as an opaque elevated surface drawn in its own renderer layer above the text pass - the theme’s component color with its divider border and a drop shadow, fully covering the terminal text behind it so the menu reads as a layer floating over the pane (before the layer fix the panel quad painted beneath the grid’s glyphs under wgpu’s by-kind draw order - terminal text showed straight through the menu however opaque its fill - and the fill itself was the window background, vanishing into the pane); the selected row highlights, and the grid under it is untouched (a selection drag or copy over it sees only cells) - Completions menu - keys and the bare-Tab promise: with the menu open, Up/Down move the highlight (clamped at both ends, the window following), Tab and Enter write the selected suffix at the prompt (completion only, nothing executes), Escape and
Ctrl+Spaceclose, and any other key closes the menu first then reaches the shell normally (the ghost’s Right accepts included); with no menu open, Tab reaches the shell byte-for-byte as always (\x09, shell completion intact) - and at an ineligible prompt (inside vim/htop, scrolled back, nothing typed)Ctrl+Spaceforwards to the shell verbatim (the NUL byte, no menu) - Completions menu - lifecycle: a resize, a focus loss (click away / Alt+Tab), and scrolling back each close an open menu; re-summoning at the same prefix reopens identically, and the ghost’s behavior is exactly as the rows above throughout (the menu is a second surface, never a ghost change)
- Completions menu - auto-popup, default off: with the keys unset the menu never opens without the chord (every typed prefix leaves it closed - today byte-for-byte); with “Open the menu as you type” on (Behavior → Terminal, applies live), typing a prefix that collects ≥2 candidates pops the menu on its own at the echo, narrowing to one candidate closes it (the one remaining candidate’s ghost still shows), and an explicit
Ctrl+Space-opened menu obeys only its own rules (narrowing does not close it) - Git generators - branches and tags at the ref seats: in a git repository (≥2 branches, ≥1 tag),
git checkout maghosts the branch’s remainder (infrommain) andgit checkout vghosts per the tag set;git switchcompletes branches only (a matching tag stays silent);Ctrl+Spaceatgit checkout flists every matching branch and tag (branches first, above the history lines - the keyword’s scope leads the list);git add/git restorecomplete changed-file paths; after one realgit checkout <branch>the history remainder outranks the cache for the ghost (the menu keeps the cached rows on top); typing continuously spawns nothing (the names come from cache) - Git generators - silence rows: a non-repository directory ghosts and lists nothing from the generator tier (history/path/table keep working); a tab spawned as ssh or a non-shell program never suggests a branch name; a hung or failing git degrades to silence within ~5s - no dialog, no error, no wrong completion
- Completions data file - extends and describes: write a
completions.tomlbeside the config keys declaring one new command (gh, with a describedprsubcommand and a described--repoflag - the format’s annotated example in configuration) and open a new tab (the file reads fresh at spawn) -gh pghostsr(strict prefix, must extend, shortest wins, unchanged policy) andCtrl+Spaceatgh plists the rows with the description in the dim second column while a re-declared, describedcheckoutghostsckoutexactly as the bare built-in does (descriptions are the menu’s, never the ghost’s); an alias mapped to the file’s command re-targets here too - Completions data file - the fallback: corrupt the
completions.toml(truncated TOML, or add ascript = "..."line to an entry) and open a new tab - behavior is byte-identical to the built-in table (git chestill ghostsckout,gh pghosts nothing) with no dialog or error, only a debug-level log; removing the file restores the same built-in behavior, and a live tab picks an edited file’s table up at the next settings change (any toggle flip) without a restart - Per-tab context menu - surface and scoping: with ≥2 tabs, right-click on any tab button opens the menu at that button (terminal side, never clipping either strip edge); every row acts on the clicked tab - rename an inactive tab in place (no activation), duplicate it (fresh shell in its cwd, landing after the active tab and activated, pin carried - exactly
duplicate-tab --tab-indexon it), Switch Profile respawns it in place keeping its slot; the plainNew Tabrow rides the default spawn; right-click on the+, the gear, a strip gap, or over a live rename input behaves exactly as before (dropdown/no-op/no-op/no-op) - Per-tab context menu - composition: zero profiles render the flat menu; ≥1 profile adds the muted
Switch Profilesection header plus one row per profile in config order (the same list the+dropdown reads);Move Tab Left/Move Tab Rightrender muted at the edge they clamp at (a muted press only dismisses), enabled and working elsewhere (the moved tab nudges one slot, the active tab stays active);Close Tabs to the Rightis absent on the rightmost tab; enabled rows highlight on hover (muted rows and the header stay flat) - Per-tab context menu - close sweeps:
Close Tabrides the confirmation gate like the×;Close Other TabsandClose Tabs to the Rightclose every non-running target at once and prompt one running target at a time (Proceed closes it; further running tabs stay open until re-invoked); with the confirmation toggle off all sweeps close everything immediately; the anchor tab always survives - Per-tab context menu - dismissal and lifecycle: while open, any press closes it with no side effects unless it picks an enabled row (nothing reaches the terminal, no drag armed, no spawn from the
+under a toggle press); the target’s shell exiting while the menu is open closes it; hide/show while open returns with it closed - Pinned tabs - the verb and indicator: the per-tab menu’s
Pin Tabrow (after Duplicate Tab) pins the clicked tab - its title then prefixes 📌 ahead of any attention marker on both light and dark panes - and the label flips toUnpin Tabon its next open; unpin clears both; the indicator survives hide/show and a mode flip - Pinned tabs - close refusals: every user close path -
Ctrl+Shift+W, the tab bar’s×, a middle-click on the button, andcosmic-ext-dropterm close-tab- refuses a pinned tab (awarnin the log, no dialog, nothing else changes; the per-tab menu’sClose Tabrow renders muted on a pinned tab instead - a muted press only dismisses,Unpin Tabsits above it);Close Other TabsandClose Tabs to the Rightskip it while loose neighbors still close (silent skip, the row label stays honest since the anchor survives); the tab’s own shell exit (Ctrl+D,exit) still closes it - Pinned tabs - stay-put and persistence: pressing a pinned tab still selects it but dragging never arms (the button never follows the cursor); its
Move Tab Left/Move Tab Rightrows render muted (a muted press only dismisses) while other tabs drag and move around it freely and the active tab’s identity never changes; a pinned tab survives a restart in its saved order, andswitch-profile-tabon it keeps the pin - Profiles as a tab: the strip’s Profiles row turns to the manager page (the drawer title stays “Settings”), and Close, Escape, and a repeat
Ctrl+Shift+Iclose the whole drawer from any page - on the Profiles page Escape peels one layer at a time (an open picker popover first - scheme or font - then an open form draft, then the drawer) - Profile form transaction: the pencil on every row opens the edit form (the same open the identity column’s click gives) and nothing writes until Save - typing, scheme picks, and env add/remove all buffer; Save (the footer’s button or an Enter in any input, text or env row) validates the whole draft and writes exactly once - the row updates and the form closes on the written row, an Add/Clone selecting the new row - while a failed Save writes nothing and shows every offending field’s inline error at once (blank name, duplicate name, blank command, an off-registry scheme, an out-of-band font size, an unparsable scrollback, bad env rows together, each under its own input; typing or re-picking clears only that field’s error); an unchanged Edit’s Save closes the form with no write; Cancel and Escape drop the draft with no write and keep the drawer; the scheme picker’s pick buffers in all three modes and the recolor lands on Save (its running tabs recolor, no respawn)
- Profile form override fields (profile-manager): the Font size and Scrollback lines inputs sit under the scheme picker with
inherits <N>placeholders naming the global values (14 and 10000 by default); empty saves the field unset (the profile then follows every global edit), a number saves the override (the next spawn under the profile uses it), and33/abc-style entries fail Save with the field’s own inline error; a seeded Edit draft round-trips a stored override (11shows as11) and saving it emptied writes the inherit shape - Profile form inherit picker (profile-manager): the Color scheme control is the shared swatch picker - every built-in scheme previewing its actual eighteen colors - with a leading Inherit row previewing the global scheme resolved for the current system mode (flip the system theme or the Application theme and the row’s swatch follows); the closed control names the picked scheme or “Inherit” over the resolved strip; the pick buffers and rides Save, and its error (a hand-edited unknown name) renders under the control
- Profile draft lock (profile-manager): with a draft open, every list control - pencil, clone, delete, up/down, the add button, and row selection - renders disabled, while the default-profile picker stays live; Save or Cancel releases the lock; arming a delete and then opening a draft hides the armed pair for the draft’s life and returns it after
- Profile armed delete (profile-manager): a row’s trash press arms that row - the trash becomes an inline Confirm / Keep pair - and Confirm deletes (the default cascade intact: deleting the profile the default names clears it), Keep disarms; arming another row disarms the first; a delete is never a bare click and never a dialog
- Profile lifecycle walk (profile-manager): add → set a scheme override, a font size, and a scrollback → Save (one write, the form closes on the new row) → a live tab under the profile re-resolves its scheme and font on the echo → edit → Cancel (nothing written) → arm the delete → Confirm (gone, the picker current) - then Escape ordering inside a dirty draft: open the scheme picker, press Escape (popover closes, draft stays), press Escape again (draft cancels, drawer stays), press a third time (drawer closes)
- Profile form legibility: the footer’s Save/Cancel pair reads on light and dark panes, and the five-icon row (pencil, clone, delete, up, down) reads at the drawer’s 344px minimum with the pencil glyph rendering (the identity column absorbs the width)
- Profile CRUD end-to-end: add a profile from an empty draft (its first Save commits it), edit its fields through the form, clone it (
name (copy),(copy 2)on collision), and delete it - the RON file is never hand-touched through any of it - Default-profile cascade on the page: renaming the default-named profile moves the picker selection to the new name in the same interaction; deleting it clears the picker to
None (plain shell)- the picker never shows a name the list lacks - Profiles-page propagation: reorder rows (up/down, the boundary direction disabled) and the
+dropdown plus the context-menu submenu show the new order on their next open - no restart; a profile added on the page launches via the+dropdown andnew-tab --profile NAMEwithout restart; a default pick applies to the next plain spawn (Ctrl+Shift+T,+click, fresh startup); running tabs are never re-launched or closed by any page write - Profile drafts: a focus-loss hide preserves an open draft with its text; leaving the Profiles tab - a strip pick or a close - clears it, and reopening starts from the config; Escape with a draft open cancels it with no write and keeps the drawer (a second Escape closes it)
- Profile appearance overrides (profile-appearance): hand-edit a profile with
color_scheme,font_size, andscrollback_linesset, then spawn a tab under it - the tab renders the profile’s scheme and font from its first paint; a global scheme or font edit (settings drawer) moves the plain tabs while the overriding tab keeps its own; editing the profile’scolor_schemein the config file recolors its running tabs live (no respawn); aswitch-profile-tabonto an overriding profile starts the fresh terminal at its font and scrollback capacity; deleting the profile under a live tab degrades that tab to every global pick on the next config pass - no rewrite, no error; a session restored with the profile tab re-resolves its appearance by name (a missing name restores global); the window background follows the focused tab’s scheme, switching with tab switches - Profile color scheme - application: a profile with
color_schemeset spawns its tabs in that scheme every way it launches (context-menu submenu row,new-tab --profile NAME, the tab bar+dropdown, and plain spawns with it asdefault_profile); a plain tab beside it keeps the global pick;switch-profile-tabonto/off the profile moves the colors with the switch; a profile with a blank command line (degraded preset) spawns global like a plain tab - Profile color scheme - the picker: the Profiles-page form’s scheme picker opens on the stored scheme (the Inherit row when unset or the stored name is unknown); the pick buffers in every draft mode - Edit, Add, and Clone alike - and rides Save, which recolors the profile’s running tabs without a respawn; picking Inherit clears the field on Save; the offered schemes are exactly the global pickers’ registry behind the Inherit row
- Profile color scheme - fallbacks: deleting a scheme-carrying profile while its tabs run recolors them to the global pick on the next config pass (no error); hand-editing the config to an unknown scheme name warns at resolve and serves the global pick (the file is never rewritten); a restart restores a profile tab’s colors by re-resolving its saved id; a COSMIC light/dark flip moves plain tabs only - scheme tabs keep their scheme; the window background follows the active tab (switching between a scheme tab with a background-less scheme and a plain tab moves the fallback bg)
- Profile env - application: a profile with
envset spawns its tabs carrying the variables (env | grep KEYinside the tab shows each verbatim, overriding an inherited name) every way it launches (context-menu submenu row,new-tab --profile NAME, the tab bar+dropdown, and plain spawns with it asdefault_profile); a plain tab, a--cmdtab, and a duplicate beside it carry none of them; a profile with a blank command line (degraded preset) spawns a plain shell with no profile env (verified 2026-10-08, agent-driven over the D-Bus seam with file-dump observables: the--profileanddefault_profilespawns carriedDROPTERM_PROFILE/HW_MARKERmerged verbatim over the inherited environment; plain,--cmd, duplicate, and degraded tabs carried none; the menu/+legs are the same unit-pinned spawn arm) - Profile env - the editor: the Profiles-page form’s Environment rows seed from the stored map in sorted key order; Add variable buffers a blank row (a wholly blank row drops at the write); an Enter in either input commits the whole map; a value without a key or a key carrying
=rejects with an inline error under that row and writes nothing; removing a row lands with the next env Enter; an Add/Clone draft’s rows ride its first valid commit - Profile env - composition: a restart re-applies a profile tab’s env by re-resolving its saved id against the current definition (edit the map, restart, the restored tab carries the new values); an env edit affects the next spawn only - the running tab’s
envis unchanged (expected, not a bug);switch-profile-tabonto the profile swaps its env in and off the profile drops it (verified 2026-10-08, agent-driven: restart re-applied the env by id, switches swapped it in and out; next-spawn-only is process semantics by nature) - Profiles-page legibility: inline errors, the empty state, row metrics, command-preview truncation, and the picker’s
Nonerow read on light and dark panes; the page scrolls when the list + form exceed the drawer height; the row identity reads at the drawer’s minimum width - the scheme chip (filled with the scheme’s own bg + six accents, hollow when unset or unknown), the dimmed mono$ commandprompt line, thedefaulttag on exactly the row the picker resolves to, tooltips on the four icon actions, and the selected row’s accent edge-and-tint (visible on light and dark); the Edit form’s title names the committed profile name toggle/show/hidestill round-trip through a second launch exactly as before (including via D-Bus activation)- A
new-tabsent during the 130 ms slide-out reverses the slide and ends shown with the new tab active - it never finishes the hide - Remote close-tab: with the app running and two tabs,
cosmic-ext-dropterm close-tabcloses the active tab (successor activates) without showing the overlay;close-tab --tab-index 1closes the first;--tab-index 0is rejected by the CLI; closing the last tab respawns a fresh one; an out-of-range index logs awarnand no-ops; with the app not running, the app starts plain (a tab running a foreground child raises the close-confirm modal instead of closing - see the close-confirmation rows below) - Remote select-tab: with three tabs,
cosmic-ext-dropterm select-tab 2activates the second and shows the overlay (a hidden terminal shows);select-tab 4warns and no-ops without showing;select-tab 0is rejected by the CLI; a select during the 130 ms slide-out reverses it and ends shown on the target tab; first launch starts plain - Remote rename-tab:
cosmic-ext-dropterm rename-tab "work"pins the title - a shell OSC title change no longer overwrites it - and it persists insession.ron;rename-tab ""clears the override and shell titles resume; an inline rename open on the target tab is discarded when the remote title lands; the overlay never shows; out-of-range warns and no-ops; first launch starts plain - Remote duplicate-tab: with the active tab cd’d somewhere,
cosmic-ext-dropterm duplicate-tabopens a fresh shell tab in that cwd after it, makes it active, and shows the overlay; duplicating a--cmd htoptab yields a plain shell in that tab’s cwd; an out-of-range--tab-indexwarns and duplicates from the active tab; the duplicate saves to the session like any plain tab; first launch starts plain - Remote switch-profile-tab: with the app running and a profile configured,
cosmic-ext-dropterm switch-profile-tab --profile web-prodrespawns the target tab in place - the profile’s program runs in the same slot, the selection and every other tab are untouched, the old child is gone perps, and no trailing output from it lands in the new terminal; the overlay shows;--tab-index Ntargets that tab; a renamed tab keeps its rename while an unrenamed one renumbers (Terminal Nfollows the fresh terminal); the fresh terminal starts at the profile’s effective appearance (its scheme and font override, else the global look - not necessarily the other tabs’); after the switch the session file records the profile (and the surviving rename), and a restart restores the tab as that profile; switching onto a one-shot profile (e.g.ls) runs the command-then-shell wrap in place - the listing lands above a prompt in the profile’s colors, and the slot survives; an unknown--profileor out-of-range--tab-indexwarns and edits nothing (the overlay still shows);Ctrl+Shift+Qhide/show cycles after a switch behave normally; first launch starts plain - Duplicate-tab keybind/menu:
Ctrl+Shift+D(and the context menu’s “Duplicate Tab” row, hint shown) opens a fresh shell tab in the active tab’s cwd directly after it and activates it; a tab renamed “work” duplicates already named “work” - the name survives the fresh shell’s first prompt title and a restart - while an unrenamed tab’s title resolves naturally (no literal pin of a shell-set title); withdefault_profileset the duplicate is still a plain shell, and withnew_tab_after_current: falseit still lands after-active whileCtrl+Shift+Tappends at the end;--tab-indexon a renamed non-active tab carries that tab’s pin;Ctrl+Din the shell still EOFs and baredtypes - Editing
height_percentin the config reshapes a shown surface immediately, in place - and a hidden surface opens at the new height on its next show width_percentbelow 100 (e.g. 60) sizes the surface to that fraction of the output width; the default 100 is indistinguishable from the full-width surfaceh_align = Left/Rightsits flush against that edge,Centercenters the surface with equal left/right margins - on bothedgevalues- Editing
width_percent/h_align/edgewhile shown also reshapes in place (width 60 → 100 clears the margins and refills); non-geometry edits (font, colors) leave the surface untouched - A visible grab bar renders on the free edge, outside the tab bar when one is shown; dragging it resizes the height live, and only the height - width and alignment stay put, including on a narrower-than-full-width surface
- Dragging respects the 10-100 % height band; with
edge = bottom, dragging away from the screen edge grows the terminal - After a drag is released the new height applies immediately (a hide/show right away opens at the new height) and
height_percentis written to the config file once, about 400 ms later - no write/reshape storm, and rapid re-drags coalesce into one write - With default config, the only visible change from the previous build is the grab bar; tab bar placement is unchanged (from the screen edge: content, tab strip, grab bar - the strip always present, its tab buttons only with more than one tab open)
tab_position: Topandtab_position: Bottompin the strip to that side on bothedgevalues - on the matching anchor the strip sits at the screen edge, on the opposite one it restores the default side; the grab bar stays on the free edge in all four layouts- Switching Tab bar position in the settings drawer’s Geometry section while the drop-down is shown reorders the strip immediately - no resize jump, the terminal’s pixel height is unchanged; a pick while hidden applies at the next show
- Tab title modes: with tabs whose shells report
user@host:~/pathtitles (plus one renamed tab), flip Tab titles in the Behavior section -Shell title(default) shows reports verbatim;Abbreviated pathrendersjpt@box:~/src/oss/l1br3/cedasjpt@box:~/s/o/l/ced(an absolute-path title abbreviates identically, a non-directory title like a static vim title is left alone);Last segmentshows the cwd’s final component (ced,(root)at/); the renamed tab never moves in any mode - An empty title report never blanks the tab: run a TUI that clears its title on interrupt (opencode: start it, press Ctrl+C once for the interrupt, again to exit) - the tab falls back to
Terminal N(or the mode’s no-report title) instead of showing only the close icon, and a later non-empty report retitles as usual - Profile title mode - labeling: with one tab spawned under a profile (context-menu submenu,
new-tab --profile, ordefault_profile) and one plain tab beside it, flip Tab titles toProfile name- the profile tab reads the profile’s name (whatever its shell reports underneath, including before the first report), the plain tab keeps its exact Shell-mode title, and a renamed tab keeps its pin across the flip; restoring a session in Profile mode labels the restored profile tabs;switch-profile-tabonto/off a profile moves the label with the switch (verified 2026-10-08, agent-driven via session-title observables: the name showed before any report, the rename pin won and persisted, a restored profile tab stayed labeled, switches moved the id - report-precedence is unit-pinned; eyeball the rendered bar once for good measure) - Profile title mode - live retitle: while in
Profile namemode, rename the profile and its running tabs leave the old name without a respawn - the recorded id dangles, so they fall back to Shell-style titles (the raw report, elseTerminal N- never a stale name) until switched or the name returns; restoring the name re-labels them live; the same edits in any other mode retitle nothing; an unrelated settings change (an opacity slide) retitles nothing in any mode (verified 2026-10-08, agent-driven: a rename dropped a live tab toTerminal 1, the name’s return re-labeled it with no respawn; the other-mode/anti-churn halves are the unit-pinned delta guard) - Switch Profile folder - surface: with ≥1 profile the terminal context menu gains the Switch Profile folder after Duplicate Tab (one row per profile in config order); with zero profiles the menu is exactly today’s flat shape (no folder, no extra row); a pick respawns the active tab in place identically to
just act switch-profile-tab --profile NAMEon that same tab (slot, activation, and a pinned rename kept; the terminal replaced) - Switch Profile folder - composition: switching onto a scheme-carrying profile recolors the tab, onto an env-carrying profile sets its env (
env | grep KEY), and Profile title mode retitles it; switching to the profile the tab already runs is a respawn anyway (CLI semantics); a stale row (config edited behind an open menu) warns and no-ops without a crash - New tabs respect Tab titles at spawn: with the mode on
Last segmentorAbbreviated path,Ctrl+Shift+Tafter acd /tmp/a/bcandCtrl+Shift+H(home) both open their tab already titled from the spawn directory (bc, or~for home itself, in Last segment;/t/a/bc-style or~-style in Abbreviated path) - never a transientTerminal N- and the first shell report then resolves per the mode as usual; with a shell that never titles, the cwd-derived title stays;Shell titlestill opens asTerminal Nuntil the first report, andTerminal numberalways showsTerminal N Terminal numberparks every un-renamed tab at itsTerminal Nwhile cwd tracking continues - acdstill marks the session dirty andsession.ronrecords the new cwd- Mode changes apply live: flipping the dropdown (or hand-writing
tab_title_modewith tabs running) retitles shown tabs immediately; an empty rename commit shows the mode’s title right away instead of the cleared text; after a relaunch with restore, the fresh shell’s first title report lands per the current mode and a restored rename survives it - The settings-gear tooltip pops toward the terminal in all four layouts (edge x pinned position), including the strip pinned at the screen edge - never clipped at the right corner
- Switching the system between light and dark updates the terminal colors
- On a multi-monitor setup, the surface appears on the output under the cursor
Ctrl+Shift+Qhides the surface, and the session survives a re-toggle- Clicking another window hides the surface (fresh config,
hide_on_focus_lossdefaulttrue) - With
hide_on_focus_loss: falsewritten in config, clicking away does not hide - and the clicked input keeps keyboard focus (typing lands there, also with the defaultkeyboard_mode: Exclusive, which a stay-open surface never holds), while clicking the terminal takes focus back - Stay-open flip releases the grab live: with the surface shown and
keyboard_mode: Exclusive, toggle Hide on focus loss off in the settings drawer and click away without a hide/show cycle - focus still leaves and stays with the clicked input (no snap-back on the next compositor refresh) - Escape still reaches the shell (e.g. works in vim) and still cancels an active selection
- With
respect_panel: trueand a top panel, the panel stays fully visible and clickable while the drop-down is open, and the terminal sits flush below it - With
respect_panel: true,edge: bottom, and a bottom dock, the drop-down sits above the dock - With
respect_panel: true, the drop-down still appears above a fullscreen window - With a config directory that has no
respect_panelkey (orfalsewritten), the drop-down still covers the panel - Toggling
respect_panelin the settings drawer or config file while the drop-down is shown applies on the next show - With an autohide panel and
respect_panel: true, the drop-down lands below the panel’s handle strip - Arrange several tabs (distinct titles and cwds, one renamed, a middle tab focused), quit, relaunch: the same tabs return in the same order, each a fresh shell in its saved cwd, the rename intact, the same tab focused
- A saved cwd that no longer exists (delete the directory, or hand-edit the file): that shell respawns in
$HOME - Open/close/reorder/rename/switch tabs and
cdaround:session.ronupdates within ~500 ms of the first change, at most one write per interval under continuous change (watch the state dir + logs) kill -9mid-write at any point: the previous session file is intact, nosession.ron.tmplitter, and the next launch restores the pre-kill layout- No
session.ron(first ever run, or deleted): a single fresh tab, no warnings - Hand-corrupt
session.ron(truncate or garble it), relaunch: the file becomessession.ron.bak, awarnis logged, and the app starts fresh - Hand-edit
session.rontoversion: 2, relaunch: same quarantine (.bak+ fresh start) - a newer schema is never guessed at - Restore spawns exactly the saved number of tabs - no default tab appears alongside them
- Right after a relaunch with otherwise idle tabs, the session file is not rewritten (a write soon after restore is still legitimate when restored shells report new titles)
- With several tabs open, change the configured
shelland trigger a save: existing tabs’ saved shells are unchanged; only new tabs use the new shell - Slide animation: showing slides the content column in from the anchored edge over ~130 ms (ease-out) and hiding slides it back out (ease-in), removing the surface only after the slide completes - on both
edge = topandedge = bottom, with horizontal placement never animating - During the slide the surface’s size and position never change (no visible resize or jump), and the content’s opacity never fades
- Keyboard focus is grabbed the instant the surface shows, and returns to the previously focused window the moment a hide starts - not when the surface disappears
- Empty-desktop focus: with every other window closed (fresh session, only the panel), toggle-show lands the surface already focused - typing and app keybinds (
Ctrl+Shift+T) work with no click - and the same holds for the surface’s first show after login and for every re-show after a hide; the keyboard mode follows thekeyboard_modeconfig in both settings - A focus-reporting program (fish; or vim/tmux in any shell) never repaints per toggle: hotkey hide/show leaves its display untouched, while a real focus loss and regain (click another window, then the overlay) still reports - the dropdown’s own toggle is not a focus event
- Toggle grid stability - steady toggle (the reported repro): press Ctrl+L (the shell’s clear-screen) to clear, then toggle hide/show five times - zero new prompt entries appear above the live prompt and the screen stays exactly the cleared prompt (before the fix each toggle added one dead prompt entry and pushed it into scrollback)
- Toggle grid stability - no intermediate grid: at first launch the prompt renders at the configured rows from its first appearance (no full-output flash then shrink), and a re-show renders the content at its settled size from the first frame
- Toggle grid stability - genuine changes land once: a grab-bar drag release, a
height_percentedit, and awidth_percentedit each reflow the terminal exactly once at the settled size - no bounce through an intermediate size - Toggle grid stability - narrow width: with
width_percent: 60and a wrapped command on screen, toggle hide/show three times - the wrap is identical and no new scrollback entries appear - Toggle grid stability - scrollback stays buried: run a few commands,
clear, toggle several times - nothing previously cleared resurrects, andShift+PageUpafter the toggles shows no duplicated prompt pairs Ctrl+Shift+Qand click-away (focus loss) hides animate exactly like the hotkey hide- A background job (
sleep 100 &) survives a hide/show cycle toggled mid-animation (toggle again within the 130 ms) - Rapid-toggle spam (10+ toggles inside 1 s): the surface always ends in the state the last toggle implies - no teleports between endpoints, no stuck or half-animated surfaces, focus correct at rest - and a toggle arriving mid-hide visibly reverses the slide on the same surface
- A toggle arriving inside the measuring window (hide immediately after show) destroys the surface immediately: no slide, no stuck state
- Pressing or dragging the grab bar during the 130 ms slide-in does nothing to the animating surface; the drag only takes once the content rests
Ctrl+Shift+Iopens the settings drawer while the terminal has focus, and the keypress never reaches the shell; a second press closes the drawer - it never opens a second window- Drawer layout on the layer surface: the drawer overlays the terminal’s right edge with the framework’s title + Close button + scrolling sections; the terminal stays visible beneath and its grid does not reflow on open/close (
resetthen toggle the drawer - the same lines stay put) - Drawer backdrop: while the drawer is open, the terminal and bars behind it sit under a dark veil (the pane itself stays full-brightness and opaque); closing the drawer lifts the veil with no residual tint, and with the drawer closed the surface renders as before (closed path composes the identical tree); with a close-confirm pending over the open drawer, the modal’s darker veil stacks above the drawer pane. Note whether the veil’s 0.4 alpha reads right on light and dark themes and at the configured terminal opacity (one constant to tune -
DRAWER_SCRIM_ALPHA) - Settings tabs: the drawer opens on Appearance with a five-tab strip (Appearance, Behavior, Profiles, Keybindings, Advanced) pinned under the drawer title; the strip stays put while any tab’s page scrolls beneath it - never scrolls away
- Scheme pickers: both Appearance-card controls show the configured scheme’s name plus its eighteen-color mini strip at rest (the COSMIC pair’s background chip follows the theme surface); a press opens the popover listing all twelve schemes as name + swatches in registry order, the picked row filled and accented; a row pick applies live to the terminal under the drawer and closes; Escape closes without picking and without closing the drawer (a second Escape closes the drawer); a click outside closes without side effects under the bubble; the popover stays anchored to its control while the Appearance page is scrolled, never leaves the drawer bounds, and both pickers never open at once; chips stay legible at the 344px drawer floor - flag it if the one-line name + chips risk not fitting - and the dark-scheme chips read against the popover surface
- Font picker: the Appearance card’s Font control shows the configured family’s name set in that family at rest (an unresolvable family renders in the UI font while the name stays what the file says); a press opens the popover listing every installed monospace family sorted, each row’s label in its own face, the persisted pick filled and accented, long names ellipsized end-ward at the clamped width and the popover never leaving the drawer bounds; a row pick live-applies (the grid re-metrics under the drawer, the zoom percent resets) and closes; a stored-but-uninstalled name renders as the leading row, selected, and re-picking it writes nothing new; the list wheel-scrolls with the offset clamped at both ends - every family reachable, and a wheel outside the bubble scrolls the drawer’s page instead; Escape closes without picking and without closing the drawer (a second Escape closes the drawer); the font picker and the scheme pickers never open at once; the default
Noto Sans Monoselects on a stock system - Tab contents: Appearance shows the Appearance and Window cards; Behavior shows the Tabs, Status bar, Dropdown, and Terminal cards; Profiles shows the manager; Advanced shows exactly the Advanced card; Keybindings shows the search box and grouped action rows - every control exactly once across the tabs, none lost or duplicated
- Tab picks are page turns: switching tabs never closes the drawer and never writes; start typing in a field on Appearance, flip to Advanced and back - the draft is intact
- Strip fit at the 344px floor: on a narrow output (
width_percent: 30), all five labels read fully on light and dark themes with no clipping - shorten “Keybindings” or bump the drawer floor if labels risk clipping - Strip focus navigation: Tab/arrows move keyboard focus into and within the strip, Enter/Space activate the focused tab; Escape from the strip still closes the whole drawer
- Close funnel from any tab: the Close button, Escape, and a repeat
Ctrl+Shift+Iclose the whole drawer from every tab alike; an explicit close reopens the drawer on Appearance - Profiles tab turn: with a draft open on Profiles, a strip pick to another tab clears the draft (the strip highlights the picked tab immediately), and a pick back to Profiles starts from the config; re-picking the Profiles row itself keeps the draft
- Focus-loss hide with the drawer open preserves the open tab and its drafts: the next show uncovers the drawer on the same tab, drafts intact
- Scroll offset across tab switches: scroll Appearance deep, switch to Advanced and back - note whether the carried offset reads badly (the default ruling accepts it, as cosmic-edit does; revisit only if it reads badly here)
- Live edits under the open drawer: drag the opacity slider and the surface transparency changes while dragging; a font-size or scheme pick re-renders the terminal immediately
- Input capture: keyboard and pointer over the terminal never reach the shell while the drawer is open (type, click, and drag over it - nothing lands in the pty); the drawer’s text inputs type normally
- Focus handoff under
keyboard_mode: Exclusive: opening the drawer moves keyboard focus off the terminal, closing it returns focus (typing works again with no extra click); repeat underOnDemand - Escape with the drawer open closes the drawer only - the surface does not hide, and the Escape is not forwarded to the shell; with the drawer closed, Escape still reaches the shell (works in vim) and still cancels an active selection
- Context-menu “Settings…” and the tab-bar gear button hit the same toggle as
Ctrl+Shift+I - Focus loss with the drawer open (with
hide_on_focus_loss: true): clicking away hides surface + drawer together; the next show uncovers the drawer exactly as left - still open, an in-flight text draft intact; explicitly closing (Escape / Close button / repeat keybind) then reopening starts fresh from the config values - Narrow-output width floor: with
width_percent: 30on a narrow output, the drawer stays inside the surface (344px floor clamped to the surface width) and remains usable - note how much of the terminal it covers - The drawer’s Close button is localized (libcosmic’s own
fl!("close")string) in a non-English locale - Drawer default width: with no
settings_drawer_widthin the config, the drawer opens visibly wider than the old 480px (560px where the output allows; half the surface below 1120px-wide surfaces) - Drawer drag-resize: grab the accent grip on the drawer’s leading edge and drag - the pane follows the pointer live in both directions, the cursor shows a horizontal-resize affordance over the strip, and the terminal’s grid beneath never reflows (overlay semantics hold mid-drag)
- Drawer drag clamps: dragging far left stops at half the surface width; dragging far right stops at the 344px floor; a fast fling past the surface edge ends cleanly (re-grab works, nothing sticks - hover after a lost release must not resize)
- Drawer release persistence: releasing the handle writes exactly once (the config file’s mtime moves once per drag); restart the app and the drawer renders the dragged width
- Drawer double-click reset: double-clicking the grip snaps the pane to the default width and removes
settings_drawer_widthfrom the config (back toNone); a plain single click on the grip writes nothing - Drawer drag interactions with drawer state: Escape/Close while mid-drag, and focus loss mid-drag (
hide_on_focus_loss: true) - the drawer state survives per the focus-loss rule, no stuck live width after the surface returns - Drawer padding continuity: drag through 392px and watch for a visible padding jump (the framework pane switches padding below 392) - if jarring, the floor rises to 392 (recorded decision pending this walk)
- Drawer stale-config safety: hand-edit
settings_drawer_width: Some(65000)- the drawer loads clamped to half the surface, never overflows it - New-tab cwd inheritance:
cd /tmpin the active tab, pressCtrl+Shift+T- the new tab’s shell starts in/tmp; with an unreadable/procentry for the shell (e.g. undersudo) the new tab falls back to the default directory, never an error - Home new-tab keybind:
cd /tmpin the active tab, pressCtrl+Shift+H- the new tab’s shell starts in the home directory (not/tmp), activates, and followsnew_tab_after_currentplacement like any new tab; with a default profile whose preset cwd points elsewhere the tab lands in that preset cwd while running the profile’s command (settings adapt - only the inheritance is replaced by home); barehtypes andCtrl+Hbackspaces in the shell - Opening the drawer never hides or resizes the surface; a remote
new-tab/togglearriving while it is open shows the terminal with the drawer still up - Closing the drawer does not exit the app (the toggle still works); reopening it shows current config values, no stale buffered state
- Every control is present exactly once across the drawer’s tabs - Appearance (the Appearance and Window cards), Behavior (the Tabs, Status bar, Dropdown, and Terminal cards), Profiles (the manager), Keybindings (the shortcut editor), and Advanced (the Advanced card); for one field per section, editing it in the drawer persists exactly what a hand-edit of the same value would (compare the config file)
- No Apply button anywhere: toggles/dropdowns write on change, sliders on release, text inputs on Enter
- Keybindings search: typing in the Keybindings tab’s search box narrows the rows live (case-insensitive); a no-match query shows the empty state; the search survives a focus-loss hide and clears on an explicit close
- Rebind round-trip: rebind Copy to
Ctrl+Alt+Cfrom its row (press the binding, press the chord), thenCtrl+Alt+Ccopies on the very next press in the same session; restart the app and it still copies; the row’s undo affordance restoresCtrl+Shift+Cand the next press copies with the default chord - Conflict handling: arm Copy’s capture and press
Ctrl+Shift+V- the inline error names Paste, the config file is unchanged (no write), and the capture is disarmed; re-arming and capturing Copy’s own chord ends the capture silently with no write - Capture consumption: while a capture is armed, every press is consumed - a bound chord (e.g.
Ctrl+Shift+T) neither triggers its action nor reaches the terminal; Escape cancels the capture without closing the drawer, and a second Escape closes the drawer as usual - Multi-chord defaults: uncustomized, zoom-in’s row shows every default chord joined (
Ctrl + + / Ctrl + = / Ctrl + Shift + + / Ctrl + Shift + =); rebinding it to one chord retires all four defaults, and revert restores them; the numbered rows (activate_tab_3) rebind independently; Copy-primary showsNoneand binds from empty - Keybindings file round-trip: hand-edit
custom_key_bindswhile the app runs - the next press uses the new binding; an entry with an unknown id or an empty key is skipped with a warn (defaults keep working), and bareCtrl+Istill reaches the shell as the TAB byte (the default map never binds it) - Controls respect the config ranges (height 10-100, width 30-100, opacity 0-100, scrollback non-negative); garbage scrollback text writes nothing
- Geometry edits from the drawer land at the next show;
layer/keyboard_mode/respect_panelat the next show;shell/args/scrollback_lineson newly spawned tabs only; font/colors/opacity on every tab session_persistence_enabled: falsestops session saves and skips launch restore (fresh single tab);session.ronis left untouched; re-enabling resumes saving over itslide_animation_enabled: falserestores instant show/hide; toggling it mid-animation never aborts the running slide- Race check (newest intent wins): drag the grab bar, release, immediately open settings and drag the height slider - the slider’s value is what lands in the config file, no write/reshape storm
- G3/G4 re-pass after the write-path refactor: drag feel unchanged - live pixel tracking, deadband, single write-back ~400 ms after release, no echo storm
- Default config (toggles
true, drawer never opened): behavior identical to the previous build except the new keybind - Color schemes: the Appearance section’s two dropdowns list all twelve schemes - COSMIC pair first, the rest alphabetically; picking one applies live on every tab, visible in the terminal beneath the open drawer, and the pick persists as the scheme’s display name
- Scheme fidelity: pick every scheme in the dark-mode dropdown (light-mode one mirrors) and compare against its canonical palette - e.g. Dracula bg
#282a36/ fg#f8f8f2, Nord bg#2e3440: cells, cursor, and background render the scheme’s look - Chrome coherence: with several tabs open, the tab bar and grab bar sit on the same background as the cells - the scheme’s own for Dracula & co., the system theme’s for the COSMIC pair
- Per-mode pairing: system dark wears the dark pick, system light the light pick; flipping the system theme switches schemes live (with
Application themeonFollow system) opacitystill multiplies over a scheme-owned background (e.g. 50 over Dracula)- Bogus-name fallback: hand-write a name that isn’t in the registry into
color_scheme_dark, relaunch - the terminal renders the COSMIC dark scheme silently (no error), the dropdown shows no selection, and picking a scheme recovers - Scheme keys never touched: colors, cursor, background, and chrome byte-identical to the previous build (COSMIC pair, system-derived background)
- Application theme: the Appearance section’s dropdown lists exactly Follow system / Dark / Light after the scheme rows, and the selection mirrors the config value (default config → Follow system)
- Pin independence: pin Dark while the system is Light, and Light while it is Dark - scheme, cells, chrome, and the settings drawer all wear the pinned mode
- Frosted-glass independence: with the COSMIC dark theme’s frosted glass enabled (COSMIC Settings → Appearance), changing the Application theme to Dark mid-session keeps the chrome opaque - no see-through/blurred restyle, matching the look a cold start with the same pin produces; the Opacity slider stays the only translucency dial
- Theme-flip repaint lands whole: flip the Application theme back and forth (Dark ↔ Light, plus Follow system) while tabs run — the terminal recolors in one frame with no stale cover: no grayish wash over the editor after choosing Dark, nothing left healing line-by-line on focus (the window-bg fallback must rewalk with the flip, not snapshot the previous theme), and switching between a scheme tab and a plain tab recolors both directions cleanly
- System mode flips both ways under a pin leave the terminal unchanged (still the pinned mode)
- Accent-color edit while pinned with the matching system mode applies to the terminal
- Restore: switching back to Follow system picks the current system mode on the spot, and system flips resume driving the terminal
- Untouched-default config behaves byte-identically to the previous build - no pin is ever issued, system flips drive everything as before
- Settings button: the strip renders in every tab state - with a single tab it carries only the gear button at its right corner, and clicking it opens the settings drawer (the same window
Ctrl+Shift+Iopens); with two or more tabs the button anchors the tab row’s right end - Crossing 1↔2 tabs resizes nothing: the terminal area keeps its size and the strip its height - opening or closing the second tab changes only the tab row itself
- Many-tab squeeze: with more tabs than fit, buttons never run under the gear button - including at the minimum-width floor, where the row clips at the button’s zone
- Gear + tooltip legibility: the gear and its
Settings…tooltip read clearly on light and dark panes; hovering shows the disc and tooltip immediately, with no delay - The tooltip never clips at the right corner, on both
edge = topandedge = bottom- it grows leftward from the corner, clearing the strip toward the terminal - Clicking the settings button during an inline tab rename commits the rename and opens the drawer
- Drag-reordering a tab stays clear of the gear button’s zone
- Close confirmation, all paths: with default config and vim running in the active tab, each of the four+one user close paths -
Ctrl+Shift+W(the context menu’s Close Tab row funnels the same), the tab bar’s×, a middle-click on the tab button, andcosmic-ext-dropterm close-tab- raises the in-surface modal naming the tab by title (never the process name), and nothing else on the surface responds while it waits (typing and pointer presses over the terminal never reach the pty) - Close confirmation, cancel: Cancel - button, scrim press anywhere outside the card, or Escape - dismisses the modal with vim exactly where it was; a follow-up keystroke reaches vim (process alive, screen and selection untouched), and focus returns to the terminal without a click (with the settings drawer open beneath the modal only Escape works - see the parked drawer click-close row)
- Close confirmation, proceed: Close Tab closes through today’s path - the successor activates (a last tab respawns fresh), the session-dirty mark lands, and Close on a modal whose tab was renamed mid-wait still closes that tab
- Close confirmation, idle never asks: a tab at its shell prompt closes instantly on every path, no modal; a backgrounded job (
sleep 300 &) also closes without the modal (foreground-only detection - the accepted gap) - Close confirmation, shell exit never asks:
exit/Ctrl+D in the shell never raises the modal whatever the toggle says, and a pending modal whose tab exits on its own vanishes (no later Close can act on it) - Close confirmation, Escape precedence: with the settings drawer open and a modal pending, Escape cancels the modal only - the drawer stays open beneath, a second Escape closes the drawer as usual; while pending, every other key is swallowed (no binds fire, nothing types)
- Close confirmation, pending survives hide/show: hide and re-show while the modal waits - it returns still naming its tab, and the decision then proceeds normally
- Close confirmation, toggle: the Behavior row “Confirm closing a tab with a running process” flips and round-trips to
close_confirmation_enabledin the config file; off, every close path is instant again (byte-identical to the pre-guardrail build); flipped mid-session, the new value applies to the very next close - Close confirmation, pre-existing config: a config directory from before the key loads with the guardrail on (missing key =
true);close_confirmation_enabled: falseon disk is honored from launch - Close confirmation, regressions: settings still shows every key exactly once (24 exposed), the drawer-resize rows above still pass, and the modal never appears when the toggle is off or no foreground child runs
- Drawer click-close parked (expected no-op): clicking the terminal with the drawer open does nothing, and the Behavior section carries no such toggle row — parked 2026-10-08: while the drawer is open, its
context_draweroverlay captures every mouse event over the surface before the base tree updates, so no in-tree wrapper can observe the press (the parked-key contract is documented in configuration); thesettings_drawer_click_closekey still loads and persists, inert - Close-confirm modal over the open drawer (known limitation, same root cause): run vim, open the settings drawer, press
Ctrl+Shift+W— the modal appears and Escape cancels it (keyboard rides the app subscription), but the modal’s Cancel/Close buttons and scrim press are inert while the drawer is open beneath; with the drawer closed every dismissal path works, so verify cancel/proceed drawer-closed — the combined state stays Escape-only until the drawer-click-close revisit lands - Release tarball walk (v0.4.0): download the x86_64 tarball from the release page, check it with
sha256sum -c sha256sums, untar and copy itsusr/tree over/usr(over the dev install), then restart the session — the overlay toggles via the bound keybind, viacosmic-ext-dropterm togglefrom another terminal, and via the panel applet, andjust checkis green on the released bits; the aarch64 tarball is shape-verified only (binaries, both desktop files, icons, and metainfo present, layout matching x86_64) unless ARM hardware is at hand — walked green on the v0.4.0 tree, 2026-11-09; the keybind clause awaits the maintainer’s Settings-UI step (custom shortcut F12, cosmic-settings#1486 territory), row held open for it - Dev/installed split, coexistence: with an installed release running (autostart or keybind),
just devstarts the dev build without killing it — both overlays answer their own launcher (cosmic-ext-dropterm-devvs the installed name), each keybind toggles only its own instance, andjust toggle/just act …drive the dev one only - Dev/installed split, files: the dev build starts with its own empty config (
~/.config/io.github.l1br3_io.CosmicExtDropterm.Dev.ron) and its own session dir, and writes there only; the installed copy’s config andsession.ronare untouched across a dev cycle (mtimes unchanged) - Dev/installed split, applet:
just dev-appletlists “DropTerm (Dev)” beside any installed applet in COSMIC Settings → Panel; added, it tracks the dev terminal’s running/visible state (kill the dev terminal and only the dev applet’s indicator flips), and with no dev terminal running a click launchescosmic-ext-dropterm-dev(not the installed name);just dev-uninstallremoves the launcher, the dev applet binary, and its desktop entry - Clipboard ownership, unfocused copy (the fix’s core row): with another app focused and DropTerm visible-but-unfocused (summon it from the CLI or applet so no activation token carries), select text and press
Ctrl+Shift+C(or right-click → Copy) —wl-paste(or pasting into the other app) shows the selection, no restart needed; the same copy while the terminal is hidden — an OSC 52 store from a running program,printf '\e]52;c;%s\a' "$(printf hidden | base64)"— lands too, and the log stays free offalling back to the iced clipboardwarnings on either path - Clipboard ownership, paste across a hide/show cycle: copy in any app, hide DropTerm, re-show it, and
Ctrl+Shift+Vpastes the foreign copy; a middle-click pastes the primary selection; with the clipboard genuinely empty both pastes type nothing and no warn fires (an empty clipboard is a state, not a failure) - Clipboard ownership, the band’s copies: with the terminal unfocused, a click on a status-band segment still flashes its
✓andwl-pasteshows the segment’s full value (never the elided display), a drag across the band copies the selected display text to both the standard clipboard and the primary selection (middle-click serves it), and the context menu’s Copy All writes every present segment’s value - Paste-image presentation, the path-only trace: copy a screenshot to the clipboard and paste — the path lands at the prompt as text and nothing renders in the grid (no pixels above the prompt line); pasting text is unchanged; the pasted file exists under
~/.cache/dropterm/paste/ - Paste-image presentation, the panel and its verbs: after a paste, the band’s pasted-images chip appears reading
1 image(pluralized at two) and hovering it pops the tip naming the count and the click; click it and the Pasted images drawer lists the paste newest-first with a thumbnail, the stamp restated for reading (2026-11-09 14:30:25, never the rawpaste-…stem) and the dimensions dimmed beneath, the thumbnail-and-stamp line itself opening the image in the system viewer (the row’s subject is its own Open target); each row’s Open (system viewer, the one standard button), Copy path (wl-pasteshows the path), Copy image (paste into an image consumer shows the image), Re-paste (the path lands at the prompt again) — the three transfer verbs as quiet text buttons — and Delete (destructive-styled, held apart at the row’s far edge, two-step: the press arms the inline ✓/✕ icon pair — tooltips Confirm delete and Keep, both glyphs whole at the drawer’s narrowest — the ✓ removes file, row, and thumbnail together — chip count drops — and the ✕ disarms deleting nothing; arming another row swaps the pair to it; closing and reopening the panel disarms) behave; deleting the last row leaves the empty state with its dimmed hint line, the chip disappears, and the header’s Open Folder action (which opened the cache directory in the file manager while rows stood) goes quiet with it; Escape closes the drawer; a second tab’s pastes stay in its own panel - Paste-image presentation, the doors that never hide: with the band’s width squeezed until the pasted chip drops, the panel still opens from the band’s right-click menu (Pasted Images…, always live — at zero pastes it opens the honest empty state) and from
Ctrl+Shift+P(a repeat press closes the open panel; with the settings drawer open it hands the surface to the panel); bareCtrl+Pand barepstill reach the shell (history navigation, typing), and the Keybindings tab lists “Open the images panel” as bindable like every other action - Paste-image presentation, staleness: with the panel closed, delete a pasted file by hand from
~/.cache/dropterm/paste/— the next panel open shows no row for it and the chip count reflects the loss - Paste-image file-copy road (paste-uri): copy an image file in COSMIC Files — one of each format, PNG, JPEG, GIF, WebP — and paste in DropTerm: the cache copy lands under
~/.cache/dropterm/paste/aspaste-<stamp>-<n>with the format’s own extension (.png/.jpg/.gif/.webp, 0600), the path pastes as text, and the panel shows the row with thumbnail and dimensions, the chip counting it; the same round in Nautilus; a portal screenshot’s URI pastes the same way; the cascade layer alone (no GUI) is repeatable on any COSMIC session viacargo test --bin cosmic-ext-dropterm live_uri_paste_smoke -- --ignored, which serves real offers and asserts the shipped code’s roads — it takes over the system clipboard while it runs - Paste-image file-copy road, what never fires: copy two files at once and paste — today’s text paste, verbatim, no cache file; copy a non-image file and paste — the same text paste; a planted hostile list (
wl-copy -t text/uri-list -- <(echo "file:///etc/passwd")or any tool serving atext/uri-listnaming a non-image local file) pastes no file bytes; a URI naming a FIFO or a directory (ln -s /dev/null-style targets aside, an actualmkfifoor a folder path) pastes without hanging and without opening it; an image file over 32 MiB pastes nothing at all (one warn in the log, no partial cache file); the panel’s Copy image on a JPEG row round-trips — paste into an image consumer shows the JPEG; plain-text pastes and clipboard screenshot pastes are unchanged