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:
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
+
+
+
+## xlset demo
+
+
+
## 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");
+ }
+}