Skip to content

Commit

Permalink
plaintext 😎
Browse files Browse the repository at this point in the history
  • Loading branch information
ngalaiko committed Dec 16, 2024
1 parent 1a90bf5 commit 65e420b
Show file tree
Hide file tree
Showing 9 changed files with 272 additions and 135 deletions.
14 changes: 9 additions & 5 deletions assets/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,19 @@ body {
font-family: "Berkeley Mono", monospace;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
}

main {
max-width: 80ch;
overflow-wrap: break-word;
max-width: 72ch;
}

pre {
overflow-x: scroll;
white-space: pre-wrap;
word-wrap: break-word;
overflow-x: scroll;
}

a {
color: inherit;
text-decoration: underline;
}

img {
Expand Down
162 changes: 81 additions & 81 deletions assets/posts/comments.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,100 +23,100 @@ here is how:
1. i have a github [action][] to create a new comment.
it's really simple: given a author name, url and comment body, create a new .md file with the coment body like this:

```md
---
pathname: "/posts/blog/cluster/"
author-name: "Nikita"
timestamp: "1664822102"
---
```md
---
pathname: "/posts/blog/cluster/"
author-name: "Nikita"
timestamp: "1664822102"
---

comment body
```
comment body
```

2. a [sveltekit form][] to trigger action via http:

```svelte
<form method="POST" use:enhance>
<!-- author name input -->
<label for="author_name"> Your name: </label>
<input name="author_name" type="text" required />
```svelte
<form method="POST" use:enhance>
<!-- author name input -->
<label for="author_name"> Your name: </label>
<input name="author_name" type="text" required />
<!-- challange text -->
<label for="solution">{data.challange} = </label>
<input name="solution" type="text" required />
<!-- challange text -->
<label for="solution">{data.challange} = </label>
<input name="solution" type="text" required />
<!-- comment body -->
<textarea rows="3" name="body" type="text" required />
<!-- comment body -->
<textarea rows="3" name="body" type="text" required />
<!-- url pathname to link comment to the page -->
<input name="pathname" value={$page.url.pathname} type="text" hidden />
<!-- url pathname to link comment to the page -->
<input name="pathname" value={$page.url.pathname} type="text" hidden />
<!-- input challange for validation -->
<input name="challange" value={data.challange} type="text" hidden />
<!-- input challange for validation -->
<input name="challange" value={data.challange} type="text" hidden />
<input type="submit" value="Comment" />
</form>
```
<input type="submit" value="Comment" />
</form>
```

3. some server-side typescript to trigger the action:

```ts
import { solve } from "$lib/challange";
import { invalid } from "@sveltejs/kit";
import type { Actions } from "./$types";
import { env } from "$env/dynamic/private";

const GITHUB_TOKEN = env.GITHUB_TOKEN;

const trigger = (inputs: any) =>
fetch(
"https://api.github.com/repos/ngalaiko/galaiko.rocks/actions/workflows/create-comment.yaml/dispatches",
{
method: "POST",
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${GITHUB_TOKEN}`,
},
body: JSON.stringify({
ref: "master",
inputs,
}),
}
);

export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const body = data.get("body");
const author_name = data.get("author_name");
const pathname = data.get("pathname");
const solution = data.get("solution");
const challange = data.get("challange");

if (body === "")
return invalid(400, { message: "Message can not be empty" });
if (author_name === "")
return invalid(400, { message: "Please, fill in name" });

if (solution === "") {
return invalid(400, { message: "Challange solution is empty" });
} else if (solution !== solve(challange as string)) {
return invalid(400, { message: "Wrong solution" });
} else {
const res = await trigger({ body, author_name, pathname });
if (res.status !== 204) {
return invalid(500, { message: await res.text() });
} else {
return {
success: true,
message:
"Thanks! Your comment will appear after moderation. Check in later!",
};
}
}
```ts
import { solve } from "$lib/challange";
import { invalid } from "@sveltejs/kit";
import type { Actions } from "./$types";
import { env } from "$env/dynamic/private";

const GITHUB_TOKEN = env.GITHUB_TOKEN;

const trigger = (inputs: any) =>
fetch(
"https://api.github.com/repos/ngalaiko/galaiko.rocks/actions/workflows/create-comment.yaml/dispatches",
{
method: "POST",
headers: {
Accept: "application/vnd.github+json",
Authorization: `Bearer ${GITHUB_TOKEN}`,
},
};
```
body: JSON.stringify({
ref: "master",
inputs,
}),
}
);

export const actions: Actions = {
default: async ({ request }) => {
const data = await request.formData();
const body = data.get("body");
const author_name = data.get("author_name");
const pathname = data.get("pathname");
const solution = data.get("solution");
const challange = data.get("challange");

if (body === "")
return invalid(400, { message: "Message can not be empty" });
if (author_name === "")
return invalid(400, { message: "Please, fill in name" });

if (solution === "") {
return invalid(400, { message: "Challange solution is empty" });
} else if (solution !== solve(challange as string)) {
return invalid(400, { message: "Wrong solution" });
} else {
const res = await trigger({ body, author_name, pathname });
if (res.status !== 204) {
return invalid(500, { message: await res.text() });
} else {
return {
success: true,
message:
"Thanks! Your comment will appear after moderation. Check in later!",
};
}
}
},
};
```

and that's it! thanks to sveltekit, the implementation is both nice, js-free and i don't need to think about hosting

Expand Down
6 changes: 1 addition & 5 deletions assets/styles/table.css
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,8 @@ td {
text-align: left;
}

th {
background-color: #f2f2f2; /* Light gray background for header */
}

tr:hover {
background-color: #f5f5f5; /* Lighter background on hover for better visibility */
text-decoration: underline;
}

/* Add borders to the table cells */
Expand Down
File renamed without changes.
142 changes: 142 additions & 0 deletions filters/plaintext_style.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
-- Pandoc Lua filter to convert Markdown to a single <pre> HTML tag
-- with 72-character text wrapping and limited tag preservation

-- Add HTML escape function
local function escape_html(text)
return text:gsub("&", "&amp;"):gsub("<", "&lt;"):gsub(">", "&gt;"):gsub('"', "&quot;"):gsub("'", "&#39;")
end

-- Custom map function to replace pandoc.utils.map
local function map(array, func)
local new_array = {}
for i, v in ipairs(array) do
new_array[i] = func(v)
end
return new_array
end

-- Function to calculate visible character count for links
local function visible_char_count(text)
return #text:gsub("<[^>]+>", "")
end

-- Function to wrap text to a specified width
local function wrap_text(text, width)
local wrapped = {}
local current_line = ""

for word in text:gmatch("%S+") do
local test_line = current_line ~= "" and current_line .. " " .. word or word

if visible_char_count(test_line) > width then
table.insert(wrapped, current_line)
current_line = word
else
current_line = test_line
end
end

if current_line ~= "" then
table.insert(wrapped, current_line)
end

return table.concat(wrapped, "\n")
end

-- Function to convert inline elements to plain text with preserved tags
local function inline_to_plaintext(inline)
if inline.t == "Str" then
return inline.text
elseif inline.t == "Space" then
return " "
elseif inline.t == "SoftBreak" or inline.t == "LineBreak" then
return "\n"
elseif inline.t == "Link" then
local content = pandoc.utils.stringify(inline.content)
return string.format("<a href='%s'>%s</a>", inline.target, content)
elseif inline.t == "Image" then
local alt_text = pandoc.utils.stringify(inline.caption)
return string.format("<img src='%s' alt='%s'>", inline.src, alt_text)
else
return ""
end
end

-- Function to process block elements
local function block_to_plaintext(block)
if block.t == "Para" or block.t == "Plain" then
local text = table.concat(map(block.content, inline_to_plaintext))
return wrap_text(text, 72)
elseif block.t == "Figure" then
local image = block.content[1] -- The first element is usually the image
local caption = pandoc.utils.stringify(block.caption)
if image and image.t == "Plain" and image.content[1].t == "Image" then
local img = image.content[1]
local alt_text = pandoc.utils.stringify(img.caption)
return string.format(
"<figure><img src='%s' alt='%s'><figcaption>%s</figcaption></figure>",
img.src,
alt_text,
caption
)
end
elseif block.t == "Header" then
local level = string.rep("#", block.level)
local text = table.concat(map(block.content, inline_to_plaintext))
return wrap_text(level .. " " .. text, 72)
elseif block.t == "BulletList" then
local items = {}
for _, item in ipairs(block.content) do
-- Join the item contents with newlines between blocks
local item_text = table.concat(map(item, block_to_plaintext), "\n")
table.insert(items, "- " .. item_text:gsub("\n", "\n ")) -- Indent continued lines
end
return table.concat(items, "\n")
elseif block.t == "OrderedList" then
local items = {}
for i, item in ipairs(block.content) do
-- Join the item contents with newlines between blocks
local item_text = table.concat(map(item, block_to_plaintext), "\n")
-- Calculate padding for alignment of continued lines
local number_width = #tostring(i) + 2 -- accounts for number and ". "
local padding = string.rep(" ", number_width)
-- Add the numbered item and indent continued lines
table.insert(items, string.format("%d. %s", i, item_text:gsub("\n", "\n" .. padding)))
end
return table.concat(items, "\n")
elseif block.t == "BlockQuote" then
local text = table.concat(map(block.content, block_to_plaintext))
return wrap_text("> " .. text, 72)
elseif block.t == "HorizontalRule" then
return string.rep("-", 72)
elseif block.t == "Table" then
return wrap_text("[Table content omitted for plaintext format]", 72)
else
return ""
end
end

-- Function to center text within 72 characters
local function center_text(text, width)
local padding_length = math.floor((width - #text) / 2)
return string.rep(" ", padding_length) .. text
end

-- Main Pandoc filter
function Pandoc(doc)
local all_text = {}

local title = doc.meta.title and pandoc.utils.stringify(doc.meta.title) or ""
if title ~= "" then
table.insert(all_text, center_text(title, 72))
end

for _, block in ipairs(doc.blocks) do
local block_text = block_to_plaintext(block)
if block_text ~= "" then
table.insert(all_text, block_text)
end
end
local output = "<pre>" .. table.concat(all_text, "\n\n") .. "</pre>"
return pandoc.Pandoc({ pandoc.RawBlock("html", output) }, doc.meta)
end
Loading

0 comments on commit 65e420b

Please sign in to comment.