akanjs 3.0.2 → 3.1.0-rc.1

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 (395) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/base/baseEnv.ts +0 -4
  3. package/base/primitiveRegistry.ts +2 -2
  4. package/client/cookie.ts +5 -1
  5. package/client/csrLocation.ts +122 -0
  6. package/client/csrTypes.ts +36 -4
  7. package/client/device.ts +2 -1
  8. package/client/frameConfig.ts +78 -7
  9. package/client/index.ts +3 -0
  10. package/client/makePageProto.tsx +3 -2
  11. package/client/native/external-view.ts +1 -0
  12. package/client/route/RootLayoutDefinition.ts +7 -1
  13. package/client/route/RouteHead.ts +108 -0
  14. package/client/route/index.ts +1 -0
  15. package/client/router.ts +36 -23
  16. package/client/rscNavigation.ts +8 -2
  17. package/client/viewportMeta.ts +60 -0
  18. package/client/webManifest.ts +18 -0
  19. package/common/Logger.ts +7 -2
  20. package/common/PipelineNames.ts +31 -0
  21. package/common/PipelinePattern.ts +70 -0
  22. package/common/PipelinePlanSchema.ts +490 -0
  23. package/common/backoff.ts +14 -0
  24. package/common/index.ts +39 -0
  25. package/common/pipelinePlan.ts +259 -0
  26. package/common/routeConvention.ts +1 -0
  27. package/common/types.ts +17 -0
  28. package/constant/constantRegistry.ts +26 -6
  29. package/dictionary/base.dictionary.ts +12 -0
  30. package/dictionary/trans.ts +9 -1
  31. package/document/into.ts +5 -0
  32. package/fetch/client/fetchClient.ts +135 -31
  33. package/fetch/client/httpClient.ts +45 -0
  34. package/fetch/client/wsClient.ts +125 -16
  35. package/fetch/fetchType/buildFetch.type.ts +1 -0
  36. package/index.ts +243 -10
  37. package/package.json +1 -5
  38. package/server/akanApp.ts +5 -2
  39. package/server/akanServer.ts +44 -8
  40. package/server/artifact/builderRpc.ts +8 -3
  41. package/server/artifact/ipcTypes.ts +5 -2
  42. package/server/di/diLifecycle.ts +39 -3
  43. package/server/di/utils.ts +28 -1
  44. package/server/ed25519Key.ts +48 -0
  45. package/server/index.ts +1 -0
  46. package/server/machine/index.ts +2 -0
  47. package/server/machine/machineAccount.ts +27 -0
  48. package/server/machine/machineToken.ts +151 -0
  49. package/server/mcp/McpApps.ts +256 -0
  50. package/server/mcp/McpDispatcher.ts +31 -7
  51. package/server/mcp/McpRouter.ts +209 -46
  52. package/server/ops/opsToken.ts +3 -10
  53. package/server/resolver/database.resolver.ts +3 -0
  54. package/server/resolver/signal.resolver.ts +40 -3
  55. package/server/routeElementComposer.tsx +78 -2
  56. package/server/routeState.ts +6 -0
  57. package/server/routeTreeBuilder.ts +26 -9
  58. package/server/routing/apiRouter.ts +6 -3
  59. package/server/routing/roomReplay.ts +88 -0
  60. package/server/rscClient.tsx +53 -8
  61. package/server/rscWorker.tsx +141 -27
  62. package/server/rscWorkerHost.ts +18 -5
  63. package/server/sitemap.ts +14 -0
  64. package/server/ssrFromRscRenderer.tsx +12 -6
  65. package/server/ssrTypes.ts +3 -1
  66. package/server/systemPageDocument.tsx +38 -30
  67. package/server/types.tsx +2 -0
  68. package/server/webRouter.ts +74 -5
  69. package/service/adapt.ts +1 -1
  70. package/service/injectInfo.ts +9 -1
  71. package/service/ipcTypes.ts +2 -1
  72. package/service/predefinedAdaptor/database.adaptor.ts +1 -146
  73. package/service/predefinedAdaptor/queue.adaptor.ts +44 -6
  74. package/service/predefinedAdaptor/queueBackoff.ts +12 -0
  75. package/service/predefinedAdaptor/solidQueue.adaptor.ts +17 -13
  76. package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +44 -2
  77. package/service/predefinedAdaptor/sql/dialect/postgres.ts +3 -0
  78. package/service/predefinedAdaptor/sql/dialect/sqlite.ts +5 -2
  79. package/service/predefinedAdaptor/sql/search/fts5.ts +1 -1
  80. package/service/predefinedAdaptor/sql/types.ts +8 -6
  81. package/service/predefinedAdaptor/sql/values.ts +0 -23
  82. package/service/serve.ts +5 -1
  83. package/signal/agent.signal.ts +1 -1
  84. package/signal/base.signal.ts +2 -2
  85. package/signal/endpointIdempotency.ts +53 -0
  86. package/signal/internalArg.ts +14 -0
  87. package/signal/mcp/McpDocument.ts +38 -1
  88. package/signal/mcp/mcpProtocol.ts +18 -0
  89. package/signal/signalContext.ts +58 -30
  90. package/signal/signalRegistry.ts +14 -4
  91. package/signal/types.ts +18 -3
  92. package/store/action.ts +17 -19
  93. package/store/agent/StoreSurfaceSource.ts +1 -1
  94. package/store/agentic/StToolBuilder.ts +3 -0
  95. package/test/conformance.ts +9 -33
  96. package/types/base/baseEnv.d.ts +0 -4
  97. package/types/base/primitiveRegistry.d.ts +1 -1
  98. package/types/client/csrLocation.d.ts +32 -0
  99. package/types/client/csrTypes.d.ts +43 -4
  100. package/types/client/frameConfig.d.ts +14 -2
  101. package/types/client/index.d.ts +3 -0
  102. package/types/client/native/external-view.d.ts +1 -0
  103. package/types/client/route/RootLayoutDefinition.d.ts +2 -1
  104. package/types/client/route/RouteHead.d.ts +13 -0
  105. package/types/client/route/index.d.ts +1 -0
  106. package/types/client/router.d.ts +6 -0
  107. package/types/client/rscNavigation.d.ts +4 -0
  108. package/types/client/viewportMeta.d.ts +18 -0
  109. package/types/client/webManifest.d.ts +4 -0
  110. package/types/common/PipelineNames.d.ts +6 -0
  111. package/types/common/PipelinePattern.d.ts +14 -0
  112. package/types/common/PipelinePlanSchema.d.ts +9 -0
  113. package/types/common/backoff.d.ts +10 -0
  114. package/types/common/index.d.ts +5 -0
  115. package/types/common/pipelinePlan.d.ts +224 -0
  116. package/types/common/types.d.ts +16 -0
  117. package/types/constant/constantRegistry.d.ts +5 -2
  118. package/types/dictionary/base.dictionary.d.ts +1 -1
  119. package/types/dictionary/dictionary.d.ts +8 -8
  120. package/types/document/into.d.ts +9 -0
  121. package/types/fetch/client/fetchClient.d.ts +31 -4
  122. package/types/fetch/client/httpClient.d.ts +8 -0
  123. package/types/fetch/client/wsClient.d.ts +26 -2
  124. package/types/fetch/fetchType/buildFetch.type.d.ts +1 -0
  125. package/types/index.d.ts +230 -9
  126. package/types/server/akanServer.d.ts +16 -1
  127. package/types/server/artifact/builderRpc.d.ts +7 -2
  128. package/types/server/artifact/ipcTypes.d.ts +5 -1
  129. package/types/server/di/utils.d.ts +1 -0
  130. package/types/server/ed25519Key.d.ts +13 -0
  131. package/types/server/index.d.ts +1 -0
  132. package/types/server/machine/index.d.ts +2 -0
  133. package/types/server/machine/machineAccount.d.ts +5 -0
  134. package/types/server/machine/machineToken.d.ts +29 -0
  135. package/types/server/machine.d.ts +1 -0
  136. package/types/server/mcp/McpApps.d.ts +50 -0
  137. package/types/server/mcp/McpDispatcher.d.ts +4 -1
  138. package/types/server/mcp/McpRouter.d.ts +4 -0
  139. package/types/server/resolver/signal.resolver.d.ts +2 -1
  140. package/types/server/routeElementComposer.d.ts +24 -4
  141. package/types/server/routeState.d.ts +4 -1
  142. package/types/server/routing/roomReplay.d.ts +24 -0
  143. package/types/server/rscWorkerHost.d.ts +5 -2
  144. package/types/server/sitemap.d.ts +2 -0
  145. package/types/server/ssrFromRscRenderer.d.ts +3 -3
  146. package/types/server/ssrTypes.d.ts +4 -1
  147. package/types/server/systemPageDocument.d.ts +2 -0
  148. package/types/server/types.d.ts +4 -1
  149. package/types/server/webRouter.d.ts +3 -1
  150. package/types/service/adapt.d.ts +1 -1
  151. package/types/service/ipcTypes.d.ts +2 -0
  152. package/types/service/predefinedAdaptor/database.adaptor.d.ts +1 -24
  153. package/types/service/predefinedAdaptor/queue.adaptor.d.ts +10 -3
  154. package/types/service/predefinedAdaptor/queueBackoff.d.ts +4 -0
  155. package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +3 -3
  156. package/types/service/predefinedAdaptor/sql/SqlDocumentStore.d.ts +6 -1
  157. package/types/service/predefinedAdaptor/sql/dialect/postgres.d.ts +1 -0
  158. package/types/service/predefinedAdaptor/sql/dialect/sqlite.d.ts +1 -0
  159. package/types/service/predefinedAdaptor/sql/search/fts5.d.ts +1 -1
  160. package/types/service/predefinedAdaptor/sql/types.d.ts +9 -5
  161. package/types/service/predefinedAdaptor/sql/values.d.ts +0 -3
  162. package/types/service/serve.d.ts +3 -0
  163. package/types/signal/endpointIdempotency.d.ts +7 -0
  164. package/types/signal/internalArg.d.ts +10 -0
  165. package/types/signal/mcp/McpDocument.d.ts +8 -0
  166. package/types/signal/mcp/mcpProtocol.d.ts +17 -0
  167. package/types/signal/signalContext.d.ts +5 -3
  168. package/types/signal/signalRegistry.d.ts +4 -4
  169. package/types/signal/types.d.ts +21 -1
  170. package/types/store/agentic/StToolBuilder.d.ts +2 -0
  171. package/types/test/conformance.d.ts +7 -10
  172. package/types/ui/Agent/Chat.d.ts +5 -1
  173. package/types/ui/Agent/EmbedApproval.d.ts +1 -0
  174. package/types/ui/Agent/index.d.ts +3 -0
  175. package/types/ui/Agent/useKeyboardFrame.d.ts +2 -0
  176. package/types/ui/Agent/useScreenActions.d.ts +18 -0
  177. package/types/ui/BottomSheet.d.ts +1 -0
  178. package/types/ui/Constant/Doc.d.ts +11 -17
  179. package/types/ui/Constant/index.d.ts +7 -2
  180. package/types/ui/Constant/schemaDoc.d.ts +11 -0
  181. package/types/ui/Dialog/Modal.d.ts +6 -1
  182. package/types/ui/Dialog/index.d.ts +1 -1
  183. package/types/ui/Dialog/modalSurface.d.ts +12 -0
  184. package/types/ui/Layout/BottomInset.d.ts +1 -1
  185. package/types/ui/Layout/BottomTab.d.ts +11 -2
  186. package/types/ui/Layout/FrameSlotFallback.d.ts +9 -0
  187. package/types/ui/Layout/Navbar.d.ts +4 -1
  188. package/types/ui/Layout/TopInset.d.ts +1 -1
  189. package/types/ui/Layout/index.d.ts +4 -4
  190. package/types/ui/Link/Action.d.ts +5 -1
  191. package/types/ui/Link/Anchor.d.ts +5 -3
  192. package/types/ui/Link/activePath.d.ts +13 -0
  193. package/types/ui/Link/index.d.ts +1 -1
  194. package/types/ui/Modal.d.ts +6 -1
  195. package/types/ui/Model/EditModal.d.ts +6 -1
  196. package/types/ui/Model/New.d.ts +6 -1
  197. package/types/ui/Model/ViewEditModal.d.ts +6 -1
  198. package/types/ui/Model/ViewModal.d.ts +6 -1
  199. package/types/ui/Model/newFormOrigin.d.ts +7 -0
  200. package/types/ui/Portal.d.ts +3 -1
  201. package/types/ui/Reference/origin.d.ts +12 -0
  202. package/types/ui/Signal/Doc.d.ts +23 -18
  203. package/types/ui/Signal/Listener.d.ts +2 -6
  204. package/types/ui/Signal/Object.d.ts +6 -10
  205. package/types/ui/Signal/Response.d.ts +3 -6
  206. package/types/ui/Signal/RestApi.d.ts +11 -15
  207. package/types/ui/Signal/WebSocket.d.ts +11 -21
  208. package/types/ui/Signal/endpointEntries.d.ts +8 -0
  209. package/types/ui/Signal/index.d.ts +35 -12
  210. package/types/ui/Signal/signalText.d.ts +178 -0
  211. package/types/ui/System/CSR.d.ts +2 -2
  212. package/types/ui/System/Client.d.ts +4 -3
  213. package/types/ui/System/Common.d.ts +27 -6
  214. package/types/ui/System/SSR.d.ts +6 -6
  215. package/types/ui/Tooltip.d.ts +4 -2
  216. package/types/ui/index.d.ts +2 -1
  217. package/types/ui/overlayLayer.d.ts +6 -0
  218. package/types/ui/overlayPosition.d.ts +17 -0
  219. package/types/vendor/@akanjs/native/packages/cli/src/api.d.ts +14 -3
  220. package/types/vendor/@akanjs/native/packages/cli/src/commands/update.d.ts +1 -1
  221. package/types/vendor/@akanjs/native/packages/cli/src/config.d.ts +30 -5
  222. package/types/vendor/@akanjs/native/packages/cli/src/lib/compat.d.ts +8 -0
  223. package/types/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.d.ts +5 -0
  224. package/types/vendor/@akanjs/native/packages/cli/src/lib/distribution.d.ts +22 -0
  225. package/types/vendor/@akanjs/native/packages/cli/src/lib/project.d.ts +4 -0
  226. package/types/vendor/@akanjs/native/packages/cli/src/lib/publish.d.ts +16 -10
  227. package/types/vendor/@akanjs/native/packages/cli/src/lib/updates.d.ts +51 -7
  228. package/types/vendor/@akanjs/native/packages/cli/src/lib/vectors.d.ts +1 -1
  229. package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.d.ts +12 -4
  230. package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows.d.ts +5 -0
  231. package/types/vendor/@akanjs/native/packages/core/src/kernel.d.ts +36 -0
  232. package/types/vendor/@akanjs/native/packages/desktop/src/dispatcher.d.ts +6 -0
  233. package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +2 -0
  234. package/types/vendor/@akanjs/native/packages/desktop/src/server.d.ts +11 -0
  235. package/types/vendor/@akanjs/native/plugins/external-view/src/desktop.d.ts +17 -0
  236. package/types/vendor/@akanjs/native/plugins/external-view/src/index.d.ts +132 -0
  237. package/types/vendor/@akanjs/native/plugins/external-view/src.d.ts +1 -0
  238. package/types/vendor/@akanjs/native/plugins/single-instance/src/socket.d.ts +1 -1
  239. package/types/vendor/@akanjs/native/plugins/updates/src/index.d.ts +8 -0
  240. package/types/vendor/@akanjs/native/plugins/window/src/desktop.d.ts +4 -1
  241. package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +49 -0
  242. package/types/vendor/use-agentic/AgentActivity.d.ts +3 -1
  243. package/types/vendor/use-agentic/AgenticSurface.d.ts +2 -2
  244. package/types/vendor/use-agentic/types.d.ts +2 -0
  245. package/types/webkit/CsrStack.d.ts +1 -1
  246. package/types/webkit/EmbeddedLayout.d.ts +10 -0
  247. package/types/webkit/McpAppAgent.d.ts +14 -0
  248. package/types/webkit/McpAppApprovals.d.ts +8 -0
  249. package/types/webkit/McpAppBridge.d.ts +101 -0
  250. package/types/webkit/McpAppView.d.ts +21 -0
  251. package/types/webkit/RenderLayer.d.ts +3 -1
  252. package/types/webkit/index.d.ts +1 -0
  253. package/types/webkit/useLocation.d.ts +1 -1
  254. package/types/webkit/useVisualViewportFrame.d.ts +13 -0
  255. package/ui/Agent/Chat.tsx +29 -10
  256. package/ui/Agent/Composer.tsx +2 -1
  257. package/ui/Agent/EmbedApproval.tsx +17 -0
  258. package/ui/Agent/RichInput.tsx +1 -1
  259. package/ui/Agent/index.ts +2 -0
  260. package/ui/Agent/useKeyboardFrame.ts +10 -0
  261. package/ui/Agent/useScreenActions.ts +45 -0
  262. package/ui/BottomSheet.tsx +82 -53
  263. package/ui/Constant/Doc.tsx +113 -60
  264. package/ui/Constant/index.ts +2 -2
  265. package/ui/Constant/schemaDoc.ts +86 -26
  266. package/ui/Dialog/LegacyModal.tsx +53 -52
  267. package/ui/Dialog/Modal.tsx +93 -49
  268. package/ui/Dialog/modalSurface.ts +61 -0
  269. package/ui/DragAction.tsx +1 -1
  270. package/ui/DraggableList.tsx +1 -1
  271. package/ui/Dropdown.tsx +4 -6
  272. package/ui/KeyboardAvoiding.tsx +1 -1
  273. package/ui/Layout/BottomInset.tsx +32 -16
  274. package/ui/Layout/BottomTab.tsx +42 -16
  275. package/ui/Layout/FrameSlotFallback.tsx +59 -0
  276. package/ui/Layout/Navbar.tsx +56 -15
  277. package/ui/Layout/Sider.tsx +1 -1
  278. package/ui/Layout/TopInset.tsx +22 -17
  279. package/ui/Link/Action.tsx +15 -5
  280. package/ui/Link/Anchor.tsx +25 -13
  281. package/ui/Link/activePath.ts +25 -0
  282. package/ui/Link/index.tsx +1 -1
  283. package/ui/Load/Pagination.tsx +1 -1
  284. package/ui/Load/Units.tsx +1 -1
  285. package/ui/Loading/ProgressBar.tsx +1 -1
  286. package/ui/Modal.tsx +9 -0
  287. package/ui/Model/EditModal.tsx +16 -1
  288. package/ui/Model/New.tsx +25 -2
  289. package/ui/Model/NewWrapper_Client.tsx +2 -0
  290. package/ui/Model/ViewEditModal.tsx +25 -10
  291. package/ui/Model/ViewModal.tsx +16 -1
  292. package/ui/Model/newFormOrigin.ts +45 -0
  293. package/ui/Popconfirm.tsx +5 -7
  294. package/ui/Portal.tsx +4 -7
  295. package/ui/Reference/origin.ts +31 -0
  296. package/ui/ScreenNavigator.tsx +1 -1
  297. package/ui/Select.tsx +14 -14
  298. package/ui/Signal/Arg.tsx +2 -2
  299. package/ui/Signal/Doc.tsx +125 -63
  300. package/ui/Signal/Endpoint.tsx +11 -10
  301. package/ui/Signal/Listener.tsx +7 -10
  302. package/ui/Signal/Object.tsx +7 -14
  303. package/ui/Signal/Response.tsx +24 -24
  304. package/ui/Signal/RestApi.tsx +63 -52
  305. package/ui/Signal/WebSocket.tsx +65 -72
  306. package/ui/Signal/endpointEntries.ts +25 -0
  307. package/ui/Signal/index.ts +23 -6
  308. package/ui/Signal/signalText.ts +59 -0
  309. package/ui/System/CSR.tsx +41 -9
  310. package/ui/System/Client.tsx +37 -5
  311. package/ui/System/Common.tsx +76 -32
  312. package/ui/System/SSR.tsx +73 -31
  313. package/ui/Toast.tsx +1 -1
  314. package/ui/Tooltip.tsx +84 -30
  315. package/ui/index.ts +11 -1
  316. package/ui/overlayLayer.ts +25 -2
  317. package/ui/overlayPosition.ts +90 -0
  318. package/ui/recipe/inputRecipe.ts +8 -1
  319. package/ui/styles.css +152 -0
  320. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +22 -2
  321. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeKernel.kt +37 -0
  322. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdateReports.kt +169 -0
  323. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdates.kt +110 -38
  324. package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeVectors.kt +20 -0
  325. package/vendor/@akanjs/native/native/desktop/Cargo.lock +16 -16
  326. package/vendor/@akanjs/native/native/desktop/src/external.rs +651 -0
  327. package/vendor/@akanjs/native/native/desktop/src/json.rs +23 -0
  328. package/vendor/@akanjs/native/native/desktop/src/lib.rs +228 -5
  329. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeApp.swift +2 -1
  330. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeKernel.swift +47 -1
  331. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdateReports.swift +145 -0
  332. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdates.swift +112 -43
  333. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeVectors.swift +18 -0
  334. package/vendor/@akanjs/native/native/ios/Sources/AkanNativeViewController.swift +24 -2
  335. package/vendor/@akanjs/native/packages/cli/src/api.ts +22 -6
  336. package/vendor/@akanjs/native/packages/cli/src/commands/build.ts +24 -8
  337. package/vendor/@akanjs/native/packages/cli/src/commands/update.ts +11 -9
  338. package/vendor/@akanjs/native/packages/cli/src/config.ts +35 -7
  339. package/vendor/@akanjs/native/packages/cli/src/lib/compat.ts +2 -0
  340. package/vendor/@akanjs/native/packages/cli/src/lib/configkeys.ts +6 -4
  341. package/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.ts +11 -1
  342. package/vendor/@akanjs/native/packages/cli/src/lib/distribution.ts +73 -0
  343. package/vendor/@akanjs/native/packages/cli/src/lib/project.ts +15 -3
  344. package/vendor/@akanjs/native/packages/cli/src/lib/publish.ts +70 -23
  345. package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +159 -23
  346. package/vendor/@akanjs/native/packages/cli/src/lib/vectors.ts +2 -0
  347. package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +9 -4
  348. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.ts +1 -1
  349. package/vendor/@akanjs/native/packages/cli/src/platforms/desktop.ts +4 -2
  350. package/vendor/@akanjs/native/packages/cli/src/platforms/ios.ts +9 -4
  351. package/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.ts +60 -21
  352. package/vendor/@akanjs/native/packages/cli/src/platforms/windows.ts +14 -3
  353. package/vendor/@akanjs/native/packages/core/src/kernel.ts +49 -0
  354. package/vendor/@akanjs/native/packages/core/vectors/updates.json +31 -0
  355. package/vendor/@akanjs/native/packages/desktop/src/dispatcher.ts +13 -5
  356. package/vendor/@akanjs/native/packages/desktop/src/main.ts +3 -0
  357. package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +2 -0
  358. package/vendor/@akanjs/native/packages/desktop/src/server.ts +70 -6
  359. package/vendor/@akanjs/native/plugins/external-view/native-plugin.json +23 -0
  360. package/vendor/@akanjs/native/plugins/external-view/src/desktop.ts +321 -0
  361. package/vendor/@akanjs/native/plugins/external-view/src/index.ts +106 -0
  362. package/vendor/@akanjs/native/plugins/single-instance/src/desktop.ts +4 -0
  363. package/vendor/@akanjs/native/plugins/single-instance/src/socket.ts +12 -5
  364. package/vendor/@akanjs/native/plugins/updates/android/UpdatesPlugin.kt +133 -41
  365. package/vendor/@akanjs/native/plugins/updates/ios/UpdatesPlugin.swift +101 -36
  366. package/vendor/@akanjs/native/plugins/updates/native-plugin.json +4 -2
  367. package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +15 -1
  368. package/vendor/@akanjs/native/plugins/updates/src/index.ts +7 -1
  369. package/vendor/@akanjs/native/plugins/window/native-plugin.json +4 -0
  370. package/vendor/@akanjs/native/plugins/window/src/desktop.ts +68 -1
  371. package/vendor/@akanjs/native/plugins/window/src/index.ts +47 -0
  372. package/vendor/use-agentic/AgentActivity.tsx +4 -2
  373. package/vendor/use-agentic/AgenticSurface.ts +3 -3
  374. package/vendor/use-agentic/ToolRunner.ts +8 -1
  375. package/vendor/use-agentic/types.ts +2 -0
  376. package/webkit/CsrRouteTable.ts +9 -1
  377. package/webkit/CsrStack.ts +9 -2
  378. package/webkit/EmbeddedLayout.tsx +77 -0
  379. package/webkit/McpAppAgent.ts +138 -0
  380. package/webkit/McpAppApprovals.ts +56 -0
  381. package/webkit/McpAppBridge.ts +291 -0
  382. package/webkit/McpAppView.ts +258 -0
  383. package/webkit/RenderLayer.tsx +20 -5
  384. package/webkit/bootCsr.tsx +54 -5
  385. package/webkit/index.ts +1 -0
  386. package/webkit/useCsrValues.ts +16 -10
  387. package/webkit/useFrameRuntime.ts +10 -1
  388. package/webkit/useLocation.ts +4 -3
  389. package/webkit/useVisualViewportFrame.tsx +52 -0
  390. package/service/predefinedAdaptor/sql/driver/libsql.ts +0 -45
  391. package/types/service/predefinedAdaptor/sql/driver/libsql.d.ts +0 -20
  392. package/types/ui/Agent/useKeyboardInset.d.ts +0 -1
  393. package/types/ui/ServerPortal.d.ts +0 -15
  394. package/ui/Agent/useKeyboardInset.ts +0 -19
  395. package/ui/ServerPortal.tsx +0 -49
@@ -0,0 +1,258 @@
1
+ "use client";
2
+ import { router, setCookie } from "akanjs/client";
3
+ import { Logger, parseAkanI18nEnv } from "akanjs/common";
4
+ import { st } from "akanjs/store";
5
+ import type { McpAppBridge, McpDownloadContent, McpHostContext } from "./McpAppBridge";
6
+
7
+ export class McpAppView {
8
+ static readonly contentGap = 24;
9
+
10
+ static readonly hostTokens = {
11
+ "--color-background-primary": ["--background", "--card", "--popover"],
12
+ "--color-background-secondary": ["--muted"],
13
+ "--color-text-primary": ["--foreground", "--card-foreground", "--popover-foreground"],
14
+ "--color-text-secondary": ["--muted-foreground"],
15
+ "--color-border-primary": ["--border", "--input"],
16
+ "--color-ring-primary": ["--ring"],
17
+ "--font-sans": ["--font-sans"],
18
+ "--font-mono": ["--font-mono"],
19
+ } as const;
20
+
21
+ readonly #bridge: McpAppBridge;
22
+ readonly #blobs = new Map<string, Blob>();
23
+ #lastHeight = 0;
24
+ #theme: string | undefined;
25
+
26
+ constructor(bridge: McpAppBridge) {
27
+ this.#bridge = bridge;
28
+ }
29
+
30
+ /** `href` in the host's language when the app speaks it: the page run used the server's MCP language. */
31
+ static localize(href: string, locale: string | undefined, locales: readonly string[]) {
32
+ const lang = locale?.split("-")[0]?.toLowerCase();
33
+ const current = /^\/([^/?#]+)/.exec(href)?.[1];
34
+ if (!lang || !current || !locales.includes(lang) || !locales.includes(current)) return href;
35
+ return `/${lang}${href.slice(current.length + 1)}`;
36
+ }
37
+
38
+ mount() {
39
+ this.#apply(this.#bridge.hostContext);
40
+ this.#bridge.onHostContext((context) => this.#apply(context));
41
+ this.#watchSize();
42
+ this.#keepBlobs();
43
+ this.#routeLinks();
44
+ }
45
+
46
+ /** In place of the app, for a result that names no screen: the host still sizes the view to what it shows. */
47
+ showUnavailable(message: string) {
48
+ this.#apply(this.#bridge.hostContext);
49
+ const notice = document.createElement("p");
50
+ notice.textContent = message;
51
+ notice.style.cssText = "margin:0;padding:16px;font:14px/1.5 system-ui,sans-serif;color:var(--foreground)";
52
+ document.getElementById("root")?.replaceChildren();
53
+ document.body.style.overflow = "visible";
54
+ document.body.replaceChildren(notice);
55
+ this.#bridge.notify("ui/notifications/size-changed", { height: Math.ceil(notice.getBoundingClientRect().height) });
56
+ }
57
+
58
+ #apply({ theme, displayMode, styles }: McpHostContext) {
59
+ const root = document.documentElement;
60
+ root.dataset.akanEmbed = displayMode ?? "inline";
61
+
62
+ if (theme && theme !== this.#theme) {
63
+ this.#theme = theme;
64
+ setCookie("theme", theme);
65
+ root.dataset.theme = theme;
66
+ st.do.setTheme(theme);
67
+
68
+ root.style.colorScheme = theme;
69
+ }
70
+ const variables = styles?.variables ?? {};
71
+ for (const [hostKey, tokens] of Object.entries(McpAppView.hostTokens)) {
72
+ const value = variables[hostKey];
73
+ for (const token of tokens)
74
+ if (value) root.style.setProperty(token, value);
75
+ else root.style.removeProperty(token);
76
+ }
77
+ }
78
+
79
+ #watchSize() {
80
+ let scheduled = false;
81
+ const report = () => {
82
+ if (scheduled) return;
83
+ scheduled = true;
84
+ requestAnimationFrame(() => {
85
+ scheduled = false;
86
+ const height = McpAppView.#contentHeight();
87
+ if (height === this.#lastHeight) return;
88
+ this.#lastHeight = height;
89
+ this.#bridge.notify("ui/notifications/size-changed", { height });
90
+ });
91
+ };
92
+ const resized = new ResizeObserver(report);
93
+ resized.observe(document.documentElement);
94
+ const observeContent = () => {
95
+ for (const content of document.querySelectorAll(".akan-page-content"))
96
+ for (const child of content.children) resized.observe(child);
97
+ };
98
+ new MutationObserver(() => {
99
+ observeContent();
100
+ report();
101
+ }).observe(document.body, { childList: true, subtree: true, attributes: true });
102
+
103
+ document.addEventListener("load", report, true);
104
+ void document.fonts.ready.then(report);
105
+ observeContent();
106
+ report();
107
+ }
108
+
109
+ static #contentHeight() {
110
+ const content = document.querySelector<HTMLElement>('[id^="pageContainer-"]:not([inert]) .akan-page-content');
111
+ if (!content) return Math.ceil(document.documentElement.scrollHeight);
112
+ const viewport = window.innerHeight;
113
+ const style = getComputedStyle(content);
114
+ const top = content.getBoundingClientRect().top - content.scrollTop;
115
+ let bottom = top + Number.parseFloat(style.paddingTop || "0");
116
+ const pending: Element[] = [content];
117
+ for (let box = pending.pop(); box; box = pending.pop())
118
+ for (const child of box.children) {
119
+ const rect = child.getBoundingClientRect();
120
+ if (getComputedStyle(child).position === "fixed") continue;
121
+ if (rect.height === 0 || rect.height >= viewport - 1 || Math.abs(rect.bottom - viewport) <= 1)
122
+ pending.push(child);
123
+ else bottom = Math.max(bottom, rect.bottom);
124
+ }
125
+ const height = bottom - top + Number.parseFloat(style.paddingBottom || "0") + McpAppView.contentGap;
126
+ return Math.ceil(Math.max(height + McpAppView.#overlayHeight(), McpAppView.#dialogHeight()));
127
+ }
128
+
129
+ static #overlayHeight() {
130
+ let height = 0;
131
+ for (const overlay of document.querySelectorAll<HTMLElement>("[data-akan-embed-overlay]"))
132
+ height += overlay.getBoundingClientRect().height;
133
+ return height;
134
+ }
135
+
136
+ static #dialogHeight() {
137
+ let height = 0;
138
+ for (const dialog of document.querySelectorAll<HTMLElement>('[aria-modal="true"]')) {
139
+ if (!dialog.getClientRects().length) continue;
140
+ let natural = 0;
141
+ for (const child of dialog.children)
142
+ if (!["absolute", "fixed"].includes(getComputedStyle(child).position)) natural += child.scrollHeight;
143
+ height = Math.max(height, natural + McpAppView.contentGap * 2);
144
+ }
145
+ return height;
146
+ }
147
+
148
+ #routeLinks() {
149
+ const { locales } = parseAkanI18nEnv();
150
+ const base = McpAppView.#parse(document.baseURI);
151
+ const open = (raw: string) => {
152
+ if (!raw) return;
153
+
154
+ if (raw.startsWith("#")) {
155
+ document.getElementById(decodeURIComponent(raw.slice(1)))?.scrollIntoView();
156
+ return;
157
+ }
158
+ const url = McpAppView.#parse(raw, document.baseURI);
159
+ if (!url) return;
160
+ const [, lang] = url.pathname.split("/");
161
+ if (url.origin === base?.origin && lang && locales.includes(lang))
162
+ router.push(`${url.pathname}${url.search}${url.hash}`);
163
+ else if (["http:", "https:", "mailto:", "tel:"].includes(url.protocol))
164
+ this.#bridge.openLink(url.href).catch((error: unknown) => {
165
+ Logger.warn(`The host did not open ${url.href}: ${error instanceof Error ? error.message : error}`);
166
+ });
167
+ };
168
+ const download = (href: string, name: string | null) =>
169
+ this.#download(href, name).catch((error: unknown) => {
170
+ Logger.warn(`The host did not save ${href}: ${error instanceof Error ? error.message : error}`);
171
+ });
172
+ document.addEventListener("click", (event) => {
173
+ if (event.defaultPrevented || event.button !== 0) return;
174
+ if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return;
175
+ const anchor = (event.target as Element | null)?.closest?.("a[href]");
176
+ if (!(anchor instanceof HTMLAnchorElement)) return;
177
+ event.preventDefault();
178
+ const href = anchor.getAttribute("href") ?? "";
179
+ if (!anchor.hasAttribute("download")) open(href);
180
+ else void download(href, anchor.getAttribute("download"));
181
+ });
182
+ window.open = (url?: string | URL) => {
183
+ if (url !== undefined) open(String(url));
184
+ return null;
185
+ };
186
+
187
+ const click = HTMLAnchorElement.prototype.click;
188
+ HTMLAnchorElement.prototype.click = function (this: HTMLAnchorElement) {
189
+ if (this.isConnected || !this.hasAttribute("download")) click.call(this);
190
+ else void download(this.getAttribute("href") ?? "", this.getAttribute("download"));
191
+ };
192
+ }
193
+
194
+ async #download(href: string, suggested: string | null) {
195
+ const content = await this.#downloadOf(href, suggested || null);
196
+ if (!content) throw new Error("it is neither a web address nor a file this view made");
197
+ if (this.#bridge.hostCapabilities.downloadFile) await this.#bridge.downloadFile([content]);
198
+ else if (content.type === "resource_link") await this.#bridge.openLink(content.uri);
199
+ else throw new Error("this host does not save files");
200
+ }
201
+
202
+ async #downloadOf(href: string, suggested: string | null): Promise<McpDownloadContent | null> {
203
+ const blob = this.#blobs.get(href) ?? (href.startsWith("data:") ? McpAppView.#dataBlob(href) : null);
204
+ if (blob) {
205
+ const name = suggested ?? "download";
206
+ const bytes = new Uint8Array(await blob.arrayBuffer());
207
+ return {
208
+ type: "resource",
209
+ resource: {
210
+ uri: `file:///${encodeURIComponent(name)}`,
211
+ mimeType: blob.type || "application/octet-stream",
212
+ blob: McpAppView.#base64(bytes),
213
+ },
214
+ };
215
+ }
216
+ const url = McpAppView.#parse(href, document.baseURI);
217
+ if (!url || !["http:", "https:"].includes(url.protocol)) return null;
218
+ return { type: "resource_link", uri: url.href, name: suggested ?? url.pathname.split("/").at(-1) ?? "download" };
219
+ }
220
+
221
+ #keepBlobs() {
222
+ const create = URL.createObjectURL.bind(URL);
223
+ const revoke = URL.revokeObjectURL.bind(URL);
224
+ URL.createObjectURL = (object: Blob | MediaSource) => {
225
+ const url = create(object);
226
+ if (object instanceof Blob) this.#blobs.set(url, object);
227
+ return url;
228
+ };
229
+ URL.revokeObjectURL = (url: string) => {
230
+ this.#blobs.delete(url);
231
+ revoke(url);
232
+ };
233
+ }
234
+
235
+ static #dataBlob(href: string) {
236
+ const match = /^data:([^,]*?)(;base64)?,(.*)$/s.exec(href);
237
+ if (!match) return null;
238
+ const [, type = "", base64, body = ""] = match;
239
+ const bytes = base64
240
+ ? Uint8Array.from(atob(body), (char) => char.charCodeAt(0))
241
+ : new TextEncoder().encode(decodeURIComponent(body));
242
+ return new Blob([bytes], { type: type.split(";")[0] });
243
+ }
244
+
245
+ static #base64(bytes: Uint8Array) {
246
+ let binary = "";
247
+ for (let at = 0; at < bytes.length; at += 0x8000) binary += String.fromCharCode(...bytes.subarray(at, at + 0x8000));
248
+ return btoa(binary);
249
+ }
250
+
251
+ static #parse(raw: string, base?: string) {
252
+ try {
253
+ return new URL(raw, base);
254
+ } catch {
255
+ return null;
256
+ }
257
+ }
258
+ }
@@ -3,6 +3,7 @@ import { debugFrame, RouteDefinition, type RouteRender, router, usePathCtx } fro
3
3
  import { isThenable, Logger } from "akanjs/common";
4
4
  import { createContext, createElement, memo, type ReactNode, useContext, useEffect, useRef, useState } from "react";
5
5
  import { CsrFrameDump } from "./csrFrameDump";
6
+ import { EmbeddedLayout, LayoutOutlet } from "./EmbeddedLayout";
6
7
  import { RenderFailure } from "./RenderFailure";
7
8
  import { useFetch } from "./useFetch";
8
9
  import { usePageActivity } from "./usePageActivity";
@@ -28,15 +29,28 @@ interface RenderLayerProps {
28
29
  params: Record<string, string>;
29
30
  searchParams: Record<string, string | string[]>;
30
31
  leaf?: ReactNode;
32
+ /** A page in an MCP view: every layout above it runs, and only the page is shown. */
33
+ isEmbedded?: boolean;
31
34
  }
32
- export const RenderLayer = memo(({ renders, index, params, searchParams, leaf = null }: RenderLayerProps) => {
35
+ export const RenderLayer = memo((props: RenderLayerProps) => {
36
+ const { renders, index, params, searchParams, leaf = null, isEmbedded = false } = props;
33
37
  const isLast = index >= renders.length - 1;
34
- const children = isLast ? (
38
+ const below = isLast ? (
35
39
  leaf
36
40
  ) : (
37
- <RenderLayer renders={renders} index={index + 1} params={params} searchParams={searchParams} leaf={leaf} />
41
+ <RenderLayer
42
+ renders={renders}
43
+ index={index + 1}
44
+ params={params}
45
+ searchParams={searchParams}
46
+ leaf={leaf}
47
+ isEmbedded={isEmbedded}
48
+ />
38
49
  );
39
50
  const routeRender = renders[index];
51
+ const isEmbeddedLayout = isEmbedded && routeRender?.kind === "layout";
52
+ const children = isEmbeddedLayout ? <LayoutOutlet>{below}</LayoutOutlet> : below;
53
+ const frame = (drawn: ReactNode) => (isEmbeddedLayout ? <EmbeddedLayout>{drawn}</EmbeddedLayout> : drawn);
40
54
  const isAsyncRender = isAsyncRouteRender(routeRender);
41
55
  const renderedRef = useRef<RenderedRoute | null>(null);
42
56
  const [, setFailures] = useState(0);
@@ -106,7 +120,8 @@ export const RenderLayer = memo(({ renders, index, params, searchParams, leaf =
106
120
  };
107
121
  }, [record]);
108
122
  if (!routeRender) return null;
109
- if (!isAsyncRender) return createElement(routeRender.render as never, { children, params, searchParams } as never);
123
+ if (!isAsyncRender)
124
+ return frame(createElement(routeRender.render as never, { children, params, searchParams } as never));
110
125
  const failure = renderedRef.current?.failure;
111
126
  if (failure)
112
127
  return (
@@ -119,7 +134,7 @@ export const RenderLayer = memo(({ renders, index, params, searchParams, leaf =
119
134
  />
120
135
  );
121
136
  if (!value) return <>{composeLoadingFallback(renders.slice(index), params)}</>;
122
- return <layerChildContext.Provider value={children}>{value}</layerChildContext.Provider>;
137
+ return frame(<layerChildContext.Provider value={children}>{value}</layerChildContext.Provider>);
123
138
  });
124
139
 
125
140
  function reportFailure(result: ReactNode | Promise<ReactNode>, layer: string): ReactNode | Promise<ReactNode> {
@@ -1,9 +1,14 @@
1
1
  "use client";
2
- import { csrContext, Device, getStoredAuthToken, initAuth } from "akanjs/client";
2
+ import { csrContext, csrLocation, Device, getStoredAuthToken, initAuth } from "akanjs/client";
3
3
  import { Logger, parseAkanI18nEnv, parseBasePaths } from "akanjs/common";
4
+ import { FetchClient } from "akanjs/fetch";
5
+ import { st } from "akanjs/store";
4
6
  import { useSyncExternalStore } from "react";
5
7
  import * as ReactDOM from "react-dom/client";
6
8
  import { type CsrRouteContext, CsrRouteTable } from "./CsrRouteTable";
9
+ import { McpAppAgent } from "./McpAppAgent";
10
+ import { McpAppBridge } from "./McpAppBridge";
11
+ import { McpAppView } from "./McpAppView";
7
12
  import { RenderLayer } from "./RenderLayer";
8
13
  import { useCsrValues } from "./useCsrValues";
9
14
 
@@ -18,8 +23,15 @@ export const bootCsr = async (context: CsrRouteContext) => {
18
23
  window.document.body.style.overflow = "hidden";
19
24
  initializeMobileTargetFromSearch();
20
25
  if (window.location.pathname === "/404") return;
26
+
27
+ let remount: () => void = () => undefined;
28
+ const embedded = globalThis.__AKAN_EMBED__ ? await bootMcpApp(i18n.locales, () => remount()) : false;
29
+ if (embedded === null) return;
21
30
 
22
- const [device, jwt] = await Promise.all([Device.load({ supportLanguages: i18n.locales }), getStoredAuthToken()]);
31
+ const [device, jwt] = await Promise.all([
32
+ Device.load({ supportLanguages: i18n.locales }),
33
+ embedded ? undefined : getStoredAuthToken(),
34
+ ]);
23
35
  const mobileTarget = window.__AKAN_MOBILE_TARGET__;
24
36
 
25
37
  if (mobileTarget && window.location.pathname === "/")
@@ -28,8 +40,8 @@ export const bootCsr = async (context: CsrRouteContext) => {
28
40
  "",
29
41
  `${mobileHomePath(device.lang, mobileTarget)}${window.location.search}${window.location.hash}`,
30
42
  );
31
- const pathname = window.location.pathname;
32
- if (!mobileTarget && !pathname.startsWith(`/${device.lang}`))
43
+ const pathname = csrLocation.pathname;
44
+ if (!mobileTarget && !embedded && !pathname.startsWith(`/${device.lang}`))
33
45
  window.location.replace(`/${device.lang}${pathname}${window.location.search}${window.location.hash}`);
34
46
 
35
47
  if (jwt) initAuth({ jwt });
@@ -62,10 +74,19 @@ export const bootCsr = async (context: CsrRouteContext) => {
62
74
  );
63
75
  };
64
76
 
77
+ st.set({ searchParams: searchParamsOf(csrLocation.search) });
65
78
  const el = document.getElementById("root");
66
79
  if (!el) throw new Error("No root element");
67
- const root = ReactDOM.createRoot(el);
80
+ let root = ReactDOM.createRoot(el);
68
81
  root.render(<RouterProvider />);
82
+
83
+ remount = () => {
84
+ setTimeout(() => {
85
+ root.unmount();
86
+ root = ReactDOM.createRoot(el);
87
+ root.render(<RouterProvider />);
88
+ }, 0);
89
+ };
69
90
  };
70
91
 
71
92
  /**
@@ -74,6 +95,34 @@ export const bootCsr = async (context: CsrRouteContext) => {
74
95
  */
75
96
  export const replacePages = async (context: CsrRouteContext) => (await CsrRouteTable.active?.replace(context)) ?? false;
76
97
 
98
+ function searchParamsOf(search: string): { [key: string]: string | string[] } {
99
+ const params: { [key: string]: string | string[] } = {};
100
+ for (const [key, value] of new URLSearchParams(search)) {
101
+ const current = params[key];
102
+ params[key] = current === undefined ? value : Array.isArray(current) ? [...current, value] : [current, value];
103
+ }
104
+ return params;
105
+ }
106
+
107
+ async function bootMcpApp(locales: readonly string[], reload: () => void) {
108
+ const bridge = new McpAppBridge();
109
+ const view = new McpAppView(bridge);
110
+ const start = await bridge.connect();
111
+ if (!start.ok) {
112
+ view.showUnavailable(start.message);
113
+ return null;
114
+ }
115
+ if (globalThis.__AKAN_EMBED__) globalThis.__AKAN_EMBED__.account = start.account;
116
+ csrLocation.useMemory(McpAppView.localize(start.href, bridge.hostContext.locale, locales), {
117
+ leave: () => view.showUnavailable("This screen is not available here."),
118
+ reload,
119
+ });
120
+ FetchClient.setBridge(bridge.fetchBridge(start.snapshot));
121
+ view.mount();
122
+ new McpAppAgent(bridge).start();
123
+ return true;
124
+ }
125
+
77
126
  function initializeMobileTargetFromSearch() {
78
127
  if (window.__AKAN_MOBILE_TARGET__) return;
79
128
 
package/webkit/index.ts CHANGED
@@ -19,3 +19,4 @@ export { usePageTool } from "./usePageTool";
19
19
  export type { ScreenScopeItem } from "./useScreenScope";
20
20
  export { useScreenScope } from "./useScreenScope";
21
21
  export { useThrottle } from "./useThrottle";
22
+ export { useVisualViewportFrame, type VisualViewportFrame } from "./useVisualViewportFrame";
@@ -4,6 +4,7 @@ import { useDrag } from "@use-gesture/react";
4
4
  import {
5
5
  type CsrContextType,
6
6
  router as clientRouter,
7
+ csrLocation,
7
8
  Device,
8
9
  debugFrame,
9
10
  defaultPageState,
@@ -109,6 +110,7 @@ const pageContentStyle = ({ frameLayout }: RouteState, pageState: FramePageState
109
110
  (frameLayout.keyboard.sticky ? frameLayout.keyboardAccessory.height : 0),
110
111
  ),
111
112
  height: frameLayout.contentViewport.height,
113
+ "--akan-content-height": `${frameLayout.contentViewport.height}px`,
112
114
  });
113
115
 
114
116
  const prevPageContentStyle = (clientHeight: number, prevPageState: FramePageState) => ({
@@ -132,6 +134,10 @@ const tweenedSafeAreas = (
132
134
  bottomSafeArea: {
133
135
  containerStyle: {
134
136
  backgroundColor: pageState.bottomSafeAreaColor,
137
+ visibility:
138
+ pageState.bottomInset > 0 || pageState.bottomSafeAreaColor !== defaultPageState.bottomSafeAreaColor
139
+ ? ("visible" as const)
140
+ : ("hidden" as const),
135
141
  top: tween(progress, clientHeight - prevPageState.bottomSafeArea, clientHeight - pageState.bottomSafeArea),
136
142
  height: tween(progress, prevPageState.bottomSafeArea, pageState.bottomSafeArea),
137
143
  },
@@ -603,7 +609,7 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
603
609
 
604
610
  const { getLocation } = useLocation({ rootRouteGuide });
605
611
  const [initialStack] = useState(() => {
606
- const current = getLocation(window.location.href.replace(window.location.origin, ""));
612
+ const current = getLocation(csrLocation.href);
607
613
  return CsrStack.restore(current, getLocation) ?? { locations: [current], idx: 0 };
608
614
  });
609
615
  const {
@@ -876,8 +882,8 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
876
882
  if (!usePendingNavigation) {
877
883
  setHistoryForward({ type: kind, location: toLocation, scrollTop, scrollToTop });
878
884
  settle(kind === "replace" ? prevLocation : fromLocation);
879
- if (kind === "push") window.history.pushState({ akanEntryId: toLocation.entryId }, "", href);
880
- else window.history.replaceState({ akanEntryId: toLocation.entryId }, "", href);
885
+ if (kind === "push") csrLocation.push({ akanEntryId: toLocation.entryId }, href);
886
+ else csrLocation.replace({ akanEntryId: toLocation.entryId }, href);
881
887
  return;
882
888
  }
883
889
 
@@ -951,8 +957,8 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
951
957
  navigationIntent: intent,
952
958
  phase: "transitioning",
953
959
  });
954
- if (kind === "push") window.history.pushState({ akanEntryId: toLocation.entryId }, "", href);
955
- else window.history.replaceState({ akanEntryId: toLocation.entryId }, "", href);
960
+ if (kind === "push") csrLocation.push({ akanEntryId: toLocation.entryId }, href);
961
+ else csrLocation.replace({ akanEntryId: toLocation.entryId }, href);
956
962
  debugFrame("navigation.commit", { id: intent.id, kind, to: href });
957
963
  window.setTimeout(
958
964
  () => {
@@ -1038,7 +1044,7 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
1038
1044
  ]);
1039
1045
  useEffect(() => {
1040
1046
  debugFrame("csr.mount", { path: resolvedLocation.pathRoute.path, viewport });
1041
- window.history.replaceState({ ...(window.history.state ?? {}), akanEntryId: getCurrentLocation().entryId }, "");
1047
+ csrLocation.replace({ ...((csrLocation.state as object | null) ?? {}), akanEntryId: getCurrentLocation().entryId });
1042
1048
  return () => debugFrame("csr.unmount", { lastPath: resolvedLocation.pathRoute.path });
1043
1049
  }, []);
1044
1050
  const getRouter = useCallback((): RouterInstance => {
@@ -1058,7 +1064,7 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
1058
1064
  push: forward("push"),
1059
1065
  replace: forward("replace"),
1060
1066
  refresh: () => {
1061
- window.location.reload();
1067
+ csrLocation.reload();
1062
1068
  },
1063
1069
  back: async ({ scrollToTop }: RouteOptions = {}) => {
1064
1070
  const targetLocation = getPrevLocation();
@@ -1072,12 +1078,12 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
1072
1078
  const scrollTop = pageContentRef.current?.scrollTop ?? 0;
1073
1079
  setHistoryBack({ type: "back", location, scrollTop, scrollToTop });
1074
1080
  settle(getPrevLocation());
1075
- window.history.back();
1081
+ csrLocation.back();
1076
1082
  },
1077
1083
  };
1078
1084
  window.onpopstate = async (event) => {
1079
- const href = window.location.href.replace(window.location.origin, "");
1080
- const entryId = (window.history.state as { akanEntryId?: string } | null)?.akanEntryId;
1085
+ const href = csrLocation.href;
1086
+ const entryId = (csrLocation.state as { akanEntryId?: string } | null)?.akanEntryId;
1081
1087
  const isAt = (target: Location | null | undefined) =>
1082
1088
  !!target && (entryId ? target.entryId === entryId : target.href === href);
1083
1089
  const routeType = isAt(getNextLocation()) ? "forward" : isAt(getPrevLocation()) ? "back" : null;
@@ -74,7 +74,16 @@ export function applyFrameSlots(
74
74
  ) {
75
75
  const slots = getFrameSlotsForPath(pathRoute, frameSlots, pathRoutes, visiblePaths);
76
76
  const pageState = clonePageState(basePageState);
77
- if (!pathRoute.explicitPageConfigKeys?.cache && slots.some((slot) => slot.cache)) pageState.cache = true;
77
+ const explicit = pathRoute.explicitPageConfigKeys ?? {};
78
+ if (!explicit.cache && slots.some((slot) => slot.cache)) pageState.cache = true;
79
+
80
+ for (const side of ["topInset", "bottomInset"] as const) {
81
+ if (explicit[side]) continue;
82
+ const heights = slots
83
+ .filter((slot) => slot.type === side && !isKeyboardAccessorySlot(slot))
84
+ .map(resolveFrameSlotHeight);
85
+ if (heights.length) pageState[side] = Math.max(...heights);
86
+ }
78
87
  return pageState;
79
88
  }
80
89
 
@@ -1,5 +1,5 @@
1
1
  "use client";
2
- import type { Location, PathRoute, RouteGuide } from "akanjs/client";
2
+ import { csrLocation, type Location, type PathRoute, type RouteGuide } from "akanjs/client";
3
3
  import { useCallback, useRef } from "react";
4
4
  import { CsrStack } from "./CsrStack";
5
5
 
@@ -34,7 +34,8 @@ export const useLocation = ({ rootRouteGuide }: UseLocationOptions) => {
34
34
  const targetRouteGuide = getTargetRouteGuide(pathSegments, routeGuide.current);
35
35
  const pathRoute = targetRouteGuide.pathRoute;
36
36
  if (!pathRoute) {
37
- window.location.assign("/404");
37
+
38
+ if (!csrLocation.isMemory || pathname === csrLocation.pathname) csrLocation.assign("/404");
38
39
  throw new Error("404");
39
40
  }
40
41
  return pathRoute;
@@ -57,7 +58,7 @@ export const useLocation = ({ rootRouteGuide }: UseLocationOptions) => {
57
58
  {},
58
59
  );
59
60
  };
60
- const hrefWithoutOrigin = href.replace(window.location.origin, "");
61
+ const hrefWithoutOrigin = csrLocation.strip(href);
61
62
  const [hrefWithoutHash, hash = ""] = hrefWithoutOrigin.split("#");
62
63
  const [pathname, search] = hrefWithoutHash.split("?");
63
64
  const pathRoute = getPathRoute(pathname);
@@ -0,0 +1,52 @@
1
+ "use client";
2
+ import { useEffect, useState } from "react";
3
+
4
+ export interface VisualViewportFrame {
5
+ /** The on-screen keyboard's height over the layout viewport's bottom; 0 while it is closed. */
6
+ keyboardHeight: number;
7
+ /** The visible rectangle's height and its scroll offset from the layout viewport's top, in CSS px. */
8
+ height: number;
9
+ offsetTop: number;
10
+ }
11
+
12
+ const closedFrame = (): VisualViewportFrame => ({
13
+ keyboardHeight: 0,
14
+ height: typeof window === "undefined" ? 0 : window.innerHeight,
15
+ offsetTop: 0,
16
+ });
17
+
18
+ /**
19
+ * The visible part of the page while the on-screen keyboard is up, on the web. Only `visualViewport` reports the
20
+ * keyboard: `dvh` tracks the browser's own chrome, and iOS pans the visual viewport to the field instead of shrinking
21
+ * the page, so a fixed bottom bar stays behind the keyboard until it is placed at `offsetTop + height`.
22
+ */
23
+ export const useVisualViewportFrame = (): VisualViewportFrame => {
24
+ const [frame, setFrame] = useState<VisualViewportFrame>(closedFrame);
25
+ useEffect(() => {
26
+ const viewport = window.visualViewport;
27
+ if (!viewport) return;
28
+ const measure = () => {
29
+
30
+ const zoomed = viewport.scale > 1.01;
31
+ const keyboardHeight = zoomed
32
+ ? 0
33
+ : Math.max(0, Math.round(window.innerHeight - viewport.height - viewport.offsetTop));
34
+ const next = zoomed
35
+ ? closedFrame()
36
+ : { keyboardHeight, height: Math.round(viewport.height), offsetTop: Math.round(viewport.offsetTop) };
37
+ setFrame((prev) =>
38
+ prev.keyboardHeight === next.keyboardHeight && prev.height === next.height && prev.offsetTop === next.offsetTop
39
+ ? prev
40
+ : next,
41
+ );
42
+ };
43
+ measure();
44
+ viewport.addEventListener("resize", measure);
45
+ viewport.addEventListener("scroll", measure);
46
+ return () => {
47
+ viewport.removeEventListener("resize", measure);
48
+ viewport.removeEventListener("scroll", measure);
49
+ };
50
+ }, []);
51
+ return frame;
52
+ };
@@ -1,45 +0,0 @@
1
- import type { Client as LibsqlClient, Transaction as LibsqlTransaction } from "@libsql/client";
2
- import type { AkanSqlClient, AkanSqlStatement } from "../types";
3
- import { toLibsqlArgs } from "../values";
4
- import { openTurn, type SqliteWriteTurn, waitForTurn } from "./bunSqlite";
5
-
6
- export class LibsqlStatement implements AkanSqlStatement {
7
- readonly #turn: () => Promise<void>;
8
- constructor(
9
- private readonly client: LibsqlClient | LibsqlTransaction,
10
- private readonly sql: string,
11
- writeTurn: SqliteWriteTurn = openTurn,
12
- ) {
13
- this.#turn = waitForTurn(sql, writeTurn);
14
- }
15
- async run(...params: unknown[]) {
16
- await this.#turn();
17
- return await this.client.execute({ sql: this.sql, args: toLibsqlArgs(params) });
18
- }
19
- async get<Row = Record<string, unknown>>(...params: unknown[]): Promise<Row | null> {
20
- await this.#turn();
21
- const result = await this.client.execute({ sql: this.sql, args: toLibsqlArgs(params) });
22
- return (result.rows[0] as Row | undefined) ?? null;
23
- }
24
- async all<Row = Record<string, unknown>>(...params: unknown[]): Promise<Row[]> {
25
- await this.#turn();
26
- const result = await this.client.execute({ sql: this.sql, args: toLibsqlArgs(params) });
27
- return result.rows as Row[];
28
- }
29
- }
30
-
31
- export class LibsqlAkanClient implements AkanSqlClient {
32
- constructor(
33
- readonly client: LibsqlClient | LibsqlTransaction,
34
- private readonly writeTurn: SqliteWriteTurn = openTurn,
35
- ) {}
36
- async execute(sql: string, params: unknown[] | Record<string, unknown> = []) {
37
- return await this.prepare(sql).run(...(Array.isArray(params) ? params : [params]));
38
- }
39
- prepare(sql: string): AkanSqlStatement {
40
- return new LibsqlStatement(this.client, sql, this.writeTurn);
41
- }
42
- async close() {
43
- this.client.close();
44
- }
45
- }
@@ -1,20 +0,0 @@
1
- import type { Client as LibsqlClient, Transaction as LibsqlTransaction } from "@libsql/client";
2
- import type { AkanSqlClient, AkanSqlStatement } from "../types.d.ts";
3
- import { type SqliteWriteTurn } from "./bunSqlite.d.ts";
4
- export declare class LibsqlStatement implements AkanSqlStatement {
5
- #private;
6
- private readonly client;
7
- private readonly sql;
8
- constructor(client: LibsqlClient | LibsqlTransaction, sql: string, writeTurn?: SqliteWriteTurn);
9
- run(...params: unknown[]): Promise<import("@libsql/client").ResultSet>;
10
- get<Row = Record<string, unknown>>(...params: unknown[]): Promise<Row | null>;
11
- all<Row = Record<string, unknown>>(...params: unknown[]): Promise<Row[]>;
12
- }
13
- export declare class LibsqlAkanClient implements AkanSqlClient {
14
- readonly client: LibsqlClient | LibsqlTransaction;
15
- private readonly writeTurn;
16
- constructor(client: LibsqlClient | LibsqlTransaction, writeTurn?: SqliteWriteTurn);
17
- execute(sql: string, params?: unknown[] | Record<string, unknown>): Promise<unknown>;
18
- prepare(sql: string): AkanSqlStatement;
19
- close(): Promise<void>;
20
- }