feat(cli): --demo demonstration mode flag + app plumbing (#22, ADR-0047 D1)
Add `--demo` (and the RDBMS_PLAYGROUND_DEMO env fallback) to enter demonstration mode, threaded onto App.demo_mode through run_loop — mirrors the --no-undo plumbing. Off by default, zero footprint when off. The --help line advertises only the visible keystroke badges; the Ctrl+] caption trigger is kept low-profile (ADR-0047 D6 updated). Phase A of ADR-0047; behaviour (badges/captions) lands in B and C.
This commit is contained in:
@@ -276,10 +276,15 @@ from `IndicatorDebounce`) pair — not an inconsistency.
|
|||||||
The CLI banner (`help.cli_banner` in `en-US.yaml`) gains a `--demo`
|
The CLI banner (`help.cli_banner` in `en-US.yaml`) gains a `--demo`
|
||||||
line. User-facing wording obeys the house rules (no engine name, no
|
line. User-facing wording obeys the house rules (no engine name, no
|
||||||
"DSL"): *"Demonstration mode — show on-screen badges for otherwise-
|
"DSL"): *"Demonstration mode — show on-screen badges for otherwise-
|
||||||
invisible keys (Tab, Enter, …) and enable scripted step captions, for
|
invisible keys (Tab, Enter, …), for screencasts and live teaching."*
|
||||||
screencasts and live teaching."* Badge labels and the `[…]` chrome are
|
|
||||||
fixed ASCII, not localised; caption content is author-supplied free
|
The help text **deliberately mentions only the visible badges, not the
|
||||||
text and likewise not a catalog string.
|
`Ctrl+]` step-caption mechanism** (user decision): the caption trigger
|
||||||
|
stays low-profile, true to #22's "secret trigger" framing — a cast
|
||||||
|
author or lesson script knows it; a casual `--help` reader is not
|
||||||
|
pointed at it. Badge labels and the `[…]` chrome are fixed ASCII, not
|
||||||
|
localised; caption content is author-supplied free text and likewise
|
||||||
|
not a catalog string.
|
||||||
|
|
||||||
## Alternatives considered
|
## Alternatives considered
|
||||||
|
|
||||||
|
|||||||
+11
@@ -368,6 +368,14 @@ pub struct App {
|
|||||||
/// flag; the `undo` / `redo` commands then report undo is off
|
/// flag; the `undo` / `redo` commands then report undo is off
|
||||||
/// rather than emitting a prepare action.
|
/// rather than emitting a prepare action.
|
||||||
pub undo_enabled: bool,
|
pub undo_enabled: bool,
|
||||||
|
/// Whether **demonstration mode** is active this session (ADR-0047,
|
||||||
|
/// issue #22). `true` under `--demo` / `RDBMS_PLAYGROUND_DEMO`. When
|
||||||
|
/// off (the default) none of the demo key handling or overlay
|
||||||
|
/// rendering runs — zero footprint. When on, otherwise-invisible
|
||||||
|
/// keys raise a transient badge (set by the runtime, see
|
||||||
|
/// `demo_badge`) and `Ctrl+]` drives the stealth step-caption
|
||||||
|
/// buffer (`demo_caption` / `demo_capturing`).
|
||||||
|
pub demo_mode: bool,
|
||||||
/// The DSL → SQL teaching echo (ADR-0038) for the command currently
|
/// The DSL → SQL teaching echo (ADR-0038) for the command currently
|
||||||
/// being rendered: set from the success event just before its handler
|
/// being rendered: set from the success event just before its handler
|
||||||
/// runs, consumed by `note_ok_summary` (which pushes it beneath
|
/// runs, consumed by `note_ok_summary` (which pushes it beneath
|
||||||
@@ -512,6 +520,9 @@ impl App {
|
|||||||
// Undo is on by default; the runtime flips this off for
|
// Undo is on by default; the runtime flips this off for
|
||||||
// a `--no-undo` session (ADR-0006 Amendment 1).
|
// a `--no-undo` session (ADR-0006 Amendment 1).
|
||||||
undo_enabled: true,
|
undo_enabled: true,
|
||||||
|
// Demo mode is off by default; the runtime flips it on for
|
||||||
|
// a `--demo` session (ADR-0047).
|
||||||
|
demo_mode: false,
|
||||||
pending_echo: None,
|
pending_echo: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+77
@@ -42,6 +42,13 @@ pub struct Args {
|
|||||||
/// mode > the default (`simple`). Combines with `--resume` and
|
/// mode > the default (`simple`). Combines with `--resume` and
|
||||||
/// a positional path; on collision the flag wins.
|
/// a positional path; on collision the flag wins.
|
||||||
pub mode: Option<Mode>,
|
pub mode: Option<Mode>,
|
||||||
|
/// `--demo` (or `RDBMS_PLAYGROUND_DEMO` set truthy): enter
|
||||||
|
/// **demonstration mode** (ADR-0047, issue #22). Off by default,
|
||||||
|
/// zero footprint when off. When on, the app shows transient
|
||||||
|
/// on-screen badges for otherwise-invisible keys (Tab, Enter, …)
|
||||||
|
/// and enables the `Ctrl+]` stealth step-caption buffer — for
|
||||||
|
/// screencasts and live teaching. The flag wins over the env var.
|
||||||
|
pub demo: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Usage banner printed by `--help`.
|
/// Usage banner printed by `--help`.
|
||||||
@@ -124,6 +131,12 @@ impl Args {
|
|||||||
let mut help = false;
|
let mut help = false;
|
||||||
let mut no_undo = false;
|
let mut no_undo = false;
|
||||||
let mut mode: Option<Mode> = None;
|
let mut mode: Option<Mode> = None;
|
||||||
|
// Demonstration mode (ADR-0047): the env var is the default,
|
||||||
|
// the `--demo` flag overrides it to on. Mirrors the
|
||||||
|
// env-then-flag layering used for the log file above.
|
||||||
|
let mut demo = env::var("RDBMS_PLAYGROUND_DEMO")
|
||||||
|
.ok()
|
||||||
|
.is_some_and(|v| demo_value_is_truthy(&v));
|
||||||
let mut iter = iter.into_iter().map(Into::into);
|
let mut iter = iter.into_iter().map(Into::into);
|
||||||
while let Some(arg) = iter.next() {
|
while let Some(arg) = iter.next() {
|
||||||
match arg.as_str() {
|
match arg.as_str() {
|
||||||
@@ -136,6 +149,9 @@ impl Args {
|
|||||||
"--no-undo" => {
|
"--no-undo" => {
|
||||||
no_undo = true;
|
no_undo = true;
|
||||||
}
|
}
|
||||||
|
"--demo" => {
|
||||||
|
demo = true;
|
||||||
|
}
|
||||||
"--theme" => {
|
"--theme" => {
|
||||||
let value = iter.next().ok_or(ArgsError::MissingValue("theme"))?;
|
let value = iter.next().ok_or(ArgsError::MissingValue("theme"))?;
|
||||||
theme = match value.as_str() {
|
theme = match value.as_str() {
|
||||||
@@ -194,10 +210,25 @@ impl Args {
|
|||||||
help,
|
help,
|
||||||
no_undo,
|
no_undo,
|
||||||
mode,
|
mode,
|
||||||
|
demo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Whether a `RDBMS_PLAYGROUND_DEMO` value enables demo mode.
|
||||||
|
///
|
||||||
|
/// Truthy for any value except the conventional "off" set
|
||||||
|
/// (`0`/`false`/`no`/`off`, case-insensitively, and the empty
|
||||||
|
/// string). So `RDBMS_PLAYGROUND_DEMO=1` and `=true` enable, while
|
||||||
|
/// `=0` / `=false` explicitly disable — letting a value of `0` turn
|
||||||
|
/// it off even if something upstream exported the variable.
|
||||||
|
fn demo_value_is_truthy(value: &str) -> bool {
|
||||||
|
!matches!(
|
||||||
|
value.trim().to_ascii_lowercase().as_str(),
|
||||||
|
"" | "0" | "false" | "no" | "off"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn default_theme() -> Theme {
|
fn default_theme() -> Theme {
|
||||||
// NFR-7: support both backgrounds. For the walking skeleton we
|
// NFR-7: support both backgrounds. For the walking skeleton we
|
||||||
// honour an explicit `--theme` flag and the COLORFGBG env var
|
// honour an explicit `--theme` flag and the COLORFGBG env var
|
||||||
@@ -391,6 +422,52 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ---- ADR-0047 (issue #22): --demo demonstration mode ----
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn demo_flag_parses() {
|
||||||
|
let args = Args::parse(["--demo"]).unwrap();
|
||||||
|
assert!(args.demo);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn demo_defaults_off() {
|
||||||
|
// Absent `--demo` (and absent env var in the test runner),
|
||||||
|
// demo mode is off — zero footprint for real users.
|
||||||
|
let args = Args::parse(std::iter::empty::<&str>()).unwrap();
|
||||||
|
assert!(!args.demo, "demo is off unless --demo or the env var is given");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn demo_flag_coexists_with_positional_path() {
|
||||||
|
let args = Args::parse(["--demo", "/home/me/MyProject"]).unwrap();
|
||||||
|
assert!(args.demo);
|
||||||
|
assert_eq!(
|
||||||
|
args.project_path.as_deref(),
|
||||||
|
Some(std::path::Path::new("/home/me/MyProject"))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn demo_flag_combines_with_resume_and_mode() {
|
||||||
|
let args = Args::parse(["--resume", "--demo", "--mode", "advanced"]).unwrap();
|
||||||
|
assert!(args.demo);
|
||||||
|
assert!(args.resume);
|
||||||
|
assert_eq!(args.mode, Some(Mode::Advanced));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn demo_env_value_truthiness() {
|
||||||
|
// Enabling values.
|
||||||
|
for v in ["1", "true", "TRUE", "yes", "on", "anything", " 1 "] {
|
||||||
|
assert!(demo_value_is_truthy(v), "{v:?} should enable demo mode");
|
||||||
|
}
|
||||||
|
// Disabling values.
|
||||||
|
for v in ["", " ", "0", "false", "False", "no", "off", "OFF"] {
|
||||||
|
assert!(!demo_value_is_truthy(v), "{v:?} should not enable demo mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unknown_double_dash_flag_errors_even_with_positional() {
|
fn unknown_double_dash_flag_errors_even_with_positional() {
|
||||||
// Make sure the path-vs-flag distinction is robust:
|
// Make sure the path-vs-flag distinction is robust:
|
||||||
|
|||||||
@@ -204,6 +204,9 @@ help:
|
|||||||
project's stored mode. Without it, the
|
project's stored mode. Without it, the
|
||||||
project's last-used mode is restored
|
project's last-used mode is restored
|
||||||
(default: simple).
|
(default: simple).
|
||||||
|
--demo Demonstration mode: show on-screen badges
|
||||||
|
for otherwise-invisible keys (Tab, Enter,
|
||||||
|
...) — for screencasts and live teaching.
|
||||||
|
|
||||||
App-level commands (typed inside the app, available in both modes):
|
App-level commands (typed inside the app, available in both modes):
|
||||||
quit Exit cleanly.
|
quit Exit cleanly.
|
||||||
|
|||||||
@@ -216,6 +216,9 @@ pub async fn run(args: Args) -> Result<()> {
|
|||||||
let db_existed = db_path.exists();
|
let db_existed = db_path.exists();
|
||||||
// Undo is on unless `--no-undo` (ADR-0006 Amendment 1).
|
// Undo is on unless `--no-undo` (ADR-0006 Amendment 1).
|
||||||
let undo_enabled = !args.no_undo;
|
let undo_enabled = !args.no_undo;
|
||||||
|
// Demonstration mode under `--demo` / `RDBMS_PLAYGROUND_DEMO`
|
||||||
|
// (ADR-0047). Off by default; threaded onto the `App` in run_loop.
|
||||||
|
let demo_mode = args.demo;
|
||||||
let database =
|
let database =
|
||||||
Database::open_with_persistence_and_undo(db_path.as_path(), persistence, undo_enabled)
|
Database::open_with_persistence_and_undo(db_path.as_path(), persistence, undo_enabled)
|
||||||
.context("open database")?;
|
.context("open database")?;
|
||||||
@@ -273,6 +276,7 @@ pub async fn run(args: Args) -> Result<()> {
|
|||||||
initial_events,
|
initial_events,
|
||||||
undo_enabled,
|
undo_enabled,
|
||||||
resolved_mode,
|
resolved_mode,
|
||||||
|
demo_mode,
|
||||||
)
|
)
|
||||||
.await;
|
.await;
|
||||||
if let Err(e) = teardown_terminal(&mut terminal) {
|
if let Err(e) = teardown_terminal(&mut terminal) {
|
||||||
@@ -331,6 +335,7 @@ async fn run_loop(
|
|||||||
initial_events: Vec<AppEvent>,
|
initial_events: Vec<AppEvent>,
|
||||||
undo_enabled: bool,
|
undo_enabled: bool,
|
||||||
initial_mode: crate::mode::Mode,
|
initial_mode: crate::mode::Mode,
|
||||||
|
demo_mode: bool,
|
||||||
) -> Result<Option<String>> {
|
) -> Result<Option<String>> {
|
||||||
let (event_tx, mut event_rx) = mpsc::channel::<AppEvent>(EVENT_CHANNEL_CAPACITY);
|
let (event_tx, mut event_rx) = mpsc::channel::<AppEvent>(EVENT_CHANNEL_CAPACITY);
|
||||||
let reader_handle = spawn_event_reader(event_tx.clone());
|
let reader_handle = spawn_event_reader(event_tx.clone());
|
||||||
@@ -339,6 +344,8 @@ async fn run_loop(
|
|||||||
app.project_name = Some(project_display_name);
|
app.project_name = Some(project_display_name);
|
||||||
app.project_is_temp = project_is_temp;
|
app.project_is_temp = project_is_temp;
|
||||||
app.undo_enabled = undo_enabled;
|
app.undo_enabled = undo_enabled;
|
||||||
|
// ADR-0047: enable the demo overlays for this session under `--demo`.
|
||||||
|
app.demo_mode = demo_mode;
|
||||||
// Start in the resolved input mode (ADR-0015 mode-restore
|
// Start in the resolved input mode (ADR-0015 mode-restore
|
||||||
// amendment, issue #14): `--mode` > stored project mode >
|
// amendment, issue #14): `--mode` > stored project mode >
|
||||||
// default. `Persistence` already carries the same value, so the
|
// default. `Persistence` already carries the same value, so the
|
||||||
|
|||||||
Reference in New Issue
Block a user