@fluixi/core 1.0.0-alpha.94 → 1.0.0-alpha.96

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.
Files changed (63) hide show
  1. package/LICENSE +1 -1
  2. package/dist/cdn/core.global.js +2 -2
  3. package/dist/src/cdn/{chunk-7XM7Q5VR.mjs → chunk-RKBFEFZP.mjs} +125 -10
  4. package/dist/src/cdn/core.cjs +132 -12
  5. package/dist/src/cdn/core.mjs +4 -4
  6. package/dist/src/cdn/{router-VOY43YNM.mjs → router-V2THK27H.mjs} +3 -3
  7. package/dist/src/index.cjs +122 -7
  8. package/dist/src/index.mjs +122 -7
  9. package/dist/src/lib/client/{chunk-TSXM2PNY.mjs → chunk-RNRJHQFG.mjs} +1 -1
  10. package/dist/src/lib/client/index.cjs +133 -13
  11. package/dist/src/lib/client/index.d.ts.map +1 -1
  12. package/dist/src/lib/client/index.js +4 -1
  13. package/dist/src/lib/client/index.mjs +5 -5
  14. package/dist/src/lib/client/{router-BDVPLLYX.mjs → router-F3Y4BIVJ.mjs} +127 -12
  15. package/dist/src/lib/core.cjs +121 -6
  16. package/dist/src/lib/core.mjs +121 -6
  17. package/dist/src/lib/i18n/index.cjs +1 -1
  18. package/dist/src/lib/i18n/index.mjs +1 -1
  19. package/dist/src/lib/index.cjs +1 -1
  20. package/dist/src/lib/index.mjs +1 -1
  21. package/dist/src/lib/isomorphic.cjs +1 -1
  22. package/dist/src/lib/isomorphic.d.ts +5 -0
  23. package/dist/src/lib/isomorphic.d.ts.map +1 -1
  24. package/dist/src/lib/isomorphic.js +1 -1
  25. package/dist/src/lib/isomorphic.mjs +1 -1
  26. package/dist/src/lib/jsx-runtime/index.cjs +1 -1
  27. package/dist/src/lib/jsx-runtime/index.mjs +1 -1
  28. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.cjs +1 -1
  29. package/dist/src/lib/jsx-runtime/jsx-dev-runtime.mjs +1 -1
  30. package/dist/src/lib/plugins/index.cjs +1 -1
  31. package/dist/src/lib/plugins/index.mjs +1 -1
  32. package/dist/src/lib/render/index.cjs +1 -1
  33. package/dist/src/lib/render/index.mjs +1 -1
  34. package/dist/src/lib/render/versions.cjs +1 -1
  35. package/dist/src/lib/render/versions.mjs +1 -1
  36. package/dist/src/lib/router/data.cjs +4 -4
  37. package/dist/src/lib/router/data.d.ts.map +1 -1
  38. package/dist/src/lib/router/data.js +9 -8
  39. package/dist/src/lib/router/data.mjs +4 -4
  40. package/dist/src/lib/router/index.cjs +126 -11
  41. package/dist/src/lib/router/index.d.ts +8 -5
  42. package/dist/src/lib/router/index.d.ts.map +1 -1
  43. package/dist/src/lib/router/index.js +94 -7
  44. package/dist/src/lib/router/index.mjs +126 -11
  45. package/dist/src/lib/router/scroll.cjs +79 -0
  46. package/dist/src/lib/router/scroll.d.ts +19 -0
  47. package/dist/src/lib/router/scroll.d.ts.map +1 -0
  48. package/dist/src/lib/router/scroll.js +68 -0
  49. package/dist/src/lib/router/scroll.mjs +58 -0
  50. package/dist/src/lib/server/index.cjs +1 -1
  51. package/dist/src/lib/server/index.mjs +1 -1
  52. package/dist/src/lib/storage/index.cjs +1 -1
  53. package/dist/src/lib/storage/index.mjs +1 -1
  54. package/dist/src/routes.cjs +1 -1
  55. package/dist/src/routes.mjs +1 -1
  56. package/dist/src/utils.cjs +1 -1
  57. package/dist/src/utils.mjs +1 -1
  58. package/dist/src/version.generated.cjs +1 -1
  59. package/dist/src/version.generated.d.ts +1 -1
  60. package/dist/src/version.generated.js +1 -1
  61. package/dist/src/version.generated.mjs +1 -1
  62. package/dist/tsconfig.lib.tsbuildinfo +1 -1
  63. package/package.json +16 -16
@@ -1,4 +1,4 @@
1
- /*! @fluixi/core v1.0.0-alpha.94 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/core v1.0.0-alpha.96 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -143,7 +143,7 @@ __export(index_exports, {
143
143
  module.exports = __toCommonJS(index_exports);
144
144
 
145
145
  // src/version.generated.ts
146
- var VERSION = "1.0.0-alpha.94";
146
+ var VERSION = "1.0.0-alpha.96";
147
147
 
148
148
  // src/lib/core.ts
149
149
  var import_signal18 = require("@fluixi/reactive/signal");
@@ -763,7 +763,7 @@ function createIsomorphicStore(key, options) {
763
763
  }
764
764
  return initPromise;
765
765
  };
766
- return { value, fields, set, setField, ensureInit, key };
766
+ return { value, fields, set, setField, ensureInit, key, seeded };
767
767
  }
768
768
 
769
769
  // src/lib/env/index.ts
@@ -1178,6 +1178,64 @@ var import_dom15 = require("@fluixi/dom");
1178
1178
  var import_signal16 = require("@fluixi/reactive/signal");
1179
1179
  var import_object = require("@fluixi/utils/object");
1180
1180
  var import_compare = require("@fluixi/utils/compare");
1181
+
1182
+ // src/lib/router/scroll.ts
1183
+ var STORE = "fx:scroll";
1184
+ var KEEP = 100;
1185
+ function keepScroll(initial) {
1186
+ const read3 = () => {
1187
+ try {
1188
+ return JSON.parse(sessionStorage.getItem(STORE) ?? "{}");
1189
+ } catch {
1190
+ return {};
1191
+ }
1192
+ };
1193
+ const positions = read3();
1194
+ const write = () => {
1195
+ const keys = Object.keys(positions);
1196
+ for (const key of keys.slice(0, Math.max(0, keys.length - KEEP))) delete positions[key];
1197
+ try {
1198
+ sessionStorage.setItem(STORE, JSON.stringify(positions));
1199
+ } catch {
1200
+ }
1201
+ };
1202
+ let current = initial.key;
1203
+ const save = () => {
1204
+ if (current === void 0) return;
1205
+ delete positions[current];
1206
+ positions[current] = window.scrollY;
1207
+ write();
1208
+ };
1209
+ const restoration = history.scrollRestoration;
1210
+ history.scrollRestoration = "manual";
1211
+ window.addEventListener("pagehide", save);
1212
+ const to = (top) => window.scrollTo({ top, left: 0, behavior: "instant" });
1213
+ const toHash = (hash) => {
1214
+ if (!hash || hash === "#") return false;
1215
+ const target = document.getElementById(decodeURIComponent(hash.slice(1)));
1216
+ target?.scrollIntoView({ behavior: "instant" });
1217
+ return target !== null;
1218
+ };
1219
+ return {
1220
+ leave: save,
1221
+ arrive(location, from) {
1222
+ current = location.key;
1223
+ if (location.action === "pop" || from === void 0) {
1224
+ const y = location.key === void 0 ? void 0 : positions[location.key];
1225
+ if (y !== void 0) to(y);
1226
+ return;
1227
+ }
1228
+ if (location.action === "replace" && location.pathname === from.pathname) return;
1229
+ if (!toHash(location.hash)) to(0);
1230
+ },
1231
+ dispose() {
1232
+ window.removeEventListener("pagehide", save);
1233
+ history.scrollRestoration = restoration;
1234
+ }
1235
+ };
1236
+ }
1237
+
1238
+ // src/lib/router/index.ts
1181
1239
  var import_router = require("@fluixi/router");
1182
1240
 
1183
1241
  // src/lib/router/data.ts
@@ -1252,14 +1310,49 @@ function renderLayer(m, depth, base) {
1252
1310
  });
1253
1311
  });
1254
1312
  };
1313
+ let readsLocation;
1255
1314
  const [resource2, { refetch }] = (0, import_signal15.createResource)(() => {
1256
- const result = loader({ params: base.params, location: base.location }, owner, () => cancelled, trackDeps);
1315
+ const params = (0, import_signal15.untrack)(base.params);
1316
+ const location = (0, import_signal15.untrack)(base.location);
1317
+ readsLocation = false;
1318
+ const result = loader(
1319
+ {
1320
+ params: () => params,
1321
+ location: () => {
1322
+ readsLocation = true;
1323
+ return location;
1324
+ }
1325
+ },
1326
+ owner,
1327
+ () => cancelled,
1328
+ trackDeps
1329
+ );
1257
1330
  return router ? router.trackLoad(result) : result;
1258
1331
  });
1259
- (0, import_signal15.onCleanup)(onRevalidate(() => void refetch()));
1332
+ (0, import_signal15.onCleanup)(
1333
+ onRevalidate(() => {
1334
+ if ((0, import_signal15.untrack)(() => base.matched()[depth]?.route) !== m.route) return;
1335
+ void refetch();
1336
+ })
1337
+ );
1338
+ let loadedFor = (0, import_signal15.untrack)(() => JSON.stringify(base.matched()[depth]?.params ?? {}));
1339
+ let searchedFor = (0, import_signal15.untrack)(() => base.location().search);
1340
+ (0, import_signal15.createEffect)(() => {
1341
+ const now = base.matched()[depth];
1342
+ const search = base.location().search;
1343
+ if (now?.route !== m.route) return;
1344
+ const params = JSON.stringify(now.params ?? {});
1345
+ const paramsMoved = params !== loadedFor;
1346
+ const searchMoved = readsLocation !== false && search !== searchedFor;
1347
+ loadedFor = params;
1348
+ searchedFor = search;
1349
+ if (paramsMoved || searchMoved) (0, import_signal15.untrack)(() => void refetch());
1350
+ });
1260
1351
  data = () => {
1261
1352
  const error = resource2.error;
1262
1353
  if (error !== void 0 && error !== null) throw error;
1354
+ const r = resource2;
1355
+ if (r.state === "refreshing") return r.latest;
1263
1356
  return resource2();
1264
1357
  };
1265
1358
  Object.defineProperty(data, "loading", { get: () => resource2.loading });
@@ -1364,16 +1457,19 @@ function buildDefaultNotFound() {
1364
1457
  }
1365
1458
  var pendingTransitionMode;
1366
1459
  function Router(props) {
1367
- const history = props.history ?? (0, import_router.createBrowserHistory)();
1460
+ const history2 = props.history ?? (0, import_router.createBrowserHistory)();
1368
1461
  const router = (0, import_router.createRouter)({
1369
1462
  routes: props.routes,
1370
1463
  base: props.base,
1371
- history
1464
+ history: history2
1372
1465
  });
1373
1466
  const [state, setState] = (0, import_signal15.createSignal)(router.state());
1374
1467
  const [navigating, setNavigating] = (0, import_signal15.createSignal)(false);
1375
1468
  const [loads, setLoads] = (0, import_signal15.createSignal)(0);
1376
1469
  let navigationId = 0;
1470
+ const scroll = history2.window ? keepScroll(router.state().location) : void 0;
1471
+ let arriving = scroll ? { id: 0, location: router.state().location, from: void 0 } : null;
1472
+ const [arrived, setArrived] = (0, import_signal15.createSignal)(0);
1377
1473
  const takeMode = () => {
1378
1474
  const mode = pendingTransitionMode ?? (props.transition ?? "auto");
1379
1475
  pendingTransitionMode = void 0;
@@ -1383,6 +1479,10 @@ function Router(props) {
1383
1479
  const id = ++navigationId;
1384
1480
  const mode = takeMode();
1385
1481
  setNavigating(true);
1482
+ if (scroll) {
1483
+ scroll.leave();
1484
+ arriving = { id, location: next.location, from: (0, import_signal15.untrack)(state).location };
1485
+ }
1386
1486
  const prepared = prepareComponents(
1387
1487
  (next.match?.matched ?? []).map((m) => m.component).filter(Boolean)
1388
1488
  );
@@ -1390,6 +1490,7 @@ function Router(props) {
1390
1490
  setState(next);
1391
1491
  const flushed = (0, import_signal15.flush)();
1392
1492
  if (id === navigationId) setNavigating(false);
1493
+ if (scroll) setArrived(id);
1393
1494
  return flushed;
1394
1495
  };
1395
1496
  if (!prepared) {
@@ -1403,6 +1504,7 @@ function Router(props) {
1403
1504
  });
1404
1505
  (0, import_signal15.onCleanup)(() => {
1405
1506
  unsub();
1507
+ scroll?.dispose();
1406
1508
  router.destroy();
1407
1509
  navigationId++;
1408
1510
  });
@@ -1425,6 +1527,19 @@ function Router(props) {
1425
1527
  return result;
1426
1528
  }
1427
1529
  };
1530
+ if (scroll) {
1531
+ const nextFrame = typeof requestAnimationFrame === "function" ? requestAnimationFrame : (fn) => setTimeout(fn, 0);
1532
+ (0, import_signal15.createEffect)(() => {
1533
+ const id = arrived();
1534
+ if (ctx.isRouting()) return;
1535
+ const next = arriving;
1536
+ if (!next || next.id !== id || id !== navigationId) return;
1537
+ arriving = null;
1538
+ nextFrame(() => {
1539
+ if (id === navigationId) scroll.arrive(next.location, next.from);
1540
+ });
1541
+ });
1542
+ }
1428
1543
  const root = {
1429
1544
  depth: -1,
1430
1545
  matched,
@@ -1,7 +1,7 @@
1
- /*! @fluixi/core v1.0.0-alpha.94 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/core v1.0.0-alpha.96 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/version.generated.ts
4
- var VERSION = "1.0.0-alpha.94";
4
+ var VERSION = "1.0.0-alpha.96";
5
5
 
6
6
  // src/lib/core.ts
7
7
  import {
@@ -690,7 +690,7 @@ function createIsomorphicStore(key, options) {
690
690
  }
691
691
  return initPromise;
692
692
  };
693
- return { value, fields, set, setField, ensureInit, key };
693
+ return { value, fields, set, setField, ensureInit, key, seeded };
694
694
  }
695
695
 
696
696
  // src/lib/env/index.ts
@@ -1126,6 +1126,64 @@ import {
1126
1126
  import { createContext as createContext5, useContext as useContext5 } from "@fluixi/reactive/signal";
1127
1127
  import { get } from "@fluixi/utils/object";
1128
1128
  import { deepEqual } from "@fluixi/utils/compare";
1129
+
1130
+ // src/lib/router/scroll.ts
1131
+ var STORE = "fx:scroll";
1132
+ var KEEP = 100;
1133
+ function keepScroll(initial) {
1134
+ const read3 = () => {
1135
+ try {
1136
+ return JSON.parse(sessionStorage.getItem(STORE) ?? "{}");
1137
+ } catch {
1138
+ return {};
1139
+ }
1140
+ };
1141
+ const positions = read3();
1142
+ const write = () => {
1143
+ const keys = Object.keys(positions);
1144
+ for (const key of keys.slice(0, Math.max(0, keys.length - KEEP))) delete positions[key];
1145
+ try {
1146
+ sessionStorage.setItem(STORE, JSON.stringify(positions));
1147
+ } catch {
1148
+ }
1149
+ };
1150
+ let current = initial.key;
1151
+ const save = () => {
1152
+ if (current === void 0) return;
1153
+ delete positions[current];
1154
+ positions[current] = window.scrollY;
1155
+ write();
1156
+ };
1157
+ const restoration = history.scrollRestoration;
1158
+ history.scrollRestoration = "manual";
1159
+ window.addEventListener("pagehide", save);
1160
+ const to = (top) => window.scrollTo({ top, left: 0, behavior: "instant" });
1161
+ const toHash = (hash) => {
1162
+ if (!hash || hash === "#") return false;
1163
+ const target = document.getElementById(decodeURIComponent(hash.slice(1)));
1164
+ target?.scrollIntoView({ behavior: "instant" });
1165
+ return target !== null;
1166
+ };
1167
+ return {
1168
+ leave: save,
1169
+ arrive(location, from) {
1170
+ current = location.key;
1171
+ if (location.action === "pop" || from === void 0) {
1172
+ const y = location.key === void 0 ? void 0 : positions[location.key];
1173
+ if (y !== void 0) to(y);
1174
+ return;
1175
+ }
1176
+ if (location.action === "replace" && location.pathname === from.pathname) return;
1177
+ if (!toHash(location.hash)) to(0);
1178
+ },
1179
+ dispose() {
1180
+ window.removeEventListener("pagehide", save);
1181
+ history.scrollRestoration = restoration;
1182
+ }
1183
+ };
1184
+ }
1185
+
1186
+ // src/lib/router/index.ts
1129
1187
  import {
1130
1188
  createRouter as createCoreRouter,
1131
1189
  createBrowserHistory,
@@ -1213,14 +1271,49 @@ function renderLayer(m, depth, base) {
1213
1271
  });
1214
1272
  });
1215
1273
  };
1274
+ let readsLocation;
1216
1275
  const [resource2, { refetch }] = createResource5(() => {
1217
- const result = loader({ params: base.params, location: base.location }, owner, () => cancelled, trackDeps);
1276
+ const params = untrack4(base.params);
1277
+ const location = untrack4(base.location);
1278
+ readsLocation = false;
1279
+ const result = loader(
1280
+ {
1281
+ params: () => params,
1282
+ location: () => {
1283
+ readsLocation = true;
1284
+ return location;
1285
+ }
1286
+ },
1287
+ owner,
1288
+ () => cancelled,
1289
+ trackDeps
1290
+ );
1218
1291
  return router ? router.trackLoad(result) : result;
1219
1292
  });
1220
- onCleanup4(onRevalidate(() => void refetch()));
1293
+ onCleanup4(
1294
+ onRevalidate(() => {
1295
+ if (untrack4(() => base.matched()[depth]?.route) !== m.route) return;
1296
+ void refetch();
1297
+ })
1298
+ );
1299
+ let loadedFor = untrack4(() => JSON.stringify(base.matched()[depth]?.params ?? {}));
1300
+ let searchedFor = untrack4(() => base.location().search);
1301
+ createEffect4(() => {
1302
+ const now = base.matched()[depth];
1303
+ const search = base.location().search;
1304
+ if (now?.route !== m.route) return;
1305
+ const params = JSON.stringify(now.params ?? {});
1306
+ const paramsMoved = params !== loadedFor;
1307
+ const searchMoved = readsLocation !== false && search !== searchedFor;
1308
+ loadedFor = params;
1309
+ searchedFor = search;
1310
+ if (paramsMoved || searchMoved) untrack4(() => void refetch());
1311
+ });
1221
1312
  data = () => {
1222
1313
  const error = resource2.error;
1223
1314
  if (error !== void 0 && error !== null) throw error;
1315
+ const r = resource2;
1316
+ if (r.state === "refreshing") return r.latest;
1224
1317
  return resource2();
1225
1318
  };
1226
1319
  Object.defineProperty(data, "loading", { get: () => resource2.loading });
@@ -1325,16 +1418,19 @@ function buildDefaultNotFound() {
1325
1418
  }
1326
1419
  var pendingTransitionMode;
1327
1420
  function Router(props) {
1328
- const history = props.history ?? createBrowserHistory();
1421
+ const history2 = props.history ?? createBrowserHistory();
1329
1422
  const router = createCoreRouter({
1330
1423
  routes: props.routes,
1331
1424
  base: props.base,
1332
- history
1425
+ history: history2
1333
1426
  });
1334
1427
  const [state, setState] = createSignal8(router.state());
1335
1428
  const [navigating, setNavigating] = createSignal8(false);
1336
1429
  const [loads, setLoads] = createSignal8(0);
1337
1430
  let navigationId = 0;
1431
+ const scroll = history2.window ? keepScroll(router.state().location) : void 0;
1432
+ let arriving = scroll ? { id: 0, location: router.state().location, from: void 0 } : null;
1433
+ const [arrived, setArrived] = createSignal8(0);
1338
1434
  const takeMode = () => {
1339
1435
  const mode = pendingTransitionMode ?? (props.transition ?? "auto");
1340
1436
  pendingTransitionMode = void 0;
@@ -1344,6 +1440,10 @@ function Router(props) {
1344
1440
  const id = ++navigationId;
1345
1441
  const mode = takeMode();
1346
1442
  setNavigating(true);
1443
+ if (scroll) {
1444
+ scroll.leave();
1445
+ arriving = { id, location: next.location, from: untrack4(state).location };
1446
+ }
1347
1447
  const prepared = prepareComponents(
1348
1448
  (next.match?.matched ?? []).map((m) => m.component).filter(Boolean)
1349
1449
  );
@@ -1351,6 +1451,7 @@ function Router(props) {
1351
1451
  setState(next);
1352
1452
  const flushed = flush();
1353
1453
  if (id === navigationId) setNavigating(false);
1454
+ if (scroll) setArrived(id);
1354
1455
  return flushed;
1355
1456
  };
1356
1457
  if (!prepared) {
@@ -1364,6 +1465,7 @@ function Router(props) {
1364
1465
  });
1365
1466
  onCleanup4(() => {
1366
1467
  unsub();
1468
+ scroll?.dispose();
1367
1469
  router.destroy();
1368
1470
  navigationId++;
1369
1471
  });
@@ -1386,6 +1488,19 @@ function Router(props) {
1386
1488
  return result;
1387
1489
  }
1388
1490
  };
1491
+ if (scroll) {
1492
+ const nextFrame = typeof requestAnimationFrame === "function" ? requestAnimationFrame : (fn) => setTimeout(fn, 0);
1493
+ createEffect4(() => {
1494
+ const id = arrived();
1495
+ if (ctx.isRouting()) return;
1496
+ const next = arriving;
1497
+ if (!next || next.id !== id || id !== navigationId) return;
1498
+ arriving = null;
1499
+ nextFrame(() => {
1500
+ if (id === navigationId) scroll.arrive(next.location, next.from);
1501
+ });
1502
+ });
1503
+ }
1389
1504
  const root = {
1390
1505
  depth: -1,
1391
1506
  matched,
@@ -1,4 +1,4 @@
1
- /*! @fluixi/core v1.0.0-alpha.94 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/core v1.0.0-alpha.96 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
 
3
3
  // src/lib/render/lazy.ts
4
4
  import { createComponent } from "@fluixi/dom";
@@ -1,4 +1,4 @@
1
- /*! @fluixi/core v1.0.0-alpha.94 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
1
+ /*! @fluixi/core v1.0.0-alpha.96 | (c) 2026 Ibrahima Touré and Fluixi contributors | MIT */
2
2
  "use strict";
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
@@ -124,6 +124,68 @@ var init_transition = __esm({
124
124
  }
125
125
  });
126
126
 
127
+ // src/lib/router/scroll.ts
128
+ function keepScroll(initial) {
129
+ const read2 = () => {
130
+ try {
131
+ return JSON.parse(sessionStorage.getItem(STORE) ?? "{}");
132
+ } catch {
133
+ return {};
134
+ }
135
+ };
136
+ const positions = read2();
137
+ const write = () => {
138
+ const keys = Object.keys(positions);
139
+ for (const key of keys.slice(0, Math.max(0, keys.length - KEEP))) delete positions[key];
140
+ try {
141
+ sessionStorage.setItem(STORE, JSON.stringify(positions));
142
+ } catch {
143
+ }
144
+ };
145
+ let current = initial.key;
146
+ const save = () => {
147
+ if (current === void 0) return;
148
+ delete positions[current];
149
+ positions[current] = window.scrollY;
150
+ write();
151
+ };
152
+ const restoration = history.scrollRestoration;
153
+ history.scrollRestoration = "manual";
154
+ window.addEventListener("pagehide", save);
155
+ const to = (top) => window.scrollTo({ top, left: 0, behavior: "instant" });
156
+ const toHash = (hash) => {
157
+ if (!hash || hash === "#") return false;
158
+ const target = document.getElementById(decodeURIComponent(hash.slice(1)));
159
+ target?.scrollIntoView({ behavior: "instant" });
160
+ return target !== null;
161
+ };
162
+ return {
163
+ leave: save,
164
+ arrive(location, from) {
165
+ current = location.key;
166
+ if (location.action === "pop" || from === void 0) {
167
+ const y = location.key === void 0 ? void 0 : positions[location.key];
168
+ if (y !== void 0) to(y);
169
+ return;
170
+ }
171
+ if (location.action === "replace" && location.pathname === from.pathname) return;
172
+ if (!toHash(location.hash)) to(0);
173
+ },
174
+ dispose() {
175
+ window.removeEventListener("pagehide", save);
176
+ history.scrollRestoration = restoration;
177
+ }
178
+ };
179
+ }
180
+ var STORE, KEEP;
181
+ var init_scroll = __esm({
182
+ "src/lib/router/scroll.ts"() {
183
+ "use strict";
184
+ STORE = "fx:scroll";
185
+ KEEP = 100;
186
+ }
187
+ });
188
+
127
189
  // src/lib/context/context.ts
128
190
  function createContext3(defaultValue, name) {
129
191
  const context = (0, import_signal7.createContext)(defaultValue, name);
@@ -225,7 +287,8 @@ function getSubmissionsSignal(url) {
225
287
  }
226
288
  function action(fn, name, options = {}) {
227
289
  const actionUrl = name || fn.name || "action";
228
- const submit = async (input) => {
290
+ const submit = async (...args) => {
291
+ const input = args.length > 1 ? args : args[0];
229
292
  const [, setSubmission] = getSubmissionSignal(actionUrl);
230
293
  const [, setSubmissions] = getSubmissionsSignal(actionUrl);
231
294
  let entry;
@@ -240,11 +303,10 @@ function action(fn, name, options = {}) {
240
303
  };
241
304
  const retry = () => {
242
305
  drop2();
243
- return submit(input);
306
+ return submit(...args);
244
307
  };
245
308
  entry = {
246
309
  input,
247
- // Approximate type for tracking
248
310
  pending: true,
249
311
  clear,
250
312
  retry
@@ -252,7 +314,7 @@ function action(fn, name, options = {}) {
252
314
  setSubmission(entry);
253
315
  setSubmissions((list) => [...list, entry]);
254
316
  try {
255
- const result = await fn(input);
317
+ const result = await fn(...args);
256
318
  setSubmission({
257
319
  input,
258
320
  result,
@@ -326,7 +388,7 @@ var init_data = __esm({
326
388
  var router_exports = {};
327
389
  __export(router_exports, {
328
390
  A: () => A,
329
- Form: () => Form,
391
+ ActionForm: () => ActionForm,
330
392
  Link: () => Link,
331
393
  Outlet: () => Outlet,
332
394
  Redirect: () => Redirect,
@@ -384,14 +446,49 @@ function renderLayer(m, depth, base) {
384
446
  });
385
447
  });
386
448
  };
449
+ let readsLocation;
387
450
  const [resource, { refetch }] = (0, import_signal10.createResource)(() => {
388
- const result = loader({ params: base.params, location: base.location }, owner, () => cancelled, trackDeps);
451
+ const params = (0, import_signal10.untrack)(base.params);
452
+ const location = (0, import_signal10.untrack)(base.location);
453
+ readsLocation = false;
454
+ const result = loader(
455
+ {
456
+ params: () => params,
457
+ location: () => {
458
+ readsLocation = true;
459
+ return location;
460
+ }
461
+ },
462
+ owner,
463
+ () => cancelled,
464
+ trackDeps
465
+ );
389
466
  return router ? router.trackLoad(result) : result;
390
467
  });
391
- (0, import_signal10.onCleanup)(onRevalidate(() => void refetch()));
468
+ (0, import_signal10.onCleanup)(
469
+ onRevalidate(() => {
470
+ if ((0, import_signal10.untrack)(() => base.matched()[depth]?.route) !== m.route) return;
471
+ void refetch();
472
+ })
473
+ );
474
+ let loadedFor = (0, import_signal10.untrack)(() => JSON.stringify(base.matched()[depth]?.params ?? {}));
475
+ let searchedFor = (0, import_signal10.untrack)(() => base.location().search);
476
+ (0, import_signal10.createEffect)(() => {
477
+ const now = base.matched()[depth];
478
+ const search = base.location().search;
479
+ if (now?.route !== m.route) return;
480
+ const params = JSON.stringify(now.params ?? {});
481
+ const paramsMoved = params !== loadedFor;
482
+ const searchMoved = readsLocation !== false && search !== searchedFor;
483
+ loadedFor = params;
484
+ searchedFor = search;
485
+ if (paramsMoved || searchMoved) (0, import_signal10.untrack)(() => void refetch());
486
+ });
392
487
  data = () => {
393
488
  const error = resource.error;
394
489
  if (error !== void 0 && error !== null) throw error;
490
+ const r = resource;
491
+ if (r.state === "refreshing") return r.latest;
395
492
  return resource();
396
493
  };
397
494
  Object.defineProperty(data, "loading", { get: () => resource.loading });
@@ -495,16 +592,19 @@ function buildDefaultNotFound() {
495
592
  return el;
496
593
  }
497
594
  function Router(props) {
498
- const history = props.history ?? (0, import_router.createBrowserHistory)();
595
+ const history2 = props.history ?? (0, import_router.createBrowserHistory)();
499
596
  const router = (0, import_router.createRouter)({
500
597
  routes: props.routes,
501
598
  base: props.base,
502
- history
599
+ history: history2
503
600
  });
504
601
  const [state, setState] = (0, import_signal10.createSignal)(router.state());
505
602
  const [navigating, setNavigating] = (0, import_signal10.createSignal)(false);
506
603
  const [loads, setLoads] = (0, import_signal10.createSignal)(0);
507
604
  let navigationId = 0;
605
+ const scroll = history2.window ? keepScroll(router.state().location) : void 0;
606
+ let arriving = scroll ? { id: 0, location: router.state().location, from: void 0 } : null;
607
+ const [arrived, setArrived] = (0, import_signal10.createSignal)(0);
508
608
  const takeMode = () => {
509
609
  const mode = pendingTransitionMode ?? (props.transition ?? "auto");
510
610
  pendingTransitionMode = void 0;
@@ -514,6 +614,10 @@ function Router(props) {
514
614
  const id = ++navigationId;
515
615
  const mode = takeMode();
516
616
  setNavigating(true);
617
+ if (scroll) {
618
+ scroll.leave();
619
+ arriving = { id, location: next.location, from: (0, import_signal10.untrack)(state).location };
620
+ }
517
621
  const prepared = prepareComponents(
518
622
  (next.match?.matched ?? []).map((m) => m.component).filter(Boolean)
519
623
  );
@@ -521,6 +625,7 @@ function Router(props) {
521
625
  setState(next);
522
626
  const flushed = (0, import_signal10.flush)();
523
627
  if (id === navigationId) setNavigating(false);
628
+ if (scroll) setArrived(id);
524
629
  return flushed;
525
630
  };
526
631
  if (!prepared) {
@@ -534,6 +639,7 @@ function Router(props) {
534
639
  });
535
640
  (0, import_signal10.onCleanup)(() => {
536
641
  unsub();
642
+ scroll?.dispose();
537
643
  router.destroy();
538
644
  navigationId++;
539
645
  });
@@ -556,6 +662,19 @@ function Router(props) {
556
662
  return result;
557
663
  }
558
664
  };
665
+ if (scroll) {
666
+ const nextFrame = typeof requestAnimationFrame === "function" ? requestAnimationFrame : (fn) => setTimeout(fn, 0);
667
+ (0, import_signal10.createEffect)(() => {
668
+ const id = arrived();
669
+ if (ctx.isRouting()) return;
670
+ const next = arriving;
671
+ if (!next || next.id !== id || id !== navigationId) return;
672
+ arriving = null;
673
+ nextFrame(() => {
674
+ if (id === navigationId) scroll.arrive(next.location, next.from);
675
+ });
676
+ });
677
+ }
559
678
  const root = {
560
679
  depth: -1,
561
680
  matched,
@@ -751,7 +870,7 @@ function Link(props) {
751
870
  if (linkChildren != null) (0, import_dom12.insert)(a, linkChildren);
752
871
  return a;
753
872
  }
754
- function Form(props) {
873
+ function ActionForm(props) {
755
874
  const action2 = props.action;
756
875
  const cls = props.className ?? props.class;
757
876
  const url = action2.url ?? "";
@@ -799,6 +918,7 @@ var init_router = __esm({
799
918
  import_signal11 = require("@fluixi/reactive/signal");
800
919
  import_object = require("@fluixi/utils/object");
801
920
  import_compare = require("@fluixi/utils/compare");
921
+ init_scroll();
802
922
  import_router = require("@fluixi/router");
803
923
  init_data();
804
924
  import_router2 = require("@fluixi/router");
@@ -847,7 +967,7 @@ var import_dom13 = require("@fluixi/dom");
847
967
  var import_dom = require("@fluixi/dom");
848
968
 
849
969
  // src/version.generated.ts
850
- var VERSION = "1.0.0-alpha.94";
970
+ var VERSION = "1.0.0-alpha.96";
851
971
 
852
972
  // src/lib/render/versions.ts
853
973
  var import_dom2 = require("@fluixi/dom");
@@ -1053,7 +1173,7 @@ async function startClient(App, options = {}) {
1053
1173
  }
1054
1174
  deserializeHydrationData();
1055
1175
  setHydrating(true);
1056
- (0, import_dom13.hydrate)(rootOf(App), container);
1176
+ (0, import_dom13.hydrate)(() => rootOf(App), container);
1057
1177
  replayEvents();
1058
1178
  } finally {
1059
1179
  (0, import_dom13.endAppHydration)();
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/client/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,OAAO,uBAAuB,CAAC;AAgB/B;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,QAOlE;AAED,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5B,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;CAChB;AAoBD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,GAAG,EACzB,OAAO,GAAE,kBAAuB,iBA8EjC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/lib/client/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,OAAO,uBAAuB,CAAC;AAgB/B;;;;;;;;;;GAUG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,SAAS,EAAE,WAAW,GAAG,IAAI,QAOlE;AAED,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAEtC,MAAM,WAAW,kBAAkB;IACjC,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,GAAG,WAAW,CAAC;IAC5B,kFAAkF;IAClF,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC;CAChB;AAoBD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,WAAW,CAC/B,GAAG,EAAE,CAAC,KAAK,CAAC,EAAE,GAAG,KAAK,GAAG,EACzB,OAAO,GAAE,kBAAuB,iBAiFjC"}