DevelopmentAll versions
On this page

KetraTerm UI Swing (:ketraterm-ui-swing)

SwingTerminal is a reusable Swing JComponent for a TerminalSession. It paints terminal frames and routes user input through the session. The host supplies the connection, session lifetime, and optional clipboard, font, hyperlink, and action services. This module requires JVM 25 and has no PTY or IntelliJ dependency.

How to Use

Create the view on the Swing Event Dispatch Thread (EDT), bind a session, and add the component to your layout. Bind before session startup when output must wait for host registration. Retain the SwingTerminal so the host can dispose it when the view closes. It can also be constructed before the session exists, then bound later.

import io.github.ketraterm.session.TerminalSession
import io.github.ketraterm.ui.swing.api.SwingTerminal
import io.github.ketraterm.ui.swing.settings.SwingSettings
import io.github.ketraterm.ui.swing.settings.TerminalTheme
import io.github.ketraterm.ui.swing.suggestion.SwingShellSuggestionProvider
import java.awt.Font
import javax.swing.SwingUtilities

fun createTerminalView(session: TerminalSession): SwingTerminal {
    return createUnboundTerminalView().apply { bind(session) }
}

fun createUnboundTerminalView(
    provider: SwingShellSuggestionProvider? = null,
): SwingTerminal {
    check(SwingUtilities.isEventDispatchThread()) { "Create terminal views on the EDT" }

    val settings = SwingSettings.create {
        it.palette = TerminalTheme.ONE_DARK.createPalette()
        it.font = Font("Cascadia Mono", Font.PLAIN, 15)
        it.columns = 80
        it.rows = 24
    }

    return SwingTerminal(settingsProvider = { settings }).apply {
        setShellSuggestionProvider(provider)
    }
}

The font name is a host choice; use a font available in your environment. columns and rows set the preferred size. Once laid out, the visible cell dimensions determine the live session's grid and connector size. Settings and session geometry require at least two columns and one row. A narrower component retains that minimum grid and clips painting to its bounds.

Binding and configuration ownership

The host owns the session. unbind() and dispose() cancel view work without closing the session or restoring earlier settings or dimensions. Rebinding clears view state and starts observation of the new session. Dispose the component when its host closes; close the session separately when its connection should end.

Binding applies the view's ambiguous-width policy, palette, cursor shape, paste policy, and visible geometry to the session. Return a new immutable SwingSettings snapshot from settingsProvider, then call reloadSettings() to apply changes. Unchanged palette and cursor settings preserve application-controlled values. Builders are mutable construction drafts; build, create, and copy freeze detached snapshots.

Access Swing state on the EDT unless a method explicitly documents another threading contract. bind, unbind, dispose, and reloadSettings update view state on the EDT and dispatch there when called elsewhere. Captured session settings, geometry, and window state run on an ordered worker so session admission cannot stall the EDT. Paste policy keeps its immediate input-admission behavior. Use bind(session, onComplete) when startup depends on initial settings and geometry; its EDT callback receives null after application or the failure.

Call setHostPolicy(policy, onComplete) on the view to preserve ordering with view settings. resizeSessionToVisibleGrid(onComplete) reconciles externally changed session dimensions with captured component bounds. Both report completion on the EDT; acceptance is not application. Superseded geometry or an obsolete binding reports cancellation. The bounded queue rejects overflow synchronously; retrying reloadSettings with the same snapshot submits changes not yet accepted, including geometry, without resetting already accepted palette/cursor settings. Default completion handlers log operational failures. Session closure remains the host's responsibility and must run off the EDT.

Use one controlling view per session. Settings, dimensions, and the published viewport belong to the session; multiple components cannot scroll or configure it independently. After session closure, the view retains the final grid and selection. Width changes clip rather than reflow it; height changes expose a bottom-anchored window over retained rows. Presentation changes do not restart the session.

search(query) starts a background search; observe searchState for results or failure. Literal, case-insensitive search is the default. Use setSearchCaseSensitive, setSearchWholeWord, and setSearchRegex to change independent options. Each setter restarts a nonempty query; clearSearch() clears the query and highlights while retaining options. Calls execute on the EDT or enqueue there when called from another thread.

Whole-word matching uses Unicode word boundaries. Regex uses Java Pattern syntax and matches one logical line at a time, including soft wraps; it does not cross hard line breaks. Trailing ASCII spaces are trimmed and empty matches are ignored. Invalid patterns appear in searchState.failure. Pattern matching buffers one logical line on the worker and checks cancellation during backtracking. Ordinary literal searches retain their streaming matcher.

Manual paste

pasteText(text) accepts host-supplied text on the EDT without reading a clipboard. For asynchronous clipboard access, capture the intended binding, perform the read in host-owned work, and verify that binding on the EDT before calling pasteText. The host must discard results from an obsolete binding.

pasteClipboardText() reads the supplied clipboard handler synchronously on the EDT. Both paste methods use session policy and admission; true means admitted, not delivered. Empty text, an unavailable session, off-EDT calls, and rejected admission return false.

copyOnSelection copies a nonempty selection at local primary-button gesture completion. It defaults to false and uses the supplied clipboard handler. Application mouse reporting takes priority. Shift restores local interaction unless the application requests Shift capture through XTSHIFTESCAPE. Clipboard callbacks run on the EDT.

Middle-button paste

middleClickPaste defaults to false. When enabled, a local middle-button press uses middleClickPasteSource: SwingPasteSource.CLIPBOARD by default, or PRIMARY_SELECTION for native primary-selection text. The built-in path reads only that source synchronously on the EDT. An unavailable primary selection never falls back to the ordinary clipboard. Keyboard, menu, and programmatic clipboard paste still use the ordinary clipboard.

For asynchronous or custom access, install SwingHostServices.middleClickPasteHandler. It receives the source and local press coordinates on the EDT. Finish once with request.complete(text) or request.cancel() on the EDT; there is no fallback after the handler returns. Completion rejects a closed or changed binding. The host owns cancellation and supersession of its work. See SwingTerminalMiddleClickPasteRequest for completion and lifetime details.

Selection and cell coordinates

selectedText() reads the complete retained selection on the EDT without clipboard access. It includes offscreen and closed-session content, joins soft wraps for linear selections, and preserves block-selection row breaks. It reads current content rather than frozen selection text. null means unavailable; an empty string can represent selected blanks removed by trimming. Clipboard copy uses the same extraction. Use createSelectionRange, setSelection, and clearSelection for host-controlled ranges; see the selection contract.

copyCellPositionAt(x, y, destination) maps component-local pixels to a zero-based logical column and displayed-frame row on the EDT. Invalid hits return false and set the caller-owned Point to (-1, -1). Use copyCellBounds for the reverse mapping. Both account for the displayed grid, including padding, bidi placement, and scrolling.

Cursor presentation

Focused cursors use the application's shape and blink state. Unfocused blocks become steady outlines; bars and underlines remain steady. Application-hidden cursors stay hidden.

Suggestion request ownership

Call setShellSuggestionProvider(provider) on the EDT before or after binding. The provider survives rebinding; replacing it cancels current requests without closing the provider. Passing null disables configured provider requests. The host retains ownership of engines, sources, persistence, and their resources.

The terminal coordinates automatic focus, eligibility, debounce, and session observation. smartSuggestionsEnabled gates all suggestions; shellSuggestionsEnabled gates automatic popups. An explicit requestActiveShellSuggestions() uses the bound session's command source and remains available when automatic popups are disabled. Use refreshShellSuggestions() to reconcile changes to external ranking context.

For command text outside the session, call requestShellSuggestions with its text, UTF-16 cursor offset, and zero-based cell anchor. Its trigger defaults to AUTOMATIC; pass EXPLICIT for an explicit command. The host must replace or hide the request when that external context changes. Supply an explicit editTarget for a custom editor. Normal terminal completion captures editing authority from the bound session, which requires a synchronized revisioned command source such as TerminalShellCommandLineState or the optional OSC producer. A read-only StateFlow model alone cannot admit conditional edits. See the conditional edit contract.

Provider open runs on the EDT before background collection; keep it cheap. Each interaction captures its source and feedback handler and accepts one provider collection. Lifecycle changes invalidate obsolete results and edits.

Integration Host responsibility
Embedded popup Configure a provider; optionally replace the keymap or view factory
Native or detached popup Install SwingShellSuggestionTarget; use its captured interaction and own popup resources
Host-produced results Capture an interaction with beginShellSuggestionInteraction before starting work, then publish on the EDT

setShellSuggestionTarget(null) restores embedded presentation. Use copyCellBounds to anchor a host popup and setShellSuggestionFailureHandler for view-owned diagnostics.

How to Extend: Custom Host Services

SwingHostServices is an immutable snapshot of host-owned services, supplied at construction. create, copy, or builder configure selected services without starting or closing them. Use the public adapters rather than assembling internal painters or input controllers.

Service Purpose
clipboardHandler Synchronous text copy/read and optional primary-selection access
middleClickPasteHandler Host-owned local paste gesture, including deferred clipboard access
hyperlinkHandler / hyperlinkDetector OSC 8 navigation / discovery of links in retained text
fontResolver Host font fallback policy
hostKeyHandler / contextMenuHandler Host keyboard actions / local right-click menus
viewportListener / scrollbarOverlayEnabled External scrollbar integration or the built-in overlay
shellSuggestionKeymap / shellSuggestionViewFactory Suggestion action mapping and embedded presentation
uiDispatcher UI scheduling; component work must still run on the EDT

mouseReportingEnabled controls application mouse reporting. alternateScreenWheelToArrowEnabled independently controls wheel-to-arrow input in the alternate screen. Both default to true; mode 1007 must also be enabled for wheel-to-arrow input. Shift restores local interaction unless the application requests Shift capture. A started button gesture keeps its route until release.

columnSpacing adds signed logical pixels to each cell advance. Negative values condense the grid without scaling the font. Construction and reload reject resolved cell widths below one pixel or overflowing geometry; a rejected reload keeps the previous settings.

SwingHyperlinkDetector.detect is suspending and returns completed, immutable results. Detectors run outside painting and Swing event handling. Choose independent-line or ordered-content context according to the detector's text dependencies. Actions and hover callbacks run on the EDT. OSC 8 links use hyperlinkHandler; detected links supply their own actions.

The hyperlink integration contract describes coordinates, cancellation, ordered replay, configuration changes, and activation. See Module.md for rendering architecture and maintainer guidance.

Sub-Documentation

See Module.md for source layout and verification.