Skip to content

Latest commit

Β 

History

History
134 lines (110 loc) Β· 7.39 KB

File metadata and controls

134 lines (110 loc) Β· 7.39 KB

remobi

Monitor and control your coding agents from your phone. Touch controls for tmux over the web. Published on npm as remobi.

Architecture

Pure TypeScript + DOM API β€” no framework. Transpiles to JS via tsdown for npm distribution. Bundles a browser client via esbuild and serves it from Node.

Stack

  • Node 22+ β€” runtime
  • pnpm β€” package manager
  • esbuild β€” browser client bundle
  • tsdown β€” transpile TS β†’ JS for npm publish
  • vitest β€” test runner
  • TypeScript (strict) β€” no any, discriminated unions for actions
  • Biome β€” lint + format
  • happy-dom β€” DOM testing
  • Hono β€” HTTP + WebSocket server (remobi serve)
  • node-pty β€” PTY bridge for remobi serve
  • xterm.js β€” browser terminal rendering

Key Commands

git config core.hooksPath .hk-hooks  # Run once after clone
pnpm test              # Run all tests
pnpm run test:pw       # Playwright e2e tests (chromium + webkit)
pnpm run check         # Biome lint + format check
pnpm run check:fix     # Auto-fix lint + format
pnpm run build         # Deprecated legacy command
pnpm run build:dist    # Transpile for publishing (tsdown)

Local Development

From source (bundles overlay on the fly, no build step):

tsx cli.ts serve                                # localhost:7681, default tmux session
tsx cli.ts serve --port 8080 -- bash --norc     # custom port, bash instead of tmux

From a local build:

pnpm run build:dist && node dist/cli.mjs serve

Conventional Commits

Commits must follow Conventional Commits format, enforced by hk commit-msg hook.

  • Format: type(scope): description
  • Types: feat, fix, chore, docs, refactor, test, ci, perf, style, build, revert
  • Breaking changes: include a BREAKING CHANGE: footer. ! after type/scope is optional shorthand only and must be paired with the footer because semantic-release major detection relies on the footer.

Choosing the right type matters β€” it controls whether semantic-release publishes to npm:

Type Release When to use
fix patch Bug fix visible to package consumers (runtime behaviour, CLI output, published types)
feat minor New feature visible to consumers
BREAKING CHANGE: footer major Breaking change to public API; ! is optional shorthand but not sufficient on its own in this repo
ci none CI/CD workflow changes (GitHub Actions, release config)
chore none Tooling, deps, repo hygiene β€” anything not shipped to consumers
docs none Documentation only
refactor none Code restructuring with no behaviour change
test none Adding or updating tests

NEVER use fix for non-consumer-facing changes. fix triggers an npm release β€” it means a bug fix visible to package consumers (runtime behaviour, CLI output, published types). If the change only affects CI, dev tooling, tests, or repo internals, use ci, chore, or test instead β€” even if it "fixes" something. When in doubt, ask: "would a consumer notice if this change didn't exist?" If no, it's not fix.

Module Layout

  • src/index.ts β€” entry: waitForTerm then init overlay
  • src/config.ts β€” defaults, defineConfig, deepMerge
  • src/types.ts β€” all shared types
  • src/toolbar/ β€” toolbar DOM + button definitions
  • src/drawer/drawer.ts β€” command drawer with flat grid
  • src/drawer/commands.ts β€” re-exports defaultDrawerButtons from config
  • src/gestures/ β€” swipe, pinch, scroll detection + gesture lock
  • src/controls/ β€” font size, help overlay, combo picker, floating buttons, scroll buttons
  • src/theme/ β€” catppuccin-mocha + apply
  • src/viewport/ β€” height management, landscape detection
  • src/util/dom.ts β€” element creation helpers
  • src/util/terminal.ts β€” sendData, resizeTerm, waitForTerm
  • src/util/haptic.ts β€” vibration feedback
  • src/util/keyboard.ts β€” isKeyboardOpen, conditionalFocus
  • src/util/tap.ts β€” onTap: touch + click handler for iOS Safari compatibility
  • src/util/node-compat.ts β€” sleep, readStdin, spawnProcess, collectStream
  • src/actions/registry.ts β€” action dispatch + clipboard
  • src/hooks/registry.ts β€” lifecycle hook system
  • src/config-schema.ts β€” Valibot validation schemas
  • src/config-resolve.ts β€” button array resolution
  • src/config-validate.ts β€” config assertions
  • src/cli/args.ts β€” CLI argument parsing
  • src/pwa/ β€” PWA manifest, meta-tags, icons
  • src/reconnect.ts β€” connection loss overlay
  • src/overlay-entry.ts β€” IIFE entry point for browser bundle
  • styles/base.css β€” all CSS
  • cli.ts β€” CLI: serve, init, deprecated build/inject, --version
  • build.ts β€” browser client bundling + HTML rendering

Publishing

  • Transpiles to JS via tsdown: bin β†’ dist/cli.mjs, exports β†’ dist/*.mjs + dist/*.d.mts
  • files array controls what's published: dist/, styles/, src/pwa/icons/, README.md, CHANGELOG.md, LICENSE
  • CI: .github/workflows/ci.yml β€” pnpm test + biome check
  • Release: release job in .github/workflows/ci.yml β€” semantic-release on push to main and dev, gated on check job
    • Versioning, changelog, npm publish, and GitHub Release are all automated
    • npx semantic-release --dry-run for local verification
    • Stable channel: main β†’ npm latest
    • Prerelease channel: dev β†’ npm dev + GitHub prereleases
    • Promote experimental releases by merging dev into main
    • Release triggers: feat: β†’ minor, fix: β†’ patch, BREAKING CHANGE β†’ major
    • No release: chore:, docs:, refactor:, test:, ci:
  • See Local Development above for running from source

Conventions

  • Button actions use discriminated unions (type: 'send' | 'ctrl-modifier' | 'paste' | 'combo-picker' | 'drawer-toggle')
  • Unified control schema: use ControlButton for both toolbar and drawer items
  • Config shape: drawer.buttons (not drawer.commands)
  • Config via defineConfig() β€” typed, with sensible defaults
  • Config resolution: --config flag β†’ cwd β†’ ~/.config/remobi/ (XDG fallback)
  • Drawer takes a flat readonly ControlButton[] β€” rendered as a single grid
  • Help overlay is config-driven and must be fail-safe (never break core controls if help fails)
  • Mobile viewport handling: lock document scroll and compute height from visual viewport (keyboard-aware)
  • Changelog and versioning are fully automated by semantic-release β€” do not manually edit CHANGELOG.md. Use conventional commit types to control releases: feat: β†’ minor, fix: β†’ patch, BREAKING CHANGE β†’ major. Non-release types: chore:, docs:, refactor:, test:, ci:
  • All DOM creation in util/dom.ts helpers
  • Keyboard state preserved: capture isKeyboardOpen() before action, use conditionalFocus() after
  • Tests use happy-dom for DOM environment (e2e/CLI tests use node environment)
  • Agent skill: .agents/skills/remobi-setup/SKILL.md provides AI agents with onboarding and config guidance. When config shape, CLI commands, action types, or validation rules change, update the skill to stay in sync.
  • Agent onboarding: when helping a user set up remobi (not develop it), read .agents/skills/remobi-setup/SKILL.md and follow its workflow. Critical: set -g mouse on must be enabled in the user's tmux config for touch scroll to work β€” the skill covers this but agents skipping it is the most common setup failure.