Skip to content

Share the shell/terminal integration as a package (v1.15.0) - #46

Merged
gmpassos merged 1 commit into
masterfrom
feat/shared-package
Jul 14, 2026
Merged

Share the shell/terminal integration as a package (v1.15.0)#46
gmpassos merged 1 commit into
masterfrom
feat/shared-package

Conversation

@gmpassos

Copy link
Copy Markdown
Contributor

Another web app can now embed an OmnyShell terminal — xterm view, PTY bridge, mobile fit engine, accessory bar — by depending on this package, instead of copying the hardest 600 lines in the repo.

The app is unchanged: same screens, same behaviour, same 151 VM + 65 Chrome tests passing, same dart2js size gate.

What was blocking reuse

WebShellHost (475 lines) already had zero app coupling. Three things around it did not:

  • The fit engine was private to a screen. ~140 lines inside SessionViewScreen — fitting cols/rows to the viewport, scaling the font, and holding the prompt above the iOS keyboard via visualViewport. Extracted to TerminalFitter in lib/terminal/terminal_fitter.dart, which the screen now delegates to.
  • Storage keys were hardcoded to omnyshell.. Two omny apps served from the same Hub origin share one localStorage, so the second would clobber the first's theme, hub and token. SettingsStore, NodeCache and CommandHistory now take a prefix, defaulting to omnyshell. — existing keys unchanged.
  • The xterm theme was a private hardcoded literal. XtermTerminalView now takes an XtermTheme.

Public surface

Four barrels, so consumers never deep-import: terminal.dart, ui_kit.dart, client.dart and foundation.dart — the last being the DOM-free half, so a consumer's own logic stays testable on the VM. app/, ui/screens/ and ui/settings_panel.dart stay app-private.

Assets ship in the archive and dart run omnyshell_web:copy_assets copies the xterm bundle and CSS into a consumer's web/. web/styles.css is split into kit.css (tokens, widgets, modal, toasts) and terminal.css (terminal chrome, keyboard insets), so an app can take the terminal without the look.

The README leads with the package recipe and example/ is a standalone terminal in ~40 lines — which also proves the barrels suffice.

omnyserver_web is the first consumer.

🤖 Generated with Claude Code

OmnyShell Web is now a package as well as an app: any Dart web app can embed a
real remote shell — PTY sizing, soft-keyboard insets, line editing, history and
flow control already solved — instead of rebuilding the hard parts. The first
consumer is the OmnyServer fleet dashboard, whose Hub can host an OmnyShell
broker on the same port.

The app itself is unchanged: same screens, same behaviour, same tests, passing
unmodified. What moved is what had to, to make the terminal embeddable.

- Three public barrels: terminal.dart, ui_kit.dart, client.dart (plus
  foundation.dart, the DOM-free half, so a consumer's service and controller
  layers stay unit-testable on the VM instead of needing headless Chrome).
  app/, ui/screens/ and settings_panel stay app-private — exporting them is what
  would let the shared tier re-acquire AppContext coupling.
- TerminalFitter: the sizing engine, lifted out of SessionViewScreen where it was
  ~140 private lines. It is the hardest code here (a terminal that grows but
  never shrinks, renders 0x0, or hides its last line behind a keyboard is what a
  wrong piece of it produces) and no consumer should copy it.
- bin/copy_assets.dart: a package ships its web/ inside the pub archive but pub
  never serves it to a consumer, so without this the terminal loads to a blank
  box and the widgets render unstyled.
- Storage prefixes are parameterized (default unchanged). localStorage is
  per-origin and one Hub can serve two apps, which would otherwise clobber each
  other's theme, Hub and token.
- XtermTheme: xterm paints to a canvas and cannot inherit CSS custom properties,
  so an embedding app must be able to state its colours.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@gmpassos
gmpassos merged commit 4ac8c9f into master Jul 14, 2026
4 checks passed
@gmpassos
gmpassos deleted the feat/shared-package branch July 14, 2026 07:55
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