xl-cli-tools

CLI tools for viewing and editing Excel files
Log | Files | Refs | README | LICENSE

commit d4b23c48b8eebc449295c93a9a146ac506b0831a
parent db1a5c21dbfdf524d3790b667d0e08cb38b9e6a4
Author: Erik Loualiche <eloualic@umn.edu>
Date:   Fri, 13 Mar 2026 19:47:51 -0500

docs: add VHS demo recordings for xlcat and xlset

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

Diffstat:
MREADME.md | 8++++++++
Ademo/budget.xlsx | 0
Ademo/sales.xlsx | 0
Ademo/xlcat.gif | 0
Ademo/xlcat.tape | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Ademo/xlset.gif | 0
Ademo/xlset.tape | 80+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aexamples/create_demo.rs | 98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 246 insertions(+), 0 deletions(-)

diff --git a/README.md b/README.md @@ -7,6 +7,14 @@ Two binaries, no runtime dependencies: - **`xlcat`** — view xlsx/xls files as markdown tables or CSV - **`xlset`** — modify cells in existing xlsx files, preserving formatting +## xlcat demo + +![xlcat demo](demo/xlcat.gif) + +## xlset demo + +![xlset demo](demo/xlset.gif) + ## Install ```bash diff --git a/demo/budget.xlsx b/demo/budget.xlsx Binary files differ. diff --git a/demo/sales.xlsx b/demo/sales.xlsx Binary files differ. diff --git a/demo/xlcat.gif b/demo/xlcat.gif Binary files differ. diff --git a/demo/xlcat.tape b/demo/xlcat.tape @@ -0,0 +1,60 @@ +# VHS tape for recording xlcat demo GIF. +# Usage: vhs demo/xlcat.tape +# +# Prerequisites: +# - xlcat binary built and in PATH +# - Demo xlsx files created: cargo run --example create_demo + +Output demo/xlcat.gif + +Set FontSize 18 +Set Width 1400 +Set Height 700 +Set Padding 20 +Set Theme "GruvboxDarkHard" + +Set TypingSpeed 80ms + +Set Shell "bash" + +Hide + Type 'export PS1="> "' + Enter + Type "clear" + Enter +Show + +# ─── 1. VIEW A SPREADSHEET ─── +Type "xlcat demo/sales.xlsx" +Enter +Sleep 3s + +# ─── 2. SCHEMA ONLY ─── +Type "xlcat demo/sales.xlsx --schema" +Enter +Sleep 2s + +# ─── 3. SUMMARY STATISTICS ─── +Type "xlcat demo/sales.xlsx --describe" +Enter +Sleep 3s + +# ─── 4. MULTI-SHEET WORKBOOK ─── +Type "xlcat demo/budget.xlsx" +Enter +Sleep 3s + +# ─── 5. SELECT A SHEET ─── +Type "xlcat demo/budget.xlsx --sheet Revenue" +Enter +Sleep 2s + +# ─── 6. HEAD AND TAIL ─── +Type "xlcat demo/sales.xlsx --head 3 --tail 2" +Enter +Sleep 2s + +# ─── 7. CSV OUTPUT ─── +Type "xlcat demo/sales.xlsx --csv --head 5" +Enter +Sleep 3s diff --git a/demo/xlset.gif b/demo/xlset.gif Binary files differ. diff --git a/demo/xlset.tape b/demo/xlset.tape @@ -0,0 +1,80 @@ +# VHS tape for recording xlset demo GIF. +# Usage: vhs demo/xlset.tape +# +# Prerequisites: +# - xlcat and xlset binaries built and in PATH +# - Demo xlsx files created: cargo run --example create_demo + +Output demo/xlset.gif + +Set FontSize 18 +Set Width 1400 +Set Height 700 +Set Padding 20 +Set Theme "GruvboxDarkHard" + +Set TypingSpeed 80ms + +Set Shell "bash" + +Hide + Type 'export PS1="> "' + Enter + Type "cp demo/sales.xlsx /tmp/demo_edit.xlsx" + Enter + Type "clear" + Enter +Show + +# ─── 1. VIEW BEFORE EDITING ─── +Type "xlcat /tmp/demo_edit.xlsx --head 3" +Enter +Sleep 2s + +# ─── 2. SET A SINGLE CELL ─── +Type "xlset /tmp/demo_edit.xlsx B2=South" +Enter +Sleep 2s + +# ─── 3. SET MULTIPLE CELLS ─── +Type "xlset /tmp/demo_edit.xlsx D2=99999 E2=500" +Enter +Sleep 2s + +# ─── 4. VERIFY CHANGES ─── +Type "xlcat /tmp/demo_edit.xlsx --head 3" +Enter +Sleep 3s + +# ─── 5. TYPE TAG: PRESERVE LEADING ZERO ─── +Type "xlset /tmp/demo_edit.xlsx C2:str=07401" +Enter +Sleep 2s + +Type "xlcat /tmp/demo_edit.xlsx --head 2" +Enter +Sleep 2s + +# ─── 6. BULK UPDATE FROM CSV ─── +Hide + Type "printf 'cell,value\nA5,2024-06-15\nB5,HQ\nD5,50000\n' > /tmp/updates.csv" + Enter + Sleep 300ms +Show + +Type "cat /tmp/updates.csv" +Enter +Sleep 2s + +Type "xlset /tmp/demo_edit.xlsx --from /tmp/updates.csv" +Enter +Sleep 2s + +# ─── 7. WRITE TO NEW FILE ─── +Type "xlset /tmp/demo_edit.xlsx --output /tmp/final.xlsx A1=ID" +Enter +Sleep 2s + +Type "xlcat /tmp/final.xlsx --head 3" +Enter +Sleep 3s diff --git a/examples/create_demo.rs b/examples/create_demo.rs @@ -0,0 +1,98 @@ +use rust_xlsxwriter::*; +use std::path::Path; + +fn main() { + let dir = Path::new("demo"); + + // --- sales.xlsx: single sheet, 12 rows --- + { + let mut wb = Workbook::new(); + let ws = wb.add_worksheet(); + ws.set_name("Q1 Sales").unwrap(); + + ws.write_string(0, 0, "Date").unwrap(); + ws.write_string(0, 1, "Region").unwrap(); + ws.write_string(0, 2, "Product").unwrap(); + ws.write_string(0, 3, "Revenue").unwrap(); + ws.write_string(0, 4, "Units").unwrap(); + + let data = [ + ("2024-01-05", "East", "Widget A", 12500.0, 250.0), + ("2024-01-12", "West", "Widget B", 8700.0, 145.0), + ("2024-01-19", "East", "Widget A", 15300.0, 306.0), + ("2024-01-26", "North", "Widget C", 4200.0, 84.0), + ("2024-02-02", "West", "Widget A", 11800.0, 236.0), + ("2024-02-09", "East", "Widget B", 9100.0, 152.0), + ("2024-02-16", "South", "Widget C", 6700.0, 134.0), + ("2024-02-23", "North", "Widget A", 13400.0, 268.0), + ("2024-03-01", "West", "Widget B", 7900.0, 132.0), + ("2024-03-08", "East", "Widget C", 5500.0, 110.0), + ("2024-03-15", "South", "Widget A", 14200.0, 284.0), + ("2024-03-22", "North", "Widget B", 10600.0, 177.0), + ]; + + for (i, (date, region, product, revenue, units)) in data.iter().enumerate() { + let row = (i + 1) as u32; + ws.write_string(row, 0, *date).unwrap(); + ws.write_string(row, 1, *region).unwrap(); + ws.write_string(row, 2, *product).unwrap(); + ws.write_number(row, 3, *revenue).unwrap(); + ws.write_number(row, 4, *units).unwrap(); + } + + wb.save(dir.join("sales.xlsx")).unwrap(); + println!("Created demo/sales.xlsx"); + } + + // --- budget.xlsx: 3 sheets --- + { + let mut wb = Workbook::new(); + + let ws1 = wb.add_worksheet(); + ws1.set_name("Revenue").unwrap(); + ws1.write_string(0, 0, "Quarter").unwrap(); + ws1.write_string(0, 1, "Amount").unwrap(); + ws1.write_string(0, 2, "Target").unwrap(); + for (i, (q, amt, tgt)) in [ + ("Q1", 145000.0, 140000.0), + ("Q2", 162000.0, 155000.0), + ("Q3", 138000.0, 150000.0), + ("Q4", 171000.0, 165000.0), + ].iter().enumerate() { + let row = (i + 1) as u32; + ws1.write_string(row, 0, *q).unwrap(); + ws1.write_number(row, 1, *amt).unwrap(); + ws1.write_number(row, 2, *tgt).unwrap(); + } + + let ws2 = wb.add_worksheet(); + ws2.set_name("Expenses").unwrap(); + ws2.write_string(0, 0, "Category").unwrap(); + ws2.write_string(0, 1, "Q1").unwrap(); + ws2.write_string(0, 2, "Q2").unwrap(); + for (i, (cat, q1, q2)) in [ + ("Payroll", 85000.0, 87000.0), + ("Marketing", 12000.0, 15000.0), + ("Infra", 8000.0, 9500.0), + ].iter().enumerate() { + let row = (i + 1) as u32; + ws2.write_string(row, 0, *cat).unwrap(); + ws2.write_number(row, 1, *q1).unwrap(); + ws2.write_number(row, 2, *q2).unwrap(); + } + + let ws3 = wb.add_worksheet(); + ws3.set_name("Summary").unwrap(); + ws3.write_string(0, 0, "Metric").unwrap(); + ws3.write_string(0, 1, "Value").unwrap(); + ws3.write_string(1, 0, "Total Revenue").unwrap(); + ws3.write_number(1, 1, 616000.0).unwrap(); + ws3.write_string(2, 0, "Total Expenses").unwrap(); + ws3.write_number(2, 1, 216500.0).unwrap(); + ws3.write_string(3, 0, "Net").unwrap(); + ws3.write_number(3, 1, 399500.0).unwrap(); + + wb.save(dir.join("budget.xlsx")).unwrap(); + println!("Created demo/budget.xlsx"); + } +}