Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ concurrency:

jobs:
build:
runs-on: ubuntu-latest
# Match the OS supported by the pinned Playwright version.
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
Expand Down Expand Up @@ -49,9 +50,18 @@ jobs:
- name: Type Check
run: pnpm run check:types

- name: Keyboard contract tests
run: pnpm test:keyboards

- name: Build
run: pnpm run build

- name: Install keyboard test browser
run: pnpm exec playwright install --with-deps chromium

- name: Keyboard browser tests
run: pnpm exec playwright test tests/keyboards --project=chromium

- name: Deploy to Cloudflare Pages
if: github.ref == 'refs/heads/develop' && github.repository == 'connectbot/connectbot.github.io' && github.event_name == 'push'
uses: cloudflare/wrangler-action@953926a2e2182532811c01a25e53647d93bf07c0 # v4.1.3
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Web page for [ConnectBot](https://connectbot.org/), the first open source SSH cl

## Contributor documentation

- [Terminal keyboard editor](docs/keyboard-editor.md): format compatibility, previews, tests, and community layout contributions.

- [Development](docs/development.md): project structure, Git hooks, LAN access, checks, and deployment.
- [Parameterized commands in MDX](docs/parameterized-commands.md): shared inputs, templates, and validation.
- [Authoring feature guides](docs/guide-authoring.md): scenarios, translations, capture, narration, and publication.
Expand Down
67 changes: 67 additions & 0 deletions docs/keyboard-editor.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Terminal keyboard editor

The keyboard library at `/keyboards/` and editor at `/keyboards/editor/` run on
this site's existing static export. Imported files, drafts, and simulated actions
stay in the browser. Community JSON is read from `public/keyboards/` at build time;
see the README there for the pull request workflow.

## Compatibility

The baseline is ConnectBot commit `a754a104`, schema 17. The reference is
`docs/terminal-keyboard.md`, `KeyboardDefaults`, `KeyboardPresets`,
`KeyboardLayers`, `KeyboardRegions`, `KeyboardGrid`, `MacroFormat`, and
`SchemaBasedExporter` in the Android repository. The PNGs under
`app/build/reports/keyboard-presets/` establish preset key order and appearance.

`src/keyboards/model.ts` defines normalized browser types, JSON5 parsing, semantic
validation, and configuration serialization. Wire files use camelCase properties
and integer booleans. Macro documents remain strings, with their independent
version 1 protocol. Unknown row fields and unsupported macro documents survive
import/export. Unsupported keys remain visible but are disabled in test mode.
Malformed structure does not replace the current document; invalid geometry can
be corrected in the inspector or JSON editor and blocks testing/export.

Local drafts and the JSON editor retain unassigned library macros. A downloaded
export contains the selected layout and the closure of its referenced macros.
It never includes host/profile data or keyboard settings. New-copy exports remap
all identities, preserving shared references. Identical repeated downloads reuse
the copy identities until the document or export options change. Preserve UUIDs
is intended for gallery maintenance: ConnectBot skips existing UUIDs, rather than
overwriting them.

## Preview behavior

The shared renderer in `src/components/keyboards/` uses pure geometry calculations
from `src/keyboards/geometry.ts`. All layers and hidden keys reserve geometry.
Pinned regions use independent coordinates; fitted mode appends their columns.
Keys retain row spans when edited, unlike the current Android editor's flattening
behavior. Labels use locally hosted Roboto with its license in `public/fonts/`.

Phone (390×844), tablet (800×1280), and desktop (1440×900) presets describe logical
app and input area dimensions. Orientation sorts/swaps the short and long sides. Custom
sizes range from 240 to 3000 dp. One dp equals one logical CSS pixel before the
whole preview is scaled to fit. The keyboard occupies at most half the console
height remaining above the input method. Phone and tablet previews include a
simulated Google Keyboard (Gboard), with a tablet number row and a shorter
landscape keyboard. The Show Google Keyboard checkbox and test-mode IME toggle
show or hide it. Desktop and custom previews omit it. These preview controls do
not change exported JSON. The simulated keys are decorative; actual Android IME
geometry, device decorations, dynamic colors, and font rendering may differ.

Test mode simulates layers, one-shot/locked modifiers, IME/compose toggles, and
hold-to-repeat. Other app controls are logged. Semantic output is an action log,
not terminal escape encoding or a live shell. Haptic preferences are exported
without vibrating the browser device.

## Checks

- `pnpm check:keyboards`: validate contributed files and built-in templates.
- `pnpm test:keyboards`: Node tests for formats, identity, macros, and geometry.
- `pnpm test:keyboards:browser --project=chromium`: editor/gallery behavior against
the production export. First build with `pnpm build` and install browsers using
`PLAYWRIGHT_BROWSERS_PATH=local/playwright pnpm exec playwright install chromium`.
- `pnpm check:types`, `pnpm lint`, and `pnpm build`: standard repository checks.

The production prebuild validates contributions. CI also runs contract tests and
Chromium browser tests. The Playwright configuration retains all three browser
projects; install their binaries to run the keyboard suite across all engines.
4 changes: 4 additions & 0 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ export default antfu(
],
...playwright.configs['flat/recommended'],
},
{
files: ['tests/keyboards/*.test.ts'],
rules: { 'test/no-import-node-test': 'off' },
},
// --- Custom Rule Overrides ---
{
rules: {
Expand Down
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@
"prune:guides": "python3 scripts/guides/prune.py",
"check:guides": "UV_CACHE_DIR=local/uv-cache uv run --no-project --with-requirements scripts/guides/requirements.lock python scripts/guides/cli.py check",
"test:guides": "UV_CACHE_DIR=local/uv-cache uv run --no-project --with-requirements scripts/guides/requirements.lock python -m unittest discover -s scripts/guides/tests -v",
"test:guides:browser": "UV_CACHE_DIR=local/uv-cache uv run --no-project --with-requirements scripts/guides/requirements.lock python scripts/guides/browser_fixtures.py && PLAYWRIGHT_BROWSERS_PATH=local/playwright playwright test"
"test:guides:browser": "UV_CACHE_DIR=local/uv-cache uv run --no-project --with-requirements scripts/guides/requirements.lock python scripts/guides/browser_fixtures.py && PLAYWRIGHT_BROWSERS_PATH=local/playwright playwright test tests/guides",
"check:keyboards": "node --import tsx scripts/keyboards/check.ts",
"test:keyboards": "node --import tsx tests/keyboards/model.test.ts",
"test:keyboards:browser": "PLAYWRIGHT_BROWSERS_PATH=local/playwright playwright test tests/keyboards",
"prebuild": "pnpm check:keyboards"
},
"dependencies": {
"json5": "^2.2.3",
"next": "^16.2.3",
"next-themes": "^0.4.6",
"nextra": "^4.6.1",
Expand Down Expand Up @@ -51,6 +56,7 @@
"lefthook": "^2.1.5",
"next-sitemap": "^4.2.3",
"pagefind": "^1.5.2",
"tsx": "^4.23.15",
"typescript": "^5.9.3",
"wrangler": "^4.83.0"
}
Expand Down
5 changes: 4 additions & 1 deletion playwright.config.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { defineConfig, devices } from '@playwright/test';

export default defineConfig({
testDir: './tests/guides',
testDir: './tests',
testMatch: '**/*.spec.ts',
fullyParallel: true,
use: { baseURL: 'http://127.0.0.1:3012', contextOptions: { reducedMotion: 'reduce' } },
webServer: {
stdout: 'ignore',
stderr: 'ignore',
command: 'python3 -m http.server 3012 --bind 127.0.0.1 --directory out',
url: 'http://127.0.0.1:3012/guides/',
reuseExistingServer: process.env.CI !== 'true',
Expand Down
Loading