Skip to content

Commit 6169bfb

Browse files
committed
Fri Nov 7 17:16:13 PST 2025
1 parent 5ce78df commit 6169bfb

File tree

18 files changed

+1150
-518
lines changed

18 files changed

+1150
-518
lines changed

app/assets/css/main.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
--font-sans: 'Fira Sans', ui-sans-serif, system-ui, sans-serif;
1616
--font-mono: 'TX-02 Variable', ui-monospace, 'Cascadia Code', monospace;
1717
--font-heading: 'Arimo', 'Arimo Placeholder', ui-sans-serif, system-ui, sans-serif;
18+
--ui-radius: 0.25rem;
1819

1920
/* ===== Line Heights =====
2021
* Proportional fonts (Fira Sans, Arimo): 1.5-1.6
@@ -330,4 +331,4 @@ samp,
330331
font-size: 11pt;
331332
line-height: 1.5;
332333
}
333-
}
334+
}

app/components/ResourcesMenu.vue

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,21 +3,22 @@
33
v-slot="{ open }"
44
:modal="false"
55
:items="[{
6-
label: 'Website',
7-
to: 'https://0x77.dev'
8-
}, {
9-
label: 'CV',
10-
to: 'https://cv.0x77.dev'
11-
}, {
12-
label: 'Index',
13-
to: 'https://docs-template.nuxt.dev/',
14-
color: 'primary',
15-
checked: true,
16-
type: 'checkbox'
17-
}, {
18-
label: 'Socials',
19-
to: 'https://mykhailo.link/'
20-
}]"
6+
label: 'Website',
7+
to: 'https://0x77.dev'
8+
},
9+
{
10+
label: 'Blog',
11+
to: 'https://blog.0x77.dev/'
12+
}, {
13+
label: 'Index',
14+
to: 'https://index.0x77.dev/',
15+
color: 'primary',
16+
checked: true,
17+
type: 'checkbox'
18+
}, {
19+
label: 'Socials',
20+
to: 'https://mykhailo.link/'
21+
}]"
2122
:ui="{ content: 'w-(--reka-dropdown-menu-trigger-width) min-w-0' }"
2223
size="xs"
2324
>

app/pages/[...slug].vue

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,9 +43,14 @@ if (page.value.schemaOrg) {
4343
4444
const headline = computed(() => findPageHeadline(navigation?.value, page.value))
4545
46-
if (!page.value.image?.src) {
47-
defineOgImageComponent('Docs', {
48-
headline: headline.value
46+
if (page.value?.ogImage) {
47+
defineOgImage(page.value.ogImage)
48+
} else if (!page.value.image?.src) {
49+
defineOgImage({
50+
component: 'Docs',
51+
props: {
52+
title: headline.value || page.value.title
53+
}
4954
})
5055
}
5156

app/pages/index.vue

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ useSeoMeta({
1414
ogDescription: description
1515
})
1616
17-
defineOgImageComponent('Docs')
17+
defineOgImage({
18+
component: 'Docs',
19+
props: {
20+
title
21+
}
22+
})
1823
</script>
1924

2025
<template>

bun.lock

Lines changed: 968 additions & 379 deletions
Large diffs are not rendered by default.

content.config.ts

Lines changed: 89 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -1,96 +1,99 @@
11
import { defineCollection, defineContentConfig, z } from '@nuxt/content'
2+
import { asOgImageCollection } from 'nuxt-og-image/content'
23

34
export default defineContentConfig({
45
collections: {
5-
docs: defineCollection({
6-
type: 'page',
7-
source: {
8-
include: '**',
9-
exclude: ['index.md']
10-
},
11-
schema: z.object({
12-
links: z.array(z.object({
13-
label: z.string(),
14-
icon: z.string(),
15-
to: z.string(),
16-
target: z.string().optional(),
17-
variant: z.enum(['link', 'solid', 'outline', 'soft', 'subtle', 'ghost']).optional(),
18-
alt: z.string().optional()
19-
})).optional(),
20-
image: z.object({ src: z.string().nonempty().editor({ input: 'media' }) }),
21-
authors: z.array(
22-
z.object({
23-
name: z.string().nonempty(),
24-
to: z.string().nonempty(),
25-
avatar: z.object({ src: z.string().nonempty().editor({ input: 'media' }) })
26-
})
27-
),
28-
hide: z.boolean().optional(),
29-
date: z.date(),
30-
badge: z.object({ label: z.string().nonempty() }),
31-
// Schema.org structured data - universal approach
32-
schemaOrg: z.union([
33-
// ScholarlyArticle
34-
z.object({
35-
type: z.literal('ScholarlyArticle'),
36-
headline: z.string().optional(),
37-
datePublished: z.string().optional(),
38-
abstract: z.string().optional(),
39-
image: z.string().optional(),
40-
publisher: z.union([
41-
z.object({
42-
type: z.literal('Organization'),
43-
name: z.string()
44-
}),
45-
z.string()
46-
]).optional(),
47-
author: z.array(z.object({
48-
name: z.string(),
49-
identifier: z.string().optional(),
50-
type: z.literal('Person').optional(),
51-
affiliation: z.string().optional(),
52-
url: z.string().optional()
53-
})).optional(),
54-
keywords: z.array(z.string()).optional(),
55-
mainEntityOfPage: z.string().optional(),
56-
sameAs: z.string().optional(),
57-
citation: z.object({
58-
type: z.string().optional(),
59-
name: z.string().optional()
60-
}).optional()
61-
}),
62-
// WebPage (default)
63-
z.object({
64-
type: z.literal('WebPage'),
65-
headline: z.string().optional(),
66-
description: z.string().optional(),
67-
image: z.string().optional(),
68-
datePublished: z.string().optional(),
69-
dateModified: z.string().optional()
70-
}),
71-
// BlogPosting
72-
z.object({
73-
type: z.literal('BlogPosting'),
74-
headline: z.string().optional(),
75-
author: z.union([
76-
z.array(z.object({
77-
type: z.literal('Person').optional(),
6+
docs: defineCollection(
7+
asOgImageCollection({
8+
type: 'page',
9+
source: {
10+
include: '**',
11+
exclude: ['index.md']
12+
},
13+
schema: z.object({
14+
links: z.array(z.object({
15+
label: z.string(),
16+
icon: z.string(),
17+
to: z.string(),
18+
target: z.string().optional(),
19+
variant: z.enum(['link', 'solid', 'outline', 'soft', 'subtle', 'ghost']).optional(),
20+
alt: z.string().optional()
21+
})).optional(),
22+
image: z.object({ src: z.string().nonempty().editor({ input: 'media' }) }),
23+
authors: z.array(
24+
z.object({
25+
name: z.string().nonempty(),
26+
to: z.string().nonempty(),
27+
avatar: z.object({ src: z.string().nonempty().editor({ input: 'media' }) })
28+
})
29+
),
30+
hide: z.boolean().optional(),
31+
date: z.date(),
32+
badge: z.object({ label: z.string().nonempty() }),
33+
// Schema.org structured data - universal approach
34+
schemaOrg: z.union([
35+
// ScholarlyArticle
36+
z.object({
37+
type: z.literal('ScholarlyArticle'),
38+
headline: z.string().optional(),
39+
datePublished: z.string().optional(),
40+
abstract: z.string().optional(),
41+
image: z.string().optional(),
42+
publisher: z.union([
43+
z.object({
44+
type: z.literal('Organization'),
45+
name: z.string()
46+
}),
47+
z.string()
48+
]).optional(),
49+
author: z.array(z.object({
7850
name: z.string(),
79-
url: z.string().optional()
80-
})),
81-
z.object({
51+
identifier: z.string().optional(),
8252
type: z.literal('Person').optional(),
83-
name: z.string(),
53+
affiliation: z.string().optional(),
8454
url: z.string().optional()
85-
})
86-
]).optional(),
87-
datePublished: z.string().optional(),
88-
dateModified: z.string().optional(),
89-
image: z.string().optional(),
90-
description: z.string().optional()
91-
})
92-
]).optional()
55+
})).optional(),
56+
keywords: z.array(z.string()).optional(),
57+
mainEntityOfPage: z.string().optional(),
58+
sameAs: z.string().optional(),
59+
citation: z.object({
60+
type: z.string().optional(),
61+
name: z.string().optional()
62+
}).optional()
63+
}),
64+
// WebPage (default)
65+
z.object({
66+
type: z.literal('WebPage'),
67+
headline: z.string().optional(),
68+
description: z.string().optional(),
69+
image: z.string().optional(),
70+
datePublished: z.string().optional(),
71+
dateModified: z.string().optional()
72+
}),
73+
// BlogPosting
74+
z.object({
75+
type: z.literal('BlogPosting'),
76+
headline: z.string().optional(),
77+
author: z.union([
78+
z.array(z.object({
79+
type: z.literal('Person').optional(),
80+
name: z.string(),
81+
url: z.string().optional()
82+
})),
83+
z.object({
84+
type: z.literal('Person').optional(),
85+
name: z.string(),
86+
url: z.string().optional()
87+
})
88+
]).optional(),
89+
datePublished: z.string().optional(),
90+
dateModified: z.string().optional(),
91+
image: z.string().optional(),
92+
description: z.string().optional()
93+
})
94+
]).optional()
95+
})
9396
})
94-
})
97+
)
9598
}
9699
})

devenv.lock

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
"devenv": {
44
"locked": {
55
"dir": "src/modules",
6-
"lastModified": 1760807882,
6+
"lastModified": 1762537545,
77
"owner": "cachix",
88
"repo": "devenv",
9-
"rev": "33f554540250ef0f983685150166985094844ba7",
9+
"rev": "45f9f5b334c7224b32214324686cda87a9c07889",
1010
"type": "github"
1111
},
1212
"original": {
@@ -19,10 +19,10 @@
1919
"flake-compat": {
2020
"flake": false,
2121
"locked": {
22-
"lastModified": 1747046372,
22+
"lastModified": 1761588595,
2323
"owner": "edolstra",
2424
"repo": "flake-compat",
25-
"rev": "9100a0f413b0c601e0533d1d94ffd501ce2e7885",
25+
"rev": "f387cd2afec9419c8ee37694406ca490c3f34ee5",
2626
"type": "github"
2727
},
2828
"original": {
@@ -40,10 +40,10 @@
4040
]
4141
},
4242
"locked": {
43-
"lastModified": 1760663237,
43+
"lastModified": 1762441963,
4444
"owner": "cachix",
4545
"repo": "git-hooks.nix",
46-
"rev": "ca5b894d3e3e151ffc1db040b6ce4dcc75d31c37",
46+
"rev": "8e7576e79b88c16d7ee3bbd112c8d90070832885",
4747
"type": "github"
4848
},
4949
"original": {
@@ -74,10 +74,10 @@
7474
},
7575
"nixpkgs": {
7676
"locked": {
77-
"lastModified": 1760596604,
77+
"lastModified": 1762361079,
7878
"owner": "NixOS",
7979
"repo": "nixpkgs",
80-
"rev": "3cbe716e2346710d6e1f7c559363d14e11c32a43",
80+
"rev": "ffcdcf99d65c61956d882df249a9be53e5902ea5",
8181
"type": "github"
8282
},
8383
"original": {

nuxt.config.ts

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,27 +41,39 @@ export default defineNuxtConfig({
4141

4242
experimental: {
4343
viewTransition: true,
44-
watcher: 'parcel',
4544
payloadExtraction: true
4645
},
4746

48-
compatibilityDate: '2025-10-18',
47+
compatibilityDate: '2025-11-07',
4948

5049
nitro: {
5150
prerender: {
5251
routes: [
5352
'/'
5453
],
55-
failOnError: false,
56-
autoSubfolderIndex: false,
5754
crawlLinks: true
55+
},
56+
rollupConfig: {
57+
external: ['readdirp', 'picomatch']
58+
},
59+
experimental: {
60+
openAPI: true
61+
},
62+
openAPI: {
63+
meta: {
64+
title: 'Index API',
65+
description: 'API for the index.0x77.dev',
66+
version: '0.0.0'
67+
}
5868
}
5969
},
6070

6171
hub: {
6272
ai: true,
6373
analytics: true,
6474
cache: true,
75+
database: true,
76+
kv: true,
6577
bindings: {
6678
observability: {
6779
logs: true
@@ -108,6 +120,17 @@ export default defineNuxtConfig({
108120
]
109121
},
110122

123+
ogImage: {
124+
// Use Satori for rendering (lightweight, no chromium needed)
125+
defaults: {
126+
renderer: 'satori',
127+
// Cache for 7 days
128+
cacheMaxAgeSeconds: 60 * 60 * 24 * 7
129+
},
130+
// Use NuxtHub's built-in cache
131+
runtimeCacheStorage: true
132+
},
133+
111134
plausible: {
112135
ignoredHostnames: ['localhost'],
113136
autoOutboundTracking: true,

0 commit comments

Comments
 (0)