tawasuyu

tawasuyu/brahman

Rust 521TOML 202Markdown 57JSON 197505 Definitions

feat(shuma-shell): etapas de pipe en la card + sin truncar la salida

- La salida de un comando ya no se trunca: si hay contenido, se
  muestra entero (fuera el «N líneas antes»).
- Las cards de un pipe muestran una fila de etapas: un clic re-ejecuta
  la línea hasta esa etapa como un comando nuevo, así se inspeccionan
  los resultados intermedios. Eficiente — sólo corre lo que pedís, sin
  bufferizar intermedios ni cambiar el modelo.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

0740d2e2aff3062f78860b9a8914595e48af2b35 · by sergio · (5 months ago) · parent 9d8f45a9f · browse the tree at this commit

Semantic delta · 2

crates/apps/shuma-shell/src/main.rs

1 files · +53 −13

diff --git a/crates/apps/shuma-shell/src/main.rs b/crates/apps/shuma-shell/src/main.rs
index 4c18fc0..901c0d6 100644
--- a/crates/apps/shuma-shell/src/main.rs
+++ b/crates/apps/shuma-shell/src/main.rs
@@ -36,9 +36,6 @@ use shuma_sysmon::{Snapshot, SystemSampler};
 
 /// Cuántas muestras guarda la curva de cada monitor.
 const HISTORY: usize = 80;
-/// Líneas de salida visibles por comando (modo launcher liviano).
-const OUTPUT_LINES: usize = 16;
-
 /// Archivos/directorios que delatan la estructura de un proyecto.
 const PROJECT_MARKERS: &[&str] = &[
     ".git",
@@ -885,6 +882,57 @@ fn render_run(
     // Cuerpo: sólo con el acordeón abierto. El filtro elige el flujo.
     let mut body: Vec<gpui::Div> = Vec::new();
     if !ui.collapsed {
+        // Etapas del pipe: un clic re-ejecuta la línea hasta esa etapa,
+        // como un comando nuevo — así se inspeccionan los intermedios.
+        if r.line.contains('|') {
+            let toks = shuma_line::tokenize(&r.line, shuma_line::Dialect::Bash);
+            let pipe = shuma_line::split_pipeline(&toks);
+            if pipe.stages.len() >= 2 {
+                let chip_bg = gpui::hsla(220.0 / 360.0, 0.18, 0.24, 1.0);
+                let accent = gpui::hsla(190.0 / 360.0, 0.62, 0.62, 1.0);
+                let mut chips: Vec<gpui::AnyElement> = vec![div()
+                    .flex_none()
+                    .text_size(px(10.))
+                    .text_color(dim)
+                    .child("⇢ etapas")
+                    .into_any_element()];
+                for (i, st) in pipe.stages.iter().enumerate() {
+                    let end = st.tokens.last().map(|t| t.end).unwrap_or(r.line.len());
+                    let prefix = r.line[..end].trim().to_string();
+                    let name =
+                        st.command.clone().unwrap_or_else(|| format!("{}", i + 1));
+                    chips.push(
+                        div()
+                            .id(SharedString::from(format!("stage-{id}-{i}")))
+                            .flex_none()
+                            .px(px(6.))
+                            .py(px(1.))
+                            .rounded(px(3.))
+                            .bg(chip_bg)
+                            .text_size(px(11.))
+                            .text_color(accent)
+                            .cursor_pointer()
+                            .hover(|s| s.text_color(gpui::hsla(0.0, 0.0, 0.95, 1.0)))
+                            .child(SharedString::from(name))
+                            .on_click(cx.listener(move |shell, _, _, cx| {
+                                shell.run_command(prefix.clone());
+                                cx.notify();
+                            }))
+                            .into_any_element(),
+                    );
+                }
+                body.push(
+                    div()
+                        .flex()
+                        .flex_row()
+                        .flex_wrap()
+                        .gap(px(4.))
+                        .items_center()
+                        .children(chips),
+                );
+            }
+        }
+
         let stream = if ui.show_stderr { Stream::Stderr } else { Stream::Stdout };
         let lines: Vec<&str> = r.lines_of(stream).collect();
         let color = if ui.show_stderr { stderr_color } else { theme.fg_text };
@@ -893,16 +941,8 @@ fn render_run(
                 if ui.show_stderr { "sin errores" } else { "sin salida" },
             ));
         } else {
-            let skipped = lines.len().saturating_sub(OUTPUT_LINES);
-            if skipped > 0 {
-                body.push(
-                    div()
-                        .text_size(px(11.))
-                        .text_color(dim)
-                        .child(SharedString::from(format!("… {skipped} líneas antes"))),
-                );
-            }
-            for l in lines.iter().skip(skipped) {
+            // Sin truncar: si hay contenido, se muestra entero.
+            for l in &lines {
                 body.push(
                     div()
                         .text_size(px(12.))