@anglefeint/astro-theme 0.1.37 → 0.1.38
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 +1 -1
- package/package.json +1 -1
- package/src/components/shared/CommonHeader.astro +222 -216
- package/src/components/shared/SocialMenu.astro +29 -29
- package/src/components/shared/ThemeFrame.astro +65 -64
- package/src/config/theme.ts +2 -0
- package/src/i18n/messages.ts +120 -5
- package/src/layouts/BlogPost.astro +51 -14
- package/src/scripts/about/modals.js +1 -1
package/README.md
CHANGED
|
@@ -54,7 +54,7 @@ This package reads site-specific config from alias imports:
|
|
|
54
54
|
|
|
55
55
|
In the starter/site project, map these aliases to `src/config/*` and `src/i18n/*` in both Vite and TS config.
|
|
56
56
|
|
|
57
|
-
Giscus comments are configured from site-side `theme.comments` (core IDs + behavior fields like `mapping`, `inputPosition`, `theme`, and `lang`). If required core fields are not set, comments are not rendered.
|
|
57
|
+
Giscus comments are configured from site-side `theme.comments` (core IDs + behavior fields like `mapping`, `inputPosition`, `theme`, and `lang`). If required core fields are not set, comments are not rendered. When `mapping="specific"` set `term`; when `mapping="number"` set `number`.
|
|
58
58
|
|
|
59
59
|
## CLI
|
|
60
60
|
|
package/package.json
CHANGED
|
@@ -6,242 +6,248 @@ import HeaderLink from '../HeaderLink.astro';
|
|
|
6
6
|
import LangSwitcher from './LangSwitcher.astro';
|
|
7
7
|
import SocialMenu from './SocialMenu.astro';
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
9
|
+
DEFAULT_LOCALE,
|
|
10
|
+
LOCALE_LABELS,
|
|
11
|
+
type Locale,
|
|
12
|
+
SUPPORTED_LOCALES,
|
|
13
|
+
alternatePathForLocale,
|
|
14
|
+
isLocale,
|
|
15
|
+
stripLocaleFromPath,
|
|
16
16
|
} from '@anglefeint/site-i18n/config';
|
|
17
17
|
|
|
18
18
|
interface Props {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
19
|
+
locale?: Locale;
|
|
20
|
+
homeHref?: string;
|
|
21
|
+
/** Optional per-locale overrides (used for existence-aware language fallback on some routes). */
|
|
22
|
+
localeHrefs?: Partial<Record<Locale, string>>;
|
|
23
|
+
/** Show Blade Runner scanlines overlay on ai-page (header/footer only) */
|
|
24
|
+
scanlines?: boolean;
|
|
25
|
+
labels?: {
|
|
26
|
+
home: string;
|
|
27
|
+
blog: string;
|
|
28
|
+
about: string;
|
|
29
|
+
status: string;
|
|
30
|
+
statusAria: string;
|
|
31
|
+
language: string;
|
|
32
|
+
};
|
|
32
33
|
}
|
|
33
34
|
|
|
34
35
|
const props = Astro.props as Props;
|
|
35
36
|
const locale: Locale = props.locale && isLocale(props.locale) ? props.locale : DEFAULT_LOCALE;
|
|
36
37
|
const labels = {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
home: props.labels?.home ?? 'Home',
|
|
39
|
+
blog: props.labels?.blog ?? 'Blog',
|
|
40
|
+
about: props.labels?.about ?? 'About',
|
|
41
|
+
status: props.labels?.status ?? 'system: online',
|
|
42
|
+
statusAria: props.labels?.statusAria ?? 'System status',
|
|
43
|
+
language: props.labels?.language ?? 'Language',
|
|
42
44
|
};
|
|
43
45
|
const showAbout = THEME.ENABLE_ABOUT_PAGE;
|
|
44
46
|
const currentSubpath = stripLocaleFromPath(Astro.url.pathname, locale);
|
|
45
47
|
const homeHref = props.homeHref ?? alternatePathForLocale(locale, '/');
|
|
46
48
|
const buildLocaleHref = (targetLocale: Locale) => {
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
// Language switcher: preserve current route when switching locales.
|
|
50
|
+
// Only fall back for About when the feature is disabled.
|
|
51
|
+
const sectionSubpath =
|
|
52
|
+
currentSubpath.startsWith('/about') && !showAbout ? '/' : currentSubpath || '/';
|
|
53
|
+
return alternatePathForLocale(targetLocale, sectionSubpath);
|
|
52
54
|
};
|
|
53
55
|
const localeOptions = SUPPORTED_LOCALES.map((targetLocale) => ({
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
locale: targetLocale,
|
|
57
|
+
label: LOCALE_LABELS[targetLocale],
|
|
58
|
+
href: props.localeHrefs?.[targetLocale] ?? buildLocaleHref(targetLocale),
|
|
57
59
|
}));
|
|
58
60
|
---
|
|
59
61
|
|
|
60
62
|
<header>
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
63
|
+
<nav>
|
|
64
|
+
<div class="nav-left">
|
|
65
|
+
<h2><a href={homeHref}>{SITE_TITLE}</a></h2>
|
|
66
|
+
</div>
|
|
67
|
+
<div class="internal-links">
|
|
68
|
+
<HeaderLink href={homeHref}>{labels.home}</HeaderLink>
|
|
69
|
+
<HeaderLink href={alternatePathForLocale(locale, '/blog/')}>{labels.blog}</HeaderLink>
|
|
70
|
+
{
|
|
71
|
+
showAbout && (
|
|
72
|
+
<HeaderLink href={alternatePathForLocale(locale, '/about/')}>{labels.about}</HeaderLink>
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
</div>
|
|
76
|
+
<div class="nav-right">
|
|
77
|
+
<div class="social-links">
|
|
78
|
+
<LangSwitcher label={labels.language} currentLocale={locale} options={localeOptions} />
|
|
79
|
+
<div class="nav-status" aria-label={labels.statusAria}>
|
|
80
|
+
<span class="nav-status-dot" aria-hidden="true"></span>
|
|
81
|
+
<span class="nav-status-text">{labels.status}</span>
|
|
82
|
+
</div>
|
|
83
|
+
<SocialMenu links={SOCIAL_LINKS} />
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
</nav>
|
|
87
|
+
{props.scanlines && <div class="ai-scanlines" aria-hidden="true" />}
|
|
82
88
|
</header>
|
|
83
89
|
<style>
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
90
|
+
header {
|
|
91
|
+
margin: 0;
|
|
92
|
+
padding: 0 1em;
|
|
93
|
+
background: var(--chrome-bg, var(--bg));
|
|
94
|
+
border-bottom: 1px solid var(--chrome-border, rgb(var(--border)));
|
|
95
|
+
}
|
|
96
|
+
h2 {
|
|
97
|
+
margin: 0;
|
|
98
|
+
font-size: 1em;
|
|
99
|
+
}
|
|
94
100
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
101
|
+
h2 a,
|
|
102
|
+
h2 a.active {
|
|
103
|
+
text-decoration: none;
|
|
104
|
+
color: var(--chrome-link, rgb(var(--text)));
|
|
105
|
+
border-bottom: none;
|
|
106
|
+
}
|
|
107
|
+
nav {
|
|
108
|
+
display: grid;
|
|
109
|
+
grid-template-columns: 1fr auto 1fr;
|
|
110
|
+
align-items: center;
|
|
111
|
+
gap: 0.6rem;
|
|
112
|
+
min-height: 56px;
|
|
113
|
+
width: 100%;
|
|
114
|
+
}
|
|
115
|
+
.nav-left {
|
|
116
|
+
justify-self: start;
|
|
117
|
+
}
|
|
118
|
+
.internal-links {
|
|
119
|
+
justify-self: center;
|
|
120
|
+
display: flex;
|
|
121
|
+
align-items: center;
|
|
122
|
+
gap: 0.1rem;
|
|
123
|
+
}
|
|
124
|
+
.nav-right {
|
|
125
|
+
justify-self: end;
|
|
126
|
+
display: flex;
|
|
127
|
+
align-items: center;
|
|
128
|
+
gap: 0.35rem;
|
|
129
|
+
min-width: max-content;
|
|
130
|
+
}
|
|
131
|
+
.internal-links :global(a) {
|
|
132
|
+
padding: 1em 0.5em;
|
|
133
|
+
color: var(--chrome-link, rgb(var(--text)));
|
|
134
|
+
border-bottom: 4px solid transparent;
|
|
135
|
+
text-decoration: none;
|
|
136
|
+
}
|
|
137
|
+
.internal-links :global(a.active) {
|
|
138
|
+
text-decoration: none;
|
|
139
|
+
border-bottom-color: var(--chrome-active, var(--accent));
|
|
140
|
+
}
|
|
141
|
+
.social-links a {
|
|
142
|
+
color: var(--chrome-link, rgb(var(--text)));
|
|
143
|
+
align-items: center;
|
|
144
|
+
justify-content: center;
|
|
145
|
+
padding: 0.6rem 0.35rem;
|
|
146
|
+
border-bottom: none;
|
|
147
|
+
}
|
|
148
|
+
.social-links a:hover {
|
|
149
|
+
color: var(--chrome-link-hover, var(--chrome-link, rgb(var(--text))));
|
|
150
|
+
}
|
|
151
|
+
.social-links,
|
|
152
|
+
.social-links a {
|
|
153
|
+
display: flex;
|
|
154
|
+
}
|
|
155
|
+
.social-links {
|
|
156
|
+
align-items: center;
|
|
157
|
+
gap: 0.4rem;
|
|
158
|
+
position: relative;
|
|
159
|
+
flex-wrap: nowrap;
|
|
160
|
+
--lang-switcher-border: rgba(132, 214, 255, 0.2);
|
|
161
|
+
--lang-switcher-bg: rgba(6, 16, 30, 0.35);
|
|
162
|
+
--lang-label-color: rgba(190, 226, 248, 0.78);
|
|
163
|
+
--lang-select-arrow: rgba(204, 236, 252, 0.82);
|
|
164
|
+
--lang-select-bg: rgba(9, 22, 40, 0.68);
|
|
165
|
+
--lang-select-border: rgba(132, 214, 255, 0.2);
|
|
166
|
+
--lang-select-text: rgba(204, 236, 252, 0.86);
|
|
167
|
+
--lang-select-focus-border: rgba(132, 214, 255, 0.5);
|
|
168
|
+
--lang-select-focus-ring: rgba(98, 180, 228, 0.18);
|
|
169
|
+
--lang-select-option-text: #ccecfb;
|
|
170
|
+
--lang-select-option-bg: #0b1c32;
|
|
171
|
+
}
|
|
172
|
+
.nav-status {
|
|
173
|
+
display: none;
|
|
174
|
+
align-items: center;
|
|
175
|
+
gap: 0.45rem;
|
|
176
|
+
padding: 0.24rem 0.5rem;
|
|
177
|
+
border-radius: 999px;
|
|
178
|
+
font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
|
|
179
|
+
font-size: 0.62rem;
|
|
180
|
+
letter-spacing: 0.12em;
|
|
181
|
+
text-transform: uppercase;
|
|
182
|
+
color: rgba(186, 232, 252, 0.86);
|
|
183
|
+
background: rgba(6, 16, 30, 0.52);
|
|
184
|
+
border: 1px solid rgba(132, 214, 255, 0.22);
|
|
185
|
+
box-shadow:
|
|
186
|
+
0 0 0 1px rgba(132, 214, 255, 0.08),
|
|
187
|
+
0 0 16px rgba(90, 180, 255, 0.16);
|
|
188
|
+
white-space: nowrap;
|
|
189
|
+
position: absolute;
|
|
190
|
+
right: calc(100% + 0.5rem);
|
|
191
|
+
top: 50%;
|
|
192
|
+
transform: translateY(-50%);
|
|
193
|
+
pointer-events: none;
|
|
194
|
+
}
|
|
195
|
+
.nav-status-dot {
|
|
196
|
+
width: 0.44rem;
|
|
197
|
+
height: 0.44rem;
|
|
198
|
+
border-radius: 50%;
|
|
199
|
+
background: rgba(150, 226, 255, 0.96);
|
|
200
|
+
box-shadow: 0 0 10px rgba(122, 210, 255, 0.72);
|
|
201
|
+
animation: nav-status-pulse 1.8s steps(1, end) infinite;
|
|
202
|
+
}
|
|
203
|
+
@keyframes nav-status-pulse {
|
|
204
|
+
0%,
|
|
205
|
+
78%,
|
|
206
|
+
100% {
|
|
207
|
+
opacity: 1;
|
|
208
|
+
transform: scale(1);
|
|
209
|
+
}
|
|
210
|
+
82% {
|
|
211
|
+
opacity: 0.2;
|
|
212
|
+
transform: scale(0.7);
|
|
213
|
+
}
|
|
214
|
+
86% {
|
|
215
|
+
opacity: 1;
|
|
216
|
+
transform: scale(1.05);
|
|
217
|
+
}
|
|
218
|
+
90% {
|
|
219
|
+
opacity: 0.28;
|
|
220
|
+
transform: scale(0.78);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
body.ai-page .nav-status {
|
|
224
|
+
display: inline-flex;
|
|
225
|
+
}
|
|
226
|
+
body.about-page .nav-status {
|
|
227
|
+
display: none;
|
|
228
|
+
}
|
|
229
|
+
@media (max-width: 720px) {
|
|
230
|
+
nav {
|
|
231
|
+
grid-template-columns: 1fr;
|
|
232
|
+
gap: 0;
|
|
233
|
+
}
|
|
234
|
+
.nav-left {
|
|
235
|
+
display: none;
|
|
236
|
+
}
|
|
237
|
+
.internal-links {
|
|
238
|
+
justify-self: start;
|
|
239
|
+
}
|
|
240
|
+
.nav-right {
|
|
241
|
+
justify-self: end;
|
|
242
|
+
}
|
|
243
|
+
.nav-status {
|
|
244
|
+
display: none !important;
|
|
245
|
+
}
|
|
246
|
+
.social-links {
|
|
247
|
+
display: none;
|
|
248
|
+
}
|
|
249
|
+
.lang-switcher {
|
|
250
|
+
margin-right: 0;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
247
253
|
</style>
|
|
@@ -3,46 +3,46 @@ import { SOCIAL_LINKS, type SocialLink } from '@anglefeint/site-config/social';
|
|
|
3
3
|
import Icon from './Icon.astro';
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
links?: SocialLink[];
|
|
7
|
+
iconSize?: number;
|
|
8
|
+
showPlaceholdersWhenEmpty?: boolean;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
const {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
links = SOCIAL_LINKS,
|
|
13
|
+
iconSize = 32,
|
|
14
|
+
showPlaceholdersWhenEmpty = true,
|
|
15
15
|
} = Astro.props as Props;
|
|
16
16
|
const placeholderLinks: SocialLink[] = [
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
{ href: '', label: 'Mastodon', icon: 'mastodon' },
|
|
18
|
+
{ href: '', label: 'Twitter', icon: 'twitter' },
|
|
19
|
+
{ href: '', label: 'GitHub', icon: 'github' },
|
|
20
20
|
];
|
|
21
21
|
const resolvedLinks = links.length > 0 || !showPlaceholdersWhenEmpty ? links : placeholderLinks;
|
|
22
22
|
---
|
|
23
23
|
|
|
24
24
|
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
resolvedLinks.map((link) =>
|
|
26
|
+
links.length > 0 ? (
|
|
27
|
+
<a href={link.href} target="_blank" rel="noopener noreferrer">
|
|
28
|
+
<span class="sr-only">{link.label}</span>
|
|
29
|
+
{link.icon ? <Icon name={link.icon} size={iconSize} /> : <span>{link.label}</span>}
|
|
30
|
+
</a>
|
|
31
|
+
) : (
|
|
32
|
+
<span class="social-placeholder" aria-hidden="true">
|
|
33
|
+
{link.icon ? <Icon name={link.icon} size={iconSize} /> : <span>{link.label}</span>}
|
|
34
|
+
</span>
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
<style>
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
.social-placeholder {
|
|
41
|
+
display: inline-flex;
|
|
42
|
+
align-items: center;
|
|
43
|
+
justify-content: center;
|
|
44
|
+
opacity: 0.45;
|
|
45
|
+
filter: saturate(0.7);
|
|
46
|
+
cursor: default;
|
|
47
|
+
}
|
|
48
48
|
</style>
|
|
@@ -7,79 +7,80 @@ import { getMessages } from '@anglefeint/site-i18n/messages';
|
|
|
7
7
|
import type { ImageMetadata } from 'astro';
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
10
|
+
locale?: Locale;
|
|
11
|
+
title: string;
|
|
12
|
+
description: string;
|
|
13
|
+
image?: ImageMetadata;
|
|
14
|
+
pageType?: 'website' | 'article';
|
|
15
|
+
publishedTime?: Date;
|
|
16
|
+
modifiedTime?: Date;
|
|
17
|
+
author?: string;
|
|
18
|
+
tags?: string[];
|
|
19
|
+
schema?: Record<string, unknown> | Record<string, unknown>[];
|
|
20
|
+
noindex?: boolean;
|
|
21
|
+
bodyClass: string;
|
|
22
|
+
mainClass?: string;
|
|
23
|
+
scanlines?: boolean;
|
|
24
|
+
localeHrefs?: Partial<Record<Locale, string>>;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
const rawLocale = Astro.props.locale ?? DEFAULT_LOCALE;
|
|
28
28
|
const locale: Locale = isLocale(rawLocale) ? rawLocale : DEFAULT_LOCALE;
|
|
29
29
|
const {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
30
|
+
title,
|
|
31
|
+
description,
|
|
32
|
+
image,
|
|
33
|
+
pageType,
|
|
34
|
+
publishedTime,
|
|
35
|
+
modifiedTime,
|
|
36
|
+
author,
|
|
37
|
+
tags,
|
|
38
|
+
schema,
|
|
39
|
+
noindex,
|
|
40
|
+
bodyClass,
|
|
41
|
+
mainClass = 'page-main',
|
|
42
|
+
scanlines = false,
|
|
43
|
+
localeHrefs,
|
|
44
44
|
} = Astro.props as Props;
|
|
45
45
|
const messages = getMessages(locale);
|
|
46
46
|
---
|
|
47
47
|
|
|
48
48
|
<!doctype html>
|
|
49
49
|
<html lang={locale}>
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
50
|
+
<head>
|
|
51
|
+
<BaseHead
|
|
52
|
+
title={title}
|
|
53
|
+
description={description}
|
|
54
|
+
image={image}
|
|
55
|
+
pageType={pageType}
|
|
56
|
+
publishedTime={publishedTime}
|
|
57
|
+
modifiedTime={modifiedTime}
|
|
58
|
+
author={author}
|
|
59
|
+
tags={tags}
|
|
60
|
+
schema={schema}
|
|
61
|
+
noindex={noindex}
|
|
62
|
+
/>
|
|
63
|
+
<slot name="head" />
|
|
64
|
+
</head>
|
|
65
|
+
<body class={bodyClass}>
|
|
66
|
+
<slot name="body-start" />
|
|
67
|
+
<CommonHeader
|
|
68
|
+
locale={locale}
|
|
69
|
+
localeHrefs={localeHrefs}
|
|
70
|
+
scanlines={scanlines}
|
|
71
|
+
labels={{
|
|
72
|
+
home: messages.nav.home,
|
|
73
|
+
blog: messages.nav.blog,
|
|
74
|
+
about: messages.nav.about,
|
|
75
|
+
status: messages.nav.status,
|
|
76
|
+
statusAria: messages.nav.statusAria,
|
|
77
|
+
language: messages.langLabel,
|
|
78
|
+
}}
|
|
79
|
+
/>
|
|
80
|
+
<main class={mainClass}>
|
|
81
|
+
<slot />
|
|
82
|
+
</main>
|
|
83
|
+
<CommonFooter scanlines={scanlines} />
|
|
84
|
+
<slot name="body-end" />
|
|
85
|
+
</body>
|
|
85
86
|
</html>
|
package/src/config/theme.ts
CHANGED
package/src/i18n/messages.ts
CHANGED
|
@@ -9,6 +9,7 @@ export type Messages = {
|
|
|
9
9
|
blog: string;
|
|
10
10
|
about: string;
|
|
11
11
|
status: string;
|
|
12
|
+
statusAria: string;
|
|
12
13
|
};
|
|
13
14
|
home: {
|
|
14
15
|
hero: string;
|
|
@@ -41,10 +42,24 @@ export type Messages = {
|
|
|
41
42
|
related: string;
|
|
42
43
|
comments: string;
|
|
43
44
|
responseOutput: string;
|
|
45
|
+
rqBadge: string;
|
|
46
|
+
rqReplayAria: string;
|
|
47
|
+
metaPublished: string;
|
|
48
|
+
metaUpdated: string;
|
|
49
|
+
metaReadMinutes: string;
|
|
50
|
+
systemStatusAria: string;
|
|
51
|
+
promptContextLabel: string;
|
|
52
|
+
latencyLabel: string;
|
|
53
|
+
confidenceLabel: string;
|
|
54
|
+
statsWords: string;
|
|
55
|
+
statsTokens: string;
|
|
44
56
|
heroMonitor: string;
|
|
45
57
|
heroSignalSync: string;
|
|
46
58
|
heroModelOnline: string;
|
|
47
59
|
regenerate: string;
|
|
60
|
+
relatedAria: string;
|
|
61
|
+
backToBlogAria: string;
|
|
62
|
+
paginationAria: string;
|
|
48
63
|
toastP10: string;
|
|
49
64
|
toastP30: string;
|
|
50
65
|
toastP60: string;
|
|
@@ -57,7 +72,13 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
57
72
|
siteTitle: 'Angle Feint',
|
|
58
73
|
siteDescription: 'Cinematic web interfaces and AI-era engineering essays.',
|
|
59
74
|
langLabel: 'Language',
|
|
60
|
-
nav: {
|
|
75
|
+
nav: {
|
|
76
|
+
home: 'Home',
|
|
77
|
+
blog: 'Blog',
|
|
78
|
+
about: 'About',
|
|
79
|
+
status: 'system: online',
|
|
80
|
+
statusAria: 'System status',
|
|
81
|
+
},
|
|
61
82
|
home: {
|
|
62
83
|
hero: 'Write a short introduction for your site and what readers can expect from your posts.',
|
|
63
84
|
latest: 'Latest Posts',
|
|
@@ -89,10 +110,24 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
89
110
|
related: 'Related',
|
|
90
111
|
comments: 'Comments',
|
|
91
112
|
responseOutput: 'Output',
|
|
113
|
+
rqBadge: 'monitor feed',
|
|
114
|
+
rqReplayAria: 'Replay monitor feed',
|
|
115
|
+
metaPublished: 'published',
|
|
116
|
+
metaUpdated: 'updated',
|
|
117
|
+
metaReadMinutes: 'min read',
|
|
118
|
+
systemStatusAria: 'Model status',
|
|
119
|
+
promptContextLabel: 'Context',
|
|
120
|
+
latencyLabel: 'latency est',
|
|
121
|
+
confidenceLabel: 'confidence',
|
|
122
|
+
statsWords: 'words',
|
|
123
|
+
statsTokens: 'tokens',
|
|
92
124
|
heroMonitor: 'neural monitor',
|
|
93
125
|
heroSignalSync: 'signal sync active',
|
|
94
126
|
heroModelOnline: 'model online',
|
|
95
127
|
regenerate: 'Regenerate',
|
|
128
|
+
relatedAria: 'Related posts',
|
|
129
|
+
backToBlogAria: 'Back to blog',
|
|
130
|
+
paginationAria: 'Pagination',
|
|
96
131
|
toastP10: 'context parsed 10%',
|
|
97
132
|
toastP30: 'context parsed 30%',
|
|
98
133
|
toastP60: 'inference stable 60%',
|
|
@@ -103,7 +138,13 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
103
138
|
siteTitle: 'Angle Feint',
|
|
104
139
|
siteDescription: '映画的なWebインターフェースとAI時代のエンジニアリング考察。',
|
|
105
140
|
langLabel: '言語',
|
|
106
|
-
nav: {
|
|
141
|
+
nav: {
|
|
142
|
+
home: 'ホーム',
|
|
143
|
+
blog: 'ブログ',
|
|
144
|
+
about: 'プロフィール',
|
|
145
|
+
status: 'system: online',
|
|
146
|
+
statusAria: 'システム状態',
|
|
147
|
+
},
|
|
107
148
|
home: {
|
|
108
149
|
hero: 'このサイトの紹介文と、読者がどんな記事を期待できるかを書いてください。',
|
|
109
150
|
latest: '最新記事',
|
|
@@ -135,10 +176,24 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
135
176
|
related: '関連記事',
|
|
136
177
|
comments: 'コメント',
|
|
137
178
|
responseOutput: '出力',
|
|
179
|
+
rqBadge: 'モニターフィード',
|
|
180
|
+
rqReplayAria: 'モニターフィードを再生',
|
|
181
|
+
metaPublished: '公開',
|
|
182
|
+
metaUpdated: '更新',
|
|
183
|
+
metaReadMinutes: '分で読了',
|
|
184
|
+
systemStatusAria: 'モデル状態',
|
|
185
|
+
promptContextLabel: 'コンテキスト',
|
|
186
|
+
latencyLabel: '推定レイテンシ',
|
|
187
|
+
confidenceLabel: '信頼度',
|
|
188
|
+
statsWords: '語',
|
|
189
|
+
statsTokens: 'トークン',
|
|
138
190
|
heroMonitor: 'ニューラルモニター',
|
|
139
191
|
heroSignalSync: 'シグナル同期中',
|
|
140
192
|
heroModelOnline: 'モデルオンライン',
|
|
141
193
|
regenerate: '再生成',
|
|
194
|
+
relatedAria: '関連記事',
|
|
195
|
+
backToBlogAria: 'ブログへ戻る',
|
|
196
|
+
paginationAria: 'ページネーション',
|
|
142
197
|
toastP10: '文脈解析 10%',
|
|
143
198
|
toastP30: '文脈解析 30%',
|
|
144
199
|
toastP60: '推論安定 60%',
|
|
@@ -149,7 +204,13 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
149
204
|
siteTitle: 'Angle Feint',
|
|
150
205
|
siteDescription: '시네마틱 웹 인터페이스와 AI 시대 엔지니어링 에세이.',
|
|
151
206
|
langLabel: '언어',
|
|
152
|
-
nav: {
|
|
207
|
+
nav: {
|
|
208
|
+
home: '홈',
|
|
209
|
+
blog: '블로그',
|
|
210
|
+
about: '소개',
|
|
211
|
+
status: 'system: online',
|
|
212
|
+
statusAria: '시스템 상태',
|
|
213
|
+
},
|
|
153
214
|
home: {
|
|
154
215
|
hero: '사이트 소개와 방문자가 어떤 글을 기대할 수 있는지 간단히 작성하세요.',
|
|
155
216
|
latest: '최신 글',
|
|
@@ -181,10 +242,24 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
181
242
|
related: '관련 글',
|
|
182
243
|
comments: '댓글',
|
|
183
244
|
responseOutput: '출력',
|
|
245
|
+
rqBadge: '모니터 피드',
|
|
246
|
+
rqReplayAria: '모니터 피드 다시 재생',
|
|
247
|
+
metaPublished: '게시',
|
|
248
|
+
metaUpdated: '수정',
|
|
249
|
+
metaReadMinutes: '분 읽기',
|
|
250
|
+
systemStatusAria: '모델 상태',
|
|
251
|
+
promptContextLabel: '컨텍스트',
|
|
252
|
+
latencyLabel: '지연 추정',
|
|
253
|
+
confidenceLabel: '신뢰도',
|
|
254
|
+
statsWords: '단어',
|
|
255
|
+
statsTokens: '토큰',
|
|
184
256
|
heroMonitor: '뉴럴 모니터',
|
|
185
257
|
heroSignalSync: '신호 동기화 활성',
|
|
186
258
|
heroModelOnline: '모델 온라인',
|
|
187
259
|
regenerate: '재생성',
|
|
260
|
+
relatedAria: '관련 글',
|
|
261
|
+
backToBlogAria: '블로그로 돌아가기',
|
|
262
|
+
paginationAria: '페이지네이션',
|
|
188
263
|
toastP10: '컨텍스트 파싱 10%',
|
|
189
264
|
toastP30: '컨텍스트 파싱 30%',
|
|
190
265
|
toastP60: '추론 안정화 60%',
|
|
@@ -195,7 +270,13 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
195
270
|
siteTitle: 'Angle Feint',
|
|
196
271
|
siteDescription: 'Interfaces web cinematográficas y ensayos de ingeniería en la era de IA.',
|
|
197
272
|
langLabel: 'Idioma',
|
|
198
|
-
nav: {
|
|
273
|
+
nav: {
|
|
274
|
+
home: 'Inicio',
|
|
275
|
+
blog: 'Blog',
|
|
276
|
+
about: 'Sobre mí',
|
|
277
|
+
status: 'system: online',
|
|
278
|
+
statusAria: 'Estado del sistema',
|
|
279
|
+
},
|
|
199
280
|
home: {
|
|
200
281
|
hero: 'Escribe una breve presentación del sitio y qué tipo de contenido encontrarán tus lectores.',
|
|
201
282
|
latest: 'Últimas publicaciones',
|
|
@@ -228,10 +309,24 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
228
309
|
related: 'Relacionados',
|
|
229
310
|
comments: 'Comentarios',
|
|
230
311
|
responseOutput: 'Salida',
|
|
312
|
+
rqBadge: 'monitor de señal',
|
|
313
|
+
rqReplayAria: 'Reproducir monitor de señal',
|
|
314
|
+
metaPublished: 'publicado',
|
|
315
|
+
metaUpdated: 'actualizado',
|
|
316
|
+
metaReadMinutes: 'min de lectura',
|
|
317
|
+
systemStatusAria: 'Estado del modelo',
|
|
318
|
+
promptContextLabel: 'Contexto',
|
|
319
|
+
latencyLabel: 'latencia est',
|
|
320
|
+
confidenceLabel: 'confianza',
|
|
321
|
+
statsWords: 'palabras',
|
|
322
|
+
statsTokens: 'tokens',
|
|
231
323
|
heroMonitor: 'monitor neural',
|
|
232
324
|
heroSignalSync: 'sincronización de señal activa',
|
|
233
325
|
heroModelOnline: 'modelo en línea',
|
|
234
326
|
regenerate: 'Regenerar',
|
|
327
|
+
relatedAria: 'Publicaciones relacionadas',
|
|
328
|
+
backToBlogAria: 'Volver al blog',
|
|
329
|
+
paginationAria: 'Paginación',
|
|
235
330
|
toastP10: 'contexto analizado 10%',
|
|
236
331
|
toastP30: 'contexto analizado 30%',
|
|
237
332
|
toastP60: 'inferencia estable 60%',
|
|
@@ -242,7 +337,13 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
242
337
|
siteTitle: 'Angle Feint',
|
|
243
338
|
siteDescription: '电影感网页界面与 AI 时代工程实践文章。',
|
|
244
339
|
langLabel: '语言',
|
|
245
|
-
nav: {
|
|
340
|
+
nav: {
|
|
341
|
+
home: '首页',
|
|
342
|
+
blog: '博客',
|
|
343
|
+
about: '关于',
|
|
344
|
+
status: 'system: online',
|
|
345
|
+
statusAria: '系统状态',
|
|
346
|
+
},
|
|
246
347
|
home: {
|
|
247
348
|
hero: '在这里写一段站点简介,并告诉读者你将发布什么类型的内容。',
|
|
248
349
|
latest: '最新文章',
|
|
@@ -274,10 +375,24 @@ export const DEFAULT_MESSAGES: Record<Locale, Messages> = {
|
|
|
274
375
|
related: '相关文章',
|
|
275
376
|
comments: '评论',
|
|
276
377
|
responseOutput: '输出',
|
|
378
|
+
rqBadge: '监视器信号',
|
|
379
|
+
rqReplayAria: '重放监视器信号',
|
|
380
|
+
metaPublished: '发布',
|
|
381
|
+
metaUpdated: '更新',
|
|
382
|
+
metaReadMinutes: '分钟阅读',
|
|
383
|
+
systemStatusAria: '模型状态',
|
|
384
|
+
promptContextLabel: '语境',
|
|
385
|
+
latencyLabel: '延迟估计',
|
|
386
|
+
confidenceLabel: '置信度',
|
|
387
|
+
statsWords: '词',
|
|
388
|
+
statsTokens: '令牌',
|
|
277
389
|
heroMonitor: '神经监视器',
|
|
278
390
|
heroSignalSync: '信号同步中',
|
|
279
391
|
heroModelOnline: '模型在线',
|
|
280
392
|
regenerate: '重新生成',
|
|
393
|
+
relatedAria: '相关文章',
|
|
394
|
+
backToBlogAria: '返回博客',
|
|
395
|
+
paginationAria: '分页导航',
|
|
281
396
|
toastP10: '语境解析 10%',
|
|
282
397
|
toastP30: '语境解析 30%',
|
|
283
398
|
toastP60: '推理稳定 60%',
|
|
@@ -58,8 +58,19 @@ const hasStats = aiModel || wordCount !== undefined || tokenCount !== undefined;
|
|
|
58
58
|
const confidenceText = aiConfidence !== undefined ? aiConfidence.toFixed(2) : undefined;
|
|
59
59
|
const enableRedQueen = THEME.EFFECTS.ENABLE_RED_QUEEN;
|
|
60
60
|
const comments = THEME.COMMENTS;
|
|
61
|
+
const hasMappingParam =
|
|
62
|
+
comments.MAPPING === 'specific'
|
|
63
|
+
? Boolean(comments.TERM)
|
|
64
|
+
: comments.MAPPING === 'number'
|
|
65
|
+
? Boolean(comments.NUMBER)
|
|
66
|
+
: true;
|
|
61
67
|
const hasCommentsConfig = Boolean(
|
|
62
|
-
comments.ENABLED &&
|
|
68
|
+
comments.ENABLED &&
|
|
69
|
+
comments.REPO &&
|
|
70
|
+
comments.REPO_ID &&
|
|
71
|
+
comments.CATEGORY &&
|
|
72
|
+
comments.CATEGORY_ID &&
|
|
73
|
+
hasMappingParam
|
|
63
74
|
);
|
|
64
75
|
---
|
|
65
76
|
|
|
@@ -105,13 +116,13 @@ const hasCommentsConfig = Boolean(
|
|
|
105
116
|
data-rq-src2={themeRedqueen2.src}
|
|
106
117
|
/>
|
|
107
118
|
<div class="rq-tv-badge">
|
|
108
|
-
|
|
119
|
+
{messages.blog.rqBadge}
|
|
109
120
|
<span class="rq-tv-dot" />
|
|
110
121
|
</div>
|
|
111
122
|
<button
|
|
112
123
|
type="button"
|
|
113
124
|
class="rq-tv-toggle"
|
|
114
|
-
aria-label=
|
|
125
|
+
aria-label={messages.blog.rqReplayAria}
|
|
115
126
|
aria-expanded="false"
|
|
116
127
|
>
|
|
117
128
|
▶
|
|
@@ -178,13 +189,28 @@ const hasCommentsConfig = Boolean(
|
|
|
178
189
|
<div class="prose">
|
|
179
190
|
<div class="title ai-title">
|
|
180
191
|
<div class="ai-meta-terminal">
|
|
181
|
-
$
|
|
182
|
-
{
|
|
183
|
-
{
|
|
192
|
+
$ {messages.blog.metaPublished}
|
|
193
|
+
{fmt(pubDate)}
|
|
194
|
+
{
|
|
195
|
+
updatedDate && (
|
|
196
|
+
<>
|
|
197
|
+
{' '}
|
|
198
|
+
| {messages.blog.metaUpdated} {fmt(updatedDate)}
|
|
199
|
+
</>
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
{
|
|
203
|
+
readMinutes !== undefined && (
|
|
204
|
+
<>
|
|
205
|
+
{' '}
|
|
206
|
+
| ~{readMinutes} {messages.blog.metaReadMinutes}
|
|
207
|
+
</>
|
|
208
|
+
)
|
|
209
|
+
}
|
|
184
210
|
</div>
|
|
185
211
|
{
|
|
186
212
|
hasSystemMeta && (
|
|
187
|
-
<div class="ai-system-row" aria-label=
|
|
213
|
+
<div class="ai-system-row" aria-label={messages.blog.systemStatusAria}>
|
|
188
214
|
{aiModel && <span class="ai-system-chip">model: {aiModel}</span>}
|
|
189
215
|
{aiMode && <span class="ai-system-chip">mode: {aiMode}</span>}
|
|
190
216
|
{aiState && <span class="ai-system-chip">state: {aiState}</span>}
|
|
@@ -194,7 +220,8 @@ const hasCommentsConfig = Boolean(
|
|
|
194
220
|
{
|
|
195
221
|
contextText && (
|
|
196
222
|
<div class="ai-prompt-line">
|
|
197
|
-
|
|
223
|
+
{messages.blog.promptContextLabel}: <span class="ai-prompt-topic">{contextText}</span>{' '}
|
|
224
|
+
→
|
|
198
225
|
</div>
|
|
199
226
|
)
|
|
200
227
|
}
|
|
@@ -212,12 +239,12 @@ const hasCommentsConfig = Boolean(
|
|
|
212
239
|
<div class="ai-response-meta">
|
|
213
240
|
{aiLatencyMs !== undefined && (
|
|
214
241
|
<span>
|
|
215
|
-
|
|
242
|
+
{messages.blog.latencyLabel} <strong>{aiLatencyMs}</strong> ms
|
|
216
243
|
</span>
|
|
217
244
|
)}
|
|
218
245
|
{confidenceText !== undefined && (
|
|
219
246
|
<span>
|
|
220
|
-
|
|
247
|
+
{messages.blog.confidenceLabel} <strong>{confidenceText}</strong>
|
|
221
248
|
</span>
|
|
222
249
|
)}
|
|
223
250
|
</div>
|
|
@@ -234,9 +261,17 @@ const hasCommentsConfig = Boolean(
|
|
|
234
261
|
<div class="ai-stats-corner">
|
|
235
262
|
{aiModel && <span class="ai-model-id">{aiModel}</span>}
|
|
236
263
|
{aiModel && (wordCount !== undefined || tokenCount !== undefined) && ' · '}
|
|
237
|
-
{wordCount !== undefined &&
|
|
264
|
+
{wordCount !== undefined && (
|
|
265
|
+
<span>
|
|
266
|
+
{compact(wordCount)} {messages.blog.statsWords}
|
|
267
|
+
</span>
|
|
268
|
+
)}
|
|
238
269
|
{wordCount !== undefined && tokenCount !== undefined && ' · '}
|
|
239
|
-
{tokenCount !== undefined &&
|
|
270
|
+
{tokenCount !== undefined && (
|
|
271
|
+
<span>
|
|
272
|
+
{compact(tokenCount)} {messages.blog.statsTokens}
|
|
273
|
+
</span>
|
|
274
|
+
)}
|
|
240
275
|
</div>
|
|
241
276
|
)
|
|
242
277
|
}
|
|
@@ -247,7 +282,7 @@ const hasCommentsConfig = Boolean(
|
|
|
247
282
|
</article>
|
|
248
283
|
{
|
|
249
284
|
related.length > 0 && (
|
|
250
|
-
<section class="ai-related" aria-label=
|
|
285
|
+
<section class="ai-related" aria-label={messages.blog.relatedAria}>
|
|
251
286
|
<h2 class="ai-related-title">{messages.blog.related}</h2>
|
|
252
287
|
<div class="ai-related-grid">
|
|
253
288
|
{related.map((p) => (
|
|
@@ -274,7 +309,7 @@ const hasCommentsConfig = Boolean(
|
|
|
274
309
|
</section>
|
|
275
310
|
)
|
|
276
311
|
}
|
|
277
|
-
<nav class="ai-back-to-blog" aria-label=
|
|
312
|
+
<nav class="ai-back-to-blog" aria-label={messages.blog.backToBlogAria}>
|
|
278
313
|
<a href={localePath(resolvedLocale, '/blog/')}>
|
|
279
314
|
<span class="ai-back-prompt">$</span>
|
|
280
315
|
<span class="ai-back-text">← {messages.blog.backToBlog}</span>
|
|
@@ -290,6 +325,8 @@ const hasCommentsConfig = Boolean(
|
|
|
290
325
|
data-category={comments.CATEGORY}
|
|
291
326
|
data-category-id={comments.CATEGORY_ID}
|
|
292
327
|
data-mapping={comments.MAPPING}
|
|
328
|
+
data-term={comments.MAPPING === 'specific' ? comments.TERM : undefined}
|
|
329
|
+
data-number={comments.MAPPING === 'number' ? comments.NUMBER : undefined}
|
|
293
330
|
data-strict={comments.STRICT}
|
|
294
331
|
data-reactions-enabled={comments.REACTIONS_ENABLED}
|
|
295
332
|
data-emit-metadata={comments.EMIT_METADATA}
|
|
@@ -25,7 +25,7 @@ export function initAboutModals(runtimeConfig, prefersReducedMotion) {
|
|
|
25
25
|
},
|
|
26
26
|
ai: {
|
|
27
27
|
title: 'AI',
|
|
28
|
-
body: '<pre>~ $ ai --status --verbose\n\nmodel:
|
|
28
|
+
body: '<pre>~ $ ai --status --verbose\n\nmodel: runtime-default\nmode: standard\ncontext window: 32k\nlatency: 100-250ms\nsafety: enabled\n\n>> system online\n>> ready</pre>',
|
|
29
29
|
type: 'plain',
|
|
30
30
|
},
|
|
31
31
|
decryptor: {
|