@x-withu/page-withu 1.1.2 → 1.1.3
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/docs/setup.md +2 -0
- package/package.json +1 -1
- package/src/app/src/app.vue +8 -1
- package/src/app/src/styles/main.css +51 -16
- package/src/vite.config.js +49 -3
package/docs/setup.md
CHANGED
package/package.json
CHANGED
package/src/app/src/app.vue
CHANGED
|
@@ -82,6 +82,13 @@ const domainsTitle = domainsFrontmatter.title || 'Domains'
|
|
|
82
82
|
const sites = domainsFrontmatter.sites || []
|
|
83
83
|
const tabTitle = userConfig.tabTitle || userConfig.title || 'PageWithU'
|
|
84
84
|
const favicon = userConfig.favicon === '/src/assets/bulb.svg' ? defaultFavicon : userConfig.favicon || defaultFavicon
|
|
85
|
+
const externalUrlPattern = /^[a-z][a-z\d+.-]*:/i
|
|
86
|
+
|
|
87
|
+
function resolveFaviconHref(value) {
|
|
88
|
+
if (value === defaultFavicon || externalUrlPattern.test(value) || value.startsWith('//')) return value
|
|
89
|
+
const localPath = value.replace(/^\.\//, '')
|
|
90
|
+
return withBasePath(localPath.startsWith('/') ? localPath : `/${localPath}`)
|
|
91
|
+
}
|
|
85
92
|
|
|
86
93
|
function applyDocumentMeta() {
|
|
87
94
|
document.title = tabTitle
|
|
@@ -91,7 +98,7 @@ function applyDocumentMeta() {
|
|
|
91
98
|
icon.rel = 'icon'
|
|
92
99
|
document.head.appendChild(icon)
|
|
93
100
|
}
|
|
94
|
-
icon.href =
|
|
101
|
+
icon.href = resolveFaviconHref(favicon)
|
|
95
102
|
}
|
|
96
103
|
|
|
97
104
|
applyDocumentMeta()
|
|
@@ -77,6 +77,49 @@
|
|
|
77
77
|
--color-tag-bg: rgba(110, 168, 254, 0.15);
|
|
78
78
|
--color-tag-text: #6ea8fe;
|
|
79
79
|
}
|
|
80
|
+
|
|
81
|
+
:root:not([data-theme="light"]) main > header {
|
|
82
|
+
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
83
|
+
background: rgba(18, 18, 18, 0.78);
|
|
84
|
+
box-shadow: 0 14px 38px rgba(0, 0, 0, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
:root:not([data-theme="light"]) .site-title,
|
|
88
|
+
:root:not([data-theme="light"]) .site-title:hover {
|
|
89
|
+
color: #fff;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
:root:not([data-theme="light"]) .nav-item {
|
|
93
|
+
color: rgba(255, 255, 255, 0.68);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
:root:not([data-theme="light"]) .nav-item:hover,
|
|
97
|
+
:root:not([data-theme="light"]) .nav-item.active {
|
|
98
|
+
color: #fff;
|
|
99
|
+
text-shadow: 0 0 18px rgba(255, 255, 255, 0.28);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
:root:not([data-theme="light"]) .theme-toggle {
|
|
103
|
+
color: rgba(255, 255, 255, 0.68);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
:root:not([data-theme="light"]) .theme-toggle:hover {
|
|
107
|
+
background: rgba(255, 255, 255, 0.1);
|
|
108
|
+
color: #fff;
|
|
109
|
+
text-shadow: 0 0 18px rgba(255, 255, 255, 0.28);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
:root:not([data-theme="light"]) .theme-toggle .icon-sun {
|
|
113
|
+
display: none;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
:root:not([data-theme="light"]) .theme-toggle .icon-moon {
|
|
117
|
+
display: none;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
:root:not([data-theme="light"]) .theme-toggle .icon-system {
|
|
121
|
+
display: block;
|
|
122
|
+
}
|
|
80
123
|
}
|
|
81
124
|
|
|
82
125
|
/* Explicit dark mode override */
|
|
@@ -184,8 +227,7 @@ main > header {
|
|
|
184
227
|
-webkit-backdrop-filter: blur(18px) saturate(1.35);
|
|
185
228
|
}
|
|
186
229
|
|
|
187
|
-
[data-theme="dark"] main > header
|
|
188
|
-
:root:not([data-theme="light"]) main > header {
|
|
230
|
+
[data-theme="dark"] main > header {
|
|
189
231
|
border: 1px solid rgba(255, 255, 255, 0.1);
|
|
190
232
|
background: rgba(18, 18, 18, 0.78);
|
|
191
233
|
box-shadow: 0 14px 38px rgba(0, 0, 0, 0.22), inset 0 1px 0 rgba(255, 255, 255, 0.08);
|
|
@@ -210,8 +252,7 @@ main > header::before {
|
|
|
210
252
|
background: none;
|
|
211
253
|
}
|
|
212
254
|
|
|
213
|
-
[data-theme="dark"] .site-title
|
|
214
|
-
:root:not([data-theme="light"]) .site-title {
|
|
255
|
+
[data-theme="dark"] .site-title {
|
|
215
256
|
color: #fff;
|
|
216
257
|
}
|
|
217
258
|
|
|
@@ -220,8 +261,7 @@ main > header::before {
|
|
|
220
261
|
background: none;
|
|
221
262
|
}
|
|
222
263
|
|
|
223
|
-
[data-theme="dark"] .site-title:hover
|
|
224
|
-
:root:not([data-theme="light"]) .site-title:hover {
|
|
264
|
+
[data-theme="dark"] .site-title:hover {
|
|
225
265
|
color: #fff;
|
|
226
266
|
}
|
|
227
267
|
|
|
@@ -248,7 +288,7 @@ main > header::before {
|
|
|
248
288
|
padding: 6px 0;
|
|
249
289
|
font-size: 0.9rem;
|
|
250
290
|
font-weight: 650;
|
|
251
|
-
color: var(--color-subtext);
|
|
291
|
+
color: var(--color-subtext-light, #8a9ba8);
|
|
252
292
|
text-decoration: none;
|
|
253
293
|
border-radius: 0;
|
|
254
294
|
transition: color 0.2s ease, text-shadow 0.2s ease;
|
|
@@ -256,8 +296,7 @@ main > header::before {
|
|
|
256
296
|
white-space: nowrap;
|
|
257
297
|
}
|
|
258
298
|
|
|
259
|
-
[data-theme="dark"] .nav-item
|
|
260
|
-
:root:not([data-theme="light"]) .nav-item {
|
|
299
|
+
[data-theme="dark"] .nav-item {
|
|
261
300
|
color: rgba(255, 255, 255, 0.68);
|
|
262
301
|
}
|
|
263
302
|
|
|
@@ -269,9 +308,7 @@ main > header::before {
|
|
|
269
308
|
}
|
|
270
309
|
|
|
271
310
|
[data-theme="dark"] .nav-item:hover,
|
|
272
|
-
[data-theme="dark"] .nav-item.active
|
|
273
|
-
:root:not([data-theme="light"]) .nav-item:hover,
|
|
274
|
-
:root:not([data-theme="light"]) .nav-item.active {
|
|
311
|
+
[data-theme="dark"] .nav-item.active {
|
|
275
312
|
color: #fff;
|
|
276
313
|
text-shadow: 0 0 18px rgba(255, 255, 255, 0.28);
|
|
277
314
|
}
|
|
@@ -516,8 +553,7 @@ footer p {
|
|
|
516
553
|
color: var(--color-subtext);
|
|
517
554
|
}
|
|
518
555
|
|
|
519
|
-
[data-theme="dark"] .theme-toggle
|
|
520
|
-
:root:not([data-theme="light"]) .theme-toggle {
|
|
556
|
+
[data-theme="dark"] .theme-toggle {
|
|
521
557
|
color: rgba(255, 255, 255, 0.68);
|
|
522
558
|
}
|
|
523
559
|
|
|
@@ -527,8 +563,7 @@ footer p {
|
|
|
527
563
|
text-shadow: none;
|
|
528
564
|
}
|
|
529
565
|
|
|
530
|
-
[data-theme="dark"] .theme-toggle:hover
|
|
531
|
-
:root:not([data-theme="light"]) .theme-toggle:hover {
|
|
566
|
+
[data-theme="dark"] .theme-toggle:hover {
|
|
532
567
|
background: rgba(255, 255, 255, 0.1);
|
|
533
568
|
color: #fff;
|
|
534
569
|
text-shadow: 0 0 18px rgba(255, 255, 255, 0.28);
|
package/src/vite.config.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto'
|
|
2
2
|
import { createRequire } from 'node:module'
|
|
3
3
|
import { tmpdir } from 'node:os'
|
|
4
|
-
import { mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
|
|
4
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs'
|
|
5
5
|
import { dirname, join, resolve } from 'node:path'
|
|
6
6
|
import { fileURLToPath, pathToFileURL } from 'node:url'
|
|
7
7
|
|
|
@@ -23,6 +23,18 @@ const userContentDir = resolve(projectRoot, 'content')
|
|
|
23
23
|
const cacheKey = createHash('sha256').update(projectRoot).digest('hex').slice(0, 12)
|
|
24
24
|
const cacheDir = resolve(tmpdir(), `page-withu-${cacheKey}`)
|
|
25
25
|
const basePath = process.env.BASE_PATH || '/'
|
|
26
|
+
const externalUrlPattern = /^[a-z][a-z\d+.-]*:/i
|
|
27
|
+
|
|
28
|
+
function resolveProjectAssetPath(path) {
|
|
29
|
+
if (!path || path === '/src/assets/bulb.svg' || externalUrlPattern.test(path) || path.startsWith('//') || path.startsWith('/')) return null
|
|
30
|
+
const relativePath = path.replace(/^\.\//, '')
|
|
31
|
+
return {
|
|
32
|
+
relativePath,
|
|
33
|
+
sourcePath: join(projectRoot, relativePath),
|
|
34
|
+
outputPath: join(resolve(projectRoot, 'dist'), relativePath),
|
|
35
|
+
href: withBasePath(`/${relativePath}`),
|
|
36
|
+
}
|
|
37
|
+
}
|
|
26
38
|
|
|
27
39
|
function slugify(text) {
|
|
28
40
|
return text
|
|
@@ -160,6 +172,27 @@ function markdown(deps) {
|
|
|
160
172
|
}
|
|
161
173
|
}
|
|
162
174
|
|
|
175
|
+
function projectAssets(userConfig) {
|
|
176
|
+
return {
|
|
177
|
+
name: 'project-assets',
|
|
178
|
+
configureServer(server) {
|
|
179
|
+
const favicon = resolveProjectAssetPath(userConfig.favicon)
|
|
180
|
+
if (!favicon) return
|
|
181
|
+
|
|
182
|
+
server.middlewares.use((req, res, next) => {
|
|
183
|
+
const pathname = decodeURI((req.url || '').split('?')[0])
|
|
184
|
+
if (pathname !== `/${favicon.relativePath}` || !existsSync(favicon.sourcePath)) {
|
|
185
|
+
next()
|
|
186
|
+
return
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (favicon.sourcePath.endsWith('.svg')) res.setHeader('Content-Type', 'image/svg+xml')
|
|
190
|
+
res.end(readFileSync(favicon.sourcePath))
|
|
191
|
+
})
|
|
192
|
+
},
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
163
196
|
function staticHtmlRoutes(userConfig) {
|
|
164
197
|
return {
|
|
165
198
|
name: 'static-html-routes',
|
|
@@ -179,7 +212,7 @@ function staticHtmlRoutes(userConfig) {
|
|
|
179
212
|
|
|
180
213
|
const pageSize = userConfig.pagination?.pageSize || 5
|
|
181
214
|
const totalPages = Math.max(1, Math.ceil(blogPosts.length / pageSize))
|
|
182
|
-
const routes = ['domains.html', 'blog.html']
|
|
215
|
+
const routes = ['index.html', 'domains.html', 'blog.html']
|
|
183
216
|
for (let page = 2; page <= totalPages; page += 1) routes.push(`blog/page/${page}.html`)
|
|
184
217
|
for (const slug of blogPosts) routes.push(`blog/${slug}.html`)
|
|
185
218
|
|
|
@@ -188,6 +221,19 @@ function staticHtmlRoutes(userConfig) {
|
|
|
188
221
|
mkdirSync(dirname(file), { recursive: true })
|
|
189
222
|
writeFileSync(file, index)
|
|
190
223
|
}
|
|
224
|
+
|
|
225
|
+
const favicon = resolveProjectAssetPath(userConfig.favicon)
|
|
226
|
+
if (favicon && existsSync(favicon.sourcePath)) {
|
|
227
|
+
mkdirSync(dirname(favicon.outputPath), { recursive: true })
|
|
228
|
+
copyFileSync(favicon.sourcePath, favicon.outputPath)
|
|
229
|
+
|
|
230
|
+
for (const route of routes) {
|
|
231
|
+
const file = join(dist, route)
|
|
232
|
+
if (!existsSync(file)) continue
|
|
233
|
+
const html = readFileSync(file, 'utf8').replace(/<link rel="icon"[^>]*href="[^"]*"[^>]*>/, `<link rel="icon" href="${favicon.href}">`)
|
|
234
|
+
writeFileSync(file, html)
|
|
235
|
+
}
|
|
236
|
+
}
|
|
191
237
|
},
|
|
192
238
|
}
|
|
193
239
|
}
|
|
@@ -225,7 +271,7 @@ export default defineConfig(async () => {
|
|
|
225
271
|
'@page-withu/user-content': userContentDir,
|
|
226
272
|
},
|
|
227
273
|
},
|
|
228
|
-
plugins: [vueModule.default(), markdown(deps), staticHtmlRoutes(userConfig)],
|
|
274
|
+
plugins: [vueModule.default(), markdown(deps), projectAssets(userConfig), staticHtmlRoutes(userConfig)],
|
|
229
275
|
server: {
|
|
230
276
|
port: 5500,
|
|
231
277
|
fs: {
|