Skip to content

Commit b2f2d8d

Browse files
Add copy button to codeblocks
1 parent 3e7aaca commit b2f2d8d

File tree

6 files changed

+51
-31
lines changed

6 files changed

+51
-31
lines changed

src/website/component.gleam

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
import contour
12
import gleam/list
3+
import gleam/option.{type Option, Some}
24
import lustre/attribute.{attribute}
35
import lustre/element.{type Element}
46
import lustre/element/html
7+
import pearl
58
import website/component/footer
69
import website/component/header
710

8-
pub fn head(page: String) -> Element(a) {
11+
pub fn head(page: String) -> Element(_) {
912
html.head([], [
1013
html.meta([attribute("charset", "UTF-8")]),
1114
html.title([], page <> " | Gears"),
@@ -38,7 +41,7 @@ pub type Section(a) {
3841
Section(content: List(Element(a)))
3942
}
4043

41-
pub fn page(name: String, sections: List(Section(a))) -> Element(a) {
44+
pub fn page(name: String, sections: List(Section(a))) -> Element(_) {
4245
html.html([attribute("lang", "en")], [
4346
head(name),
4447
html.body([attribute.class("min-h-screen bg-slate-800 text-white")], [
@@ -64,6 +67,33 @@ pub fn text_page(header: String, content: List(Element(a))) -> Section(a) {
6467
])
6568
}
6669

67-
pub fn dangerous_html(html: String) -> Element(a) {
70+
pub fn dangerous_html(html: String) -> Element(_) {
6871
element.unsafe_raw_html("", "span", [], html)
6972
}
73+
74+
pub fn code_block(language: Option(String), code_text: String) -> Element(_) {
75+
let code = case language {
76+
Some("erlang") | Some("erl") ->
77+
dangerous_html(pearl.highlight_html(code_text))
78+
Some("gleam") -> dangerous_html(contour.to_html(code_text))
79+
_ -> html.text(code_text)
80+
}
81+
82+
html.div([attribute.class("codeblock")], [
83+
html.button(
84+
[
85+
attribute.class("copy-button"),
86+
attribute(
87+
"onclick",
88+
"navigator.clipboard.writeText(`" <> code_text <> "`)",
89+
),
90+
],
91+
[html.text("copy")],
92+
),
93+
html.pre([], [
94+
html.code([attribute("data-lang", option.unwrap(language, "text"))], [
95+
code,
96+
]),
97+
]),
98+
])
99+
}

src/website/component/footer.gleam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import lustre/attribute
22
import lustre/element.{type Element}
33
import lustre/element/html
44

5-
pub fn view() -> Element(a) {
5+
pub fn view() -> Element(_) {
66
html.footer(
77
[
88
attribute.class("right-5 bottom-4 fixed"),

src/website/component/header.gleam

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import lustre/element.{type Element}
33
import lustre/element/html
44
import lustre/element/svg
55

6-
pub fn view() -> Element(a) {
6+
pub fn view() -> Element(_) {
77
html.header([attribute.class("bg-purple-700 flex w-full select-none")], [
88
html.nav(
99
[

src/website/data/blog.gleam

Lines changed: 4 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,17 @@
1-
import contour
21
import gleam/bool
32
import gleam/dict.{type Dict}
43
import gleam/list
5-
import gleam/option
64
import gleam/string
75
import jot
8-
import lustre/attribute.{attribute}
6+
import lustre/attribute
97
import lustre/element
108
import lustre/element/html
119
import lustre/ssg/djot
12-
import pearl
1310
import simplifile
1411
import tom.{type Toml}
1512
import website/component
1613

17-
pub fn posts() -> List(Post(a)) {
14+
pub fn posts() -> List(Post(_)) {
1815
let assert Ok(files) = simplifile.read_directory("blog/")
1916

2017
list.filter_map(files, fn(file) {
@@ -35,7 +32,7 @@ pub fn posts() -> List(Post(a)) {
3532
})
3633
}
3734

38-
fn renderer() -> djot.Renderer(element.Element(a)) {
35+
fn renderer() -> djot.Renderer(element.Element(_)) {
3936
let default = djot.default_renderer()
4037
djot.Renderer(
4138
..default,
@@ -72,24 +69,7 @@ fn renderer() -> djot.Renderer(element.Element(a)) {
7269
)
7370
}
7471
},
75-
codeblock: fn(attrs, lang, code) {
76-
let lang = option.unwrap(lang, "text")
77-
78-
let code = case lang {
79-
"erlang" | "erl" -> component.dangerous_html(pearl.highlight_html(code))
80-
"gleam" -> component.dangerous_html(contour.to_html(code))
81-
_ -> html.text(code)
82-
}
83-
84-
html.pre(
85-
dict.fold(attrs, [], fn(attrs, name, value) {
86-
[attribute(name, value), ..attrs]
87-
}),
88-
[
89-
html.code([attribute("data-lang", lang)], [code]),
90-
],
91-
)
92-
},
72+
codeblock: fn(_attrs, lang, code) { component.code_block(lang, code) },
9373
)
9474
}
9575

src/website/page/index.gleam

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ type Talk {
183183
)
184184
}
185185

186-
fn talk(talk: Talk) -> Element(a) {
186+
fn talk(talk: Talk) -> Element(_) {
187187
html.p([attribute.class("my-3")], [
188188
html.a(
189189
[
@@ -203,7 +203,7 @@ fn talk(talk: Talk) -> Element(a) {
203203
])
204204
}
205205

206-
fn social(social: Social) -> Element(a) {
206+
fn social(social: Social) -> Element(_) {
207207
html.a([attribute.href(social.url), attribute.target("_blank")], [
208208
html.img([
209209
attribute.src("/images/" <> social.icon),

static/main.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,14 @@ pre code .hl-atom {
6363

6464
pre code .hl-variant {
6565
color: #ffddfa
66+
}
67+
68+
button.copy-button {
69+
position: absolute;
70+
top: 0.1rem;
71+
right: 0.5rem;
72+
}
73+
74+
div.codeblock {
75+
position: sticky;
6676
}

0 commit comments

Comments
 (0)