@uniweb/runtime 0.5.7 → 0.5.9
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/package.json +2 -2
- package/src/components/PageRenderer.jsx +14 -3
- package/src/hooks/useLinkInterceptor.js +33 -4
- package/src/index.jsx +16 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@uniweb/runtime",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.9",
|
|
4
4
|
"description": "Minimal runtime for loading Uniweb foundations",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"node": ">=20.19"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@uniweb/core": "0.3.
|
|
34
|
+
"@uniweb/core": "0.3.9"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"@vitejs/plugin-react": "^4.5.2",
|
|
@@ -60,10 +60,10 @@ export default function PageRenderer() {
|
|
|
60
60
|
// This ensures correct page renders immediately on client-side navigation
|
|
61
61
|
let page = website?.getPage(location.pathname)
|
|
62
62
|
|
|
63
|
-
// If no page found, try the 404 page
|
|
63
|
+
// If no page found, try the 404 page (do NOT fall back to activePage/homepage)
|
|
64
64
|
const isNotFound = !page
|
|
65
65
|
if (isNotFound) {
|
|
66
|
-
page = website?.getNotFoundPage?.() ||
|
|
66
|
+
page = website?.getNotFoundPage?.() || null
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
// Get head metadata from page (uses Page.getHeadMeta() if available)
|
|
@@ -76,12 +76,23 @@ export default function PageRenderer() {
|
|
|
76
76
|
useHeadMeta(headMeta, { siteName })
|
|
77
77
|
|
|
78
78
|
if (!page) {
|
|
79
|
-
// No page and no 404 page defined - show minimal fallback
|
|
79
|
+
// No page and no 404 page defined - show minimal fallback with debug info
|
|
80
|
+
const requestedPath = location.pathname
|
|
81
|
+
const isDev = import.meta.env?.DEV
|
|
80
82
|
return (
|
|
81
83
|
<div className="page-not-found" style={{ padding: '4rem 2rem', textAlign: 'center' }}>
|
|
82
84
|
<h1 style={{ fontSize: '3rem', fontWeight: 'bold', color: '#1f2937', marginBottom: '1rem' }}>404</h1>
|
|
83
85
|
<p style={{ color: '#64748b', marginBottom: '2rem' }}>Page not found</p>
|
|
84
86
|
<a href="/" style={{ color: '#3b82f6', textDecoration: 'underline' }}>Go to homepage</a>
|
|
87
|
+
{isDev && (
|
|
88
|
+
<div style={{ marginTop: '2rem', padding: '1rem', background: '#f1f5f9', borderRadius: '0.5rem', textAlign: 'left', maxWidth: '32rem', margin: '2rem auto 0' }}>
|
|
89
|
+
<p style={{ fontWeight: '600', color: '#475569', marginBottom: '0.5rem' }}>Debug info</p>
|
|
90
|
+
<p style={{ fontSize: '0.875rem', color: '#64748b' }}>Path: {requestedPath}</p>
|
|
91
|
+
<p style={{ fontSize: '0.875rem', color: '#64748b' }}>Locale: {website?.getActiveLocale?.() || 'unknown'}</p>
|
|
92
|
+
<p style={{ fontSize: '0.875rem', color: '#64748b' }}>Known routes: {website?.pageRoutes?.join(', ') || 'none'}</p>
|
|
93
|
+
<p style={{ fontSize: '0.75rem', color: '#94a3b8', marginTop: '0.5rem' }}>Tip: Create a pages/404/ folder with a page.yml and content to customize this page.</p>
|
|
94
|
+
</div>
|
|
95
|
+
)}
|
|
85
96
|
</div>
|
|
86
97
|
)
|
|
87
98
|
}
|
|
@@ -147,12 +147,41 @@ export function useLinkInterceptor(options = {}) {
|
|
|
147
147
|
const target = anchor.getAttribute('target')
|
|
148
148
|
if (target && target !== '_self') return
|
|
149
149
|
|
|
150
|
+
// Strip basePath from href for internal processing
|
|
151
|
+
// Links from <Link reload> include the basePath, but React Router
|
|
152
|
+
// and locale detection work with router-relative paths
|
|
153
|
+
const website = globalThis.uniweb?.activeWebsite
|
|
154
|
+
const basePath = website?.basePath || ''
|
|
155
|
+
|
|
156
|
+
let routeHref = href
|
|
157
|
+
if (basePath && href.startsWith(basePath)) {
|
|
158
|
+
routeHref = href.slice(basePath.length) || '/'
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Check if this link switches locale (requires full page reload)
|
|
162
|
+
if (website?.hasMultipleLocales()) {
|
|
163
|
+
const activeLocale = website.getActiveLocale()
|
|
164
|
+
const defaultLocale = website.getDefaultLocale()
|
|
165
|
+
const localeCodes = website.getLocales().map(l => l.code)
|
|
166
|
+
const hrefMatch = routeHref.match(/^\/([a-z]{2,3}(?:-[A-Z]{2})?)(?:\/|$)/)
|
|
167
|
+
const hrefLocale = hrefMatch?.[1]
|
|
168
|
+
|
|
169
|
+
let isLocaleSwitch = false
|
|
170
|
+
if (hrefLocale && localeCodes.includes(hrefLocale)) {
|
|
171
|
+
isLocaleSwitch = hrefLocale !== activeLocale
|
|
172
|
+
} else {
|
|
173
|
+
// No locale prefix = targets default locale
|
|
174
|
+
isLocaleSwitch = activeLocale !== defaultLocale
|
|
175
|
+
}
|
|
176
|
+
if (isLocaleSwitch) return // Allow full page reload
|
|
177
|
+
}
|
|
178
|
+
|
|
150
179
|
// Prevent the default browser navigation
|
|
151
180
|
event.preventDefault()
|
|
152
181
|
|
|
153
182
|
// Handle hash-only links (same page scroll)
|
|
154
|
-
if (
|
|
155
|
-
const elementId =
|
|
183
|
+
if (routeHref.startsWith('#')) {
|
|
184
|
+
const elementId = routeHref.slice(1)
|
|
156
185
|
if (elementId) {
|
|
157
186
|
scrollToElement(elementId)
|
|
158
187
|
// Update URL hash without navigation
|
|
@@ -161,10 +190,10 @@ export function useLinkInterceptor(options = {}) {
|
|
|
161
190
|
return
|
|
162
191
|
}
|
|
163
192
|
|
|
164
|
-
// Use React Router navigation
|
|
193
|
+
// Use React Router navigation (with router-relative path)
|
|
165
194
|
// React Router will handle the path, and our useEffect above
|
|
166
195
|
// will handle scrolling to hash after navigation completes
|
|
167
|
-
navigate(
|
|
196
|
+
navigate(routeHref)
|
|
168
197
|
}
|
|
169
198
|
|
|
170
199
|
// Add click listener to document
|
package/src/index.jsx
CHANGED
|
@@ -291,6 +291,22 @@ function render({ development = false, basename } = {}) {
|
|
|
291
291
|
// Use provided basename, or derive from Vite's BASE_URL
|
|
292
292
|
const routerBasename = basename ?? getBasename()
|
|
293
293
|
|
|
294
|
+
// Set initial active page from browser URL so getLocaleUrl() works on first render
|
|
295
|
+
const website = globalThis.uniweb?.activeWebsite
|
|
296
|
+
if (website && typeof window !== 'undefined') {
|
|
297
|
+
const rawPath = window.location.pathname
|
|
298
|
+
const basePath = routerBasename || ''
|
|
299
|
+
const routePath = basePath && rawPath.startsWith(basePath)
|
|
300
|
+
? rawPath.slice(basePath.length) || '/'
|
|
301
|
+
: rawPath
|
|
302
|
+
website.setActivePage(routePath)
|
|
303
|
+
|
|
304
|
+
// Store base path on Website for components that need it (e.g., Link reload)
|
|
305
|
+
if (website.setBasePath) {
|
|
306
|
+
website.setBasePath(routerBasename || '')
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
294
310
|
const root = createRoot(container)
|
|
295
311
|
|
|
296
312
|
const app = (
|