merge: dashboard hint cleanup
This commit is contained in:
commit
5abf16f9e6
|
|
@ -1437,31 +1437,6 @@ impl DashboardApp {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn selected_open_disabled_reason(&self) -> Option<String> {
|
|
||||||
if let Some(row) = self
|
|
||||||
.selected_panel_row()
|
|
||||||
.filter(|row| row.is_ticket_action())
|
|
||||||
{
|
|
||||||
return Some(
|
|
||||||
row.disabled_reason
|
|
||||||
.clone()
|
|
||||||
.or_else(|| row.key_hint.clone())
|
|
||||||
.unwrap_or_else(|| {
|
|
||||||
"Enter dispatches this Ticket action after re-checking current Ticket authority."
|
|
||||||
.to_string()
|
|
||||||
}),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
if let Some(entry) = self.selected_pod_entry() {
|
|
||||||
if entry.actions.can_open {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
return Some(open_disabled_reason(entry));
|
|
||||||
}
|
|
||||||
self.selected_panel_row()
|
|
||||||
.and_then(|row| row.disabled_reason.clone().or_else(|| row.key_hint.clone()))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn select_next(&mut self) {
|
pub(crate) fn select_next(&mut self) {
|
||||||
let visible = visible_panel_keys(&self.panel, &self.list);
|
let visible = visible_panel_keys(&self.panel, &self.list);
|
||||||
if visible.is_empty() {
|
if visible.is_empty() {
|
||||||
|
|
@ -5037,33 +5012,6 @@ fn segments_are_blank(segments: &[Segment]) -> bool {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn open_disabled_reason(entry: &PodListEntry) -> String {
|
|
||||||
if let Some(live) = entry.live.as_ref() {
|
|
||||||
if !live.reachable {
|
|
||||||
return "Selected live Pod is unreachable.".to_string();
|
|
||||||
}
|
|
||||||
return match live.status {
|
|
||||||
Some(PodStatus::Running) => {
|
|
||||||
"Selected Pod is running; Enter opens/attaches for inspection.".to_string()
|
|
||||||
}
|
|
||||||
Some(PodStatus::Paused) => {
|
|
||||||
"Selected Pod is paused; open it explicitly to resume or start a new turn."
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
Some(PodStatus::Idle) => "Selected Pod can be opened/attached.".to_string(),
|
|
||||||
None => "Selected Pod did not report a live status.".to_string(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if entry.stored.is_some() {
|
|
||||||
return "Selected Pod is stopped; Enter restores/opens for inspection.".to_string();
|
|
||||||
}
|
|
||||||
entry
|
|
||||||
.actions
|
|
||||||
.disabled_reason
|
|
||||||
.clone()
|
|
||||||
.unwrap_or_else(|| "Selected Pod cannot be opened from this row.".to_string())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn selected_ticket_notice(row: Option<&PanelRow>) -> String {
|
fn selected_ticket_notice(row: Option<&PanelRow>) -> String {
|
||||||
match row {
|
match row {
|
||||||
Some(row) if row.is_ticket_action() => {
|
Some(row) if row.is_ticket_action() => {
|
||||||
|
|
|
||||||
|
|
@ -57,24 +57,14 @@ pub(super) fn input_area_height(render: &crate::input::InputRender, terminal_hei
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn draw_title(frame: &mut Frame<'_>, app: &DashboardApp, area: Rect) {
|
pub(super) fn draw_title(frame: &mut Frame<'_>, app: &DashboardApp, area: Rect) {
|
||||||
let guidance = if app
|
frame.render_widget(Paragraph::new(title_line(app)), area);
|
||||||
.panel
|
}
|
||||||
.composer
|
|
||||||
.is_available(ComposerTarget::TicketIntake)
|
pub(super) fn title_line(app: &DashboardApp) -> Line<'static> {
|
||||||
{
|
let mut spans = vec![Span::styled(
|
||||||
" Row selection: blank Enter opens/dispatches · text Enter uses target · Tab target"
|
|
||||||
} else if app.panel.header.ticket_configured {
|
|
||||||
" Row selection: blank Enter opens/dispatches · text Enter sends to Companion"
|
|
||||||
} else {
|
|
||||||
" Pod-centric view · Row selection: blank Enter opens · text Enter sends to Companion"
|
|
||||||
};
|
|
||||||
let mut spans = vec![
|
|
||||||
Span::styled(
|
|
||||||
"workspace dashboard",
|
"workspace dashboard",
|
||||||
Style::default().add_modifier(Modifier::BOLD),
|
Style::default().add_modifier(Modifier::BOLD),
|
||||||
),
|
)];
|
||||||
Span::styled(guidance, Style::default().fg(Color::DarkGray)),
|
|
||||||
];
|
|
||||||
if let Some(companion) = &app.panel.header.companion {
|
if let Some(companion) = &app.panel.header.companion {
|
||||||
spans.push(Span::styled(
|
spans.push(Span::styled(
|
||||||
" · companion ",
|
" · companion ",
|
||||||
|
|
@ -101,7 +91,7 @@ pub(super) fn draw_title(frame: &mut Frame<'_>, app: &DashboardApp, area: Rect)
|
||||||
orchestrator_status_style(orchestrator.status),
|
orchestrator_status_style(orchestrator.status),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
frame.render_widget(Paragraph::new(Line::from(spans)), area);
|
Line::from(spans)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn companion_status_style(status: CompanionPanelStatus) -> Style {
|
pub(super) fn companion_status_style(status: CompanionPanelStatus) -> Style {
|
||||||
|
|
@ -688,115 +678,8 @@ pub(super) fn draw_target_status(frame: &mut Frame<'_>, app: &DashboardApp, area
|
||||||
frame.render_widget(Paragraph::new(target_status_line(app)), area);
|
frame.render_widget(Paragraph::new(target_status_line(app)), area);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn target_status_line(app: &DashboardApp) -> Line<'static> {
|
pub(super) fn target_status_line(_app: &DashboardApp) -> Line<'static> {
|
||||||
if !app.composer_is_blank() {
|
Line::from(Span::raw(""))
|
||||||
return Line::from(vec![
|
|
||||||
Span::styled("composer target ", Style::default().fg(Color::DarkGray)),
|
|
||||||
Span::styled(
|
|
||||||
app.composer_target().label(),
|
|
||||||
Style::default()
|
|
||||||
.fg(Color::Green)
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
),
|
|
||||||
Span::styled(" · draft Enter ", Style::default().fg(Color::DarkGray)),
|
|
||||||
Span::styled(
|
|
||||||
composer_enter_status_text(app),
|
|
||||||
Style::default().fg(Color::Green),
|
|
||||||
),
|
|
||||||
Span::styled(
|
|
||||||
" · row selection waits until composer is blank",
|
|
||||||
Style::default().fg(Color::DarkGray),
|
|
||||||
),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(row) = app
|
|
||||||
.selected_panel_row()
|
|
||||||
.filter(|row| row.is_ticket_action())
|
|
||||||
{
|
|
||||||
let action = row
|
|
||||||
.next_action
|
|
||||||
.map(|action| panel_ticket_action_label(row, action))
|
|
||||||
.unwrap_or("View");
|
|
||||||
let mut spans = vec![
|
|
||||||
Span::styled("composer target ", Style::default().fg(Color::DarkGray)),
|
|
||||||
Span::styled(
|
|
||||||
app.composer_target().label(),
|
|
||||||
Style::default()
|
|
||||||
.fg(Color::Magenta)
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
),
|
|
||||||
Span::styled(" · selected Ticket ", Style::default().fg(Color::DarkGray)),
|
|
||||||
Span::styled(row.status.clone(), panel_priority_style(row.priority)),
|
|
||||||
Span::styled(" · blank Enter ", Style::default().fg(Color::DarkGray)),
|
|
||||||
Span::styled(action, Style::default().fg(Color::Magenta)),
|
|
||||||
];
|
|
||||||
if let Some(reason) = panel_ticket_reason(row) {
|
|
||||||
spans.push(Span::styled(" · ", Style::default().fg(Color::DarkGray)));
|
|
||||||
spans.push(Span::styled(
|
|
||||||
truncate_with_ellipsis(reason, 100),
|
|
||||||
ticket_detail_style(row),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
Line::from(spans)
|
|
||||||
} else if let Some(row) = app
|
|
||||||
.selected_panel_row()
|
|
||||||
.filter(|row| row.kind == PanelRowKind::TicketIntakePod)
|
|
||||||
{
|
|
||||||
let ticket_id = panel_ticket_reference(row);
|
|
||||||
let action = if row.next_action == Some(NextUserAction::OpenPod) {
|
|
||||||
"open/attach"
|
|
||||||
} else {
|
|
||||||
"unavailable"
|
|
||||||
};
|
|
||||||
Line::from(vec![
|
|
||||||
Span::styled("composer target ", Style::default().fg(Color::DarkGray)),
|
|
||||||
Span::styled(
|
|
||||||
app.composer_target().label(),
|
|
||||||
Style::default()
|
|
||||||
.fg(Color::Cyan)
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
),
|
|
||||||
Span::styled(
|
|
||||||
" · selected Intake Pod ",
|
|
||||||
Style::default().fg(Color::DarkGray),
|
|
||||||
),
|
|
||||||
Span::styled(row.status.clone(), intake_status_style(&row.status)),
|
|
||||||
Span::styled(
|
|
||||||
format!(" · Ticket {ticket_id} · blank Enter {action}"),
|
|
||||||
Style::default().fg(Color::DarkGray),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
} else if let Some(entry) = app.selected_pod_entry() {
|
|
||||||
let (status, status_style) = row_status_label(entry);
|
|
||||||
Line::from(vec![
|
|
||||||
Span::styled("composer target ", Style::default().fg(Color::DarkGray)),
|
|
||||||
Span::styled(
|
|
||||||
app.composer_target().label(),
|
|
||||||
Style::default()
|
|
||||||
.fg(Color::Green)
|
|
||||||
.add_modifier(Modifier::BOLD),
|
|
||||||
),
|
|
||||||
Span::styled(" · selected Pod ", Style::default().fg(Color::DarkGray)),
|
|
||||||
Span::styled(status.to_string(), status_style),
|
|
||||||
Span::styled(
|
|
||||||
" · blank Enter open/attach",
|
|
||||||
Style::default().fg(Color::DarkGray),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
} else {
|
|
||||||
Line::from(vec![
|
|
||||||
Span::styled("composer target ", Style::default().fg(Color::DarkGray)),
|
|
||||||
Span::styled(
|
|
||||||
app.composer_target().label(),
|
|
||||||
Style::default().fg(Color::DarkGray),
|
|
||||||
),
|
|
||||||
Span::styled(
|
|
||||||
" · no row selected · ↑/↓ selects a row",
|
|
||||||
Style::default().fg(Color::DarkGray),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn draw_input(frame: &mut Frame<'_>, render: &crate::input::InputRender, area: Rect) {
|
pub(super) fn draw_input(frame: &mut Frame<'_>, render: &crate::input::InputRender, area: Rect) {
|
||||||
|
|
@ -817,103 +700,6 @@ pub(super) fn draw_input(frame: &mut Frame<'_>, render: &crate::input::InputRend
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn composer_enter_status_text(app: &DashboardApp) -> String {
|
|
||||||
match app.composer_target() {
|
|
||||||
ComposerTarget::Companion => companion_enter_status_text(app),
|
|
||||||
ComposerTarget::TicketIntake
|
|
||||||
if app.selected_ticket_action() == Some(NextUserAction::Queue) =>
|
|
||||||
{
|
|
||||||
"return selected ready Ticket to planning".to_string()
|
|
||||||
}
|
|
||||||
ComposerTarget::TicketIntake => "launch Intake with composer text".to_string(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn composer_enter_actionbar_text(app: &DashboardApp) -> String {
|
|
||||||
match app.composer_target() {
|
|
||||||
ComposerTarget::Companion => companion_enter_actionbar_text(app),
|
|
||||||
ComposerTarget::TicketIntake if app.selected_ticket_action() == Some(NextUserAction::Queue) => {
|
|
||||||
"Ticket Intake target: Enter records instructions and returns selected ready Ticket to planning".to_string()
|
|
||||||
}
|
|
||||||
ComposerTarget::TicketIntake => {
|
|
||||||
"Ticket Intake target: Enter launches Intake with composer text".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn companion_enter_status_text(app: &DashboardApp) -> String {
|
|
||||||
match companion_send_availability(app) {
|
|
||||||
CompanionSendAvailability::Ready => "send composer text to workspace Companion".to_string(),
|
|
||||||
CompanionSendAvailability::Unavailable(reason) => format!("keep draft; {reason}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn companion_enter_actionbar_text(app: &DashboardApp) -> String {
|
|
||||||
match companion_send_availability(app) {
|
|
||||||
CompanionSendAvailability::Ready => {
|
|
||||||
"Companion target: Enter sends composer text to workspace Companion".to_string()
|
|
||||||
}
|
|
||||||
CompanionSendAvailability::Unavailable(reason) => {
|
|
||||||
format!("Companion target: Enter keeps draft; {reason}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
|
||||||
pub(super) enum CompanionSendAvailability {
|
|
||||||
Ready,
|
|
||||||
Unavailable(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn companion_send_availability(app: &DashboardApp) -> CompanionSendAvailability {
|
|
||||||
let Some(companion) = app.panel.header.companion.as_ref() else {
|
|
||||||
return CompanionSendAvailability::Unavailable(
|
|
||||||
"workspace Companion is unavailable".to_string(),
|
|
||||||
);
|
|
||||||
};
|
|
||||||
if matches!(
|
|
||||||
companion.status,
|
|
||||||
CompanionPanelStatus::Unavailable
|
|
||||||
| CompanionPanelStatus::Missing
|
|
||||||
| CompanionPanelStatus::Stopped
|
|
||||||
) {
|
|
||||||
return CompanionSendAvailability::Unavailable(format!(
|
|
||||||
"workspace Companion is {}",
|
|
||||||
companion.status.label()
|
|
||||||
));
|
|
||||||
}
|
|
||||||
let Some(entry) = app
|
|
||||||
.list
|
|
||||||
.entries
|
|
||||||
.iter()
|
|
||||||
.find(|entry| entry.name == companion.pod_name)
|
|
||||||
else {
|
|
||||||
return CompanionSendAvailability::Unavailable(format!(
|
|
||||||
"workspace Companion `{}` is not in the Pod list",
|
|
||||||
companion.pod_name
|
|
||||||
));
|
|
||||||
};
|
|
||||||
let Some(live) = entry.live.as_ref() else {
|
|
||||||
return CompanionSendAvailability::Unavailable(format!(
|
|
||||||
"workspace Companion `{}` is stopped",
|
|
||||||
companion.pod_name
|
|
||||||
));
|
|
||||||
};
|
|
||||||
if !live.reachable {
|
|
||||||
return CompanionSendAvailability::Unavailable(format!(
|
|
||||||
"workspace Companion `{}` is unreachable",
|
|
||||||
companion.pod_name
|
|
||||||
));
|
|
||||||
}
|
|
||||||
if live.status == Some(PodStatus::Running) {
|
|
||||||
return CompanionSendAvailability::Unavailable(format!(
|
|
||||||
"workspace Companion `{}` is running",
|
|
||||||
companion.pod_name
|
|
||||||
));
|
|
||||||
}
|
|
||||||
CompanionSendAvailability::Ready
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(super) fn actionbar_left_text(app: &DashboardApp) -> String {
|
pub(super) fn actionbar_left_text(app: &DashboardApp) -> String {
|
||||||
if app.sending && app.composer_target() == ComposerTarget::TicketIntake {
|
if app.sending && app.composer_target() == ComposerTarget::TicketIntake {
|
||||||
"launching Ticket Intake…".to_string()
|
"launching Ticket Intake…".to_string()
|
||||||
|
|
@ -927,52 +713,20 @@ pub(super) fn actionbar_left_text(app: &DashboardApp) -> String {
|
||||||
Some(notice) => format!("{notice} Refreshing workspace…"),
|
Some(notice) => format!("{notice} Refreshing workspace…"),
|
||||||
None => "Refreshing workspace…".to_string(),
|
None => "Refreshing workspace…".to_string(),
|
||||||
}
|
}
|
||||||
} else if !app.composer_is_blank() {
|
|
||||||
composer_enter_actionbar_text(app)
|
|
||||||
} else if let Some(notice) = app.notice.as_deref() {
|
} else if let Some(notice) = app.notice.as_deref() {
|
||||||
notice.to_string()
|
notice.to_string()
|
||||||
} else if let Some(reason) = app.selected_open_disabled_reason() {
|
|
||||||
reason
|
|
||||||
} else {
|
} else {
|
||||||
match app.composer_target() {
|
String::new()
|
||||||
ComposerTarget::Companion => {
|
|
||||||
"Composer target: Companion; type text to send, or use ↑/↓ then blank Enter for rows"
|
|
||||||
.to_string()
|
|
||||||
}
|
|
||||||
ComposerTarget::TicketIntake => {
|
|
||||||
if app.selected_ticket_action() == Some(NextUserAction::Queue) {
|
|
||||||
"Composer target: Ticket Intake; text + Enter returns selected ready Ticket to planning".to_string()
|
|
||||||
} else {
|
|
||||||
"Composer target: Ticket Intake; type a request, then Enter launches Intake".to_string()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn actionbar_right_text(app: &DashboardApp) -> &'static str {
|
pub(super) fn actionbar_right_text(app: &DashboardApp) -> &'static str {
|
||||||
if app.panel_diagnostic_open {
|
if app.panel_diagnostic_open {
|
||||||
"F2/Esc close details Ctrl+C quit"
|
"F2/Esc close details"
|
||||||
} else if app.panel_diagnostic.is_some() {
|
} else if app.panel_diagnostic.is_some() {
|
||||||
"F2 details ↑/↓ select row Enter selected row Tab target Esc clear selection Left/Right cursor Ctrl+C quit"
|
"F2 details"
|
||||||
} else if !app.composer_is_blank() {
|
|
||||||
if app
|
|
||||||
.panel
|
|
||||||
.composer
|
|
||||||
.is_available(ComposerTarget::TicketIntake)
|
|
||||||
{
|
|
||||||
"↑/↓ draft lines Left/Right cursor Enter composer target Tab target Esc clear selection Ctrl+C quit"
|
|
||||||
} else {
|
} else {
|
||||||
"↑/↓ draft lines Left/Right cursor Enter composer target Esc clear selection Ctrl+C quit"
|
""
|
||||||
}
|
|
||||||
} else if app
|
|
||||||
.panel
|
|
||||||
.composer
|
|
||||||
.is_available(ComposerTarget::TicketIntake)
|
|
||||||
{
|
|
||||||
"↑/↓ select row Enter selected row Tab target Esc clear selection Left/Right cursor Ctrl+C quit"
|
|
||||||
} else {
|
|
||||||
"↑/↓ select row Enter selected row Esc clear selection Left/Right cursor Ctrl+C quit"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1072,7 +1072,9 @@ fn mouse_click_selects_panel_row_for_blank_enter_action() {
|
||||||
);
|
);
|
||||||
assert_eq!(app.selected_panel_row().unwrap().title, "Queued");
|
assert_eq!(app.selected_panel_row().unwrap().title, "Queued");
|
||||||
assert_eq!(app.selected_ticket_action(), Some(NextUserAction::Wait));
|
assert_eq!(app.selected_ticket_action(), Some(NextUserAction::Wait));
|
||||||
assert!(plain_line(&target_status_line(&app)).contains("blank Enter Wait"));
|
let selected_title =
|
||||||
|
plain_line(&panel_row_lines(app.selected_panel_row().unwrap(), true, 80)[0]);
|
||||||
|
assert!(selected_title.starts_with("▶ queued"));
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
app.handle_key(key(KeyCode::Enter)),
|
app.handle_key(key(KeyCode::Enter)),
|
||||||
DashboardAction::DispatchTicketAction(request) if request.ticket_id == "TICKET-2"
|
DashboardAction::DispatchTicketAction(request) if request.ticket_id == "TICKET-2"
|
||||||
|
|
@ -1152,7 +1154,7 @@ fn mouse_click_does_not_override_existing_composer_keyboard_behavior() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn selected_ticket_row_with_non_empty_composer_shows_composer_enter_behavior() {
|
fn selected_ticket_row_with_non_empty_composer_hides_redundant_status_hints() {
|
||||||
let mut panel = WorkspacePanelViewModel::empty(Path::new("test"));
|
let mut panel = WorkspacePanelViewModel::empty(Path::new("test"));
|
||||||
panel.header.companion = Some(CompanionPanelState::new(
|
panel.header.companion = Some(CompanionPanelState::new(
|
||||||
"yoi",
|
"yoi",
|
||||||
|
|
@ -1185,14 +1187,12 @@ fn selected_ticket_row_with_non_empty_composer_shows_composer_enter_behavior() {
|
||||||
let actionbar_right = actionbar_right_text(&app);
|
let actionbar_right = actionbar_right_text(&app);
|
||||||
let target_status = plain_line(&target_status_line(&app));
|
let target_status = plain_line(&target_status_line(&app));
|
||||||
|
|
||||||
assert!(actionbar_left.contains("Companion target: Enter sends composer text"));
|
assert_eq!(actionbar_left, "");
|
||||||
assert!(actionbar_right.contains("Enter composer target"));
|
assert_eq!(actionbar_right, "");
|
||||||
assert!(!actionbar_left.contains("Queue"));
|
assert_eq!(target_status, "");
|
||||||
assert!(!actionbar_right.contains("selected row"));
|
let selected_title =
|
||||||
assert!(target_status.contains("composer target Companion"));
|
plain_line(&panel_row_lines(app.selected_panel_row().unwrap(), true, 80)[0]);
|
||||||
assert!(target_status.contains("draft Enter send composer text to workspace Companion"));
|
assert!(selected_title.starts_with("▶ ready"));
|
||||||
assert!(target_status.contains("row selection waits until composer is blank"));
|
|
||||||
assert!(!target_status.contains("blank Enter Queue"));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1529,7 +1529,6 @@ fn dashboard_idle_live_selected_target_is_open_eligible() {
|
||||||
let app = test_app(vec![live_info("idle", PodStatus::Idle)]);
|
let app = test_app(vec![live_info("idle", PodStatus::Idle)]);
|
||||||
|
|
||||||
assert_eq!(app.selected_open_eligibility(), OpenEligibility::OpenNow);
|
assert_eq!(app.selected_open_eligibility(), OpenEligibility::OpenNow);
|
||||||
assert!(app.selected_open_disabled_reason().is_none());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1557,6 +1556,35 @@ fn dashboard_status_labels_preserve_explicit_live_statuses() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dashboard_title_omits_redundant_key_hint_guidance() {
|
||||||
|
let mut panel = WorkspacePanelViewModel::empty(Path::new("test"));
|
||||||
|
panel.header.ticket_configured = true;
|
||||||
|
panel.header.companion = Some(CompanionPanelState::new(
|
||||||
|
"yoi",
|
||||||
|
CompanionPanelStatus::Live,
|
||||||
|
Some("idle".to_string()),
|
||||||
|
));
|
||||||
|
let app = app_with_panel(
|
||||||
|
PodList::from_sources(
|
||||||
|
PodVisibilitySource::ResumePicker,
|
||||||
|
vec![],
|
||||||
|
vec![live_info("yoi", PodStatus::Idle)],
|
||||||
|
None,
|
||||||
|
10,
|
||||||
|
),
|
||||||
|
panel,
|
||||||
|
);
|
||||||
|
|
||||||
|
let title = plain_line(&title_line(&app));
|
||||||
|
|
||||||
|
assert!(title.contains("workspace dashboard"));
|
||||||
|
assert!(title.contains("companion live"));
|
||||||
|
assert!(!title.contains("Row selection"));
|
||||||
|
assert!(!title.contains("blank Enter"));
|
||||||
|
assert!(!title.contains("Tab target"));
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn panel_ticket_rows_render_state_title_then_detail_line() {
|
fn panel_ticket_rows_render_state_title_then_detail_line() {
|
||||||
let row = panel_test_ticket_row(
|
let row = panel_test_ticket_row(
|
||||||
|
|
@ -1725,7 +1753,7 @@ fn panel_ticket_intake_child_rows_render_as_indented_single_line() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn selected_ticket_intake_child_status_is_not_rendered_as_generic_ticket_or_pod() {
|
fn selected_ticket_intake_child_keeps_row_marker_without_status_line() {
|
||||||
let ticket_id = "00001TICKET";
|
let ticket_id = "00001TICKET";
|
||||||
let pod_name = "intake-live";
|
let pod_name = "intake-live";
|
||||||
let mut panel = WorkspacePanelViewModel::empty(Path::new("test"));
|
let mut panel = WorkspacePanelViewModel::empty(Path::new("test"));
|
||||||
|
|
@ -1750,11 +1778,11 @@ fn selected_ticket_intake_child_status_is_not_rendered_as_generic_ticket_or_pod(
|
||||||
|
|
||||||
let status = plain_line(&target_status_line(&app));
|
let status = plain_line(&target_status_line(&app));
|
||||||
|
|
||||||
assert!(status.contains("selected Intake Pod live"));
|
assert_eq!(status, "");
|
||||||
assert!(status.contains("Ticket 00001TICKET"));
|
|
||||||
assert!(status.contains("blank Enter open/attach"));
|
let title_line = plain_line(&panel_row_lines(app.selected_panel_row().unwrap(), true, 80)[0]);
|
||||||
assert!(!status.contains("selected Ticket"));
|
assert!(title_line.starts_with(" ▶ live"));
|
||||||
assert!(!status.contains("selected Pod live"));
|
assert!(title_line.contains("Intake"));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
@ -1828,15 +1856,12 @@ fn dashboard_running_paused_and_stopped_targets_are_open_eligible() {
|
||||||
app.ensure_selection_visible();
|
app.ensure_selection_visible();
|
||||||
|
|
||||||
assert_eq!(app.selected_open_eligibility(), OpenEligibility::OpenNow);
|
assert_eq!(app.selected_open_eligibility(), OpenEligibility::OpenNow);
|
||||||
assert!(app.selected_open_disabled_reason().is_none());
|
|
||||||
app.select_next();
|
app.select_next();
|
||||||
assert_eq!(app.list.selected_entry().unwrap().name, "paused");
|
assert_eq!(app.list.selected_entry().unwrap().name, "paused");
|
||||||
assert_eq!(app.selected_open_eligibility(), OpenEligibility::OpenNow);
|
assert_eq!(app.selected_open_eligibility(), OpenEligibility::OpenNow);
|
||||||
assert!(app.selected_open_disabled_reason().is_none());
|
|
||||||
app.select_next();
|
app.select_next();
|
||||||
assert_eq!(app.list.selected_entry().unwrap().name, "stopped");
|
assert_eq!(app.list.selected_entry().unwrap().name, "stopped");
|
||||||
assert_eq!(app.selected_open_eligibility(), OpenEligibility::OpenNow);
|
assert_eq!(app.selected_open_eligibility(), OpenEligibility::OpenNow);
|
||||||
assert!(app.selected_open_disabled_reason().is_none());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user