WIP: transformations
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m38s
All checks were successful
Zig Project Action / Lint, Spell-check and test zig project (push) Successful in 1m38s
This commit is contained in:
15
README.md
Normal file
15
README.md
Normal 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
|
||||
@@ -2,9 +2,8 @@
|
||||
|
||||
const std = @import("std");
|
||||
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 Event = widget.Event;
|
||||
@@ -61,14 +60,10 @@ pub fn draw(this: *@This(), win: vaxis.Window) void {
|
||||
\\**h** home
|
||||
\\**t** test
|
||||
;
|
||||
var zmd = Zmd.init(this.allocator);
|
||||
defer zmd.deinit();
|
||||
|
||||
var cells = std.ArrayList(vaxis.Cell).init(this.allocator);
|
||||
defer cells.deinit();
|
||||
|
||||
zmd.parse(msg) catch @panic("failed to parse markdown file");
|
||||
node2buffer.toBuffer(zmd.nodes.items[0], this.allocator, msg, &cells, .{}, null) catch @panic("failed to transform to cell array");
|
||||
converter.toBuffer(msg, this.allocator, &cells);
|
||||
|
||||
var col: usize = 0;
|
||||
var row: usize = 0;
|
||||
|
||||
@@ -4,7 +4,7 @@ const std = @import("std");
|
||||
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 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
|
||||
// - 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();
|
||||
zmd.parse(this.contents.?) catch @panic("failed to parse markdown contents");
|
||||
node2buffer.toBuffer(
|
||||
zmd.nodes.items[0],
|
||||
this.allocator,
|
||||
this.contents.?,
|
||||
&this.buffer,
|
||||
.{},
|
||||
null,
|
||||
) catch @panic("failed to transform to cell array");
|
||||
converter.toBuffer(this.contents.?, this.allocator, &this.buffer);
|
||||
},
|
||||
else => {},
|
||||
}
|
||||
|
||||
@@ -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 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(
|
||||
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;
|
||||
const digits = "0123456789";
|
||||
|
||||
// determine general styling changes
|
||||
switch (node.token.element.type) {
|
||||
.bold => {
|
||||
style.bold = true;
|
||||
next_start = node.token.start;
|
||||
style.fg = .{ .index = 5 };
|
||||
},
|
||||
.italic => {
|
||||
style.italic = true;
|
||||
next_start = node.token.start;
|
||||
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 => {},
|
||||
pub fn toBuffer(
|
||||
input: []const u8,
|
||||
allocator: std.mem.Allocator,
|
||||
array: *std.ArrayList(vaxis.Cell),
|
||||
) void {
|
||||
var z = zmd.Zmd.init(allocator);
|
||||
defer z.deinit();
|
||||
|
||||
z.parse(input) catch @panic("failed to parse markdown contents");
|
||||
convert(
|
||||
z.nodes.items[0],
|
||||
allocator,
|
||||
input,
|
||||
array,
|
||||
.{},
|
||||
null,
|
||||
) catch @panic("failed to transform parsed markdown to cell array");
|
||||
}
|
||||
|
||||
// determine content that needs to be displayed
|
||||
const content = value: {
|
||||
fn convert(
|
||||
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) {
|
||||
// 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 => {
|
||||
style.bold = true;
|
||||
next_start = node.token.start;
|
||||
style.fg = .{ .index = 5 };
|
||||
},
|
||||
.bold_close, .italic_close, .block_close, .title_close, .href_close => {
|
||||
if (next_start) |s| {
|
||||
next_start = null;
|
||||
break :value input[s..node.token.end];
|
||||
.italic => {
|
||||
style.italic = true;
|
||||
next_start = node.token.start;
|
||||
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 => {
|
||||
break :value "";
|
||||
for (content, 0..) |_, i| {
|
||||
try array.append(.{
|
||||
.char = .{ .grapheme = content[i .. i + 1] },
|
||||
.style = style,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
// 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,
|
||||
});
|
||||
}
|
||||
},
|
||||
else => {
|
||||
for (content, 0..) |_, i| {
|
||||
try array.append(.{
|
||||
.char = .{ .grapheme = content[i .. i + 1] },
|
||||
.style = style,
|
||||
});
|
||||
}
|
||||
},
|
||||
}
|
||||
// close styling after creating the corresponding cells
|
||||
switch (node.token.element.type) {
|
||||
.bold_close => {
|
||||
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 => {},
|
||||
}
|
||||
|
||||
// close styling after creating the corresponding cells
|
||||
switch (node.token.element.type) {
|
||||
.bold_close => {
|
||||
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
|
||||
for (node.children.items) |child_node| {
|
||||
try convert(child_node, allocator, input, array, style, next_start);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// run conversion for all childrens
|
||||
for (node.children.items) |child_node| {
|
||||
try toBuffer(child_node, allocator, input, array, style, next_start);
|
||||
pub const Typst = struct {
|
||||
pub fn toBuffer(
|
||||
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");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user