WIP: transformations
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m38s

This commit is contained in:
2024-11-01 20:46:27 +01:00
parent aff062f144
commit c62fc6fb43
4 changed files with 314 additions and 268 deletions

15
README.md Normal file
View File

@@ -0,0 +1,15 @@
# Tui-Website
This is my terminal based website. It is served as a tui application via ssh and as a simple html page via https.
It contains information about me and my projects as well as blog entries about something I feel like writing something about.
## Open tasks
- [ ] BUG: when served via `wish-serve` the corresponding outputs are not pushed through the ssh connection
- they are instead showed locally, which might cause issues with the docker container running in the background
- very likely it is `tui-website` which causes this issue
- not entirely as inputs are not passed through correctly to the below running application (i.e. `diffnav` via `serve git diff`)
- [ ] Improve navigation
- [ ] Have clickable/navigatable links inside of the tui application
- [ ] Launch simple http server alongside tui application

View File

@@ -2,9 +2,8 @@
const std = @import("std"); const std = @import("std");
const vaxis = @import("vaxis"); const vaxis = @import("vaxis");
const Zmd = @import("zmd").Zmd;
const node2buffer = @import("node2buffer.zig"); const converter = @import("node2buffer.zig").Markdown;
const widget = @import("../widget.zig"); const widget = @import("../widget.zig");
const Event = widget.Event; const Event = widget.Event;
@@ -61,14 +60,10 @@ pub fn draw(this: *@This(), win: vaxis.Window) void {
\\**h** home \\**h** home
\\**t** test \\**t** test
; ;
var zmd = Zmd.init(this.allocator);
defer zmd.deinit();
var cells = std.ArrayList(vaxis.Cell).init(this.allocator); var cells = std.ArrayList(vaxis.Cell).init(this.allocator);
defer cells.deinit(); defer cells.deinit();
zmd.parse(msg) catch @panic("failed to parse markdown file"); converter.toBuffer(msg, this.allocator, &cells);
node2buffer.toBuffer(zmd.nodes.items[0], this.allocator, msg, &cells, .{}, null) catch @panic("failed to transform to cell array");
var col: usize = 0; var col: usize = 0;
var row: usize = 0; var row: usize = 0;

View File

@@ -4,7 +4,7 @@ const std = @import("std");
const vaxis = @import("vaxis"); const vaxis = @import("vaxis");
const Zmd = @import("zmd").Zmd; const Zmd = @import("zmd").Zmd;
const node2buffer = @import("node2buffer.zig"); const converter = @import("node2buffer.zig").Markdown;
const widget = @import("../widget.zig"); const widget = @import("../widget.zig");
const Event = widget.Event; const Event = widget.Event;
@@ -67,19 +67,9 @@ pub fn update(this: *@This(), event: Event) void {
// TODO: support typst files as parser and display driver -> as I'll be using this file format anyway with my personal note system // TODO: support typst files as parser and display driver -> as I'll be using this file format anyway with my personal note system
// - I should leverage the typst compiler! (i.e. maybe use the html export, once that is available?) // - I should leverage the typst compiler! (i.e. maybe use the html export, once that is available?)
var zmd = Zmd.init(this.allocator);
defer zmd.deinit();
this.buffer.clearRetainingCapacity(); this.buffer.clearRetainingCapacity();
zmd.parse(this.contents.?) catch @panic("failed to parse markdown contents"); converter.toBuffer(this.contents.?, this.allocator, &this.buffer);
node2buffer.toBuffer(
zmd.nodes.items[0],
this.allocator,
this.contents.?,
&this.buffer,
.{},
null,
) catch @panic("failed to transform to cell array");
}, },
else => {}, else => {},
} }

View File

@@ -1,268 +1,314 @@
///! Transform a given `zmd.Node` into a buffer which can be used by any `vaxis.widgets.View` ///! Transform a given file type into a buffer which can be used by any `vaxis.widgets.View`
///!
///! Use the `toBuffer` method of any struct to convert the file type described by the
///! struct to convert the file contents accordingly.
const std = @import("std"); const std = @import("std");
const vaxis = @import("vaxis"); const vaxis = @import("vaxis");
const zmd = @import("zmd");
const digits = "0123456789"; /// Markdown tronsformation to convert a markdown file as a `std.ArrayList(vaxis.cell)`
pub const Markdown = struct {
const zmd = @import("zmd");
pub fn toBuffer( const digits = "0123456789";
node: *zmd.Node,
allocator: std.mem.Allocator,
input: []const u8,
array: *std.ArrayList(vaxis.Cell),
sty: vaxis.Cell.Style,
start: ?usize,
) !void {
var next_start: ?usize = start;
var style = sty;
// determine general styling changes pub fn toBuffer(
switch (node.token.element.type) { input: []const u8,
.bold => { allocator: std.mem.Allocator,
style.bold = true; array: *std.ArrayList(vaxis.Cell),
next_start = node.token.start; ) void {
style.fg = .{ .index = 5 }; var z = zmd.Zmd.init(allocator);
}, defer z.deinit();
.italic => {
style.italic = true; z.parse(input) catch @panic("failed to parse markdown contents");
next_start = node.token.start; convert(
style.fg = .{ .index = 2 }; z.nodes.items[0],
}, allocator,
.block => { input,
style.fg = .{ .index = 252 }; array,
style.dim = true; .{},
next_start = node.token.start; null,
}, ) catch @panic("failed to transform parsed markdown to cell array");
.code => {
next_start = node.token.start;
style.fg = .{ .index = 6 };
},
.href => {
next_start = node.token.start;
style.fg = .{ .index = 8 };
},
.link => {
style.fg = .{ .index = 3 };
style.ul_style = .single;
},
.list_item => {
style.fg = .{ .index = 1 };
},
else => {},
} }
// determine content that needs to be displayed fn convert(
const content = value: { node: *zmd.Node,
allocator: std.mem.Allocator,
input: []const u8,
array: *std.ArrayList(vaxis.Cell),
sty: vaxis.Cell.Style,
start: ?usize,
) !void {
var next_start: ?usize = start;
var style = sty;
// determine general styling changes
switch (node.token.element.type) { switch (node.token.element.type) {
// NOTE: do not support ordered lists? (as it only accepts `1.` and not `2.`, etc.) .bold => {
.text, .list_item => break :value input[node.token.start..node.token.end], style.bold = true;
.link => break :value input[node.token.start + 1 .. node.token.start + 1 + node.title.?.len], next_start = node.token.start;
.code_close => { style.fg = .{ .index = 5 };
if (next_start) |s| {
next_start = null;
break :value input[s + 1 .. node.token.end - 1];
}
break :value "";
}, },
.bold_close, .italic_close, .block_close, .title_close, .href_close => { .italic => {
if (next_start) |s| { style.italic = true;
next_start = null; next_start = node.token.start;
break :value input[s..node.token.end]; style.fg = .{ .index = 2 };
},
.block => {
style.fg = .{ .index = 252 };
style.dim = true;
next_start = node.token.start;
},
.code => {
next_start = node.token.start;
style.fg = .{ .index = 6 };
},
.href => {
next_start = node.token.start;
style.fg = .{ .index = 8 };
},
.link => {
style.fg = .{ .index = 3 };
style.ul_style = .single;
},
.list_item => {
style.fg = .{ .index = 1 };
},
else => {},
}
// determine content that needs to be displayed
const content = value: {
switch (node.token.element.type) {
// NOTE: do not support ordered lists? (as it only accepts `1.` and not `2.`, etc.)
.text, .list_item => break :value input[node.token.start..node.token.end],
.link => break :value input[node.token.start + 1 .. node.token.start + 1 + node.title.?.len],
.code_close => {
if (next_start) |s| {
next_start = null;
break :value input[s + 1 .. node.token.end - 1];
}
break :value "";
},
.bold_close, .italic_close, .block_close, .title_close, .href_close => {
if (next_start) |s| {
next_start = null;
break :value input[s..node.token.end];
}
break :value "";
},
else => {
break :value "";
},
}
};
// display content
switch (node.token.element.type) {
.linebreak, .paragraph => {
try array.append(.{
.char = .{ .grapheme = "\n" },
.style = style,
});
style.ul_style = .off;
},
.h1 => {
style.ul_style = .single;
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
},
.h2 => {
style.ul_style = .single;
for (0..2) |_| {
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
}
},
.h3 => {
style.ul_style = .single;
for (0..3) |_| {
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
}
},
.h4 => {
style.ul_style = .single;
for (0..4) |_| {
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
}
},
.h5 => {
style.ul_style = .single;
for (0..5) |_| {
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
}
},
.h6 => {
style.ul_style = .single;
for (0..6) |_| {
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
}
},
.link => {
const uri = input[node.token.start + 1 + node.title.?.len + 2 .. node.token.start + 1 + node.title.?.len + 2 + node.href.?.len];
for (content, 0..) |_, i| {
try array.append(.{
.link = .{ .uri = uri },
.char = .{ .grapheme = content[i .. i + 1] },
.style = style,
});
}
},
.block_close => {
// generate a `block` i.e.
// 01 | ...
// 02 | ...
// 03 | ...
// ...
// 10 | ...
try array.append(.{
.char = .{ .grapheme = "\n" },
});
var rows: usize = 0;
var c: usize = 0;
// TODO: would be cool to not have to re-iterate over the contents
for (content, 0..) |char, i| {
if (char == '\n') {
// NOTE: start after the ```<language>
if (c == 0) {
c = i + 1;
}
rows += 1;
}
}
rows = rows -| 1;
const pad = vaxis.widgets.LineNumbers.numDigits(rows);
for (1..rows + 1) |r| {
try array.append(.{
.char = .{ .grapheme = " " },
.style = style,
});
try array.append(.{
.char = .{ .grapheme = " " },
.style = style,
});
for (1..pad + 1) |i| {
const digit = vaxis.widgets.LineNumbers.extractDigit(r, pad - i);
try array.append(.{
.char = .{ .grapheme = digits[digit .. digit + 1] },
.style = style,
});
}
try array.append(.{
.char = .{ .grapheme = " " },
.style = style,
});
try array.append(.{
.char = .{ .grapheme = "" },
.style = style,
});
try array.append(.{
.char = .{ .grapheme = " " },
.style = style,
});
for (c..content.len) |c_i| {
if (r == rows and content[c_i] == '\n') {
break;
}
try array.append(.{
.char = .{ .grapheme = content[c_i .. c_i + 1] },
.style = style,
});
if (content[c_i] == '\n') {
c = c_i + 1;
break;
}
}
}
},
.list_item => {
try array.append(.{
.char = .{ .grapheme = "\n" },
});
for (content, 0..) |_, i| {
try array.append(.{
.char = .{ .grapheme = content[i .. i + 1] },
.style = style,
});
} }
break :value "";
}, },
else => { else => {
break :value ""; for (content, 0..) |_, i| {
try array.append(.{
.char = .{ .grapheme = content[i .. i + 1] },
.style = style,
});
}
}, },
} }
};
// display content // close styling after creating the corresponding cells
switch (node.token.element.type) { switch (node.token.element.type) {
.linebreak, .paragraph => { .bold_close => {
try array.append(.{ style.bold = false;
.char = .{ .grapheme = "\n" }, style.fg = .default;
.style = style, },
}); .italic_close => {
style.ul_style = .off; style.italic = false;
}, style.fg = .default;
.h1 => { },
style.ul_style = .single; .block_close => {
try array.append(.{ style.fg = .default;
.char = .{ .grapheme = "#" }, },
.style = style, .code_close => {
}); style.fg = .default;
}, },
.h2 => { .href_close => {
style.ul_style = .single; style.fg = .default;
for (0..2) |_| { },
try array.append(.{ .list_item => {
.char = .{ .grapheme = "#" }, style.fg = .default;
.style = style, },
}); else => {},
} }
},
.h3 => {
style.ul_style = .single;
for (0..3) |_| {
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
}
},
.h4 => {
style.ul_style = .single;
for (0..4) |_| {
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
}
},
.h5 => {
style.ul_style = .single;
for (0..5) |_| {
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
}
},
.h6 => {
style.ul_style = .single;
for (0..6) |_| {
try array.append(.{
.char = .{ .grapheme = "#" },
.style = style,
});
}
},
.link => {
const uri = input[node.token.start + 1 + node.title.?.len + 2 .. node.token.start + 1 + node.title.?.len + 2 + node.href.?.len];
for (content, 0..) |_, i| {
try array.append(.{
.link = .{ .uri = uri },
.char = .{ .grapheme = content[i .. i + 1] },
.style = style,
});
}
},
.block_close => {
// generate a `block` i.e.
// 01 | ...
// 02 | ...
// 03 | ...
// ...
// 10 | ...
try array.append(.{
.char = .{ .grapheme = "\n" },
});
var rows: usize = 0;
var c: usize = 0;
// TODO: would be cool to not have to re-iterate over the contents
for (content, 0..) |char, i| {
if (char == '\n') {
// NOTE: start after the ```<language>
if (c == 0) {
c = i + 1;
}
rows += 1;
}
}
rows = rows -| 1;
const pad = vaxis.widgets.LineNumbers.numDigits(rows);
for (1..rows + 1) |r| {
try array.append(.{
.char = .{ .grapheme = " " },
.style = style,
});
try array.append(.{
.char = .{ .grapheme = " " },
.style = style,
});
for (1..pad + 1) |i| {
const digit = vaxis.widgets.LineNumbers.extractDigit(r, pad - i);
try array.append(.{
.char = .{ .grapheme = digits[digit .. digit + 1] },
.style = style,
});
}
try array.append(.{
.char = .{ .grapheme = " " },
.style = style,
});
try array.append(.{
.char = .{ .grapheme = "" },
.style = style,
});
try array.append(.{
.char = .{ .grapheme = " " },
.style = style,
});
for (c..content.len) |c_i| {
if (r == rows and content[c_i] == '\n') {
break;
}
try array.append(.{
.char = .{ .grapheme = content[c_i .. c_i + 1] },
.style = style,
});
if (content[c_i] == '\n') {
c = c_i + 1;
break;
}
}
}
},
.list_item => {
try array.append(.{
.char = .{ .grapheme = "\n" },
});
for (content, 0..) |_, i| {
try array.append(.{
.char = .{ .grapheme = content[i .. i + 1] },
.style = style,
});
}
},
else => {
for (content, 0..) |_, i| {
try array.append(.{
.char = .{ .grapheme = content[i .. i + 1] },
.style = style,
});
}
},
}
// close styling after creating the corresponding cells // run conversion for all childrens
switch (node.token.element.type) { for (node.children.items) |child_node| {
.bold_close => { try convert(child_node, allocator, input, array, style, next_start);
style.bold = false; }
style.fg = .default;
},
.italic_close => {
style.italic = false;
style.fg = .default;
},
.block_close => {
style.fg = .default;
},
.code_close => {
style.fg = .default;
},
.href_close => {
style.fg = .default;
},
.list_item => {
style.fg = .default;
},
else => {},
} }
};
// run conversion for all childrens pub const Typst = struct {
for (node.children.items) |child_node| { pub fn toBuffer(
try toBuffer(child_node, allocator, input, array, style, next_start); input: []const u8,
allocator: std.mem.Allocator,
array: *std.ArrayList(vaxis.Cell),
) void {
// TODO: leverage the compiler to create corresponding file?
// -> would enable functions to be executed, however this is not possible currently
// -> this would only work if I serve the corresponding pdf's (maybe I can have a download server for these?)
// NOTE: currently the typst compiler does not allow such a usage from the outside
// -> I would need to wait for html export I guess
// TODO: use pret parsing to parse typst file contents and transform them into `vaxis.Cell`s accordingly
_ = input;
_ = allocator;
_ = array;
@panic("Typst parsing not yet implemented");
} }
} };