czon 0.7.4 → 0.7.6
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/dist/build/robots.js
CHANGED
|
@@ -105,7 +105,7 @@ Disallow:
|
|
|
105
105
|
User-agent: KHTML, like Gecko
|
|
106
106
|
Disallow:
|
|
107
107
|
|
|
108
|
-
${metadata_1.MetaData.options.site?.baseUrl ? `Sitemap: ${
|
|
108
|
+
${metadata_1.MetaData.options.site?.baseUrl ? `Sitemap: ${new URL('sitemap.xml', metadata_1.MetaData.options.site.baseUrl).href}` : ''}
|
|
109
109
|
`;
|
|
110
110
|
const robotsPath = path.join(paths_1.CZON_DIST_DIR, 'robots.txt');
|
|
111
111
|
await (0, writeFile_1.writeFile)(robotsPath, robotsTxtContent);
|
package/dist/build/sitemap.js
CHANGED
|
@@ -37,6 +37,17 @@ exports.clearSitemapCollection = exports.generateSitemap = exports.collectCatego
|
|
|
37
37
|
const path = __importStar(require("path"));
|
|
38
38
|
const paths_1 = require("../paths");
|
|
39
39
|
const writeFile_1 = require("../utils/writeFile");
|
|
40
|
+
/**
|
|
41
|
+
* Escape special XML characters to prevent parsing errors
|
|
42
|
+
*/
|
|
43
|
+
const escapeXml = (str) => {
|
|
44
|
+
return str
|
|
45
|
+
.replace(/&/g, '&')
|
|
46
|
+
.replace(/</g, '<')
|
|
47
|
+
.replace(/>/g, '>')
|
|
48
|
+
.replace(/"/g, '"')
|
|
49
|
+
.replace(/'/g, ''');
|
|
50
|
+
};
|
|
40
51
|
const sitemapUrls = new Map();
|
|
41
52
|
const collectUrl = (lang, slug) => {
|
|
42
53
|
if (!slug)
|
|
@@ -89,11 +100,12 @@ const generateSitemap = async (baseUrl) => {
|
|
|
89
100
|
sitemap += ` <url>\n`;
|
|
90
101
|
for (const langEntry of entry.langs) {
|
|
91
102
|
const fullUrl = `${baseUrlClean}${langEntry.path}`;
|
|
92
|
-
|
|
103
|
+
// encodeURI handles URL encoding, escapeXml handles XML special characters
|
|
104
|
+
const fullUrlEscaped = escapeXml(encodeURI(fullUrl));
|
|
93
105
|
if (langEntry.lang === entry.langs[0].lang) {
|
|
94
|
-
sitemap += ` <loc>${
|
|
106
|
+
sitemap += ` <loc>${fullUrlEscaped}</loc>\n`;
|
|
95
107
|
}
|
|
96
|
-
sitemap += ` <xhtml:link rel="alternate" hreflang="${langEntry.lang}" href="${
|
|
108
|
+
sitemap += ` <xhtml:link rel="alternate" hreflang="${langEntry.lang}" href="${fullUrlEscaped}"/>\n`;
|
|
97
109
|
}
|
|
98
110
|
sitemap += ` </url>\n`;
|
|
99
111
|
}
|
|
@@ -7,10 +7,16 @@ exports.CZONHeader = void 0;
|
|
|
7
7
|
const react_1 = __importDefault(require("react"));
|
|
8
8
|
const DarkModeSwitch_1 = require("./DarkModeSwitch");
|
|
9
9
|
const LanguageSwitch_1 = require("./LanguageSwitch");
|
|
10
|
+
const NavLinks_1 = require("./NavLinks");
|
|
10
11
|
const CZONHeader = props => {
|
|
12
|
+
const navLinks = props.ctx.site.options.site?.navLinks;
|
|
13
|
+
const hasNavLinks = navLinks && navLinks.length > 0;
|
|
11
14
|
return (react_1.default.createElement("header", { className: "czon-header py-4 border-b flex justify-between items-center px-6" },
|
|
12
|
-
react_1.default.createElement("
|
|
13
|
-
react_1.default.createElement(
|
|
15
|
+
react_1.default.createElement("div", { className: "flex items-center gap-4" },
|
|
16
|
+
hasNavLinks && react_1.default.createElement(NavLinks_1.NavLinksMobile, { navLinks: navLinks }),
|
|
17
|
+
react_1.default.createElement("h1", { className: "text-2xl font-bold" },
|
|
18
|
+
react_1.default.createElement("a", { href: "index.html" }, props.ctx.site.options.site?.title ?? 'CZON')),
|
|
19
|
+
hasNavLinks && react_1.default.createElement(NavLinks_1.NavLinksDesktop, { navLinks: navLinks })),
|
|
14
20
|
react_1.default.createElement("div", { className: "flex items-center gap-4" },
|
|
15
21
|
react_1.default.createElement(DarkModeSwitch_1.DarkModeSwitch, null),
|
|
16
22
|
props.lang && react_1.default.createElement(LanguageSwitch_1.LanguageSwitch, { ctx: props.ctx, lang: props.lang, file: props.file }))));
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.NavLinksDesktop = exports.NavLinksMobile = void 0;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const mobileStyle = `
|
|
9
|
+
/* Mobile hamburger menu styles */
|
|
10
|
+
.nav-links-mobile {
|
|
11
|
+
display: block;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@media (min-width: 768px) {
|
|
15
|
+
.nav-links-mobile {
|
|
16
|
+
display: none;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.nav-links-mobile-trigger {
|
|
21
|
+
display: flex;
|
|
22
|
+
align-items: center;
|
|
23
|
+
justify-content: center;
|
|
24
|
+
width: 2.5rem;
|
|
25
|
+
height: 2.5rem;
|
|
26
|
+
border-radius: 0.375rem;
|
|
27
|
+
background: var(--bg-secondary);
|
|
28
|
+
border: 1px solid var(--border-color);
|
|
29
|
+
color: var(--text-primary);
|
|
30
|
+
cursor: pointer;
|
|
31
|
+
transition: all 0.2s ease;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
.nav-links-mobile-trigger:hover {
|
|
35
|
+
background: var(--ls-bg-hover);
|
|
36
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* Hamburger icon animation */
|
|
40
|
+
.nav-links-hamburger {
|
|
41
|
+
width: 1.25rem;
|
|
42
|
+
height: 1.25rem;
|
|
43
|
+
position: relative;
|
|
44
|
+
display: flex;
|
|
45
|
+
flex-direction: column;
|
|
46
|
+
justify-content: center;
|
|
47
|
+
gap: 4px;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.nav-links-hamburger span {
|
|
51
|
+
display: block;
|
|
52
|
+
width: 100%;
|
|
53
|
+
height: 2px;
|
|
54
|
+
background: currentColor;
|
|
55
|
+
border-radius: 1px;
|
|
56
|
+
transition: all 0.3s ease;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
#nav-links-toggle:checked ~ .nav-links-mobile-trigger .nav-links-hamburger span:nth-child(1) {
|
|
60
|
+
transform: translateY(6px) rotate(45deg);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
#nav-links-toggle:checked ~ .nav-links-mobile-trigger .nav-links-hamburger span:nth-child(2) {
|
|
64
|
+
opacity: 0;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#nav-links-toggle:checked ~ .nav-links-mobile-trigger .nav-links-hamburger span:nth-child(3) {
|
|
68
|
+
transform: translateY(-6px) rotate(-45deg);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Mobile dropdown */
|
|
72
|
+
.nav-links-dropdown {
|
|
73
|
+
position: fixed;
|
|
74
|
+
top: 4rem;
|
|
75
|
+
left: 0;
|
|
76
|
+
right: 0;
|
|
77
|
+
background: var(--bg-secondary);
|
|
78
|
+
border-bottom: 1px solid var(--border-color);
|
|
79
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
80
|
+
z-index: 100;
|
|
81
|
+
max-height: 0;
|
|
82
|
+
overflow: hidden;
|
|
83
|
+
opacity: 0;
|
|
84
|
+
transition: max-height 0.3s ease, opacity 0.3s ease;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
#nav-links-toggle:checked ~ .nav-links-dropdown {
|
|
88
|
+
max-height: 80vh;
|
|
89
|
+
opacity: 1;
|
|
90
|
+
overflow-y: auto;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.nav-links-dropdown-list {
|
|
94
|
+
display: flex;
|
|
95
|
+
flex-direction: column;
|
|
96
|
+
padding: 0.5rem;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
.nav-link-mobile-item {
|
|
100
|
+
display: block;
|
|
101
|
+
padding: 0.75rem 1rem;
|
|
102
|
+
font-size: 1rem;
|
|
103
|
+
font-weight: 500;
|
|
104
|
+
color: var(--text-secondary);
|
|
105
|
+
text-decoration: none;
|
|
106
|
+
border-radius: 0.375rem;
|
|
107
|
+
transition: all 0.15s ease;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.nav-link-mobile-item:hover {
|
|
111
|
+
background: var(--ls-bg-hover);
|
|
112
|
+
color: var(--text-primary);
|
|
113
|
+
}
|
|
114
|
+
`;
|
|
115
|
+
const desktopStyle = `
|
|
116
|
+
/* Desktop nav styles */
|
|
117
|
+
.nav-links-desktop {
|
|
118
|
+
display: none;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
@media (min-width: 768px) {
|
|
122
|
+
.nav-links-desktop {
|
|
123
|
+
display: flex;
|
|
124
|
+
max-width: 40vw;
|
|
125
|
+
overflow: hidden;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
.nav-links-desktop-list {
|
|
130
|
+
display: flex;
|
|
131
|
+
gap: 0.5rem;
|
|
132
|
+
overflow: hidden;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
.nav-link-item {
|
|
136
|
+
white-space: nowrap;
|
|
137
|
+
padding: 0.5rem 0.75rem;
|
|
138
|
+
border-radius: 0.375rem;
|
|
139
|
+
font-size: 0.875rem;
|
|
140
|
+
font-weight: 500;
|
|
141
|
+
color: var(--text-secondary);
|
|
142
|
+
text-decoration: none;
|
|
143
|
+
transition: all 0.15s ease;
|
|
144
|
+
flex-shrink: 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.nav-link-item:hover {
|
|
148
|
+
background: var(--ls-bg-hover);
|
|
149
|
+
color: var(--text-primary);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/* Desktop overflow menu */
|
|
153
|
+
.nav-links-more-container {
|
|
154
|
+
position: relative;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.nav-links-more-trigger {
|
|
158
|
+
display: flex;
|
|
159
|
+
align-items: center;
|
|
160
|
+
gap: 0.25rem;
|
|
161
|
+
padding: 0.5rem 0.75rem;
|
|
162
|
+
border-radius: 0.375rem;
|
|
163
|
+
font-size: 0.875rem;
|
|
164
|
+
font-weight: 500;
|
|
165
|
+
color: var(--text-secondary);
|
|
166
|
+
background: transparent;
|
|
167
|
+
border: none;
|
|
168
|
+
cursor: pointer;
|
|
169
|
+
transition: all 0.15s ease;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.nav-links-more-trigger:hover {
|
|
173
|
+
background: var(--ls-bg-hover);
|
|
174
|
+
color: var(--text-primary);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.nav-links-more-icon {
|
|
178
|
+
width: 1rem;
|
|
179
|
+
height: 1rem;
|
|
180
|
+
transition: transform 0.2s ease;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
#nav-links-more-toggle:checked ~ .nav-links-more-trigger .nav-links-more-icon {
|
|
184
|
+
transform: rotate(180deg);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.nav-links-more-dropdown {
|
|
188
|
+
position: absolute;
|
|
189
|
+
top: 100%;
|
|
190
|
+
left: 0;
|
|
191
|
+
margin-top: 0.25rem;
|
|
192
|
+
min-width: 12rem;
|
|
193
|
+
background: var(--bg-secondary);
|
|
194
|
+
border: 1px solid var(--border-color);
|
|
195
|
+
border-radius: 0.5rem;
|
|
196
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
197
|
+
z-index: 50;
|
|
198
|
+
opacity: 0;
|
|
199
|
+
visibility: hidden;
|
|
200
|
+
transform: translateY(-10px);
|
|
201
|
+
transition: opacity 0.2s ease, visibility 0.2s ease, transform 0.2s ease;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
#nav-links-more-toggle:checked ~ .nav-links-more-dropdown {
|
|
205
|
+
opacity: 1;
|
|
206
|
+
visibility: visible;
|
|
207
|
+
transform: translateY(0);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
.nav-links-more-list {
|
|
211
|
+
display: flex;
|
|
212
|
+
flex-direction: column;
|
|
213
|
+
padding: 0.5rem;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.nav-link-more-item {
|
|
217
|
+
display: block;
|
|
218
|
+
padding: 0.5rem 0.75rem;
|
|
219
|
+
font-size: 0.875rem;
|
|
220
|
+
font-weight: 500;
|
|
221
|
+
color: var(--text-secondary);
|
|
222
|
+
text-decoration: none;
|
|
223
|
+
border-radius: 0.375rem;
|
|
224
|
+
transition: all 0.15s ease;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.nav-link-more-item:hover {
|
|
228
|
+
background: var(--ls-bg-hover);
|
|
229
|
+
color: var(--text-primary);
|
|
230
|
+
}
|
|
231
|
+
`;
|
|
232
|
+
/**
|
|
233
|
+
* 移动端汉堡菜单导航组件
|
|
234
|
+
* 仅在移动端显示(< 768px)
|
|
235
|
+
*/
|
|
236
|
+
const NavLinksMobile = ({ navLinks }) => {
|
|
237
|
+
if (!navLinks || navLinks.length === 0) {
|
|
238
|
+
return null;
|
|
239
|
+
}
|
|
240
|
+
return (react_1.default.createElement("nav", { className: "nav-links-mobile", "aria-label": "Mobile navigation" },
|
|
241
|
+
react_1.default.createElement("style", null, mobileStyle),
|
|
242
|
+
react_1.default.createElement("input", { id: "nav-links-toggle", type: "checkbox", className: "hidden", "aria-hidden": "true" }),
|
|
243
|
+
react_1.default.createElement("label", { htmlFor: "nav-links-toggle", className: "nav-links-mobile-trigger", "aria-label": "Toggle navigation menu", "aria-haspopup": "true", "aria-expanded": "false" },
|
|
244
|
+
react_1.default.createElement("div", { className: "nav-links-hamburger" },
|
|
245
|
+
react_1.default.createElement("span", null),
|
|
246
|
+
react_1.default.createElement("span", null),
|
|
247
|
+
react_1.default.createElement("span", null))),
|
|
248
|
+
react_1.default.createElement("div", { className: "nav-links-dropdown", role: "menu", "aria-label": "Navigation links" },
|
|
249
|
+
react_1.default.createElement("div", { className: "nav-links-dropdown-list" }, navLinks.map((link, index) => (react_1.default.createElement("a", { key: index, href: link.href, className: "nav-link-mobile-item", role: "menuitem" }, link.title)))))));
|
|
250
|
+
};
|
|
251
|
+
exports.NavLinksMobile = NavLinksMobile;
|
|
252
|
+
/**
|
|
253
|
+
* 计算在 40% 宽度内可以显示的链接数量
|
|
254
|
+
* 由于是 SSG,这里使用固定值,假设每个链接平均宽度约 100px
|
|
255
|
+
* 在 40vw 下大约可以显示 4-6 个链接
|
|
256
|
+
*/
|
|
257
|
+
const MAX_VISIBLE_LINKS = 5;
|
|
258
|
+
/**
|
|
259
|
+
* 桌面端导航组件
|
|
260
|
+
* 仅在桌面端显示(>= 768px),最大宽度 40vw
|
|
261
|
+
*/
|
|
262
|
+
const NavLinksDesktop = ({ navLinks }) => {
|
|
263
|
+
if (!navLinks || navLinks.length === 0) {
|
|
264
|
+
return null;
|
|
265
|
+
}
|
|
266
|
+
const visibleLinks = navLinks.slice(0, MAX_VISIBLE_LINKS);
|
|
267
|
+
const overflowLinks = navLinks.slice(MAX_VISIBLE_LINKS);
|
|
268
|
+
const hasOverflow = overflowLinks.length > 0;
|
|
269
|
+
return (react_1.default.createElement("nav", { className: "nav-links-desktop", "aria-label": "Main navigation" },
|
|
270
|
+
react_1.default.createElement("style", null, desktopStyle),
|
|
271
|
+
react_1.default.createElement("div", { className: "nav-links-desktop-list" }, visibleLinks.map((link, index) => (react_1.default.createElement("a", { key: index, href: link.href, className: "nav-link-item" }, link.title)))),
|
|
272
|
+
hasOverflow && (react_1.default.createElement("div", { className: "nav-links-more-container" },
|
|
273
|
+
react_1.default.createElement("input", { id: "nav-links-more-toggle", type: "checkbox", className: "hidden", "aria-hidden": "true" }),
|
|
274
|
+
react_1.default.createElement("label", { htmlFor: "nav-links-more-toggle", className: "nav-links-more-trigger", "aria-label": "More navigation links", "aria-haspopup": "true", "aria-expanded": "false" },
|
|
275
|
+
react_1.default.createElement("span", null, "More"),
|
|
276
|
+
react_1.default.createElement("svg", { className: "nav-links-more-icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", "aria-hidden": "true" },
|
|
277
|
+
react_1.default.createElement("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M19 9l-7 7-7-7" }))),
|
|
278
|
+
react_1.default.createElement("div", { className: "nav-links-more-dropdown", role: "menu", "aria-label": "Additional navigation links" },
|
|
279
|
+
react_1.default.createElement("div", { className: "nav-links-more-list" }, overflowLinks.map((link, index) => (react_1.default.createElement("a", { key: index, href: link.href, className: "nav-link-more-item", role: "menuitem" }, link.title)))))))));
|
|
280
|
+
};
|
|
281
|
+
exports.NavLinksDesktop = NavLinksDesktop;
|
|
282
|
+
//# sourceMappingURL=NavLinks.js.map
|