generated from eblume/project-template
feat(heph): accept a1-a4, 1-4, or colour words for -a/--attention
All checks were successful
Build / validate (pull_request) Successful in 8m24s
All checks were successful
Build / validate (pull_request) Successful in 8m24s
The CLI's attention flag (on task/list/attention/edit/promote) now takes the a1–a4 labels, a bare digit 1–4, or a colour word, normalizing to the storage colour before the RPC. Adds Attention::parse_input() in heph-core (lenient human input) alongside the strict storage parse(), with a clear error listing the accepted forms. `heph attention` now echoes the band as `a1 (red)`. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ebb2366236
commit
730863b832
3 changed files with 67 additions and 10 deletions
|
|
@ -118,6 +118,24 @@ impl Attention {
|
|||
Attention::Blue => "a4",
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a *user-facing* attention input: the `a1`..`a4` label, a bare digit
|
||||
/// `1`..`4`, or a colour word (`red`/`orange`/`white`/`blue`). Surfaces
|
||||
/// accept any of these; the colour mapping matches [`Attention::ui_label`].
|
||||
/// Use this for human input; [`Attention::parse`] is the strict storage form.
|
||||
pub fn parse_input(s: &str) -> Result<Attention> {
|
||||
Ok(match s.trim().to_ascii_lowercase().as_str() {
|
||||
"1" | "a1" | "red" => Attention::Red,
|
||||
"2" | "a2" | "orange" => Attention::Orange,
|
||||
"3" | "a3" | "white" => Attention::White,
|
||||
"4" | "a4" | "blue" => Attention::Blue,
|
||||
other => {
|
||||
return Err(Error::Integrity(format!(
|
||||
"unknown attention: {other} (use a1-a4, 1-4, or red/orange/white/blue)"
|
||||
)))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A committed task's lifecycle state (tech-spec §4.3). `done` and `dropped`
|
||||
|
|
@ -410,3 +428,29 @@ impl NewNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn parse_input_accepts_labels_digits_and_colours() {
|
||||
for (inputs, want) in [
|
||||
(["a1", "1", "red"], Attention::Red),
|
||||
(["a2", "2", "orange"], Attention::Orange),
|
||||
(["a3", "3", "white"], Attention::White),
|
||||
(["a4", "4", "blue"], Attention::Blue),
|
||||
] {
|
||||
for s in inputs {
|
||||
assert_eq!(Attention::parse_input(s).unwrap(), want, "input {s:?}");
|
||||
}
|
||||
}
|
||||
// Case-insensitive and whitespace-tolerant.
|
||||
assert_eq!(Attention::parse_input(" A1 ").unwrap(), Attention::Red);
|
||||
assert_eq!(Attention::parse_input("RED").unwrap(), Attention::Red);
|
||||
// The a-label maps to its colour, and round-trips back to the label.
|
||||
assert_eq!(Attention::Red.ui_label(), "a1");
|
||||
assert!(Attention::parse_input("p1").is_err());
|
||||
assert!(Attention::parse_input("5").is_err());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ use anyhow::{bail, Context, Result};
|
|||
use clap::{Parser, Subcommand};
|
||||
use serde_json::{json, Value};
|
||||
|
||||
use heph_core::{Node, RankedTask, Task};
|
||||
use heph_core::{Attention, Node, RankedTask, Task};
|
||||
use hephd::{datespec, default_socket_path, Client, DeviceFlow, KeyringTokenStore, TokenStore};
|
||||
|
||||
mod service;
|
||||
|
|
@ -43,7 +43,7 @@ enum Command {
|
|||
Task {
|
||||
/// The task title.
|
||||
title: String,
|
||||
/// Attention-state: white|orange|red|blue.
|
||||
/// Attention: a1|a2|a3|a4 (or 1-4, or red|orange|white|blue).
|
||||
#[arg(short = 'a', long)]
|
||||
attention: Option<String>,
|
||||
/// Do-date (earliest-actionable): today|tomorrow|+3d|fri|YYYY-MM-DD.
|
||||
|
|
@ -71,7 +71,7 @@ enum Command {
|
|||
/// Restrict to a project by NAME (subtree-expanded). e.g. --project Hephaestus.
|
||||
#[arg(long)]
|
||||
project: Option<String>,
|
||||
/// Only this attention-state: white|orange|red|blue.
|
||||
/// Only this attention: a1|a2|a3|a4 (or 1-4, or red|orange|white|blue).
|
||||
#[arg(short = 'a', long)]
|
||||
attention: Option<String>,
|
||||
/// Hide on-deck (blue) items.
|
||||
|
|
@ -105,7 +105,7 @@ enum Command {
|
|||
Attention {
|
||||
/// Task node id.
|
||||
id: String,
|
||||
/// white|orange|red|blue.
|
||||
/// a1|a2|a3|a4 (or 1-4, or red|orange|white|blue).
|
||||
attention: String,
|
||||
},
|
||||
/// Reschedule a task: change do-date / late-on / recurrence (use `none` to
|
||||
|
|
@ -125,7 +125,7 @@ enum Command {
|
|||
/// A raw RRULE or `none`.
|
||||
#[arg(long)]
|
||||
rrule: Option<String>,
|
||||
/// Set attention: white|orange|red|blue.
|
||||
/// Set attention: a1|a2|a3|a4 (or 1-4, or red|orange|white|blue).
|
||||
#[arg(short = 'a', long)]
|
||||
attention: Option<String>,
|
||||
/// Re-file under a project (by name); `none` unfiles the task.
|
||||
|
|
@ -138,7 +138,7 @@ enum Command {
|
|||
container_id: String,
|
||||
/// 1-based index of the context item to promote (document order).
|
||||
item_ref: usize,
|
||||
/// Attention for the new task: white|orange|red|blue.
|
||||
/// Attention for the new task: a1|a2|a3|a4 (or 1-4, or red|orange|white|blue).
|
||||
#[arg(short = 'a', long)]
|
||||
attention: Option<String>,
|
||||
/// Project name to file the new task under.
|
||||
|
|
@ -489,6 +489,7 @@ fn main() -> Result<()> {
|
|||
recur,
|
||||
rrule,
|
||||
} => {
|
||||
let attention = norm_attention(attention)?;
|
||||
let recurrence = recurrence_value(recur.as_deref(), rrule.as_deref())?;
|
||||
let project_id = resolve_project(&mut client, project.as_deref())?;
|
||||
let result = client.call(
|
||||
|
|
@ -515,6 +516,7 @@ fn main() -> Result<()> {
|
|||
// `list` takes a ListFilter (tech-spec §8.2). Map the flags: a single
|
||||
// `--scope` id or `--project` NAME (resolved + subtree-expanded by the
|
||||
// daemon), a single `--attention` whitelist, and `--no-blue`.
|
||||
let attention = norm_attention(attention)?;
|
||||
let mut filter = json!({});
|
||||
if let Some(s) = scope {
|
||||
filter["scope"] = json!([s]);
|
||||
|
|
@ -558,11 +560,12 @@ fn main() -> Result<()> {
|
|||
println!("Skipped occurrence of {id}");
|
||||
}
|
||||
Command::Attention { id, attention } => {
|
||||
let att = Attention::parse_input(&attention)?;
|
||||
client.call(
|
||||
"task.set_attention",
|
||||
json!({ "id": id, "attention": attention }),
|
||||
json!({ "id": id, "attention": att.as_str() }),
|
||||
)?;
|
||||
println!("{id} attention → {attention}");
|
||||
println!("{id} attention → {} ({})", att.ui_label(), att.as_str());
|
||||
}
|
||||
Command::Edit {
|
||||
id,
|
||||
|
|
@ -588,7 +591,7 @@ fn main() -> Result<()> {
|
|||
if patch.len() > 1 {
|
||||
client.call("task.set_schedule", Value::Object(patch))?;
|
||||
}
|
||||
if let Some(a) = attention {
|
||||
if let Some(a) = norm_attention(attention)? {
|
||||
client.call("task.set_attention", json!({ "id": id, "attention": a }))?;
|
||||
}
|
||||
if let Some(spec) = project.as_deref() {
|
||||
|
|
@ -612,6 +615,7 @@ fn main() -> Result<()> {
|
|||
attention,
|
||||
project,
|
||||
} => {
|
||||
let attention = norm_attention(attention)?;
|
||||
let project_id = resolve_project(&mut client, project.as_deref())?;
|
||||
let result = client.call(
|
||||
"task.promote",
|
||||
|
|
@ -863,6 +867,15 @@ fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
/// Parse an optional human date into epoch-ms JSON (for `task.create`).
|
||||
/// Normalize a user-facing `--attention` value to its storage colour string.
|
||||
/// Accepts the `a1`..`a4` labels, a bare digit `1`..`4`, or a colour word
|
||||
/// (`red`/`orange`/`white`/`blue`). `None` passes through unchanged.
|
||||
fn norm_attention(a: Option<String>) -> Result<Option<String>> {
|
||||
a.map(|s| Attention::parse_input(&s).map(|att| att.as_str().to_string()))
|
||||
.transpose()
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
fn opt_date_ms(spec: Option<&str>) -> Result<Option<i64>> {
|
||||
spec.map(datespec::parse_date_ms).transpose()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Attention is now set directly instead of cycled, and surfaces it as `a1`–`a4` (a1=red, a2=orange, a3=white, a4=blue) rather than the colour words. In heph-tui press `a` then `1`–`4` to set a band (the old `A` cycle and `b` push-to-blue are retired; quick-add moves to `n`); heph-quickadd and the PWA show the same `a1`–`a4` labels, and the PWA's Attn action now pops a band picker. Quick-add inline syntax changes from `p1`–`p4` to `a1`–`a4` across every capture surface. The colour mappings are unchanged.
|
||||
Attention is now set directly instead of cycled, and surfaces it as `a1`–`a4` (a1=red, a2=orange, a3=white, a4=blue) rather than the colour words. In heph-tui press `a` then `1`–`4` to set a band (the old `A` cycle and `b` push-to-blue are retired; quick-add moves to `n`); heph-quickadd and the PWA show the same `a1`–`a4` labels, and the PWA's Attn action now pops a band picker. Quick-add inline syntax changes from `p1`–`p4` to `a1`–`a4` across every capture surface. The `heph` CLI's `-a/--attention` flag now accepts `a1`–`a4`, a bare `1`–`4`, or a colour word (`red`/`orange`/`white`/`blue`). The colour mappings are unchanged.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue