Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
W
www
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
1
Issues
1
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PLN
www
Commits
346abc3d
Commit
346abc3d
authored
Apr 11, 2025
by
PLN (Algolia)
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
feat(poems): Improvements
parent
426231d8
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
283 additions
and
45 deletions
+283
-45
utils.js
next/lib/utils.js
+3
-2
index.js
next/pages/index.js
+10
-10
poesie.js
next/pages/poesie.js
+84
-27
[id].js
next/pages/poesie/[id].js
+34
-4
utils.module.css
next/styles/utils.module.css
+152
-2
No files found.
next/lib/utils.js
View file @
346abc3d
...
@@ -12,9 +12,10 @@ function getAllContentData(name, sorted = false) {
...
@@ -12,9 +12,10 @@ function getAllContentData(name, sorted = false) {
// Get file names under /content/{name}
// Get file names under /content/{name}
const
contentDirectory
=
getContentDirectory
(
name
);
const
contentDirectory
=
getContentDirectory
(
name
);
const
fileNames
=
fs
.
readdirSync
(
contentDirectory
);
const
fileNames
=
fs
.
readdirSync
(
contentDirectory
);
// console.log(`gACD: total ${name}(s): ${fileNames.length}`);
const
allContentData
=
fileNames
.
map
((
fileName
)
=>
{
const
allContentData
=
fileNames
.
filter
(
fileName
=>
fileName
.
endsWith
(
'.md'
))
// Keep only .md files
.
map
((
fileName
)
=>
{
// Remove ".md" from file name to get id
// Remove ".md" from file name to get id
const
id
=
fileName
.
replace
(
/
\.
md$/
,
""
);
const
id
=
fileName
.
replace
(
/
\.
md$/
,
""
);
...
...
next/pages/index.js
View file @
346abc3d
...
@@ -43,8 +43,8 @@ export default function Home({ posts, talks }) {
...
@@ -43,8 +43,8 @@ export default function Home({ posts, talks }) {
</p>
</p>
</section>
</section>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<Link href="
/
parvagues
/
" className={utilStyles.sectionLink}
legacyBehavior
>
<Link href="
/
parvagues
/
" className={utilStyles.sectionLink}>
…code into <em>music</em
>
<span>…code into <em>music</em></span
>
</Link>
</Link>
<p className="
description
">
<p className="
description
">
As <Link href="
/
parvagues
/
" className={utilStyles.inlineLink}>ParVagues</Link>, I write patterns that
As <Link href="
/
parvagues
/
" className={utilStyles.inlineLink}>ParVagues</Link>, I write patterns that
...
@@ -52,8 +52,8 @@ export default function Home({ posts, talks }) {
...
@@ -52,8 +52,8 @@ export default function Home({ posts, talks }) {
</p>
</p>
</section>
</section>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<Link href="
/
hydra
/
" className={utilStyles.sectionLink}
legacyBehavior
>
<Link href="
/
hydra
/
" className={utilStyles.sectionLink}>
…code into <em>animated pixelscapes</em
>
<span>…code into <em>animated pixelscapes</em></span
>
</Link>
</Link>
<p className="
description
">
<p className="
description
">
Using <Link href="
/
hydra
/
" className={utilStyles.inlineLink}>Hydra</Link>, I create animations that
Using <Link href="
/
hydra
/
" className={utilStyles.inlineLink}>Hydra</Link>, I create animations that
...
@@ -61,16 +61,16 @@ export default function Home({ posts, talks }) {
...
@@ -61,16 +61,16 @@ export default function Home({ posts, talks }) {
</p>
</p>
</section>
</section>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<Link href="
/
poesie
/
" className={utilStyles.sectionLink}
legacyBehavior
>
<Link href="
/
poesie
/
" className={utilStyles.sectionLink}>
…words into <em>thoughts</em
>
<span>…words into <em>thoughts</em></span
>
</Link>
</Link>
<p className="
description
">
<p className="
description
">
A selection of texts scattered across note books and note apps.
A selection of texts scattered across note books and note apps.
</p>
</p>
</section>
</section>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}>
<Link href="
/
talks
/
" className={utilStyles.sectionLink}
legacyBehavior
>
<Link href="
/
talks
/
" className={utilStyles.sectionLink}>
…ideas into <em>talks</em
>
<span>…ideas into <em>talks</em></span
>
</Link>
</Link>
<p className="
description
">
<p className="
description
">
I <Link href="
/
talks
/
" className={utilStyles.inlineLink}>speak</Link> about topics I care about: from
I <Link href="
/
talks
/
" className={utilStyles.inlineLink}>speak</Link> about topics I care about: from
...
@@ -82,8 +82,8 @@ export default function Home({ posts, talks }) {
...
@@ -82,8 +82,8 @@ export default function Home({ posts, talks }) {
<ul className={utilStyles.list}>
<ul className={utilStyles.list}>
{talks.map(({ id, title, description }) => (
{talks.map(({ id, title, description }) => (
<li className={utilStyles.listItem} key={id}>
<li className={utilStyles.listItem} key={id}>
<Link href={`/talks#${id}`} className={utilStyles.listItemLink}
legacyBehavior
>
<Link href={`/talks#${id}`} className={utilStyles.listItemLink}>
{title}
<span>{title}</span>
</Link>
</Link>
{description && (
{description && (
<small className={utilStyles.lightText}>{description}</small>
<small className={utilStyles.lightText}>{description}</small>
...
...
next/pages/poesie.js
View file @
346abc3d
...
@@ -23,7 +23,8 @@ export default function Poems({ poems }) {
...
@@ -23,7 +23,8 @@ export default function Poems({ poems }) {
// Get unique languages and tags
// Get unique languages and tags
const
languages
=
[
'all'
,
...
new
Set
(
poems
.
map
(
p
=>
p
.
language
||
'unknown'
))];
const
languages
=
[
'all'
,
...
new
Set
(
poems
.
map
(
p
=>
p
.
language
||
'unknown'
))];
const
tags
=
[
'all'
,
...
new
Set
(
poems
.
flatMap
(
p
=>
p
.
tags
||
[]))];
const
allTags
=
[...
new
Set
(
poems
.
flatMap
(
p
=>
p
.
tags
||
[]))];
const
tags
=
[
'all'
,
...
allTags
];
// Filter and sort poems
// Filter and sort poems
const
filteredPoems
=
poems
const
filteredPoems
=
poems
...
@@ -47,57 +48,113 @@ export default function Poems({ poems }) {
...
@@ -47,57 +48,113 @@ export default function Poems({ poems }) {
return
(
return
(
<
Layout
>
<
Layout
>
<
Head
>
<
Head
>
<
title
>
Poems
<
/title
>
<
title
>
Poems
|
Words
into
thoughts
<
/title
>
<
meta
name
=
"description"
content
=
"A collection of poems and thoughts"
/>
<
meta
name
=
"viewport"
content
=
"width=device-width, initial-scale=1"
/>
<
/Head
>
<
/Head
>
<
div
className
=
{
utilStyles
.
headingMd
}
>
<
div
className
=
{
utilStyles
.
headingMd
}
>
<
h1
>
Words
into
thoughts
<
/h1
>
<
h1
>
Words
into
thoughts
<
/h1
>
<
p
>
A
selection
of
texts
scattered
across
note
books
and
note
apps
.
<
/p
>
<
div
className
=
{
utilStyles
.
filters
}
>
<
div
className
=
{
utilStyles
.
filtersContainer
}
>
<
div
>
<
div
className
=
{
utilStyles
.
filtersGroup
}
>
<
label
>
Sort
by
:
<
/label
>
<
div
className
=
{
utilStyles
.
filterItem
}
>
<
select
value
=
{
sortBy
}
onChange
=
{(
e
)
=>
setSortBy
(
e
.
target
.
value
)}
>
<
span
className
=
{
utilStyles
.
filterLabel
}
>
Sort
by
<
/span
>
<
select
className
=
{
utilStyles
.
filterSelect
}
value
=
{
sortBy
}
onChange
=
{(
e
)
=>
setSortBy
(
e
.
target
.
value
)}
aria
-
label
=
"Sort poems by"
>
<
option
value
=
"date"
>
Date
<
/option
>
<
option
value
=
"date"
>
Date
<
/option
>
<
option
value
=
"wordcount"
>
Word
count
<
/option
>
<
option
value
=
"wordcount"
>
Word
count
<
/option
>
<
/select
>
<
/select
>
<
select
value
=
{
sortOrder
}
onChange
=
{(
e
)
=>
setSortOrder
(
e
.
target
.
value
)}
>
<
/div
>
<
option
value
=
"desc"
>
Descending
<
/option
>
<
option
value
=
"asc"
>
Ascending
<
/option
>
<
div
className
=
{
utilStyles
.
filterItem
}
>
<
span
className
=
{
utilStyles
.
filterLabel
}
>
Order
<
/span
>
<
select
className
=
{
utilStyles
.
filterSelect
}
value
=
{
sortOrder
}
onChange
=
{(
e
)
=>
setSortOrder
(
e
.
target
.
value
)}
aria
-
label
=
"Sort order"
>
<
option
value
=
"desc"
>
Newest
first
<
/option
>
<
option
value
=
"asc"
>
Oldest
first
<
/option
>
<
/select
>
<
/select
>
<
/div
>
<
/div
>
<
div
>
<
div
className
=
{
utilStyles
.
filterItem
}
>
<
label
>
Language
:
<
/label
>
<
span
className
=
{
utilStyles
.
filterLabel
}
>
Language
<
/span
>
<
select
value
=
{
filterLanguage
}
onChange
=
{(
e
)
=>
setFilterLanguage
(
e
.
target
.
value
)}
>
<
select
className
=
{
utilStyles
.
filterSelect
}
value
=
{
filterLanguage
}
onChange
=
{(
e
)
=>
setFilterLanguage
(
e
.
target
.
value
)}
aria
-
label
=
"Filter by language"
>
{
languages
.
map
(
lang
=>
(
{
languages
.
map
(
lang
=>
(
<
option
key
=
{
lang
}
value
=
{
lang
}
>
{
lang
}
<
/option
>
<
option
key
=
{
lang
}
value
=
{
lang
}
>
{
lang
===
'all'
?
'All languages'
:
lang
}
<
/option
>
))}
))}
<
/select
>
<
/select
>
<
/div
>
<
/div
>
<
div
>
<
div
className
=
{
utilStyles
.
filterItem
}
>
<
label
>
Tag
:
<
/label
>
<
span
className
=
{
utilStyles
.
filterLabel
}
>
Tags
<
/span
>
<
select
value
=
{
filterTags
}
onChange
=
{(
e
)
=>
setFilterTags
(
e
.
target
.
value
)}
>
<
select
className
=
{
utilStyles
.
filterSelect
}
value
=
{
filterTags
}
onChange
=
{(
e
)
=>
setFilterTags
(
e
.
target
.
value
)}
aria
-
label
=
"Filter by tag"
>
{
tags
.
map
(
tag
=>
(
{
tags
.
map
(
tag
=>
(
<
option
key
=
{
tag
}
value
=
{
tag
}
>
{
tag
}
<
/option
>
<
option
key
=
{
tag
}
value
=
{
tag
}
>
{
tag
===
'all'
?
'All tags'
:
tag
}
<
/option
>
))}
))}
<
/select
>
<
/select
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
ul
className
=
{
utilStyles
.
list
}
>
<
div
className
=
{
utilStyles
.
list
}
>
{
sortedPoems
.
map
(({
id
,
title
,
date
,
language
,
tags
})
=>
(
{
sortedPoems
.
length
===
0
?
(
<
li
className
=
{
utilStyles
.
listItem
}
key
=
{
id
}
>
<
p
>
No
poems
match
your
current
filters
.
Try
adjusting
your
selection
.
<
/p
>
<
Link
href
=
{
`/poesie/
${
id
}
`
}
className
=
{
utilStyles
.
listItemLink
}
legacyBehavior
>
)
:
(
sortedPoems
.
map
(({
id
,
title
,
date
,
language
,
tags
})
=>
(
<
div
className
=
{
utilStyles
.
poemCard
}
key
=
{
id
}
>
<
Link
href
=
{
`/poesie/
${
id
}
`
}
className
=
{
utilStyles
.
listItemLink
}
>
{
title
}
{
title
}
<
/Link
>
<
/Link
>
<
small
className
=
{
utilStyles
.
lightText
}
>
<
div
className
=
{
utilStyles
.
poemMeta
}
>
{
date
&&
(
<
span
className
=
{
utilStyles
.
poemMetaItem
}
>
<
Date
dateString
=
{
date
}
/
>
<
Date
dateString
=
{
date
}
/
>
{
language
&&
` •
${
language
}
`
}
<
/span
>
{
tags
&&
tags
.
length
>
0
&&
` •
${
tags
.
join
(
', '
)}
`
}
)}
<
/small
>
<
/li
>
{
language
&&
(
<
span
className
=
{
utilStyles
.
poemMetaItem
}
>
{
language
}
<
/span
>
)}
<
/div
>
{
tags
&&
tags
.
length
>
0
&&
(
<
div
className
=
{
utilStyles
.
tagList
}
>
{
tags
.
map
(
tag
=>
(
<
span
key
=
{
tag
}
className
=
{
utilStyles
.
tag
}
>
{
tag
}
<
/span
>
))}
))}
<
/ul
>
<
/div
>
)}
<
/div
>
))
)}
<
/div
>
<
/div
>
<
/div
>
<
/Layout
>
<
/Layout
>
);
);
...
...
next/pages/poesie/[id].js
View file @
346abc3d
...
@@ -3,6 +3,7 @@ import Layout from '@/components/layout';
...
@@ -3,6 +3,7 @@ import Layout from '@/components/layout';
import
Date
from
'@/components/date'
;
import
Date
from
'@/components/date'
;
import
utilStyles
from
'@/styles/utils.module.css'
;
import
utilStyles
from
'@/styles/utils.module.css'
;
import
{
getPoemData
,
getAllPoemIds
}
from
'@/lib/poems'
;
import
{
getPoemData
,
getAllPoemIds
}
from
'@/lib/poems'
;
import
Link
from
'next/link'
;
export
async
function
getStaticProps
({
params
})
{
export
async
function
getStaticProps
({
params
})
{
const
poemData
=
await
getPoemData
(
params
.
id
);
const
poemData
=
await
getPoemData
(
params
.
id
);
...
@@ -26,14 +27,43 @@ export default function Poem({ poemData }) {
...
@@ -26,14 +27,43 @@ export default function Poem({ poemData }) {
<
Layout
>
<
Layout
>
<
Head
>
<
Head
>
<
title
>
{
poemData
.
title
}
<
/title
>
<
title
>
{
poemData
.
title
}
<
/title
>
<
meta
name
=
"description"
content
=
{
`
${
poemData
.
title
}
- Poem by PLN`
}
/
>
<
meta
name
=
"viewport"
content
=
"width=device-width, initial-scale=1"
/>
<
/Head
>
<
/Head
>
<
article
>
<
Link
href
=
"/poesie"
className
=
{
utilStyles
.
backLink
}
>
←
Back
to
poems
<
/Link
>
<
article
className
=
{
utilStyles
.
poemContainer
}
>
<
header
>
<
h1
className
=
{
utilStyles
.
headingXl
}
>
{
poemData
.
title
}
<
/h1
>
<
h1
className
=
{
utilStyles
.
headingXl
}
>
{
poemData
.
title
}
<
/h1
>
<
div
className
=
{
utilStyles
.
lightText
}
>
<
div
className
=
{
utilStyles
.
poemMeta
}
>
{
poemData
.
date
&&
(
<
span
className
=
{
utilStyles
.
poemMetaItem
}
>
<
Date
dateString
=
{
poemData
.
date
}
/
>
<
Date
dateString
=
{
poemData
.
date
}
/
>
{
poemData
.
language
&&
` •
${
poemData
.
language
}
`
}
<
/span
>
{
poemData
.
tags
&&
poemData
.
tags
.
length
>
0
&&
` •
${
poemData
.
tags
.
join
(
', '
)}
`
}
)}
{
poemData
.
language
&&
(
<
span
className
=
{
utilStyles
.
poemMetaItem
}
>
{
poemData
.
language
}
<
/span
>
)}
<
/div
>
<
/div
>
{
poemData
.
tags
&&
poemData
.
tags
.
length
>
0
&&
(
<
div
className
=
{
utilStyles
.
tagList
}
>
{
poemData
.
tags
.
map
(
tag
=>
(
<
span
key
=
{
tag
}
className
=
{
utilStyles
.
tag
}
>
{
tag
}
<
/span
>
))}
<
/div
>
)}
<
/header
>
<
div
<
div
className
=
{
utilStyles
.
poemContent
}
className
=
{
utilStyles
.
poemContent
}
dangerouslySetInnerHTML
=
{{
__html
:
poemData
.
contentHtml
}}
dangerouslySetInnerHTML
=
{{
__html
:
poemData
.
contentHtml
}}
...
...
next/styles/utils.module.css
View file @
346abc3d
...
@@ -65,20 +65,140 @@
...
@@ -65,20 +65,140 @@
border-radius
:
4px
;
border-radius
:
4px
;
}
}
.poemCard
{
background-color
:
#fff
;
border-radius
:
8px
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0.1
);
padding
:
1.5rem
;
margin-bottom
:
1.5rem
;
transition
:
transform
0.2s
ease-in-out
,
box-shadow
0.2s
ease-in-out
;
}
.poemCard
:hover
{
transform
:
translateY
(
-2px
);
box-shadow
:
0
4px
12px
rgba
(
0
,
0
,
0
,
0.15
);
}
.poemMeta
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
0.5rem
;
margin-top
:
0.5rem
;
color
:
#666
;
font-size
:
0.9rem
;
}
.poemMetaItem
{
display
:
inline-flex
;
align-items
:
center
;
}
.poemMetaItem
:not
(
:last-child
)
::after
{
content
:
"•"
;
margin-left
:
0.5rem
;
opacity
:
0.6
;
}
.tagList
{
display
:
flex
;
flex-wrap
:
wrap
;
gap
:
0.5rem
;
margin-top
:
0.75rem
;
}
.tag
{
background-color
:
#f0f0f0
;
border-radius
:
12px
;
padding
:
0.25rem
0.75rem
;
font-size
:
0.85rem
;
color
:
#555
;
transition
:
background-color
0.2s
;
}
.tag
:hover
{
background-color
:
#e0e0e0
;
}
.filtersContainer
{
background-color
:
#f8f9fa
;
border-radius
:
8px
;
padding
:
1.5rem
;
margin-bottom
:
2rem
;
}
.filtersGroup
{
display
:
flex
;
flex-direction
:
column
;
gap
:
1rem
;
}
@media
(
min-width
:
768px
)
{
.filtersGroup
{
flex-direction
:
row
;
flex-wrap
:
wrap
;
}
}
.filterItem
{
display
:
flex
;
align-items
:
center
;
flex-wrap
:
wrap
;
gap
:
0.5rem
;
}
.filterLabel
{
font-weight
:
600
;
color
:
#333
;
min-width
:
80px
;
}
.filterSelect
{
padding
:
0.5rem
0.75rem
;
border
:
1px
solid
#ddd
;
border-radius
:
4px
;
background-color
:
white
;
min-width
:
120px
;
font-size
:
0.9rem
;
color
:
#333
;
transition
:
border-color
0.2s
,
box-shadow
0.2s
;
}
.filterSelect
:focus
{
border-color
:
#0070f3
;
box-shadow
:
0
0
0
2px
rgba
(
0
,
112
,
243
,
0.2
);
outline
:
none
;
}
.filterSelect
:hover
{
border-color
:
#999
;
}
.poemContent
{
.poemContent
{
margin-top
:
2rem
;
margin-top
:
2rem
;
line-height
:
1.8
;
line-height
:
1.8
;
white-space
:
pre-wrap
;
white-space
:
pre-wrap
;
font-size
:
1.1rem
;
color
:
#333
;
max-width
:
70ch
;
}
@media
(
max-width
:
768px
)
{
.poemContent
{
font-size
:
1rem
;
line-height
:
1.7
;
}
}
}
.poemContent
p
{
.poemContent
p
{
margin
:
1rem
0
;
margin
:
1
.5
rem
0
;
}
}
.poemContent
img
{
.poemContent
img
{
max-width
:
100%
;
max-width
:
100%
;
height
:
auto
;
height
:
auto
;
margin
:
1rem
0
;
margin
:
1.5rem
0
;
border-radius
:
4px
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0.1
);
}
}
.listItemLink
{
.listItemLink
{
...
@@ -115,3 +235,33 @@
...
@@ -115,3 +235,33 @@
.inlineLink
:hover
{
.inlineLink
:hover
{
opacity
:
0.8
;
opacity
:
0.8
;
}
}
.poemContainer
{
max-width
:
800px
;
margin
:
2rem
auto
;
padding
:
2rem
;
background-color
:
#fff
;
border-radius
:
8px
;
box-shadow
:
0
2px
10px
rgba
(
0
,
0
,
0
,
0.1
);
}
.backLink
{
display
:
inline-block
;
margin
:
1rem
0
;
color
:
#666
;
text-decoration
:
none
;
font-size
:
0.9rem
;
transition
:
color
0.2s
;
}
.backLink
:hover
{
color
:
#0070f3
;
text-decoration
:
underline
;
}
@media
(
max-width
:
768px
)
{
.poemContainer
{
padding
:
1.5rem
;
margin
:
1rem
auto
;
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment