release: prepare v0.0.1-rc.4

This commit is contained in:
2026-03-07 15:51:21 -05:00
parent 86875075fc
commit da09094d3e
52 changed files with 1076 additions and 1234 deletions

View File

@@ -9,7 +9,7 @@ resolver = "2"
[workspace.package]
edition = "2021"
version = "0.0.1-rc.2"
version = "0.0.1-rc.4"
license = "BSD-3-Clause"
[workspace.dependencies]

View File

@@ -1,135 +1,90 @@
# OpenBitdo SDK
OpenBitdo SDK includes:
- `bitdo_proto`: protocol/transport/session library
- `bitdo_app_core`: shared firmware-first workflow and policy layer
- `bitdo_tui`: Ratatui/Crossterm terminal app
- `openbitdo`: beginner-first launcher (`openbitdo` starts guided TUI)
This workspace contains the OpenBitdo runtime, protocol layer, and release packaging scripts.
## Crates
- `bitdo_proto`: command registry, transport, session, and diagnostics behavior
- `bitdo_app_core`: firmware policy, device workflows, and support-tier gating
- `bitdo_tui`: terminal UI, app state, runtime loop, persistence, and headless API
- `openbitdo`: beginner-first launcher binary
## Build And Test
From `cleanroom/sdk`:
## Build
```bash
cargo build --workspace
```
## Test
```bash
cargo clippy --workspace --all-targets -- -D warnings
cargo test --workspace --all-targets
```
## Guard
```bash
./scripts/cleanroom_guard.sh
```
## Hardware smoke report
```bash
./scripts/run_hardware_smoke.sh
```
## Local Run
## TUI app examples (`openbitdo`)
```bash
cargo run -p openbitdo --
cargo run -p openbitdo -- --mock
```
## CLI surface
- `openbitdo [--mock]`: interactive dashboard flow (mouse-primary, minimal keyboard).
## Interactive behavior (`openbitdo`)
- dashboard starts with:
- searchable device list (left)
- quick actions (center)
- persistent event panel (right)
- primary quick actions:
- `Refresh`
- `Diagnose`
- `Recommended Update`
- `Edit Mapping` (capability-gated)
- `Settings`
- `Quit`
- firmware transfer path:
- preflight generation
- explicit confirm/cancel action
- updating progress and final result screen
- mapping editors are draft-first with:
- apply
- undo
- reset
- restore backup
- recovery lock behavior is preserved when rollback fails.
## Headless library API
- headless automation remains available as a Rust API in `bitdo_tui`:
- `run_headless`
- `RunLaunchOptions`
- `HeadlessOutputMode`
- `openbitdo` CLI does not expose a headless command surface.
## Config schema (v2)
- persisted fields:
- `schema_version`
- `advanced_mode`
- `report_save_mode`
- `device_filter_text`
- `dashboard_layout_mode`
- `last_panel_focus`
- v1 files are read with compatibility defaults and normalized to v2 fields at load time.
`openbitdo` intentionally exposes a single interactive CLI surface.
Headless automation remains available through the Rust API in `bitdo_tui`.
## Packaging
```bash
./scripts/package-linux.sh v0.0.1-rc.2 x86_64
./scripts/package-linux.sh v0.0.1-rc.2 aarch64
./scripts/package-macos.sh v0.0.1-rc.2 arm64 aarch64-apple-darwin
./scripts/package-linux.sh v0.0.0-local x86_64
./scripts/package-linux.sh v0.0.0-local aarch64
./scripts/package-macos.sh v0.0.0-local arm64 aarch64-apple-darwin
```
Packaging outputs use:
- `openbitdo-<version>-<os>-<arch>.tar.gz`
- `openbitdo-<version>-<os>-<arch>` standalone binary
- `.sha256` checksum file for each artifact
- macOS arm64 additionally emits `.pkg` (unsigned/ad-hoc for RC)
Outputs:
## Release Workflow
- CI checks remain in `.github/workflows/ci.yml`.
- Tag-based release workflow is in `.github/workflows/release.yml`.
- Release tags must originate from `main`.
- `v0.0.1-rc.2` style tags publish GitHub pre-releases.
- Release notes are sourced from `/Users/brooklyn/data/8bitdo/cleanroom/CHANGELOG.md`.
- Package-manager publish runs only after release assets are published.
- `openbitdo-<version>-linux-x86_64.tar.gz`
- `openbitdo-<version>-linux-aarch64.tar.gz`
- `openbitdo-<version>-macos-arm64.tar.gz`
- standalone binaries for each packaged target
- `.sha256` files for every artifact
- macOS `.pkg` from `pkgbuild`
## Public RC Gate
- No open GitHub issues with label `release-blocker`.
- Scope-completeness gate:
- JP108 RC scope is dedicated mapping only (`A/B/K1-K8`).
- Ultimate2 RC scope is expanded mapping for required fields only.
- Clean-tree requirement from `/Users/brooklyn/data/8bitdo/cleanroom/RC_CHECKLIST.md` must be satisfied before tagging.
Current macOS packaging remains unsigned and non-notarized by design.
## Distribution Prep
- Homebrew install path for public RC:
- `brew tap bybrooklyn/openbitdo`
- `brew install openbitdo`
- Homebrew Core inclusion is not required for `v0.0.1-rc.2`.
- Homebrew formula scaffold: `/Users/brooklyn/data/8bitdo/cleanroom/packaging/homebrew/Formula/openbitdo.rb`
- Homebrew tap sync script (disabled by default): `/Users/brooklyn/data/8bitdo/cleanroom/packaging/homebrew/sync_tap.sh`
- Tap repository: `bybrooklyn/homebrew-openbitdo`
- AUR package sources:
- `/Users/brooklyn/data/8bitdo/cleanroom/packaging/aur/openbitdo-bin`
- AUR package names:
- `openbitdo-bin`
- Release metadata renderer:
- `/Users/brooklyn/data/8bitdo/cleanroom/packaging/scripts/render_release_metadata.sh`
- AUR publish workflow:
- `/Users/brooklyn/data/8bitdo/cleanroom/.github/workflows/aur-publish.yml`
- gated by `AUR_PUBLISH_ENABLED=1`
- Homebrew publish path:
- `release.yml` renders checksum-pinned formula and runs `sync_tap.sh`
- gated by `HOMEBREW_PUBLISH_ENABLED=1`
- macOS `.pkg` caveat:
- unsigned/ad-hoc is accepted for `v0.0.1-rc.2`
- notarization required for `v0.1.0`
## Release Flow
## CI Gates
- required:
- `guard`
- `test`
- `tui-smoke-test`
- `aur-validate`
- `build-macos-arm64`
1. Tag from `main` using a `v*` tag.
2. `release.yml` verifies CI, secrets, and release blockers.
3. Linux and macOS artifacts are built and uploaded.
4. GitHub prerelease assets are published from those artifacts.
5. AUR and Homebrew metadata are rendered from published release assets.
6. AUR and Homebrew publication run only when their repo-variable gates are enabled.
## Package Manager Publishing
- AUR workflow: `.github/workflows/aur-publish.yml`
- Homebrew workflow: `.github/workflows/homebrew-publish.yml`
- Release metadata renderer: `packaging/scripts/render_release_metadata.sh`
- AUR source of truth:
- tracked package metadata in `packaging/aur/openbitdo-bin`
- template in `packaging/aur/openbitdo-bin/PKGBUILD.tmpl`
- Homebrew source of truth:
- template in `packaging/homebrew/Formula/openbitdo.rb.tmpl`
- published tap repo `bybrooklyn/homebrew-openbitdo`
Current repo-variable contract:
- `AUR_PUBLISH_ENABLED=1`
- `HOMEBREW_PUBLISH_ENABLED=1` when Homebrew publication is enabled
- `HOMEBREW_TAP_REPO=bybrooklyn/homebrew-openbitdo`
Required secrets:
- `AUR_USERNAME`
- `AUR_SSH_PRIVATE_KEY`
- `HOMEBREW_TAP_TOKEN`
## Docs Map
- Public project overview: [`../README.md`](../README.md)
- RC checklist: [`../RC_CHECKLIST.md`](../RC_CHECKLIST.md)
- Process docs: [`../process`](../process)
- Spec docs: [`../spec`](../spec)

View File

@@ -1,23 +1,22 @@
---
source: crates/bitdo_tui/src/tests.rs
assertion_line: 196
expression: rendered
---
╭Session───────────────────────────────────────────────────────────────────────╮
│OpenBitDo Dashboard • 3 devices • reports fail-only • safe │
╰──────────────────────────────────────────────────────────────────────────────╯
╭Search filter────────────────╮╭Device full──────────╮╭Actions Enter/click──╮
│Search active ││Ultimate2 2dc8:5209 ││› Refresh • scan
╰──────────────────────────────╯│Support: supported ││ Diagnose • probe
╭Search filter────────────────╮╭Device supported─────╮╭Actions Enter/click──╮
│Search active ││Ultimate2 2dc8:5209 ││› Refresh • look for
╰──────────────────────────────╯│Support: Supported ││ Diagnose • run saf
╭Controllers detected─────────╮│Protocol: Standard64 ││ Recommended Update │
│› 2dc8:5209 Ultimate2 ││Evidence: Confirmed ││ Edit Mapping • map
full • S64 • conf ││ ││ Settings • prefs
│ 2dc8:6009 Ultimate ││Capabilities ││ Quit • exit
│ro • S64 • infer ││• firmware ││ │
│ 2dc8:901a Candidate ││• profile rw │╰──────────────────────╯
│ro • unknown • untest ││• mode switch │╭Activity events──────╮
│ ││• JP108 mapping ││ │
│ ││• U2 slot + map ││ │
│› 2dc8:5209 Ultimate2 ││Evidence: Confirmed ││ Edit Mapping • cha
supported • standard64 • confi││ ││ Settings • report
│ 2dc8:6009 Ultimate ││Capabilities ││ Quit • close OpenB
│read-only • standard64 • infer││• firmware updates ││ │
│ 2dc8:901a Candidate ││• profile read and wri│╰──────────────────────╯
│read-only • unknown • untested││• mode switching │╭Activity events──────╮
│ ││• JP108 dedicated mapp││ │
│ ││• Ultimate 2 slot and ││ │
│ ││ ││ │
│ ││ ││ │
│ ││ ││ │
@@ -25,5 +24,5 @@ expression: rendered
╰──────────────────────────────╯╰──────────────────────╯╰──────────────────────╯
╭Status────────────────────────────────────────────────────────────────────────╮
│Ready │
│Ultimate2 • click • arrows Enter Esc/q
│Ultimate2 • click a device or action • arrows, Enter, Esc, and q still work
╰──────────────────────────────────────────────────────────────────────────────╯

View File

@@ -1,6 +1,5 @@
---
source: crates/bitdo_tui/src/tests.rs
assertion_line: 317
expression: rendered
---
╭Session───────────────────────────────────────────────────────────────────────────────────────────╮
@@ -8,7 +7,7 @@ expression: rendered
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
╭Diagnostics summary──────────────────────────────────────────────────────────────────────────────╮
│3/5 passed • 2 issues • 2 experimental │
│Tier: full • Family: Standard64 • Transport: ready
│Tier: supported • Family: Standard64 • Transport: ready │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
╭Filter All──╮╭Filter Issu╮╭Filter Exper╮╭Selected Check detail────────────────────────────────╮
@@ -22,7 +21,7 @@ expression: rendered
│ ATTN Version invalid response… ││Validator: test:GetPid │
│ │╰──────────────────────────────────────────────────────╯
│ │╭Next Steps guidance──────────────────────────────────╮
│ ││Action: Return to the dashboard and choose Recomme… │
│ ││Action: Return to the dashboard for update or mapp… │
│ ││Summary: 3/5 checks passed. Experimental checks: 1… │
│ ││Saved report: not yet saved in this screen │
│ ││ │
@@ -31,5 +30,5 @@ expression: rendered
╰──────────────────────────────────────────╯╰──────────────────────────────────────────────────────╯
╭Run Again──────────────────────╮╭Save Report────────────────────╮╭Back───────────────────────────╮
│Run Again ││Save Report ││Back │
rerun safe-read probe ││write support report ││return to dashboard │
│run the safe checks again ││save a shareable support re… ││return to dashboard │
╰───────────────────────────────╯╰───────────────────────────────╯╰───────────────────────────────╯

View File

@@ -1,6 +1,5 @@
---
source: crates/bitdo_tui/src/tests.rs
assertion_line: 327
expression: rendered
---
╭Session───────────────────────────────────────────────────────────────────────╮
@@ -8,7 +7,7 @@ expression: rendered
╰──────────────────────────────────────────────────────────────────────────────╯
╭Diagnostics summary──────────────────────────────────────────────────────────╮
│3/5 passed • 2 issues • 2 experimental │
│Tier: full • Family: Standard64 • Transport: ready
│Tier: supported • Family: Standard64 • Transport: ready │
│ │
╰──────────────────────────────────────────────────────────────────────────────╯
╭Checks tab cycles filter─────────────────────────────────────────────────────╮
@@ -21,9 +20,9 @@ expression: rendered
│Severity: Ok │
╰──────────────────────────────────────────────────────────────────────────────╯
╭Next Steps guidance──────────────────────────────────────────────────────────╮
│Action: Return to the dashboard and choose Recommended Update or Edit Mapp… │
│Action: Return to the dashboard for update or mapping if this device still… │
╰──────────────────────────────────────────────────────────────────────────────╯
╭Run Again───────────────╮╭Save Report──────────────╮╭Back────────────────────╮
│Run Again ││Save Report ││Back │
rerun safe-read probe ││write support report ││return to dashboard │
│run the safe checks … ││save a shareable supp… ││return to dashboard │
╰────────────────────────╯╰─────────────────────────╯╰────────────────────────╯

View File

@@ -1,6 +1,5 @@
---
source: crates/bitdo_tui/src/tests.rs
assertion_line: 343
expression: rendered
---
╭Session───────────────────────────────────────────────────────────────────────────────────────────╮
@@ -8,7 +7,7 @@ expression: rendered
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
╭Diagnostics summary──────────────────────────────────────────────────────────────────────────────╮
│3/5 passed • 2 issues • 2 experimental │
│Tier: full • Family: Standard64 • Transport: ready
│Tier: supported • Family: Standard64 • Transport: ready │
│ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
╭Filter All──╮╭Filter Issu╮╭Filter Exper╮╭Selected Check detail────────────────────────────────╮
@@ -22,7 +21,7 @@ expression: rendered
│ ││Validator: test:Version │
│ │╰──────────────────────────────────────────────────────╯
│ │╭Next Steps guidance──────────────────────────────────╮
│ ││Action: Return to the dashboard and choose Recomme… │
│ ││Action: Return to the dashboard for update or mapp… │
│ ││Summary: 3/5 checks passed. Experimental checks: 1… │
│ ││Saved report: /tmp/openbitdo-diag-report.toml │
│ ││ │
@@ -31,5 +30,5 @@ expression: rendered
╰──────────────────────────────────────────╯╰──────────────────────────────────────────────────────╯
╭Run Again──────────────────────╮╭Save Report────────────────────╮╭Back───────────────────────────╮
│Run Again ││Save Report ││Back │
rerun safe-read probe ││write support report ││return to dashboard │
│run the safe checks again ││save a shareable support re… ││return to dashboard │
╰───────────────────────────────╯╰───────────────────────────────╯╰───────────────────────────────╯

View File

@@ -1,13 +1,12 @@
---
source: crates/bitdo_tui/src/tests.rs
assertion_line: 212
expression: rendered
---
╭Session───────────────────────────────────────────────────────────────────────────────────────────╮
│OpenBitDo Workflow • Ready • reports fail-only • safe │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
Preflight status and intent──────────────────────────────────────────────────────────────────────
Preflight Workflow preflight safety check
Safety Check status and intent───────────────────────────────────────────────────────────────────╮
Safety Check Workflow reviewing update safety
│ │
│Ready to confirm transfer │
│ │
@@ -17,9 +16,9 @@ expression: rendered
│ ││█████ 12% │
│ │╰────────────────────────────────────────╯
│ │╭Context current session────────────────╮
│ ││Stage: preflight safety check
│ ││Current stage: reviewing update safety
│ ││Progress: 12% │
│ ││Reports: failure_only
│ ││Report policy: failure_only │
│ ││Ready │
│ ││ │
│ ││ │
@@ -31,5 +30,5 @@ expression: rendered
╰────────────────────────────────────────────────────────╯╰────────────────────────────────────────╯
╭Confirm────────────────────────╮╭Cancel─────────────────────────╮╭Back───────────────────────────╮
│Confirm ││Cancel ││Back │
│acknowledge risk + start ││stop this workflow ││return to dashboard
│acknowledge risk and start ││stop and discard this step ││leave this screen
╰───────────────────────────────╯╰───────────────────────────────╯╰───────────────────────────────╯

View File

@@ -15,8 +15,12 @@ pub fn render(frame: &mut Frame<'_>, state: &AppState, area: Rect) -> HitMap {
.split(area);
let status_hint = match state.dashboard_layout_mode {
DashboardLayoutMode::Compact => "compact layout • resize for full three-panel view",
DashboardLayoutMode::Wide => "click arrows Enter • Esc/q",
DashboardLayoutMode::Compact => {
"compact layout • resize for three panels or keep using click, arrows, and Enter"
}
DashboardLayoutMode::Wide => {
"click a device or action • arrows, Enter, Esc, and q still work"
}
};
let selected_summary = state
.selected_device()
@@ -94,7 +98,7 @@ fn render_devices(frame: &mut Frame<'_>, state: &AppState, area: Rect, map: &mut
let filter = Paragraph::new(Line::from(vec![
Span::styled("Search ", crate::ui::theme::title_style()),
Span::raw(if filter_label.is_empty() {
"type a model, VID, or PID".to_owned()
"type a model name or USB ID".to_owned()
} else {
filter_label
}),
@@ -194,7 +198,7 @@ fn render_selected_device(frame: &mut Frame<'_>, state: &AppState, area: Rect) {
if device.support_tier != bitdo_proto::SupportTier::Full {
details.push(Line::from(""));
details.push(Line::from(Span::styled(
"Write actions stay blocked until hardware confirmation lands.",
"This device is still read-only here. Safe diagnostics work, but mapping and update stay blocked until hardware confirmation lands.",
crate::ui::theme::warning_style(),
)));
}
@@ -333,17 +337,31 @@ fn truncate_reason(reason: &str) -> String {
fn action_caption(action: crate::app::action::QuickAction) -> &'static str {
match action {
crate::app::action::QuickAction::Refresh => "scan",
crate::app::action::QuickAction::Diagnose => "probe",
crate::app::action::QuickAction::RecommendedUpdate => "safe update",
crate::app::action::QuickAction::EditMappings => "mapping",
crate::app::action::QuickAction::Settings => "prefs",
crate::app::action::QuickAction::Quit => "exit",
crate::app::action::QuickAction::Refresh => "look for connected controllers",
crate::app::action::QuickAction::Diagnose => {
"run safe diagnostics and build a support summary"
}
crate::app::action::QuickAction::RecommendedUpdate => {
"download and stage a verified firmware update"
}
crate::app::action::QuickAction::EditMappings => {
"change supported buttons on supported devices"
}
crate::app::action::QuickAction::Settings => "report saving and interface preferences",
crate::app::action::QuickAction::Quit => "close OpenBitdo",
_ => "available",
}
}
fn support_tier_label(tier: bitdo_proto::SupportTier) -> &'static str {
match tier {
bitdo_proto::SupportTier::Full => "Supported",
bitdo_proto::SupportTier::CandidateReadOnly => "Read-only candidate",
bitdo_proto::SupportTier::DetectOnly => "Detection only",
}
}
fn support_tier_short(tier: bitdo_proto::SupportTier) -> &'static str {
match tier {
bitdo_proto::SupportTier::Full => "supported",
bitdo_proto::SupportTier::CandidateReadOnly => "read-only",
@@ -351,34 +369,26 @@ fn support_tier_label(tier: bitdo_proto::SupportTier) -> &'static str {
}
}
fn support_tier_short(tier: bitdo_proto::SupportTier) -> &'static str {
match tier {
bitdo_proto::SupportTier::Full => "full",
bitdo_proto::SupportTier::CandidateReadOnly => "ro",
bitdo_proto::SupportTier::DetectOnly => "detect",
}
}
fn capability_lines(device: &bitdo_app_core::AppDevice) -> Vec<String> {
let mut lines = Vec::new();
if device.capability.supports_firmware {
lines.push("• firmware".to_owned());
lines.push("• firmware updates".to_owned());
}
if device.capability.supports_profile_rw {
lines.push("• profile rw".to_owned());
lines.push("• profile read and write".to_owned());
}
if device.capability.supports_mode {
lines.push("• mode switch".to_owned());
lines.push("• mode switching".to_owned());
}
if device.capability.supports_jp108_dedicated_map {
lines.push("• JP108 mapping".to_owned());
lines.push("• JP108 dedicated mapping".to_owned());
}
if device.capability.supports_u2_button_map || device.capability.supports_u2_slot_config {
lines.push("• U2 slot + map".to_owned());
lines.push("• Ultimate 2 slot and mapping".to_owned());
}
if lines.is_empty() {
lines.push("• detect only".to_owned());
lines.push("• detection only".to_owned());
}
lines
@@ -396,7 +406,9 @@ fn compact_reason(reason: &str) -> String {
fn protocol_short(protocol: bitdo_proto::ProtocolFamily) -> &'static str {
match protocol {
bitdo_proto::ProtocolFamily::Standard64 => "S64",
bitdo_proto::ProtocolFamily::Standard64 => "standard64",
bitdo_proto::ProtocolFamily::DInput => "dinput",
bitdo_proto::ProtocolFamily::JpHandshake => "jp",
bitdo_proto::ProtocolFamily::Unknown => "unknown",
_ => "other",
}
@@ -404,8 +416,8 @@ fn protocol_short(protocol: bitdo_proto::ProtocolFamily) -> &'static str {
fn evidence_short(evidence: bitdo_proto::SupportEvidence) -> &'static str {
match evidence {
bitdo_proto::SupportEvidence::Confirmed => "conf",
bitdo_proto::SupportEvidence::Inferred => "infer",
bitdo_proto::SupportEvidence::Untested => "untest",
bitdo_proto::SupportEvidence::Confirmed => "confirmed",
bitdo_proto::SupportEvidence::Inferred => "inferred",
bitdo_proto::SupportEvidence::Untested => "untested",
}
}

View File

@@ -467,8 +467,8 @@ fn render_next_steps(frame: &mut Frame<'_>, state: &AppState, area: Rect) {
fn diagnostics_action_caption(action: QuickAction) -> &'static str {
match action {
QuickAction::RunAgain => "rerun safe-read probe",
QuickAction::SaveReport => "write support report",
QuickAction::RunAgain => "run the safe checks again",
QuickAction::SaveReport => "save a shareable support report",
QuickAction::Back => "return to dashboard",
_ => "available",
}
@@ -477,13 +477,13 @@ fn diagnostics_action_caption(action: QuickAction) -> &'static str {
fn recommended_next_action(diagnostics: &crate::app::state::DiagnosticsState) -> &'static str {
match diagnostics.result.support_tier {
bitdo_proto::SupportTier::Full => {
"Return to the dashboard and choose Recommended Update or Edit Mapping if needed."
"Return to the dashboard for update or mapping if this device still needs work."
}
bitdo_proto::SupportTier::CandidateReadOnly => {
"Save or share the report. Update and mapping remain blocked until confirmation lands."
"Save or share the report. Update and mapping stay blocked until this device family is hardware-confirmed."
}
bitdo_proto::SupportTier::DetectOnly => {
"Diagnostics only. Do not attempt update or mapping for this device."
"Use diagnostics only. This device is not ready for update or mapping flows."
}
}
}
@@ -514,8 +514,8 @@ fn severity_style(has_issues: bool) -> Style {
fn support_tier_label(tier: bitdo_proto::SupportTier) -> &'static str {
match tier {
bitdo_proto::SupportTier::Full => "full",
bitdo_proto::SupportTier::CandidateReadOnly => "candidate-readonly",
bitdo_proto::SupportTier::Full => "supported",
bitdo_proto::SupportTier::CandidateReadOnly => "read-only candidate",
bitdo_proto::SupportTier::DetectOnly => "detect-only",
}
}

View File

@@ -24,18 +24,18 @@ pub fn render(frame: &mut Frame<'_>, state: &AppState, area: Rect) -> HitMap {
let adv = Paragraph::new(vec![
Line::from(Span::styled(
if state.advanced_mode {
"Advanced mode is on"
"Advanced controls are on"
} else {
"Advanced mode is off"
"Advanced controls are off"
},
crate::ui::theme::screen_title_style(),
)),
Line::from(Span::styled(
"Toggle to expose expert-only report and workflow options.",
"Turn this on only if you want expert labels and extra workflow options.",
crate::ui::theme::subtle_style(),
)),
])
.block(panel_block("Advanced", Some("press t or click"), true));
.block(panel_block("Advanced", Some("toggle"), true));
frame.render_widget(adv, rows[0]);
let report = Paragraph::new(vec![
@@ -44,11 +44,11 @@ pub fn render(frame: &mut Frame<'_>, state: &AppState, area: Rect) -> HitMap {
crate::ui::theme::screen_title_style(),
)),
Line::from(Span::styled(
"Cycle report persistence policy with r or mouse.",
"Choose whether support reports save automatically after diagnostics or firmware work.",
crate::ui::theme::subtle_style(),
)),
])
.block(panel_block("Reports", Some("press r or click"), true));
.block(panel_block("Reports", Some("save policy"), true));
frame.render_widget(report, rows[1]);
let settings_path = state
@@ -61,11 +61,11 @@ pub fn render(frame: &mut Frame<'_>, state: &AppState, area: Rect) -> HitMap {
Line::from(""),
Line::from(format!("Config path: {settings_path}")),
Line::from(Span::styled(
"Dashboard layout and filter state persist when a settings path is configured.",
"Dashboard layout, filters, and report preferences persist when this path is available.",
crate::ui::theme::subtle_style(),
)),
])
.block(panel_block("Status", Some("saved preferences"), true));
.block(panel_block("Status", Some("persistence"), true));
frame.render_widget(status, rows[2]);
let actions = state
@@ -95,7 +95,7 @@ fn inner_click_rect(rect: Rect) -> Rect {
fn settings_action_caption(action: crate::app::action::QuickAction) -> &'static str {
match action {
crate::app::action::QuickAction::Back => "return to dashboard",
crate::app::action::QuickAction::Quit => "exit openbitdo",
crate::app::action::QuickAction::Quit => "close OpenBitdo",
_ => "available",
}
}

View File

@@ -22,8 +22,8 @@ pub fn render(frame: &mut Frame<'_>, state: &AppState, area: Rect) -> HitMap {
let task = state.task_state.as_ref();
let title = match task.map(|t| t.mode) {
Some(TaskMode::Diagnostics) => "Diagnostics",
Some(TaskMode::Preflight) => "Preflight",
Some(TaskMode::Updating) => "Updating",
Some(TaskMode::Preflight) => "Safety Check",
Some(TaskMode::Updating) => "Update In Progress",
Some(TaskMode::Final) => "Result",
None => "Task",
};
@@ -51,7 +51,7 @@ pub fn render(frame: &mut Frame<'_>, state: &AppState, area: Rect) -> HitMap {
crate::ui::theme::screen_title_style(),
)),
Line::from(""),
Line::from("Select a device action from the dashboard to begin."),
Line::from("Choose a controller action from the dashboard to begin."),
]
};
@@ -96,14 +96,20 @@ fn render_task_details(frame: &mut Frame<'_>, state: &AppState, area: Rect) {
let mut lines = vec![Line::from(task.status.clone())];
if let Some(plan) = task.plan.as_ref() {
lines.push(Line::from(""));
lines.push(Line::from(format!("Session: {:?}", plan.session_id)));
lines.push(Line::from(format!(
"Transfer session: {:?}",
plan.session_id
)));
lines.push(Line::from(format!("Chunk size: {} bytes", plan.chunk_size)));
lines.push(Line::from(format!("Chunks: {}", plan.chunks_total)));
lines.push(Line::from(format!("Estimated: {}s", plan.expected_seconds)));
lines.push(Line::from(format!("Total chunks: {}", plan.chunks_total)));
lines.push(Line::from(format!(
"Estimated transfer time: {}s",
plan.expected_seconds
)));
if !plan.warnings.is_empty() {
lines.push(Line::from(""));
lines.push(Line::from(Span::styled(
"Warnings",
"Safety notes",
crate::ui::theme::warning_style(),
)));
for warning in &plan.warnings {
@@ -145,16 +151,19 @@ fn render_task_details(frame: &mut Frame<'_>, state: &AppState, area: Rect) {
let summary_lines = if let Some(task) = task {
vec![
Line::from(format!("Stage: {}", task_mode_caption(task.mode))),
Line::from(format!("Current stage: {}", task_mode_caption(task.mode))),
Line::from(format!("Progress: {progress}%")),
Line::from(format!("Reports: {}", state.report_save_mode.as_str())),
Line::from(format!(
"Report policy: {}",
state.report_save_mode.as_str()
)),
Line::from(Span::styled(
state.status_line.clone(),
crate::ui::theme::subtle_style(),
)),
]
} else {
vec![Line::from("Select an action to see task details.")]
vec![Line::from("Choose an action to see its workflow details.")]
};
let summary =
Paragraph::new(summary_lines).block(panel_block("Context", Some("current session"), true));
@@ -163,18 +172,18 @@ fn render_task_details(frame: &mut Frame<'_>, state: &AppState, area: Rect) {
fn task_mode_caption(mode: TaskMode) -> &'static str {
match mode {
TaskMode::Diagnostics => "diagnostic probe",
TaskMode::Preflight => "preflight safety check",
TaskMode::Updating => "firmware transfer",
TaskMode::Final => "final outcome",
TaskMode::Diagnostics => "running safe diagnostics",
TaskMode::Preflight => "reviewing update safety",
TaskMode::Updating => "sending verified firmware",
TaskMode::Final => "showing the final result",
}
}
fn task_action_caption(action: crate::app::action::QuickAction) -> &'static str {
match action {
crate::app::action::QuickAction::Confirm => "acknowledge risk + start",
crate::app::action::QuickAction::Cancel => "stop this workflow",
crate::app::action::QuickAction::Back => "return to dashboard",
crate::app::action::QuickAction::Confirm => "acknowledge risk and start the update",
crate::app::action::QuickAction::Cancel => "stop and discard this step",
crate::app::action::QuickAction::Back => "leave this screen",
_ => "available",
}
}

View File

@@ -4,9 +4,25 @@ use bitdo_tui::{run_ui, UiLaunchOptions};
use clap::Parser;
use openbitdo::{load_user_settings, user_settings_path, BuildInfo, UserSettings};
const CLI_AFTER_HELP: &str = "\
Examples:
openbitdo
openbitdo --mock
Install:
Homebrew: brew tap bybrooklyn/openbitdo && brew install openbitdo
AUR: paru -S openbitdo-bin
Releases: download a tarball, then run bin/openbitdo
Notes:
--mock starts the app without real hardware.
macOS packages are currently unsigned and non-notarized.
";
#[derive(Debug, Parser)]
#[command(name = "openbitdo")]
#[command(about = "OpenBitdo beginner-first launcher")]
#[command(about = "Beginner-first 8BitDo controller utility")]
#[command(after_help = CLI_AFTER_HELP)]
struct Cli {
#[arg(long, help = "Use mock transport/devices")]
mock: bool,

View File

@@ -9,8 +9,16 @@ fn help_mentions_single_command_surface() {
.success()
.stdout(predicate::str::contains("Usage: openbitdo [OPTIONS]"))
.stdout(predicate::str::contains("--mock"))
.stdout(predicate::str::contains("Examples:"))
.stdout(predicate::str::contains(
"Homebrew: brew tap bybrooklyn/openbitdo && brew install openbitdo",
))
.stdout(predicate::str::contains(
"macOS packages are currently unsigned and non-notarized.",
))
.stdout(predicate::str::contains("Commands:").not())
.stdout(predicate::str::contains("ui").not())
.stdout(predicate::str::contains("run").not());
.stdout(predicate::str::contains("run [OPTIONS]").not());
}
#[test]

View File

@@ -0,0 +1,26 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
cd "$ROOT"
if rg -n \
--glob '*.md' \
--glob '*.yml' \
--glob '*.sh' \
--glob '*.rb' \
--glob 'PKGBUILD' \
--glob '.SRCINFO' \
-g '!CHANGELOG.md' \
'v0\.0\.1-rc\.1|v0\.0\.1-rc\.2|0\.0\.1-rc\.1|0\.0\.1-rc\.2|0\.0\.1rc1|0\.0\.1rc2' \
.github \
README.md \
MIGRATION.md \
RC_CHECKLIST.md \
packaging \
process \
sdk \
spec; then
echo "stale rc.1/rc.2 references remain outside CHANGELOG.md" >&2
exit 1
fi

View File

@@ -3,7 +3,7 @@ set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO_ROOT="$(cd "$ROOT/.." && pwd)"
VERSION="${1:-v0.0.1-rc.1}"
VERSION="${1:-v0.0.0-local}"
ARCH_LABEL="${2:-$(uname -m)}"
TARGET_TRIPLE="${3:-}"

View File

@@ -3,7 +3,7 @@ set -euo pipefail
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
REPO_ROOT="$(cd "$ROOT/.." && pwd)"
VERSION="${1:-v0.0.1-rc.1}"
VERSION="${1:-v0.0.0-local}"
ARCH_LABEL="${2:-arm64}"
TARGET_TRIPLE="${3:-aarch64-apple-darwin}"
INSTALL_PREFIX="${4:-/opt/homebrew/bin}"