Skip to content

feat: Add initialCommand parameter to TabbedTerminal - #318

Open
Shivang (kshivang) wants to merge 947 commits into
JetBrains:masterfrom
kshivang:feat/initial-command-support
Open

feat: Add initialCommand parameter to TabbedTerminal#318
Shivang (kshivang) wants to merge 947 commits into
JetBrains:masterfrom
kshivang:feat/initial-command-support

Conversation

@kshivang

Copy link
Copy Markdown

Summary

  • Add initialCommand parameter to TabbedTerminal composable to run a command when terminal starts
  • Pass the initial command through to TabController.createTab()
  • Send the command to PTY after shell is ready (500ms delay)

Use Case

This enables embedding applications (like BOSS) to open a terminal and automatically execute a specific command. For example:

TabbedTerminal(
    onExit = { /* handle exit */ },
    initialCommand = "ls -la"
)

Test plan

  • Create terminal with initialCommand = "echo test" and verify command runs
  • Create terminal without initial command and verify normal behavior
  • Test with long-running commands like top or htop

🤖 Generated with Claude Code

github-actions Bot and others added 30 commits December 7, 2025 03:55
- Update release notes to include Homebrew install instructions
- Add update-homebrew job that auto-updates kshivang/homebrew-bossterm
- Calculates SHA256 and updates Cask formula on each release

Requires HOMEBREW_TAP_TOKEN secret (PAT with repo scope)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Requires approval from @kshivang before release runs.
Only master and dev branches can trigger releases.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: CI/CD, Homebrew tap, and multiple fixes
feat: CI/CD, Homebrew tap, code signing & fixes
- Add vanniktech maven-publish plugin for KMP publishing
- Configure bossterm-core for Maven Central + GitHub Packages
- Configure bossterm-compose for Maven Central + GitHub Packages
- Add publish-libraries job to release workflow
- Libraries will publish on each release

Artifacts:
- ai.rever.bossterm:bossterm-core
- ai.rever.bossterm:bossterm-compose

Requires secrets:
- MAVEN_CENTRAL_USERNAME
- MAVEN_CENTRAL_PASSWORD
- GPG_SIGNING_KEY
- GPG_SIGNING_KEY_ID
- GPG_SIGNING_PASSPHRASE

🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Add vanniktech maven-publish plugin for KMP publishing
- Configure bossterm-core for Maven Central + GitHub Packages
- Configure bossterm-compose for Maven Central + GitHub Packages
- Add publish-libraries job to release workflow
- Libraries will publish on each release

Artifacts:
- ai.rever.bossterm:bossterm-core
- ai.rever.bossterm:bossterm-compose

Requires secrets:
- MAVEN_CENTRAL_USERNAME
- MAVEN_CENTRAL_PASSWORD
- GPG_SIGNING_KEY
- GPG_SIGNING_KEY_ID
- GPG_SIGNING_PASSPHRASE

🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Add Homebrew installation instructions
- Add DMG download section
- Add Maven Central, JitPack, GitHub Packages dependency setup
- Add release and Maven Central badges

🤖 Generated with [Claude Code](https://claude.com/claude-code)
These targets were declared but had no actual source implementations,
causing the CI build to fail during artifact generation.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
compose-ui is Desktop-only. The core library (bossterm-core-mpp)
still supports Android.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Maven Central doesn't support snapshot versions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Allows manual triggering to publish to GitHub Packages, Maven Central, or both.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix: Add GPG signing env vars to GitHub Packages publish step
- Rename parameters to match supertype (isCursorVisible, scrollRegionSize, bracketedPasteModeEnabled)
- Replace Divider with HorizontalDivider
- Replace Icons.Filled.KeyboardArrow* with Icons.AutoMirrored.Filled.*
- Replace deprecated PtyProcess.exec with PtyProcessBuilder

🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Migrate ScrollbarAdapter from deprecated v1 to v2 API
  - TerminalScrollbarAdapter: Use contentSize/viewportSize properties
  - AlwaysVisibleScrollbar: Update to v2 interface with Double types

- Fix parameter naming to match supertype interfaces
  - equals(o: Any?) → equals(other: Any?) in 9 classes
  - sendBytes(bytes:) → sendBytes(response:) in TerminalStarter
  - designateCharacterSet(charset:) → designateCharacterSet(ch:) in BossTerminal
  - resize(columns:) → resize(width:) in BossTerminal.DefaultTabulator
  - subSequence(start:, end:) → subSequence(startIndex:, endIndex:) in CharBuffer

- Remove redundant code
  - Remove redundant else branch in exhaustive when statement (BossTerminal)
  - Remove deprecated resize(Dimension) methods from TtyConnector
  - Remove @deprecated from Questioner interface (actively used)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Shivang (kshivang) and others added 23 commits December 12, 2025 20:44
feat: Linux Distribution Support (Snap, Deb, RPM)
On Linux, the app was always showing version 1.0.0 because there's no
Info.plist like on macOS. The Version.kt code falls back to system
property bossterm.version which was never set.

Add -Dbossterm.version to JVM args in native distribution config so the
correct version is available at runtime on all platforms.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The approval gate should be on the first job (prepare-version) which
handles version bumping and commits, not on build-macos. This ensures
no release actions happen without approval.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix: Pass version to JVM args for Linux runtime detection
Previously, prepare-version computed the version but each build job
recalculated it from VERSION + BUILD_NUMBER. This caused mismatches
when triggered by tags (tag version != run_number).

Now:
- prepare-version computes version once
- All build jobs receive APP_VERSION env var
- Gradle uses APP_VERSION if set, else falls back to local computation
- Ensures artifact names match actual binary versions

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add comprehensive About section as the last tab in settings navigation:
- Application info (version, release channel)
- System info (OS, architecture, Java version/vendor)
- Clickable links (GitHub repo, releases, issues)
- Keyboard shortcuts table with platform-aware keys
- License information (Apache 2.0 + LGPLv3)
- Acknowledgments (JediTerm, Compose Multiplatform, ICU4J, Meslo Nerd Font)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
- Derive release channel from Version.preRelease (shows "beta" for pre-release)
- Show "(dev)" indicator for fallback version 1.0.0
- Add Java Runtime name to system info (identifies JBR, OpenJDK, etc.)
- Log errors when browser opening fails

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Working directory inheritance now works without shell OSC 7 configuration:
- Added getPid() and getWorkingDirectory() to ProcessHandle interface
- Implemented process CWD detection via /proc/<pid>/cwd (Linux) and lsof (macOS)
- TabController.getActiveWorkingDirectory() now falls back to process query
- Split panes also use the fallback mechanism

The setting "Inherit Working Directory" now works immediately without
requiring shell integration setup.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
New tabs now always start in home directory. Only splits inherit working
directory (controlled by splitInheritWorkingDirectory setting).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
feat: Add About BossTerm section to settings
The snapcraft.yaml had a hardcoded version that wasn't being updated
during releases. Added a step to update the version using sed before
building the snap.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
…-reactive

Problem: Cursor state used mutableStateOf() which triggered immediate Compose
recomposition, while requestRedraw() was delayed through debouncing. This caused
two renders per cursor update - one with new cursor + old snapshot (flicker).

Solution: Changed cursor state from reactive mutableStateOf to @volatile plain
variables. Now only redrawTrigger controls recomposition, and cursor is captured
atomically with snapshot in remember() blocks.

Changes:
- ComposeTerminalDisplay.kt: Cursor state now uses @volatile vars with snapshot getters
- ProperTerminal.kt: Cursor captured inside remember(currentTrigger) blocks

Result: Single atomic render per update, no partial state = no flickering.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
… output

Changed ModelListener to use requestRedraw() instead of requestImmediateRedraw()
to enable adaptive debouncing (16ms interactive, 50ms high-volume) for buffer changes.

Also fixed debouncing logic to always wait before rendering, ensuring clear+write
sequences coalesce into a single frame instead of showing intermediate "cleared" state.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Dev to master: TUI flickering fix + snap version update
Allow snap to build for all supported architectures instead of limiting to amd64/arm64.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix: Remove architecture constraints from snapcraft.yaml
Add continue-on-error to snap publish step so builds don't fail
when snap store has pending reviews in queue.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
fix: Make snap publish step non-blocking
- Add VersionSelector.kt with version management UI components
  - Settings-style dropdown to select available versions
  - Pre-release toggle to show/hide beta versions
  - Download progress indicator
  - Downgrade warning dialog with confirmation
  - Platform-specific install instructions dialog

- Extend DesktopUpdateService with:
  - getAllReleases() to fetch all GitHub releases
  - downloadRelease() to download specific version assets

- Update AboutSection to include Version Management section

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Version Selector & Platform Improvements
Add support for passing an initial command to run when a terminal tab
is first created. This enables embedding applications (like BOSS) to
open a terminal and automatically execute a specific command.

Changes:
- Add `initialCommand` parameter to TabbedTerminal composable
- Add `initialCommand` parameter to TabController.createTab()
- Send initial command to PTY after shell is ready (500ms delay)

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@kshivang
Shivang (kshivang) force-pushed the feat/initial-command-support branch from 9a17f3c to 7caa082 Compare December 14, 2025 23:06
Add a configurable setting to run a command automatically when new
terminal tabs are created. This complements the programmatic
initialCommand parameter added in the previous commit.

Changes:
- Add `initialCommand` setting to TerminalSettings (default: empty)
- Add "Initial Command" text field in Behavior > Shell settings
- Wire up setting to all createTab() calls in TabbedTerminal
- Priority: programmatic parameter > setting > none

Use case: Users can configure commands like "neofetch", "cd ~/projects",
or custom startup scripts to run automatically in every new tab.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Add verticalScroll to NavigationRail so categories can be scrolled
when the window is too small to show all items.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The LaunchedEffect that wires up menu callbacks (onSplitVertical,
onSplitHorizontal) only had keys (menuActions, activeTabIndex). It ran
before any tabs existed and returned early. When the first tab was
created, activeTabIndex stayed at 0, so the effect never re-ran to
set up the callbacks.

Added tabController.tabs.size to the LaunchedEffect keys so it re-runs
when tabs are added, ensuring menu actions are properly wired up.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
The initial command was sent after a hardcoded 500ms delay which was a
brittle race condition - too short for slow shells, too long for fast
ones, and varying by machine performance.

Changes:
- Use OSC 133;A (prompt started) signal to detect when shell is ready
- Add configurable fallback delay (initialCommandDelayMs) for shells
  without OSC 133 integration (default: 500ms, range: 100-2000ms)
- Add UI slider in Behavior Settings to configure the fallback delay
- Clean up temporary listener after initial command is sent

With OSC 133 shell integration configured, the initial command is sent
immediately when the prompt appears. Without it, falls back to the
configurable delay.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Updated descriptions for Initial Command and Initial Command Delay
settings to clarify they only affect newly created tabs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
When clicking on a split pane running a mouse-aware TUI app (like nvim),
the click was being forwarded to the app without first focusing the pane.
This made it impossible to switch focus to panes running such apps.

Changes:
- Add onPaneFocus callback to ProperTerminal
- Call onPaneFocus at the start of press handler, before any other
  processing (including mouse event forwarding)
- Remove redundant pointerInput handler from SplitContainer
- Clean up unused imports

Now clicking on any split pane will focus it first, then forward the
mouse event to the terminal application if applicable.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant