weifuwu 0.17.18 → 0.17.21
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 +63 -3
- package/dist/client-locale.d.ts +5 -0
- package/dist/client-pref.d.ts +3 -0
- package/dist/client-router.d.ts +1 -1
- package/dist/client-theme.d.ts +7 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -14
- package/dist/react.d.ts +4 -2
- package/dist/react.js +141 -80
- package/dist/tsx-context.d.ts +0 -9
- package/dist/tsx-instance.d.ts +2 -2
- package/dist/tsx.d.ts +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -322,8 +322,13 @@ app.use(preferences({ dir: './locales', locale: { default: 'en' }, theme: { defa
|
|
|
322
322
|
| `theme.cookie` | `'theme'` | Cookie name |
|
|
323
323
|
|
|
324
324
|
```tsx
|
|
325
|
-
// Client-side no-refresh switching
|
|
326
|
-
|
|
325
|
+
// Client-side no-refresh switching — import enables it automatically
|
|
326
|
+
import { useLocale, useTheme } from 'weifuwu/react'
|
|
327
|
+
|
|
328
|
+
<Link href="/__lang/zh">中文</Link> // <Link> handles it via interceptor
|
|
329
|
+
<button onClick={() => setLocale('en')}>EN</button> // or programmatic
|
|
330
|
+
const { theme, resolvedTheme, setTheme } = useTheme()
|
|
331
|
+
// resolvedTheme resolves 'system' → 'dark'|'light' based on prefers-color-scheme
|
|
327
332
|
```
|
|
328
333
|
|
|
329
334
|
### queue [B]
|
|
@@ -467,12 +472,15 @@ const navigate = useNavigate() // programmatic: navigate('/contact
|
|
|
467
472
|
const loading = useNavigating() // reactive loading state
|
|
468
473
|
```
|
|
469
474
|
|
|
470
|
-
`navigate()` fetches SSR, extracts `__weifuwu_root`, replaces in-place. `load.ts` runs on server each nav.
|
|
475
|
+
`navigate()` fetches SSR, extracts `__weifuwu_root`, replaces in-place. `load.ts` runs on server each nav.
|
|
476
|
+
|
|
477
|
+
**Preference URLs** (`/__lang/`, `/__theme/`) are intercepted by modular interceptors registered via `addInterceptor()` — no page reload needed. Importing `useLocale` or `useTheme` registers the interceptor automatically.
|
|
471
478
|
|
|
472
479
|
### Client-side hooks
|
|
473
480
|
|
|
474
481
|
```tsx
|
|
475
482
|
import { useWebsocket, useAction, useData, useQueryState, createStore, Head, setCtx } from 'weifuwu/react'
|
|
483
|
+
import { useLocale, useTheme, applyTheme, addInterceptor } from 'weifuwu/react'
|
|
476
484
|
|
|
477
485
|
// WebSocket — auto-reconnecting
|
|
478
486
|
const { send, lastMessage, readyState } = useWebsocket('/ws/chat', { onMessage: (d) => console.log(d), reconnect: { maxRetries: 10, delay: 3000 } })
|
|
@@ -499,6 +507,58 @@ const count = useStore(s => s.count)
|
|
|
499
507
|
setCtx({ locale: 'en', prefs: { locale: 'en' } })
|
|
500
508
|
```
|
|
501
509
|
|
|
510
|
+
### Locale & Theme
|
|
511
|
+
|
|
512
|
+
```tsx
|
|
513
|
+
import { useLocale } from 'weifuwu/react'
|
|
514
|
+
function LangSwitch() {
|
|
515
|
+
const { locale, setLocale, t } = useLocale()
|
|
516
|
+
return <button onClick={() => setLocale('zh-CN')}>{t('switch_lang')}</button>
|
|
517
|
+
}
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
| Return | Description |
|
|
521
|
+
|--------|-------------|
|
|
522
|
+
| `locale` | Current locale string (from `ctx.prefs.locale`) |
|
|
523
|
+
| `setLocale(locale)` | Switch locale (calls `navigate('/__lang/' + locale)`) |
|
|
524
|
+
| `t` | Translation function (same as `useCtx().t`) |
|
|
525
|
+
|
|
526
|
+
```tsx
|
|
527
|
+
import { useTheme } from 'weifuwu/react'
|
|
528
|
+
function ThemeToggle() {
|
|
529
|
+
const { theme, resolvedTheme, setTheme } = useTheme()
|
|
530
|
+
return (
|
|
531
|
+
<>
|
|
532
|
+
<span>Current: {resolvedTheme}</span> {/* 'dark' | 'light' — never 'system' */}
|
|
533
|
+
<select value={theme} onChange={e => setTheme(e.target.value)}>
|
|
534
|
+
<option value="light">☀ Light</option>
|
|
535
|
+
<option value="dark">🌙 Dark</option>
|
|
536
|
+
<option value="system">💻 System</option>
|
|
537
|
+
</select>
|
|
538
|
+
</>
|
|
539
|
+
)
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
| Return | Description |
|
|
544
|
+
|--------|-------------|
|
|
545
|
+
| `theme` | Raw preference (`'light'` \| `'dark'` \| `'system'`) |
|
|
546
|
+
| `resolvedTheme` | Resolved value (`'light'` \| `'dark'`) — `'system'` → matchMedia |
|
|
547
|
+
| `setTheme(theme)` | Switch theme (calls `navigate('/__theme/' + theme)`) |
|
|
548
|
+
|
|
549
|
+
**`applyTheme(theme)`** — DOM-only theme application. Sets `data-theme` on `<html>`, registers `matchMedia` listener for `'system'`. Used by the interceptor; exported for custom scenarios.
|
|
550
|
+
|
|
551
|
+
**`addInterceptor(fn)`** — Register a URL interceptor. Interceptors run before SPA navigation; if one returns `true`, `navigate()` skips the fetch-and-swap.
|
|
552
|
+
|
|
553
|
+
```ts
|
|
554
|
+
import { addInterceptor } from 'weifuwu/react'
|
|
555
|
+
addInterceptor(async (url) => {
|
|
556
|
+
if (!url.pathname.startsWith('/__custom/')) return false
|
|
557
|
+
// handle without page reload
|
|
558
|
+
return true
|
|
559
|
+
})
|
|
560
|
+
```
|
|
561
|
+
|
|
502
562
|
### Flash messages
|
|
503
563
|
|
|
504
564
|
```ts
|
package/dist/client-router.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
export { addInterceptor } from './client-pref.ts';
|
|
1
2
|
export declare function isNavigating(): boolean;
|
|
2
3
|
export declare function onNavigate(fn: (v: boolean) => void): () => void;
|
|
3
4
|
export declare function navigate(href: string): Promise<void>;
|
|
@@ -297,4 +298,3 @@ export declare function Link({ href, children, onClick, prefetch, ...props }: Li
|
|
|
297
298
|
href: string;
|
|
298
299
|
onClick: (e: React.MouseEvent<HTMLAnchorElement>) => void;
|
|
299
300
|
}, HTMLElement>;
|
|
300
|
-
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export { serve, createTestServer } from './serve.ts';
|
|
|
4
4
|
export type { ServeOptions, Server } from './serve.ts';
|
|
5
5
|
export { Router } from './router.ts';
|
|
6
6
|
export type { WebSocketHandler } from './router.ts';
|
|
7
|
-
export { tsx, TsxContext, useCtx, setCtx
|
|
7
|
+
export { tsx, TsxContext, useCtx, setCtx } from './tsx.ts';
|
|
8
8
|
export type { TsxOptions } from './tsx.ts';
|
|
9
9
|
export { auth, cors, logger } from './middleware.ts';
|
|
10
10
|
export type { AuthOptions, CORSOptions, LoggerOptions } from './middleware.ts';
|
package/dist/index.js
CHANGED
|
@@ -598,15 +598,10 @@ function setCtx(value) {
|
|
|
598
598
|
_snapshot = { params: _ctx.params, query: _ctx.query, user: _ctx.user, parsed: _ctx.parsed, prefs: _ctx.prefs, env: _ctx.env };
|
|
599
599
|
_listeners.forEach((fn) => fn());
|
|
600
600
|
}
|
|
601
|
-
var _cachedT = null;
|
|
602
601
|
function _buildT() {
|
|
603
|
-
if (_cachedT) return _cachedT;
|
|
604
602
|
const messages2 = typeof window !== "undefined" ? window.__LOCALE_DATA__ : globalThis.__LOCALE_DATA__;
|
|
605
|
-
if (!messages2)
|
|
606
|
-
|
|
607
|
-
return fallbackT;
|
|
608
|
-
}
|
|
609
|
-
_cachedT = (key, params, fallback) => {
|
|
603
|
+
if (!messages2) return fallbackT;
|
|
604
|
+
return (key, params, fallback) => {
|
|
610
605
|
const msg = key.split(".").reduce((o, k) => o?.[k], messages2);
|
|
611
606
|
if (msg === void 0 || msg === null) return fallback ?? key;
|
|
612
607
|
if (!params) return String(msg);
|
|
@@ -614,22 +609,17 @@ function _buildT() {
|
|
|
614
609
|
for (const [k, v] of Object.entries(params)) result = result.replace(`{${k}}`, v);
|
|
615
610
|
return result;
|
|
616
611
|
};
|
|
617
|
-
return _cachedT;
|
|
618
612
|
}
|
|
619
613
|
function _readCtx() {
|
|
620
614
|
const alsStore = _alsGetStore?.();
|
|
621
615
|
const base = alsStore ?? _ctx;
|
|
622
616
|
const data = typeof window !== "undefined" ? window.__WEIFUWU_CTX : null;
|
|
623
|
-
|
|
624
|
-
return { ...base, ...data, t };
|
|
617
|
+
return { ...base, ...data, t: _buildT() };
|
|
625
618
|
}
|
|
626
619
|
function useCtx() {
|
|
627
620
|
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
628
621
|
return _readCtx();
|
|
629
622
|
}
|
|
630
|
-
function getCtx() {
|
|
631
|
-
return _readCtx();
|
|
632
|
-
}
|
|
633
623
|
var TsxContext = createContext(DEFAULT_CTX);
|
|
634
624
|
|
|
635
625
|
// tsx-instance.ts
|
|
@@ -8635,7 +8625,6 @@ export {
|
|
|
8635
8625
|
generateObject,
|
|
8636
8626
|
generateText2 as generateText,
|
|
8637
8627
|
getCookies,
|
|
8638
|
-
getCtx,
|
|
8639
8628
|
graphql,
|
|
8640
8629
|
health,
|
|
8641
8630
|
helmet,
|
package/dist/react.d.ts
CHANGED
|
@@ -2,8 +2,10 @@ export { useWebsocket } from './use-websocket.ts';
|
|
|
2
2
|
export type { UseWebsocketOptions, UseWebsocketReturn } from './use-websocket.ts';
|
|
3
3
|
export { useAction } from './use-action.ts';
|
|
4
4
|
export type { UseActionOptions, UseActionReturn } from './use-action.ts';
|
|
5
|
-
export { Link, useNavigate, navigate, useNavigating } from './client-router.ts';
|
|
6
|
-
export { TsxContext, useCtx, setCtx
|
|
5
|
+
export { Link, useNavigate, navigate, useNavigating, addInterceptor } from './client-router.ts';
|
|
6
|
+
export { TsxContext, useCtx, setCtx } from './tsx-context.ts';
|
|
7
7
|
export { Head } from './head.tsx';
|
|
8
8
|
export { createStore, useData, useQueryState } from './client-state.ts';
|
|
9
9
|
export type { StoreApi } from './client-state.ts';
|
|
10
|
+
export { useLocale } from './client-locale.ts';
|
|
11
|
+
export { useTheme, applyTheme } from './client-theme.ts';
|
package/dist/react.js
CHANGED
|
@@ -141,73 +141,30 @@ function useAction(url, options) {
|
|
|
141
141
|
// client-router.ts
|
|
142
142
|
import { createElement, useCallback as useCallback3, useState as useState3, useEffect as useEffect2 } from "react";
|
|
143
143
|
|
|
144
|
-
//
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
var _ctx = DEFAULT_CTX;
|
|
149
|
-
var _snapshot = { params: _ctx.params, query: _ctx.query, user: _ctx.user, parsed: _ctx.parsed, prefs: _ctx.prefs, env: _ctx.env };
|
|
150
|
-
var _listeners = /* @__PURE__ */ new Set();
|
|
151
|
-
var subscribe = (cb) => {
|
|
152
|
-
_listeners.add(cb);
|
|
153
|
-
return () => {
|
|
154
|
-
_listeners.delete(cb);
|
|
155
|
-
};
|
|
156
|
-
};
|
|
157
|
-
var getSnapshot = () => _snapshot;
|
|
158
|
-
var getServerSnapshot = getSnapshot;
|
|
159
|
-
var _alsGetStore = null;
|
|
160
|
-
function setCtx(value) {
|
|
161
|
-
_ctx = { ..._ctx, ...value };
|
|
162
|
-
_snapshot = { params: _ctx.params, query: _ctx.query, user: _ctx.user, parsed: _ctx.parsed, prefs: _ctx.prefs, env: _ctx.env };
|
|
163
|
-
_listeners.forEach((fn) => fn());
|
|
144
|
+
// client-pref.ts
|
|
145
|
+
var interceptors = [];
|
|
146
|
+
function addInterceptor(fn) {
|
|
147
|
+
interceptors.push(fn);
|
|
164
148
|
}
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
const messages = typeof window !== "undefined" ? window.__LOCALE_DATA__ : globalThis.__LOCALE_DATA__;
|
|
169
|
-
if (!messages) {
|
|
170
|
-
_cachedT = fallbackT;
|
|
171
|
-
return fallbackT;
|
|
149
|
+
async function runInterceptors(url) {
|
|
150
|
+
for (const fn of interceptors) {
|
|
151
|
+
if (await fn(url)) return true;
|
|
172
152
|
}
|
|
173
|
-
|
|
174
|
-
const msg = key.split(".").reduce((o, k) => o?.[k], messages);
|
|
175
|
-
if (msg === void 0 || msg === null) return fallback ?? key;
|
|
176
|
-
if (!params) return String(msg);
|
|
177
|
-
let result = String(msg);
|
|
178
|
-
for (const [k, v] of Object.entries(params)) result = result.replace(`{${k}}`, v);
|
|
179
|
-
return result;
|
|
180
|
-
};
|
|
181
|
-
return _cachedT;
|
|
182
|
-
}
|
|
183
|
-
function _readCtx() {
|
|
184
|
-
const alsStore = _alsGetStore?.();
|
|
185
|
-
const base = alsStore ?? _ctx;
|
|
186
|
-
const data = typeof window !== "undefined" ? window.__WEIFUWU_CTX : null;
|
|
187
|
-
const t = typeof base.t === "function" && base.t !== fallbackT ? base.t : _buildT();
|
|
188
|
-
return { ...base, ...data, t };
|
|
153
|
+
return false;
|
|
189
154
|
}
|
|
190
|
-
function useCtx() {
|
|
191
|
-
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
192
|
-
return _readCtx();
|
|
193
|
-
}
|
|
194
|
-
function getCtx() {
|
|
195
|
-
return _readCtx();
|
|
196
|
-
}
|
|
197
|
-
var TsxContext = createContext(DEFAULT_CTX);
|
|
198
155
|
|
|
199
156
|
// client-router.ts
|
|
200
157
|
var _navigating = false;
|
|
201
|
-
var
|
|
158
|
+
var _listeners = [];
|
|
202
159
|
function onNavigate(fn) {
|
|
203
|
-
|
|
160
|
+
_listeners.push(fn);
|
|
204
161
|
return () => {
|
|
205
|
-
|
|
162
|
+
_listeners = _listeners.filter((l) => l !== fn);
|
|
206
163
|
};
|
|
207
164
|
}
|
|
208
165
|
function setNavigating(v) {
|
|
209
166
|
_navigating = v;
|
|
210
|
-
for (const fn of
|
|
167
|
+
for (const fn of _listeners) fn(v);
|
|
211
168
|
}
|
|
212
169
|
async function navigate(href) {
|
|
213
170
|
if (typeof document === "undefined") return;
|
|
@@ -216,30 +173,7 @@ async function navigate(href) {
|
|
|
216
173
|
location.href = href;
|
|
217
174
|
return;
|
|
218
175
|
}
|
|
219
|
-
|
|
220
|
-
const themeMatch = url.pathname.match(/^\/__theme\/(\w+)$/);
|
|
221
|
-
if (langMatch || themeMatch) {
|
|
222
|
-
try {
|
|
223
|
-
const res = await fetch(url.pathname, {
|
|
224
|
-
headers: { accept: "application/json" }
|
|
225
|
-
});
|
|
226
|
-
const data = await res.json();
|
|
227
|
-
const ctx = { ...window.__WEIFUWU_CTX || {}, params: {}, query: {} };
|
|
228
|
-
if (data.locale) {
|
|
229
|
-
ctx.prefs = { ...ctx.prefs, locale: data.locale };
|
|
230
|
-
if (data.messages) window.__LOCALE_DATA__ = data.messages;
|
|
231
|
-
}
|
|
232
|
-
if (data.theme) {
|
|
233
|
-
ctx.prefs = { ...ctx.prefs, theme: data.theme };
|
|
234
|
-
}
|
|
235
|
-
;
|
|
236
|
-
window.__WEIFUWU_CTX = ctx;
|
|
237
|
-
setCtx(ctx);
|
|
238
|
-
} catch {
|
|
239
|
-
location.href = href;
|
|
240
|
-
}
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
176
|
+
if (await runInterceptors(url)) return;
|
|
243
177
|
const scrollPos = [window.scrollX, window.scrollY];
|
|
244
178
|
setNavigating(true);
|
|
245
179
|
try {
|
|
@@ -396,6 +330,51 @@ async function prefetchPage(href) {
|
|
|
396
330
|
}
|
|
397
331
|
}
|
|
398
332
|
|
|
333
|
+
// tsx-context.ts
|
|
334
|
+
import { useSyncExternalStore, createContext } from "react";
|
|
335
|
+
var fallbackT = (key, _params, fallback) => fallback ?? key;
|
|
336
|
+
var DEFAULT_CTX = { params: {}, query: {}, parsed: {}, prefs: {}, env: {}, t: fallbackT, user: {} };
|
|
337
|
+
var _ctx = DEFAULT_CTX;
|
|
338
|
+
var _snapshot = { params: _ctx.params, query: _ctx.query, user: _ctx.user, parsed: _ctx.parsed, prefs: _ctx.prefs, env: _ctx.env };
|
|
339
|
+
var _listeners2 = /* @__PURE__ */ new Set();
|
|
340
|
+
var subscribe = (cb) => {
|
|
341
|
+
_listeners2.add(cb);
|
|
342
|
+
return () => {
|
|
343
|
+
_listeners2.delete(cb);
|
|
344
|
+
};
|
|
345
|
+
};
|
|
346
|
+
var getSnapshot = () => _snapshot;
|
|
347
|
+
var getServerSnapshot = getSnapshot;
|
|
348
|
+
var _alsGetStore = null;
|
|
349
|
+
function setCtx(value) {
|
|
350
|
+
_ctx = { ..._ctx, ...value };
|
|
351
|
+
_snapshot = { params: _ctx.params, query: _ctx.query, user: _ctx.user, parsed: _ctx.parsed, prefs: _ctx.prefs, env: _ctx.env };
|
|
352
|
+
_listeners2.forEach((fn) => fn());
|
|
353
|
+
}
|
|
354
|
+
function _buildT() {
|
|
355
|
+
const messages = typeof window !== "undefined" ? window.__LOCALE_DATA__ : globalThis.__LOCALE_DATA__;
|
|
356
|
+
if (!messages) return fallbackT;
|
|
357
|
+
return (key, params, fallback) => {
|
|
358
|
+
const msg = key.split(".").reduce((o, k) => o?.[k], messages);
|
|
359
|
+
if (msg === void 0 || msg === null) return fallback ?? key;
|
|
360
|
+
if (!params) return String(msg);
|
|
361
|
+
let result = String(msg);
|
|
362
|
+
for (const [k, v] of Object.entries(params)) result = result.replace(`{${k}}`, v);
|
|
363
|
+
return result;
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
function _readCtx() {
|
|
367
|
+
const alsStore = _alsGetStore?.();
|
|
368
|
+
const base = alsStore ?? _ctx;
|
|
369
|
+
const data = typeof window !== "undefined" ? window.__WEIFUWU_CTX : null;
|
|
370
|
+
return { ...base, ...data, t: _buildT() };
|
|
371
|
+
}
|
|
372
|
+
function useCtx() {
|
|
373
|
+
useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);
|
|
374
|
+
return _readCtx();
|
|
375
|
+
}
|
|
376
|
+
var TsxContext = createContext(DEFAULT_CTX);
|
|
377
|
+
|
|
399
378
|
// head.tsx
|
|
400
379
|
import { createElement as createElement2 } from "react";
|
|
401
380
|
function Head({ children }) {
|
|
@@ -534,19 +513,101 @@ function useQueryState(key, defaultValue = "") {
|
|
|
534
513
|
}, [key, defaultValue]);
|
|
535
514
|
return [value, setValue];
|
|
536
515
|
}
|
|
516
|
+
|
|
517
|
+
// client-locale.ts
|
|
518
|
+
addInterceptor(async (url) => {
|
|
519
|
+
const m = url.pathname.match(/^\/__lang\/(\w+)$/);
|
|
520
|
+
if (!m) return false;
|
|
521
|
+
try {
|
|
522
|
+
const res = await fetch(url.pathname, {
|
|
523
|
+
headers: { accept: "application/json" }
|
|
524
|
+
});
|
|
525
|
+
const data = await res.json();
|
|
526
|
+
const ctx = { ...window.__WEIFUWU_CTX || {}, params: {}, query: {} };
|
|
527
|
+
ctx.prefs = { ...ctx.prefs, locale: data.locale };
|
|
528
|
+
if (data.messages) window.__LOCALE_DATA__ = data.messages;
|
|
529
|
+
window.__WEIFUWU_CTX = ctx;
|
|
530
|
+
setCtx(ctx);
|
|
531
|
+
} catch {
|
|
532
|
+
location.href = url.href;
|
|
533
|
+
}
|
|
534
|
+
return true;
|
|
535
|
+
});
|
|
536
|
+
function useLocale() {
|
|
537
|
+
const ctx = useCtx();
|
|
538
|
+
return {
|
|
539
|
+
locale: ctx.prefs.locale,
|
|
540
|
+
setLocale: (locale) => navigate("/__lang/" + locale),
|
|
541
|
+
t: ctx.t
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// client-theme.ts
|
|
546
|
+
function resolveTheme(theme) {
|
|
547
|
+
if (theme === "system") {
|
|
548
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
549
|
+
}
|
|
550
|
+
return theme;
|
|
551
|
+
}
|
|
552
|
+
var _mqListener = null;
|
|
553
|
+
function applyTheme(theme) {
|
|
554
|
+
const resolved = resolveTheme(theme);
|
|
555
|
+
document.documentElement.dataset.theme = resolved;
|
|
556
|
+
if (theme === "system") {
|
|
557
|
+
if (!_mqListener) {
|
|
558
|
+
const mq = window.matchMedia("(prefers-color-scheme: dark)");
|
|
559
|
+
mq.addEventListener("change", (e) => {
|
|
560
|
+
if (window.__WEIFUWU_CTX?.prefs?.theme === "system") {
|
|
561
|
+
document.documentElement.dataset.theme = e.matches ? "dark" : "light";
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
_mqListener = mq;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
addInterceptor(async (url) => {
|
|
569
|
+
const m = url.pathname.match(/^\/__theme\/(\w+)$/);
|
|
570
|
+
if (!m) return false;
|
|
571
|
+
try {
|
|
572
|
+
const res = await fetch(url.pathname, {
|
|
573
|
+
headers: { accept: "application/json" }
|
|
574
|
+
});
|
|
575
|
+
const data = await res.json();
|
|
576
|
+
const ctx = { ...window.__WEIFUWU_CTX || {}, params: {}, query: {} };
|
|
577
|
+
ctx.prefs = { ...ctx.prefs, theme: data.theme };
|
|
578
|
+
window.__WEIFUWU_CTX = ctx;
|
|
579
|
+
applyTheme(data.theme);
|
|
580
|
+
setCtx(ctx);
|
|
581
|
+
} catch {
|
|
582
|
+
location.href = url.href;
|
|
583
|
+
}
|
|
584
|
+
return true;
|
|
585
|
+
});
|
|
586
|
+
function useTheme() {
|
|
587
|
+
const ctx = useCtx();
|
|
588
|
+
const theme = ctx.prefs.theme ?? "system";
|
|
589
|
+
return {
|
|
590
|
+
theme,
|
|
591
|
+
resolvedTheme: resolveTheme(theme),
|
|
592
|
+
setTheme: (t) => navigate("/__theme/" + t)
|
|
593
|
+
};
|
|
594
|
+
}
|
|
537
595
|
export {
|
|
538
596
|
Head,
|
|
539
597
|
Link,
|
|
540
598
|
TsxContext,
|
|
599
|
+
addInterceptor,
|
|
600
|
+
applyTheme,
|
|
541
601
|
createStore,
|
|
542
|
-
getCtx,
|
|
543
602
|
navigate,
|
|
544
603
|
setCtx,
|
|
545
604
|
useAction,
|
|
546
605
|
useCtx,
|
|
547
606
|
useData,
|
|
607
|
+
useLocale,
|
|
548
608
|
useNavigate,
|
|
549
609
|
useNavigating,
|
|
550
610
|
useQueryState,
|
|
611
|
+
useTheme,
|
|
551
612
|
useWebsocket
|
|
552
613
|
};
|
package/dist/tsx-context.d.ts
CHANGED
|
@@ -12,14 +12,5 @@ export interface CtxValue {
|
|
|
12
12
|
/** @internal Injected by tsx-instance.ts for async-safe context isolation */
|
|
13
13
|
export declare function __registerAls(getStore: () => CtxValue | undefined): void;
|
|
14
14
|
export declare function setCtx(value: Partial<CtxValue>): void;
|
|
15
|
-
/**
|
|
16
|
-
* React hook — returns the current request context.
|
|
17
|
-
* Must be called from a React component or custom hook.
|
|
18
|
-
*/
|
|
19
15
|
export declare function useCtx(): CtxValue;
|
|
20
|
-
/**
|
|
21
|
-
* Plain accessor — returns the current request context without calling any React hooks.
|
|
22
|
-
* Safe to call from utility functions, route handlers, middleware, load functions, etc.
|
|
23
|
-
*/
|
|
24
|
-
export declare function getCtx(): CtxValue;
|
|
25
16
|
export declare const TsxContext: import("react").Context<CtxValue>;
|
package/dist/tsx-instance.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Router } from './router.ts';
|
|
2
|
-
import { TsxContext, useCtx, setCtx
|
|
3
|
-
export { TsxContext, useCtx, setCtx
|
|
2
|
+
import { TsxContext, useCtx, setCtx } from './tsx-context.ts';
|
|
3
|
+
export { TsxContext, useCtx, setCtx };
|
|
4
4
|
export interface TsxOptions {
|
|
5
5
|
dir: string;
|
|
6
6
|
}
|
package/dist/tsx.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { TsxContext, useCtx, setCtx
|
|
1
|
+
import { TsxContext, useCtx, setCtx } from './tsx-instance.ts';
|
|
2
2
|
import type { TsxOptions } from './tsx-instance.ts';
|
|
3
3
|
import type { Router } from './router.ts';
|
|
4
|
-
export { TsxContext, useCtx, setCtx
|
|
4
|
+
export { TsxContext, useCtx, setCtx };
|
|
5
5
|
export type { TsxOptions };
|
|
6
6
|
export declare function tsx(options: TsxOptions): Promise<Router & {
|
|
7
7
|
stop: () => void;
|