@anglefeint/astro-theme 0.1.0-alpha.0 → 0.1.0-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +9 -0
- package/package.json +1 -1
- package/public/scripts/about-effects.js +75 -75
- package/public/scripts/blogpost-effects.js +19 -19
- package/public/styles/about-page.css +125 -125
- package/public/styles/blog-post.css +16 -16
- package/src/cli-new-page.mjs +12 -6
- package/src/cli-new-post.mjs +0 -0
- package/src/components/Footer.astro +2 -71
- package/src/components/Header.astro +2 -325
- package/src/components/shared/CommonFooter.astro +74 -0
- package/src/components/shared/CommonHeader.astro +328 -0
- package/src/components/shared/ThemeFrame.astro +85 -0
- package/src/layouts/AiPageLayout.astro +16 -0
- package/src/layouts/BasePageLayout.astro +6 -57
- package/src/layouts/BlogPost.astro +76 -93
- package/src/layouts/CyberPageLayout.astro +16 -0
- package/src/layouts/HackerPageLayout.astro +16 -0
- package/src/layouts/HomePage.astro +29 -55
- package/src/layouts/MatrixPageLayout.astro +16 -0
- package/src/layouts/shells/AiShell.astro +58 -0
- package/src/layouts/shells/BaseShell.astro +16 -0
- package/src/layouts/shells/CyberShell.astro +30 -0
- package/src/layouts/shells/HackerShell.astro +21 -0
- package/src/layouts/shells/MatrixShell.astro +19 -0
- package/src/styles/global.css +236 -236
|
@@ -3,10 +3,8 @@ import { Image } from 'astro:assets';
|
|
|
3
3
|
import type { CollectionEntry } from 'astro:content';
|
|
4
4
|
import themeRedqueen1 from '../assets/theme/red-queen/theme-redqueen1.webp';
|
|
5
5
|
import themeRedqueen2 from '../assets/theme/red-queen/theme-redqueen2.gif';
|
|
6
|
-
import BaseHead from '../components/BaseHead.astro';
|
|
7
|
-
import Footer from '../components/Footer.astro';
|
|
8
6
|
import FormattedDate from '../components/FormattedDate.astro';
|
|
9
|
-
import
|
|
7
|
+
import AiShell from './shells/AiShell.astro';
|
|
10
8
|
import { SITE_AUTHOR } from '../consts';
|
|
11
9
|
import { DEFAULT_LOCALE, type Locale, isLocale, localePath, blogIdToSlugAnyLocale } from '../i18n/config';
|
|
12
10
|
import { getMessages } from '../i18n/messages';
|
|
@@ -75,42 +73,40 @@ for (let i = 0; i < pts.length; i++) {
|
|
|
75
73
|
}
|
|
76
74
|
---
|
|
77
75
|
|
|
78
|
-
<
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
<div class="
|
|
96
|
-
<div class="
|
|
97
|
-
<div class="
|
|
98
|
-
<div class="
|
|
99
|
-
<div class="
|
|
100
|
-
<div class="mesh-hex-grid" aria-hidden="true"></div>
|
|
101
|
-
<div class="mesh-thought-particles" aria-hidden="true">
|
|
76
|
+
<AiShell
|
|
77
|
+
locale={resolvedLocale}
|
|
78
|
+
title={title}
|
|
79
|
+
description={description}
|
|
80
|
+
image={heroImage}
|
|
81
|
+
pageType="article"
|
|
82
|
+
publishedTime={pubDate}
|
|
83
|
+
modifiedTime={updatedDate}
|
|
84
|
+
author={author ?? SITE_AUTHOR}
|
|
85
|
+
tags={tags}
|
|
86
|
+
localeHrefs={localeHrefs}
|
|
87
|
+
>
|
|
88
|
+
<link slot="head" rel="stylesheet" href="/styles/blog-post.css" />
|
|
89
|
+
<Fragment slot="body-start">
|
|
90
|
+
<div class="ai-bg" aria-hidden="true">
|
|
91
|
+
<div class="ai-glow ai-glow-shift"></div>
|
|
92
|
+
<div class="ai-haze" aria-hidden="true"></div>
|
|
93
|
+
<div class="ai-vignette" aria-hidden="true"></div>
|
|
94
|
+
<div class="ai-stripe"></div>
|
|
95
|
+
<div class="ai-noise" aria-hidden="true"></div>
|
|
96
|
+
<div class="ai-hex-grid" aria-hidden="true"></div>
|
|
97
|
+
<div class="ai-thought-particles" aria-hidden="true">
|
|
102
98
|
{[...Array(12)].map((_, i) => (
|
|
103
|
-
<span class="
|
|
99
|
+
<span class="ai-particle" style={`--x: ${20 + (i * 7) % 60}%; --y: ${10 + (i * 11) % 70}%; --d: ${3 + (i % 4)}s`}></span>
|
|
104
100
|
))}
|
|
105
101
|
</div>
|
|
106
|
-
<svg class="
|
|
102
|
+
<svg class="ai-network" viewBox="0 0 1200 800" preserveAspectRatio="xMidYMid slice">
|
|
107
103
|
<defs>
|
|
108
|
-
<linearGradient id="
|
|
104
|
+
<linearGradient id="ai-line-grad" x1="0%" y1="0%" x2="100%" y2="0%">
|
|
109
105
|
<stop offset="0%" stop-color="rgba(180,235,255,0.26)" />
|
|
110
106
|
<stop offset="50%" stop-color="rgba(236,252,255,0.74)" />
|
|
111
107
|
<stop offset="100%" stop-color="rgba(180,235,255,0.26)" />
|
|
112
108
|
</linearGradient>
|
|
113
|
-
<filter id="
|
|
109
|
+
<filter id="ai-dot-glow">
|
|
114
110
|
<feGaussianBlur stdDeviation="2.2" result="blur" />
|
|
115
111
|
<feMerge>
|
|
116
112
|
<feMergeNode in="blur" />
|
|
@@ -118,57 +114,45 @@ for (let i = 0; i < pts.length; i++) {
|
|
|
118
114
|
</feMerge>
|
|
119
115
|
</filter>
|
|
120
116
|
</defs>
|
|
121
|
-
<g class="
|
|
117
|
+
<g class="ai-lines">
|
|
122
118
|
{edges.map(([i, j]) => (
|
|
123
119
|
<line
|
|
124
120
|
x1={pts[i].x}
|
|
125
121
|
y1={pts[i].y}
|
|
126
122
|
x2={pts[j].x}
|
|
127
123
|
y2={pts[j].y}
|
|
128
|
-
stroke="url(#
|
|
124
|
+
stroke="url(#ai-line-grad)"
|
|
129
125
|
stroke-width="0.4"
|
|
130
126
|
/>
|
|
131
127
|
))}
|
|
132
128
|
</g>
|
|
133
|
-
<g class="
|
|
129
|
+
<g class="ai-dots">
|
|
134
130
|
{pts.map((p) => (
|
|
135
131
|
<circle
|
|
136
132
|
cx={p.x}
|
|
137
133
|
cy={p.y}
|
|
138
134
|
r={p.r}
|
|
139
135
|
fill={`rgba(232,255,255,${0.72 + p.depth * 0.48})`}
|
|
140
|
-
filter="url(#
|
|
136
|
+
filter="url(#ai-dot-glow)"
|
|
141
137
|
/>
|
|
142
138
|
))}
|
|
143
139
|
</g>
|
|
144
140
|
</svg>
|
|
145
141
|
</div>
|
|
146
|
-
<Header
|
|
147
|
-
locale={resolvedLocale}
|
|
148
|
-
localeHrefs={localeHrefs}
|
|
149
|
-
scanlines
|
|
150
|
-
labels={{
|
|
151
|
-
home: messages.nav.home,
|
|
152
|
-
blog: messages.nav.blog,
|
|
153
|
-
about: messages.nav.about,
|
|
154
|
-
status: messages.nav.status,
|
|
155
|
-
language: messages.langLabel,
|
|
156
|
-
}}
|
|
157
|
-
/>
|
|
158
142
|
<aside class="rq-tv rq-tv-collapsed">
|
|
159
143
|
<div class="rq-tv-stage" data-rq-src={themeRedqueen1.src} data-rq-src2={themeRedqueen2.src}></div>
|
|
160
144
|
<div class="rq-tv-badge">monitor feed<span class="rq-tv-dot"></span></div>
|
|
161
145
|
<button type="button" class="rq-tv-toggle" aria-label="Replay monitor feed" aria-expanded="false">▶</button>
|
|
162
146
|
</aside>
|
|
163
|
-
<div class="
|
|
164
|
-
<button type="button" class="
|
|
165
|
-
<div class="
|
|
166
|
-
<div class="
|
|
167
|
-
<div class="
|
|
168
|
-
<div class="
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
147
|
+
<div class="ai-read-progress" aria-hidden="true"></div>
|
|
148
|
+
<button type="button" class="ai-back-to-top" aria-label="Back to top" title="Back to top">↑</button>
|
|
149
|
+
<div class="ai-stage-toast" aria-live="polite" aria-atomic="true"></div>
|
|
150
|
+
<div class="ai-mouse-glow" aria-hidden="true"></div>
|
|
151
|
+
<div class="ai-depth-blur" aria-hidden="true"></div>
|
|
152
|
+
<div class="ai-thinking-dots" aria-hidden="true"><span></span><span></span><span></span></div>
|
|
153
|
+
</Fragment>
|
|
154
|
+
<div class="ai-load-scan" aria-hidden="true"></div>
|
|
155
|
+
<article class="ai-article">
|
|
172
156
|
{heroImage && (
|
|
173
157
|
<div class="hero-shell">
|
|
174
158
|
<div class="hero-pane">
|
|
@@ -191,74 +175,74 @@ for (let i = 0; i < pts.length; i++) {
|
|
|
191
175
|
</div>
|
|
192
176
|
)}
|
|
193
177
|
<div class="prose">
|
|
194
|
-
<div class="title
|
|
195
|
-
<div class="
|
|
178
|
+
<div class="title ai-title">
|
|
179
|
+
<div class="ai-meta-terminal">
|
|
196
180
|
$ published {fmt(pubDate)}
|
|
197
181
|
{updatedDate && <> | updated {fmt(updatedDate)}</>}
|
|
198
182
|
{readMinutes !== undefined && <> | ~{readMinutes} min read</>}
|
|
199
183
|
</div>
|
|
200
184
|
{hasSystemMeta && (
|
|
201
|
-
<div class="
|
|
202
|
-
{aiModel && <span class="
|
|
203
|
-
{aiMode && <span class="
|
|
204
|
-
{aiState && <span class="
|
|
185
|
+
<div class="ai-system-row" aria-label="Model status">
|
|
186
|
+
{aiModel && <span class="ai-system-chip">model: {aiModel}</span>}
|
|
187
|
+
{aiMode && <span class="ai-system-chip">mode: {aiMode}</span>}
|
|
188
|
+
{aiState && <span class="ai-system-chip">state: {aiState}</span>}
|
|
205
189
|
</div>
|
|
206
190
|
)}
|
|
207
191
|
{contextText && (
|
|
208
|
-
<div class="
|
|
209
|
-
Context: <span class="
|
|
192
|
+
<div class="ai-prompt-line">
|
|
193
|
+
Context: <span class="ai-prompt-topic">{contextText}</span> →
|
|
210
194
|
</div>
|
|
211
195
|
)}
|
|
212
|
-
<h1 class="
|
|
213
|
-
{subtitle && <p class="
|
|
196
|
+
<h1 class="ai-title-text">{title}</h1>
|
|
197
|
+
{subtitle && <p class="ai-subtitle-text">{subtitle}</p>}
|
|
214
198
|
<hr />
|
|
215
|
-
<div class="
|
|
199
|
+
<div class="ai-title-flow" aria-hidden="true"></div>
|
|
216
200
|
</div>
|
|
217
|
-
<div class="
|
|
218
|
-
<div class="
|
|
219
|
-
<div class="
|
|
220
|
-
<span class="
|
|
201
|
+
<div class="ai-response-wrap">
|
|
202
|
+
<div class="ai-response-header">
|
|
203
|
+
<div class="ai-response-avatar" aria-hidden="true"></div>
|
|
204
|
+
<span class="ai-response-label">Output</span>
|
|
221
205
|
{hasResponseMeta && (
|
|
222
|
-
<div class="
|
|
206
|
+
<div class="ai-response-meta">
|
|
223
207
|
{aiLatencyMs !== undefined && <span>latency est <strong>{aiLatencyMs}</strong> ms</span>}
|
|
224
208
|
{confidenceText !== undefined && <span>confidence <strong>{confidenceText}</strong></span>}
|
|
225
209
|
</div>
|
|
226
210
|
)}
|
|
227
211
|
</div>
|
|
228
|
-
<div class="
|
|
212
|
+
<div class="ai-prose-body ai-prose-fade">
|
|
229
213
|
<slot />
|
|
230
|
-
<span class="
|
|
214
|
+
<span class="ai-block-cursor" aria-hidden="true"></span>
|
|
231
215
|
</div>
|
|
232
216
|
</div>
|
|
233
217
|
{hasStats && (
|
|
234
|
-
<div class="
|
|
235
|
-
{aiModel && <span class="
|
|
218
|
+
<div class="ai-stats-corner">
|
|
219
|
+
{aiModel && <span class="ai-model-id">{aiModel}</span>}
|
|
236
220
|
{aiModel && (wordCount !== undefined || tokenCount !== undefined) && ' · '}
|
|
237
221
|
{wordCount !== undefined && <span>{compact(wordCount)} words</span>}
|
|
238
222
|
{wordCount !== undefined && tokenCount !== undefined && ' · '}
|
|
239
223
|
{tokenCount !== undefined && <span>{compact(tokenCount)} tokens</span>}
|
|
240
224
|
</div>
|
|
241
225
|
)}
|
|
242
|
-
<button type="button" class="
|
|
226
|
+
<button type="button" class="ai-regenerate" aria-label={messages.blog.regenerate}>{messages.blog.regenerate}</button>
|
|
243
227
|
</div>
|
|
244
228
|
</article>
|
|
245
229
|
{related.length > 0 && (
|
|
246
|
-
<section class="
|
|
247
|
-
<h2 class="
|
|
248
|
-
<div class="
|
|
230
|
+
<section class="ai-related" aria-label="Related posts">
|
|
231
|
+
<h2 class="ai-related-title">{messages.blog.related}</h2>
|
|
232
|
+
<div class="ai-related-grid">
|
|
249
233
|
{related.map((p) => (
|
|
250
|
-
<a href={localePath(resolvedLocale, `/blog/${blogIdToSlugAnyLocale(p.id)}`)} class="
|
|
234
|
+
<a href={localePath(resolvedLocale, `/blog/${blogIdToSlugAnyLocale(p.id)}`)} class="ai-related-card">
|
|
251
235
|
{p.data.heroImage ? (
|
|
252
|
-
<div class="
|
|
236
|
+
<div class="ai-related-img">
|
|
253
237
|
<Image width={320} height={180} src={p.data.heroImage} alt={p.data.title} />
|
|
254
238
|
</div>
|
|
255
239
|
) : (
|
|
256
|
-
<div class="
|
|
240
|
+
<div class="ai-related-placeholder">
|
|
257
241
|
<span>{p.data.title.charAt(0)}</span>
|
|
258
242
|
</div>
|
|
259
243
|
)}
|
|
260
|
-
<h3 class="
|
|
261
|
-
<p class="
|
|
244
|
+
<h3 class="ai-related-card-title">{p.data.title}</h3>
|
|
245
|
+
<p class="ai-related-card-date">
|
|
262
246
|
<FormattedDate date={p.data.pubDate} />
|
|
263
247
|
</p>
|
|
264
248
|
</a>
|
|
@@ -266,14 +250,13 @@ for (let i = 0; i < pts.length; i++) {
|
|
|
266
250
|
</div>
|
|
267
251
|
</section>
|
|
268
252
|
)}
|
|
269
|
-
<nav class="
|
|
253
|
+
<nav class="ai-back-to-blog" aria-label="Back to blog">
|
|
270
254
|
<a href={localePath(resolvedLocale, '/blog/')}>
|
|
271
|
-
<span class="
|
|
272
|
-
<span class="
|
|
255
|
+
<span class="ai-back-prompt">$</span>
|
|
256
|
+
<span class="ai-back-text">← {messages.blog.backToBlog}</span>
|
|
273
257
|
</a>
|
|
274
258
|
</nav>
|
|
275
|
-
|
|
276
|
-
<
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
</html>
|
|
259
|
+
<Fragment slot="body-end">
|
|
260
|
+
<script is:inline src="/scripts/blogpost-effects.js" defer></script>
|
|
261
|
+
</Fragment>
|
|
262
|
+
</AiShell>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Locale } from '../i18n/config';
|
|
3
|
+
import CyberShell from './shells/CyberShell.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
locale?: Locale;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { locale, title, description } = Astro.props as Props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<CyberShell locale={locale} title={title} description={description}>
|
|
15
|
+
<slot />
|
|
16
|
+
</CyberShell>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Locale } from '../i18n/config';
|
|
3
|
+
import HackerShell from './shells/HackerShell.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
locale?: Locale;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { locale, title, description } = Astro.props as Props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<HackerShell locale={locale} title={title} description={description}>
|
|
15
|
+
<slot />
|
|
16
|
+
</HackerShell>
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
---
|
|
2
2
|
import type { CollectionEntry } from 'astro:content';
|
|
3
|
-
import BaseHead from '../components/BaseHead.astro';
|
|
4
|
-
import Footer from '../components/Footer.astro';
|
|
5
3
|
import FormattedDate from '../components/FormattedDate.astro';
|
|
6
|
-
import Header from '../components/Header.astro';
|
|
7
4
|
import { SITE_HERO_BY_LOCALE, SITE_TITLE } from '../consts';
|
|
8
5
|
import { type Locale, blogIdToSlugAnyLocale, localePath } from '../i18n/config';
|
|
9
6
|
import { getMessages } from '../i18n/messages';
|
|
7
|
+
import MatrixShell from './shells/MatrixShell.astro';
|
|
10
8
|
|
|
11
9
|
interface Props {
|
|
12
10
|
locale: Locale;
|
|
@@ -18,56 +16,32 @@ const messages = getMessages(locale);
|
|
|
18
16
|
const heroText = SITE_HERO_BY_LOCALE[locale] ?? messages.home.hero;
|
|
19
17
|
---
|
|
20
18
|
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
<link rel="stylesheet" href="/styles/home-page.css" />
|
|
26
|
-
<link rel="preload" href="/fonts/ff2a407fe06752facf8828a86955e988.woff2" as="font" type="font/woff2" crossorigin />
|
|
27
|
-
</head>
|
|
28
|
-
<body class="page-home">
|
|
29
|
-
<canvas id="matrix-bg" aria-hidden="true"></canvas>
|
|
30
|
-
<Header
|
|
31
|
-
locale={locale}
|
|
32
|
-
labels={{
|
|
33
|
-
home: messages.nav.home,
|
|
34
|
-
blog: messages.nav.blog,
|
|
35
|
-
about: messages.nav.about,
|
|
36
|
-
status: messages.nav.status,
|
|
37
|
-
language: messages.langLabel,
|
|
38
|
-
}}
|
|
39
|
-
/>
|
|
40
|
-
<main>
|
|
41
|
-
<h1>{SITE_TITLE}</h1>
|
|
42
|
-
<p class="home-hero-copy">
|
|
43
|
-
{heroText}
|
|
44
|
-
</p>
|
|
19
|
+
<MatrixShell locale={locale} title={SITE_TITLE} description={messages.siteDescription}>
|
|
20
|
+
<link slot="head" rel="preload" href="/fonts/ff2a407fe06752facf8828a86955e988.woff2" as="font" type="font/woff2" crossorigin />
|
|
21
|
+
<h1>{SITE_TITLE}</h1>
|
|
22
|
+
<p class="home-hero-copy">{heroText}</p>
|
|
45
23
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<Footer />
|
|
71
|
-
<script is:inline src="/scripts/home-matrix.js" defer></script>
|
|
72
|
-
</body>
|
|
73
|
-
</html>
|
|
24
|
+
<section class="home-latest">
|
|
25
|
+
<h2 class="home-section-title">{messages.home.latest}</h2>
|
|
26
|
+
<ul class="home-post-list">
|
|
27
|
+
{
|
|
28
|
+
latestPosts.length > 0 ? (
|
|
29
|
+
latestPosts.map((post) => (
|
|
30
|
+
<li>
|
|
31
|
+
<a class="home-post-title" href={localePath(locale, `/blog/${blogIdToSlugAnyLocale(post.id)}`)}>
|
|
32
|
+
{post.data.title}
|
|
33
|
+
</a>
|
|
34
|
+
<div class="home-post-meta">
|
|
35
|
+
<FormattedDate date={post.data.pubDate} />
|
|
36
|
+
{post.data.description && <> · {post.data.description}</>}
|
|
37
|
+
</div>
|
|
38
|
+
</li>
|
|
39
|
+
))
|
|
40
|
+
) : (
|
|
41
|
+
<li>{messages.home.noPosts}</li>
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
</ul>
|
|
45
|
+
<p><a href={localePath(locale, '/blog/')}>{messages.home.viewAll}</a></p>
|
|
46
|
+
</section>
|
|
47
|
+
</MatrixShell>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Locale } from '../i18n/config';
|
|
3
|
+
import MatrixShell from './shells/MatrixShell.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
locale?: Locale;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { locale, title, description } = Astro.props as Props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<MatrixShell locale={locale} title={title} description={description}>
|
|
15
|
+
<slot />
|
|
16
|
+
</MatrixShell>
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Locale } from '../../i18n/config';
|
|
3
|
+
import ThemeFrame from '../../components/shared/ThemeFrame.astro';
|
|
4
|
+
import type { ImageMetadata } from 'astro';
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
locale?: Locale;
|
|
8
|
+
title: string;
|
|
9
|
+
description: string;
|
|
10
|
+
image?: ImageMetadata;
|
|
11
|
+
pageType?: 'website' | 'article';
|
|
12
|
+
publishedTime?: Date;
|
|
13
|
+
modifiedTime?: Date;
|
|
14
|
+
author?: string;
|
|
15
|
+
tags?: string[];
|
|
16
|
+
schema?: Record<string, unknown> | Record<string, unknown>[];
|
|
17
|
+
noindex?: boolean;
|
|
18
|
+
localeHrefs?: Partial<Record<Locale, string>>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const {
|
|
22
|
+
locale,
|
|
23
|
+
title,
|
|
24
|
+
description,
|
|
25
|
+
image,
|
|
26
|
+
pageType,
|
|
27
|
+
publishedTime,
|
|
28
|
+
modifiedTime,
|
|
29
|
+
author,
|
|
30
|
+
tags,
|
|
31
|
+
schema,
|
|
32
|
+
noindex,
|
|
33
|
+
localeHrefs,
|
|
34
|
+
} = Astro.props as Props;
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
<ThemeFrame
|
|
38
|
+
locale={locale}
|
|
39
|
+
title={title}
|
|
40
|
+
description={description}
|
|
41
|
+
image={image}
|
|
42
|
+
pageType={pageType}
|
|
43
|
+
publishedTime={publishedTime}
|
|
44
|
+
modifiedTime={modifiedTime}
|
|
45
|
+
author={author}
|
|
46
|
+
tags={tags}
|
|
47
|
+
schema={schema}
|
|
48
|
+
noindex={noindex}
|
|
49
|
+
bodyClass="ai-page"
|
|
50
|
+
mainClass="page-main ai-content"
|
|
51
|
+
scanlines
|
|
52
|
+
localeHrefs={localeHrefs}
|
|
53
|
+
>
|
|
54
|
+
<slot name="head" slot="head" />
|
|
55
|
+
<slot name="body-start" slot="body-start" />
|
|
56
|
+
<slot />
|
|
57
|
+
<slot name="body-end" slot="body-end" />
|
|
58
|
+
</ThemeFrame>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Locale } from '../../i18n/config';
|
|
3
|
+
import ThemeFrame from '../../components/shared/ThemeFrame.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
locale?: Locale;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { locale, title, description } = Astro.props as Props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<ThemeFrame locale={locale} title={title} description={description} bodyClass="page-default" mainClass="page-main">
|
|
15
|
+
<slot />
|
|
16
|
+
</ThemeFrame>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Locale } from '../../i18n/config';
|
|
3
|
+
import ThemeFrame from '../../components/shared/ThemeFrame.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
locale?: Locale;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
localeHrefs?: Partial<Record<Locale, string>>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { locale, title, description, localeHrefs } = Astro.props as Props;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<ThemeFrame locale={locale} title={title} description={description} bodyClass="cyber-page" mainClass="page-main cyber-content" localeHrefs={localeHrefs}>
|
|
16
|
+
<Fragment slot="body-start">
|
|
17
|
+
<div class="cyber-load-glitch" aria-hidden="true"></div>
|
|
18
|
+
<div class="cyber-spotlight" aria-hidden="true">
|
|
19
|
+
<div class="cyber-spotlight-tr"></div>
|
|
20
|
+
<div class="cyber-spotlight-tl"></div>
|
|
21
|
+
</div>
|
|
22
|
+
<div class="cyber-flicker" aria-hidden="true"></div>
|
|
23
|
+
<div class="cyber-haze" aria-hidden="true"></div>
|
|
24
|
+
<div class="cyber-vignette" aria-hidden="true"></div>
|
|
25
|
+
<slot name="body-start" />
|
|
26
|
+
</Fragment>
|
|
27
|
+
<slot name="head" slot="head" />
|
|
28
|
+
<slot />
|
|
29
|
+
<slot name="body-end" slot="body-end" />
|
|
30
|
+
</ThemeFrame>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Locale } from '../../i18n/config';
|
|
3
|
+
import ThemeFrame from '../../components/shared/ThemeFrame.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
locale?: Locale;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
bodyClass?: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const { locale, title, description, bodyClass = 'hacker-page' } = Astro.props as Props;
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<ThemeFrame locale={locale} title={title} description={description} bodyClass={bodyClass} mainClass="page-main hacker-content">
|
|
16
|
+
<link slot="head" rel="stylesheet" href="/styles/about-page.css" />
|
|
17
|
+
<slot name="head" slot="head" />
|
|
18
|
+
<slot name="body-start" slot="body-start" />
|
|
19
|
+
<slot />
|
|
20
|
+
<slot name="body-end" slot="body-end" />
|
|
21
|
+
</ThemeFrame>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
---
|
|
2
|
+
import type { Locale } from '../../i18n/config';
|
|
3
|
+
import ThemeFrame from '../../components/shared/ThemeFrame.astro';
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
locale?: Locale;
|
|
7
|
+
title: string;
|
|
8
|
+
description: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const { locale, title, description } = Astro.props as Props;
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
<ThemeFrame locale={locale} title={title} description={description} bodyClass="page-home" mainClass="page-main home-content">
|
|
15
|
+
<link slot="head" rel="stylesheet" href="/styles/home-page.css" />
|
|
16
|
+
<canvas slot="body-start" id="matrix-bg" aria-hidden="true"></canvas>
|
|
17
|
+
<script slot="body-end" is:inline src="/scripts/home-matrix.js" defer></script>
|
|
18
|
+
<slot />
|
|
19
|
+
</ThemeFrame>
|