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.
- package/CHANGELOG.md +26 -0
- package/base/baseEnv.ts +0 -4
- package/base/primitiveRegistry.ts +2 -2
- package/client/cookie.ts +5 -1
- package/client/csrLocation.ts +122 -0
- package/client/csrTypes.ts +36 -4
- package/client/device.ts +2 -1
- package/client/frameConfig.ts +78 -7
- package/client/index.ts +3 -0
- package/client/makePageProto.tsx +3 -2
- package/client/native/external-view.ts +1 -0
- package/client/route/RootLayoutDefinition.ts +7 -1
- package/client/route/RouteHead.ts +108 -0
- package/client/route/index.ts +1 -0
- package/client/router.ts +36 -23
- package/client/rscNavigation.ts +8 -2
- package/client/viewportMeta.ts +60 -0
- package/client/webManifest.ts +18 -0
- package/common/Logger.ts +7 -2
- package/common/PipelineNames.ts +31 -0
- package/common/PipelinePattern.ts +70 -0
- package/common/PipelinePlanSchema.ts +490 -0
- package/common/backoff.ts +14 -0
- package/common/index.ts +39 -0
- package/common/pipelinePlan.ts +259 -0
- package/common/routeConvention.ts +1 -0
- package/common/types.ts +17 -0
- package/constant/constantRegistry.ts +26 -6
- package/dictionary/base.dictionary.ts +12 -0
- package/dictionary/trans.ts +9 -1
- package/document/into.ts +5 -0
- package/fetch/client/fetchClient.ts +135 -31
- package/fetch/client/httpClient.ts +45 -0
- package/fetch/client/wsClient.ts +125 -16
- package/fetch/fetchType/buildFetch.type.ts +1 -0
- package/index.ts +243 -10
- package/package.json +1 -5
- package/server/akanApp.ts +5 -2
- package/server/akanServer.ts +44 -8
- package/server/artifact/builderRpc.ts +8 -3
- package/server/artifact/ipcTypes.ts +5 -2
- package/server/di/diLifecycle.ts +39 -3
- package/server/di/utils.ts +28 -1
- package/server/ed25519Key.ts +48 -0
- package/server/index.ts +1 -0
- package/server/machine/index.ts +2 -0
- package/server/machine/machineAccount.ts +27 -0
- package/server/machine/machineToken.ts +151 -0
- package/server/mcp/McpApps.ts +256 -0
- package/server/mcp/McpDispatcher.ts +31 -7
- package/server/mcp/McpRouter.ts +209 -46
- package/server/ops/opsToken.ts +3 -10
- package/server/resolver/database.resolver.ts +3 -0
- package/server/resolver/signal.resolver.ts +40 -3
- package/server/routeElementComposer.tsx +78 -2
- package/server/routeState.ts +6 -0
- package/server/routeTreeBuilder.ts +26 -9
- package/server/routing/apiRouter.ts +6 -3
- package/server/routing/roomReplay.ts +88 -0
- package/server/rscClient.tsx +53 -8
- package/server/rscWorker.tsx +141 -27
- package/server/rscWorkerHost.ts +18 -5
- package/server/sitemap.ts +14 -0
- package/server/ssrFromRscRenderer.tsx +12 -6
- package/server/ssrTypes.ts +3 -1
- package/server/systemPageDocument.tsx +38 -30
- package/server/types.tsx +2 -0
- package/server/webRouter.ts +74 -5
- package/service/adapt.ts +1 -1
- package/service/injectInfo.ts +9 -1
- package/service/ipcTypes.ts +2 -1
- package/service/predefinedAdaptor/database.adaptor.ts +1 -146
- package/service/predefinedAdaptor/queue.adaptor.ts +44 -6
- package/service/predefinedAdaptor/queueBackoff.ts +12 -0
- package/service/predefinedAdaptor/solidQueue.adaptor.ts +17 -13
- package/service/predefinedAdaptor/sql/SqlDocumentStore.ts +44 -2
- package/service/predefinedAdaptor/sql/dialect/postgres.ts +3 -0
- package/service/predefinedAdaptor/sql/dialect/sqlite.ts +5 -2
- package/service/predefinedAdaptor/sql/search/fts5.ts +1 -1
- package/service/predefinedAdaptor/sql/types.ts +8 -6
- package/service/predefinedAdaptor/sql/values.ts +0 -23
- package/service/serve.ts +5 -1
- package/signal/agent.signal.ts +1 -1
- package/signal/base.signal.ts +2 -2
- package/signal/endpointIdempotency.ts +53 -0
- package/signal/internalArg.ts +14 -0
- package/signal/mcp/McpDocument.ts +38 -1
- package/signal/mcp/mcpProtocol.ts +18 -0
- package/signal/signalContext.ts +58 -30
- package/signal/signalRegistry.ts +14 -4
- package/signal/types.ts +18 -3
- package/store/action.ts +17 -19
- package/store/agent/StoreSurfaceSource.ts +1 -1
- package/store/agentic/StToolBuilder.ts +3 -0
- package/test/conformance.ts +9 -33
- package/types/base/baseEnv.d.ts +0 -4
- package/types/base/primitiveRegistry.d.ts +1 -1
- package/types/client/csrLocation.d.ts +32 -0
- package/types/client/csrTypes.d.ts +43 -4
- package/types/client/frameConfig.d.ts +14 -2
- package/types/client/index.d.ts +3 -0
- package/types/client/native/external-view.d.ts +1 -0
- package/types/client/route/RootLayoutDefinition.d.ts +2 -1
- package/types/client/route/RouteHead.d.ts +13 -0
- package/types/client/route/index.d.ts +1 -0
- package/types/client/router.d.ts +6 -0
- package/types/client/rscNavigation.d.ts +4 -0
- package/types/client/viewportMeta.d.ts +18 -0
- package/types/client/webManifest.d.ts +4 -0
- package/types/common/PipelineNames.d.ts +6 -0
- package/types/common/PipelinePattern.d.ts +14 -0
- package/types/common/PipelinePlanSchema.d.ts +9 -0
- package/types/common/backoff.d.ts +10 -0
- package/types/common/index.d.ts +5 -0
- package/types/common/pipelinePlan.d.ts +224 -0
- package/types/common/types.d.ts +16 -0
- package/types/constant/constantRegistry.d.ts +5 -2
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictionary.d.ts +8 -8
- package/types/document/into.d.ts +9 -0
- package/types/fetch/client/fetchClient.d.ts +31 -4
- package/types/fetch/client/httpClient.d.ts +8 -0
- package/types/fetch/client/wsClient.d.ts +26 -2
- package/types/fetch/fetchType/buildFetch.type.d.ts +1 -0
- package/types/index.d.ts +230 -9
- package/types/server/akanServer.d.ts +16 -1
- package/types/server/artifact/builderRpc.d.ts +7 -2
- package/types/server/artifact/ipcTypes.d.ts +5 -1
- package/types/server/di/utils.d.ts +1 -0
- package/types/server/ed25519Key.d.ts +13 -0
- package/types/server/index.d.ts +1 -0
- package/types/server/machine/index.d.ts +2 -0
- package/types/server/machine/machineAccount.d.ts +5 -0
- package/types/server/machine/machineToken.d.ts +29 -0
- package/types/server/machine.d.ts +1 -0
- package/types/server/mcp/McpApps.d.ts +50 -0
- package/types/server/mcp/McpDispatcher.d.ts +4 -1
- package/types/server/mcp/McpRouter.d.ts +4 -0
- package/types/server/resolver/signal.resolver.d.ts +2 -1
- package/types/server/routeElementComposer.d.ts +24 -4
- package/types/server/routeState.d.ts +4 -1
- package/types/server/routing/roomReplay.d.ts +24 -0
- package/types/server/rscWorkerHost.d.ts +5 -2
- package/types/server/sitemap.d.ts +2 -0
- package/types/server/ssrFromRscRenderer.d.ts +3 -3
- package/types/server/ssrTypes.d.ts +4 -1
- package/types/server/systemPageDocument.d.ts +2 -0
- package/types/server/types.d.ts +4 -1
- package/types/server/webRouter.d.ts +3 -1
- package/types/service/adapt.d.ts +1 -1
- package/types/service/ipcTypes.d.ts +2 -0
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +1 -24
- package/types/service/predefinedAdaptor/queue.adaptor.d.ts +10 -3
- package/types/service/predefinedAdaptor/queueBackoff.d.ts +4 -0
- package/types/service/predefinedAdaptor/solidQueue.adaptor.d.ts +3 -3
- package/types/service/predefinedAdaptor/sql/SqlDocumentStore.d.ts +6 -1
- package/types/service/predefinedAdaptor/sql/dialect/postgres.d.ts +1 -0
- package/types/service/predefinedAdaptor/sql/dialect/sqlite.d.ts +1 -0
- package/types/service/predefinedAdaptor/sql/search/fts5.d.ts +1 -1
- package/types/service/predefinedAdaptor/sql/types.d.ts +9 -5
- package/types/service/predefinedAdaptor/sql/values.d.ts +0 -3
- package/types/service/serve.d.ts +3 -0
- package/types/signal/endpointIdempotency.d.ts +7 -0
- package/types/signal/internalArg.d.ts +10 -0
- package/types/signal/mcp/McpDocument.d.ts +8 -0
- package/types/signal/mcp/mcpProtocol.d.ts +17 -0
- package/types/signal/signalContext.d.ts +5 -3
- package/types/signal/signalRegistry.d.ts +4 -4
- package/types/signal/types.d.ts +21 -1
- package/types/store/agentic/StToolBuilder.d.ts +2 -0
- package/types/test/conformance.d.ts +7 -10
- package/types/ui/Agent/Chat.d.ts +5 -1
- package/types/ui/Agent/EmbedApproval.d.ts +1 -0
- package/types/ui/Agent/index.d.ts +3 -0
- package/types/ui/Agent/useKeyboardFrame.d.ts +2 -0
- package/types/ui/Agent/useScreenActions.d.ts +18 -0
- package/types/ui/BottomSheet.d.ts +1 -0
- package/types/ui/Constant/Doc.d.ts +11 -17
- package/types/ui/Constant/index.d.ts +7 -2
- package/types/ui/Constant/schemaDoc.d.ts +11 -0
- package/types/ui/Dialog/Modal.d.ts +6 -1
- package/types/ui/Dialog/index.d.ts +1 -1
- package/types/ui/Dialog/modalSurface.d.ts +12 -0
- package/types/ui/Layout/BottomInset.d.ts +1 -1
- package/types/ui/Layout/BottomTab.d.ts +11 -2
- package/types/ui/Layout/FrameSlotFallback.d.ts +9 -0
- package/types/ui/Layout/Navbar.d.ts +4 -1
- package/types/ui/Layout/TopInset.d.ts +1 -1
- package/types/ui/Layout/index.d.ts +4 -4
- package/types/ui/Link/Action.d.ts +5 -1
- package/types/ui/Link/Anchor.d.ts +5 -3
- package/types/ui/Link/activePath.d.ts +13 -0
- package/types/ui/Link/index.d.ts +1 -1
- package/types/ui/Modal.d.ts +6 -1
- package/types/ui/Model/EditModal.d.ts +6 -1
- package/types/ui/Model/New.d.ts +6 -1
- package/types/ui/Model/ViewEditModal.d.ts +6 -1
- package/types/ui/Model/ViewModal.d.ts +6 -1
- package/types/ui/Model/newFormOrigin.d.ts +7 -0
- package/types/ui/Portal.d.ts +3 -1
- package/types/ui/Reference/origin.d.ts +12 -0
- package/types/ui/Signal/Doc.d.ts +23 -18
- package/types/ui/Signal/Listener.d.ts +2 -6
- package/types/ui/Signal/Object.d.ts +6 -10
- package/types/ui/Signal/Response.d.ts +3 -6
- package/types/ui/Signal/RestApi.d.ts +11 -15
- package/types/ui/Signal/WebSocket.d.ts +11 -21
- package/types/ui/Signal/endpointEntries.d.ts +8 -0
- package/types/ui/Signal/index.d.ts +35 -12
- package/types/ui/Signal/signalText.d.ts +178 -0
- package/types/ui/System/CSR.d.ts +2 -2
- package/types/ui/System/Client.d.ts +4 -3
- package/types/ui/System/Common.d.ts +27 -6
- package/types/ui/System/SSR.d.ts +6 -6
- package/types/ui/Tooltip.d.ts +4 -2
- package/types/ui/index.d.ts +2 -1
- package/types/ui/overlayLayer.d.ts +6 -0
- package/types/ui/overlayPosition.d.ts +17 -0
- package/types/vendor/@akanjs/native/packages/cli/src/api.d.ts +14 -3
- package/types/vendor/@akanjs/native/packages/cli/src/commands/update.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/cli/src/config.d.ts +30 -5
- package/types/vendor/@akanjs/native/packages/cli/src/lib/compat.d.ts +8 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.d.ts +5 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/distribution.d.ts +22 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/project.d.ts +4 -0
- package/types/vendor/@akanjs/native/packages/cli/src/lib/publish.d.ts +16 -10
- package/types/vendor/@akanjs/native/packages/cli/src/lib/updates.d.ts +51 -7
- package/types/vendor/@akanjs/native/packages/cli/src/lib/vectors.d.ts +1 -1
- package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.d.ts +12 -4
- package/types/vendor/@akanjs/native/packages/cli/src/platforms/windows.d.ts +5 -0
- package/types/vendor/@akanjs/native/packages/core/src/kernel.d.ts +36 -0
- package/types/vendor/@akanjs/native/packages/desktop/src/dispatcher.d.ts +6 -0
- package/types/vendor/@akanjs/native/packages/desktop/src/plugin.d.ts +2 -0
- package/types/vendor/@akanjs/native/packages/desktop/src/server.d.ts +11 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src/desktop.d.ts +17 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src/index.d.ts +132 -0
- package/types/vendor/@akanjs/native/plugins/external-view/src.d.ts +1 -0
- package/types/vendor/@akanjs/native/plugins/single-instance/src/socket.d.ts +1 -1
- package/types/vendor/@akanjs/native/plugins/updates/src/index.d.ts +8 -0
- package/types/vendor/@akanjs/native/plugins/window/src/desktop.d.ts +4 -1
- package/types/vendor/@akanjs/native/plugins/window/src/index.d.ts +49 -0
- package/types/vendor/use-agentic/AgentActivity.d.ts +3 -1
- package/types/vendor/use-agentic/AgenticSurface.d.ts +2 -2
- package/types/vendor/use-agentic/types.d.ts +2 -0
- package/types/webkit/CsrStack.d.ts +1 -1
- package/types/webkit/EmbeddedLayout.d.ts +10 -0
- package/types/webkit/McpAppAgent.d.ts +14 -0
- package/types/webkit/McpAppApprovals.d.ts +8 -0
- package/types/webkit/McpAppBridge.d.ts +101 -0
- package/types/webkit/McpAppView.d.ts +21 -0
- package/types/webkit/RenderLayer.d.ts +3 -1
- package/types/webkit/index.d.ts +1 -0
- package/types/webkit/useLocation.d.ts +1 -1
- package/types/webkit/useVisualViewportFrame.d.ts +13 -0
- package/ui/Agent/Chat.tsx +29 -10
- package/ui/Agent/Composer.tsx +2 -1
- package/ui/Agent/EmbedApproval.tsx +17 -0
- package/ui/Agent/RichInput.tsx +1 -1
- package/ui/Agent/index.ts +2 -0
- package/ui/Agent/useKeyboardFrame.ts +10 -0
- package/ui/Agent/useScreenActions.ts +45 -0
- package/ui/BottomSheet.tsx +82 -53
- package/ui/Constant/Doc.tsx +113 -60
- package/ui/Constant/index.ts +2 -2
- package/ui/Constant/schemaDoc.ts +86 -26
- package/ui/Dialog/LegacyModal.tsx +53 -52
- package/ui/Dialog/Modal.tsx +93 -49
- package/ui/Dialog/modalSurface.ts +61 -0
- package/ui/DragAction.tsx +1 -1
- package/ui/DraggableList.tsx +1 -1
- package/ui/Dropdown.tsx +4 -6
- package/ui/KeyboardAvoiding.tsx +1 -1
- package/ui/Layout/BottomInset.tsx +32 -16
- package/ui/Layout/BottomTab.tsx +42 -16
- package/ui/Layout/FrameSlotFallback.tsx +59 -0
- package/ui/Layout/Navbar.tsx +56 -15
- package/ui/Layout/Sider.tsx +1 -1
- package/ui/Layout/TopInset.tsx +22 -17
- package/ui/Link/Action.tsx +15 -5
- package/ui/Link/Anchor.tsx +25 -13
- package/ui/Link/activePath.ts +25 -0
- package/ui/Link/index.tsx +1 -1
- package/ui/Load/Pagination.tsx +1 -1
- package/ui/Load/Units.tsx +1 -1
- package/ui/Loading/ProgressBar.tsx +1 -1
- package/ui/Modal.tsx +9 -0
- package/ui/Model/EditModal.tsx +16 -1
- package/ui/Model/New.tsx +25 -2
- package/ui/Model/NewWrapper_Client.tsx +2 -0
- package/ui/Model/ViewEditModal.tsx +25 -10
- package/ui/Model/ViewModal.tsx +16 -1
- package/ui/Model/newFormOrigin.ts +45 -0
- package/ui/Popconfirm.tsx +5 -7
- package/ui/Portal.tsx +4 -7
- package/ui/Reference/origin.ts +31 -0
- package/ui/ScreenNavigator.tsx +1 -1
- package/ui/Select.tsx +14 -14
- package/ui/Signal/Arg.tsx +2 -2
- package/ui/Signal/Doc.tsx +125 -63
- package/ui/Signal/Endpoint.tsx +11 -10
- package/ui/Signal/Listener.tsx +7 -10
- package/ui/Signal/Object.tsx +7 -14
- package/ui/Signal/Response.tsx +24 -24
- package/ui/Signal/RestApi.tsx +63 -52
- package/ui/Signal/WebSocket.tsx +65 -72
- package/ui/Signal/endpointEntries.ts +25 -0
- package/ui/Signal/index.ts +23 -6
- package/ui/Signal/signalText.ts +59 -0
- package/ui/System/CSR.tsx +41 -9
- package/ui/System/Client.tsx +37 -5
- package/ui/System/Common.tsx +76 -32
- package/ui/System/SSR.tsx +73 -31
- package/ui/Toast.tsx +1 -1
- package/ui/Tooltip.tsx +84 -30
- package/ui/index.ts +11 -1
- package/ui/overlayLayer.ts +25 -2
- package/ui/overlayPosition.ts +90 -0
- package/ui/recipe/inputRecipe.ts +8 -1
- package/ui/styles.css +152 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeActivity.kt +22 -2
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeKernel.kt +37 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdateReports.kt +169 -0
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeUpdates.kt +110 -38
- package/vendor/@akanjs/native/native/android/src/com/akanjs/runtime/AkanNativeVectors.kt +20 -0
- package/vendor/@akanjs/native/native/desktop/Cargo.lock +16 -16
- package/vendor/@akanjs/native/native/desktop/src/external.rs +651 -0
- package/vendor/@akanjs/native/native/desktop/src/json.rs +23 -0
- package/vendor/@akanjs/native/native/desktop/src/lib.rs +228 -5
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeApp.swift +2 -1
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeKernel.swift +47 -1
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdateReports.swift +145 -0
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeUpdates.swift +112 -43
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeVectors.swift +18 -0
- package/vendor/@akanjs/native/native/ios/Sources/AkanNativeViewController.swift +24 -2
- package/vendor/@akanjs/native/packages/cli/src/api.ts +22 -6
- package/vendor/@akanjs/native/packages/cli/src/commands/build.ts +24 -8
- package/vendor/@akanjs/native/packages/cli/src/commands/update.ts +11 -9
- package/vendor/@akanjs/native/packages/cli/src/config.ts +35 -7
- package/vendor/@akanjs/native/packages/cli/src/lib/compat.ts +2 -0
- package/vendor/@akanjs/native/packages/cli/src/lib/configkeys.ts +6 -4
- package/vendor/@akanjs/native/packages/cli/src/lib/desktop-server.ts +11 -1
- package/vendor/@akanjs/native/packages/cli/src/lib/distribution.ts +73 -0
- package/vendor/@akanjs/native/packages/cli/src/lib/project.ts +15 -3
- package/vendor/@akanjs/native/packages/cli/src/lib/publish.ts +70 -23
- package/vendor/@akanjs/native/packages/cli/src/lib/updates.ts +159 -23
- package/vendor/@akanjs/native/packages/cli/src/lib/vectors.ts +2 -0
- package/vendor/@akanjs/native/packages/cli/src/platforms/android.ts +9 -4
- package/vendor/@akanjs/native/packages/cli/src/platforms/desktop-update.ts +1 -1
- package/vendor/@akanjs/native/packages/cli/src/platforms/desktop.ts +4 -2
- package/vendor/@akanjs/native/packages/cli/src/platforms/ios.ts +9 -4
- package/vendor/@akanjs/native/packages/cli/src/platforms/windows-installer.ts +60 -21
- package/vendor/@akanjs/native/packages/cli/src/platforms/windows.ts +14 -3
- package/vendor/@akanjs/native/packages/core/src/kernel.ts +49 -0
- package/vendor/@akanjs/native/packages/core/vectors/updates.json +31 -0
- package/vendor/@akanjs/native/packages/desktop/src/dispatcher.ts +13 -5
- package/vendor/@akanjs/native/packages/desktop/src/main.ts +3 -0
- package/vendor/@akanjs/native/packages/desktop/src/plugin.ts +2 -0
- package/vendor/@akanjs/native/packages/desktop/src/server.ts +70 -6
- package/vendor/@akanjs/native/plugins/external-view/native-plugin.json +23 -0
- package/vendor/@akanjs/native/plugins/external-view/src/desktop.ts +321 -0
- package/vendor/@akanjs/native/plugins/external-view/src/index.ts +106 -0
- package/vendor/@akanjs/native/plugins/single-instance/src/desktop.ts +4 -0
- package/vendor/@akanjs/native/plugins/single-instance/src/socket.ts +12 -5
- package/vendor/@akanjs/native/plugins/updates/android/UpdatesPlugin.kt +133 -41
- package/vendor/@akanjs/native/plugins/updates/ios/UpdatesPlugin.swift +101 -36
- package/vendor/@akanjs/native/plugins/updates/native-plugin.json +4 -2
- package/vendor/@akanjs/native/plugins/updates/src/desktop.ts +15 -1
- package/vendor/@akanjs/native/plugins/updates/src/index.ts +7 -1
- package/vendor/@akanjs/native/plugins/window/native-plugin.json +4 -0
- package/vendor/@akanjs/native/plugins/window/src/desktop.ts +68 -1
- package/vendor/@akanjs/native/plugins/window/src/index.ts +47 -0
- package/vendor/use-agentic/AgentActivity.tsx +4 -2
- package/vendor/use-agentic/AgenticSurface.ts +3 -3
- package/vendor/use-agentic/ToolRunner.ts +8 -1
- package/vendor/use-agentic/types.ts +2 -0
- package/webkit/CsrRouteTable.ts +9 -1
- package/webkit/CsrStack.ts +9 -2
- package/webkit/EmbeddedLayout.tsx +77 -0
- package/webkit/McpAppAgent.ts +138 -0
- package/webkit/McpAppApprovals.ts +56 -0
- package/webkit/McpAppBridge.ts +291 -0
- package/webkit/McpAppView.ts +258 -0
- package/webkit/RenderLayer.tsx +20 -5
- package/webkit/bootCsr.tsx +54 -5
- package/webkit/index.ts +1 -0
- package/webkit/useCsrValues.ts +16 -10
- package/webkit/useFrameRuntime.ts +10 -1
- package/webkit/useLocation.ts +4 -3
- package/webkit/useVisualViewportFrame.tsx +52 -0
- package/service/predefinedAdaptor/sql/driver/libsql.ts +0 -45
- package/types/service/predefinedAdaptor/sql/driver/libsql.d.ts +0 -20
- package/types/ui/Agent/useKeyboardInset.d.ts +0 -1
- package/types/ui/ServerPortal.d.ts +0 -15
- package/ui/Agent/useKeyboardInset.ts +0 -19
- 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
|
+
}
|
package/webkit/RenderLayer.tsx
CHANGED
|
@@ -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((
|
|
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
|
|
38
|
+
const below = isLast ? (
|
|
35
39
|
leaf
|
|
36
40
|
) : (
|
|
37
|
-
<RenderLayer
|
|
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)
|
|
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> {
|
package/webkit/bootCsr.tsx
CHANGED
|
@@ -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([
|
|
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 =
|
|
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
|
-
|
|
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";
|
package/webkit/useCsrValues.ts
CHANGED
|
@@ -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(
|
|
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")
|
|
880
|
-
else
|
|
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")
|
|
955
|
-
else
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1081
|
+
csrLocation.back();
|
|
1076
1082
|
},
|
|
1077
1083
|
};
|
|
1078
1084
|
window.onpopstate = async (event) => {
|
|
1079
|
-
const href =
|
|
1080
|
-
const entryId = (
|
|
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
|
-
|
|
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
|
|
package/webkit/useLocation.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import type
|
|
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
|
-
|
|
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 =
|
|
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
|
-
}
|