New modules (13): - src/resize.zig: SIGWINCH terminal resize detection - src/drag.zig: Mouse drag state and Splitter panels - src/diagnostic.zig: Elm-style error messages with code snippets - src/debug.zig: Debug overlay (FPS, timing, widget count) - src/profile.zig: Performance profiling with scoped timers - src/sixel.zig: Sixel graphics encoding for terminal images - src/async_loop.zig: epoll-based async event loop with timers - src/compose.zig: Widget composition utilities - src/shortcuts.zig: Keyboard shortcut registry - src/widgets/logo.zig: ASCII art logo widget Enhanced modules: - src/layout.zig: Added Constraint.ratio(num, denom) - src/terminal.zig: Integrated resize handling - src/root.zig: Re-exports all new modules New examples (9): - resize_demo, splitter_demo, dirtree_demo - help_demo, markdown_demo, progress_demo - spinner_demo, syntax_demo, viewport_demo Package manager: - build.zig.zon: Zig package manager support Stats: 60+ source files, 186+ tests, 20 executables 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
176 lines
4.9 KiB
Zig
176 lines
4.9 KiB
Zig
//! Syntax Highlighting Demo
|
|
//!
|
|
//! Run with: zig build syntax-demo
|
|
|
|
const std = @import("std");
|
|
const zcatui = @import("zcatui");
|
|
|
|
const Terminal = zcatui.Terminal;
|
|
const Rect = zcatui.Rect;
|
|
const Buffer = zcatui.Buffer;
|
|
const Style = zcatui.Style;
|
|
const Color = zcatui.Color;
|
|
const Block = zcatui.widgets.Block;
|
|
const Borders = zcatui.widgets.Borders;
|
|
const SyntaxHighlighter = zcatui.widgets.SyntaxHighlighter;
|
|
const SyntaxLanguage = zcatui.widgets.SyntaxLanguage;
|
|
|
|
const zig_code =
|
|
\\const std = @import("std");
|
|
\\
|
|
\\pub fn main() !void {
|
|
\\ const allocator = std.heap.page_allocator;
|
|
\\ var list = std.ArrayList(u32).init(allocator);
|
|
\\ defer list.deinit();
|
|
\\
|
|
\\ try list.append(42);
|
|
\\ try list.append(100);
|
|
\\
|
|
\\ for (list.items) |item| {
|
|
\\ std.debug.print("{d}\n", .{item});
|
|
\\ }
|
|
\\}
|
|
;
|
|
|
|
const rust_code =
|
|
\\use std::collections::HashMap;
|
|
\\
|
|
\\fn main() {
|
|
\\ let mut map = HashMap::new();
|
|
\\ map.insert("key", "value");
|
|
\\
|
|
\\ if let Some(val) = map.get("key") {
|
|
\\ println!("Found: {}", val);
|
|
\\ }
|
|
\\
|
|
\\ // Iterate over map
|
|
\\ for (k, v) in &map {
|
|
\\ println!("{}: {}", k, v);
|
|
\\ }
|
|
\\}
|
|
;
|
|
|
|
const python_code =
|
|
\\import json
|
|
\\from typing import List, Dict
|
|
\\
|
|
\\def process_data(items: List[str]) -> Dict:
|
|
\\ """Process a list of items."""
|
|
\\ result = {}
|
|
\\ for i, item in enumerate(items):
|
|
\\ result[f"item_{i}"] = item.upper()
|
|
\\ return result
|
|
\\
|
|
\\if __name__ == "__main__":
|
|
\\ data = ["hello", "world"]
|
|
\\ print(json.dumps(process_data(data)))
|
|
;
|
|
|
|
const js_code =
|
|
\\const express = require('express');
|
|
\\const app = express();
|
|
\\
|
|
\\// Middleware
|
|
\\app.use(express.json());
|
|
\\
|
|
\\app.get('/api/users', async (req, res) => {
|
|
\\ try {
|
|
\\ const users = await fetchUsers();
|
|
\\ res.json({ success: true, data: users });
|
|
\\ } catch (err) {
|
|
\\ res.status(500).json({ error: err.message });
|
|
\\ }
|
|
\\});
|
|
\\
|
|
\\app.listen(3000, () => console.log('Server running'));
|
|
;
|
|
|
|
const languages = [_]SyntaxLanguage{ .zig, .rust, .python, .javascript };
|
|
const lang_names = [_][]const u8{ "Zig", "Rust", "Python", "JavaScript" };
|
|
const codes = [_][]const u8{ zig_code, rust_code, python_code, js_code };
|
|
|
|
/// State for the demo
|
|
const State = struct {
|
|
selected: usize = 0,
|
|
running: bool = true,
|
|
};
|
|
|
|
pub fn main() !void {
|
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
|
defer _ = gpa.deinit();
|
|
const allocator = gpa.allocator();
|
|
|
|
var term = try Terminal.init(allocator);
|
|
defer term.deinit();
|
|
|
|
var state = State{};
|
|
|
|
while (state.running) {
|
|
try term.drawWithContext(&state, render);
|
|
|
|
if (try term.pollEvent(100)) |event| {
|
|
switch (event) {
|
|
.key => |key| {
|
|
switch (key.code) {
|
|
.char => |c| {
|
|
if (c == 'q') state.running = false;
|
|
if (c >= '1' and c <= '4') {
|
|
state.selected = c - '1';
|
|
}
|
|
},
|
|
.left => {
|
|
if (state.selected > 0) state.selected -= 1;
|
|
},
|
|
.right => {
|
|
if (state.selected < languages.len - 1) state.selected += 1;
|
|
},
|
|
.esc => state.running = false,
|
|
else => {},
|
|
}
|
|
},
|
|
else => {},
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn render(state: *State, area: Rect, buf: *Buffer) void {
|
|
// Header with language tabs
|
|
var x: u16 = 2;
|
|
for (lang_names, 0..) |name, i| {
|
|
const style = if (i == state.selected)
|
|
(Style{}).fg(Color.black).bg(Color.cyan).bold()
|
|
else
|
|
(Style{}).fg(Color.white);
|
|
|
|
_ = buf.setString(x, 0, " ", style);
|
|
_ = buf.setString(x + 1, 0, name, style);
|
|
_ = buf.setString(x + 1 + @as(u16, @intCast(name.len)), 0, " ", style);
|
|
x += @as(u16, @intCast(name.len)) + 3;
|
|
}
|
|
|
|
// Code area
|
|
const code_area = Rect.init(0, 2, area.width, area.height -| 4);
|
|
|
|
const block = Block.init()
|
|
.title(" Code ")
|
|
.setBorders(Borders.all)
|
|
.borderStyle((Style{}).fg(Color.rgb(80, 80, 80)));
|
|
block.render(code_area, buf);
|
|
|
|
const inner = Rect.init(1, 3, area.width -| 2, area.height -| 6);
|
|
|
|
// Syntax highlighter
|
|
const highlighter = SyntaxHighlighter.init(languages[state.selected])
|
|
.setLineNumbers(true);
|
|
|
|
highlighter.render(codes[state.selected], 0, inner, buf);
|
|
|
|
// Footer
|
|
_ = buf.setString(
|
|
2,
|
|
area.height -| 1,
|
|
"Press 1-4 or left/right to change language, q to quit",
|
|
(Style{}).fg(Color.rgb(100, 100, 100)),
|
|
);
|
|
}
|