akanjs 3.0.0-alpha.2 → 3.0.0-alpha.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +1 -1
- package/README.md +1 -1
- package/base/symbols.ts +4 -0
- package/client/clientRuntime.ts +8 -0
- package/client/cn.ts +7 -0
- package/client/csrTypes.ts +2 -0
- package/client/frameDebug.ts +3 -2
- package/common/Logger.ts +4 -0
- package/common/deepObjectify.ts +21 -7
- package/common/index.ts +14 -0
- package/common/mcpExposure.ts +99 -0
- package/common/pathSet.ts +17 -5
- package/common/routeConvention.ts +30 -0
- package/constant/crystalize.ts +3 -1
- package/constant/fieldInfo.ts +6 -0
- package/constant/getDefault.ts +37 -9
- package/constant/immerify.ts +1 -1
- package/constant/index.ts +2 -0
- package/constant/labelOf.ts +17 -0
- package/constant/mask.ts +60 -0
- package/constant/purify.ts +17 -11
- package/constant/types.ts +14 -12
- package/dictionary/DictionaryLookup.ts +30 -0
- package/dictionary/agent.dictionary.ts +31 -0
- package/dictionary/agentTurn.dictionary.ts +29 -0
- package/dictionary/base.dictionary.ts +8 -0
- package/dictionary/dictInfo.ts +8 -0
- package/dictionary/dictionary.ts +9 -3
- package/dictionary/index.ts +1 -0
- package/document/database.ts +21 -0
- package/document/filterMeta.ts +7 -0
- package/document/index.ts +1 -0
- package/document/into.ts +7 -1
- package/document/noDocumentError.ts +12 -0
- package/fetch/agentTurn.ts +19 -0
- package/fetch/client/fetchClient.ts +71 -13
- package/fetch/client/wsClient.ts +17 -2
- package/fetch/fetchType/endpointFetch.type.ts +10 -4
- package/fetch/index.ts +1 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local.db-wal +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-shm +0 -0
- package/local/apps/serverLifecycle/serverLifecycle-local_solid.db-wal +0 -0
- package/package.json +11 -9
- package/server/akanApp.ts +30 -34
- package/server/akanOption.ts +65 -7
- package/server/akanServer.ts +172 -7
- package/server/animatedImage.ts +83 -0
- package/server/artifact/routeClientCache.ts +14 -0
- package/server/assetEncoding.ts +47 -0
- package/server/devtools/signalSerializer.ts +2 -2
- package/server/devtools/types.ts +2 -2
- package/server/di/diLifecycle.ts +31 -3
- package/server/di/predefinedAdaptor.ts +6 -0
- package/server/imageOptimizer.ts +33 -46
- package/server/index.ts +1 -2
- package/server/mcp/McpAuth.ts +177 -0
- package/server/mcp/McpDispatcher.ts +245 -0
- package/server/mcp/McpEventStream.ts +76 -0
- package/server/mcp/McpExecutionContext.ts +105 -0
- package/server/mcp/McpRouter.ts +591 -0
- package/server/mcp/index.ts +5 -0
- package/server/resolver/CascadeRunner.ts +1 -1
- package/server/resolver/database.resolver.ts +42 -5
- package/server/resolver/service.resolver.ts +29 -0
- package/server/resolver/signal.resolver.ts +16 -1
- package/server/routeTreeBuilder.ts +2 -44
- package/server/rscWorker.tsx +6 -2
- package/server/subRouteIndexDocument.tsx +61 -0
- package/server/systemPageDocument.tsx +2 -2
- package/server/systemPages.tsx +49 -16
- package/server/vendor/akanjs-fetch.ts +1 -0
- package/server/webRouter.ts +159 -35
- package/service/agent.service.ts +14 -0
- package/service/base.service.ts +2 -1
- package/service/index.ts +1 -0
- package/service/predefinedAdaptor/database.adaptor.ts +78 -24
- package/service/predefinedAdaptor/deepseekLlm.ts +230 -0
- package/service/predefinedAdaptor/index.ts +3 -0
- package/service/predefinedAdaptor/insightQuery.ts +183 -0
- package/service/predefinedAdaptor/llm.adaptor.ts +76 -0
- package/service/predefinedAdaptor/role.adaptor.ts +2 -0
- package/service/predefinedAdaptor/schedule.adaptor.ts +12 -5
- package/service/serviceModule.ts +17 -0
- package/service/types.ts +4 -0
- package/signal/agent/AgentCatalogue.ts +165 -0
- package/signal/agent/index.ts +1 -0
- package/signal/agent.signal.ts +42 -0
- package/signal/agentTurn.ts +8 -0
- package/signal/agentTurnStream.ts +48 -0
- package/signal/base.signal.ts +2 -1
- package/signal/endpointInfo.ts +38 -2
- package/signal/guard.ts +13 -1
- package/signal/guards.ts +52 -1
- package/signal/index.ts +6 -0
- package/signal/mcp/McpDocument.ts +286 -0
- package/signal/mcp/McpProgress.ts +106 -0
- package/signal/mcp/McpUriTemplate.ts +85 -0
- package/signal/mcp/Msg.ts +368 -0
- package/signal/mcp/index.ts +5 -0
- package/signal/mcp/mcpProtocol.ts +120 -0
- package/signal/middleware.ts +7 -3
- package/signal/openapi/openapi.ts +32 -171
- package/signal/schema/JsonSchemaBuilder.ts +211 -0
- package/signal/schema/index.ts +1 -0
- package/signal/signalContext.ts +85 -9
- package/signal/slice.ts +9 -9
- package/signal/types.ts +1 -1
- package/store/action.ts +11 -22
- package/store/actionTag.ts +33 -0
- package/store/agent/AgentBridge.ts +323 -0
- package/store/agent/AgentContext.ts +74 -0
- package/store/agent/AgentPrompts.ts +82 -0
- package/store/agent/AgentVisibility.ts +68 -0
- package/store/agent/ScreenReader.ts +208 -0
- package/store/agent/StoreCatalogue.ts +351 -0
- package/store/agent/StoreSurfaceSource.ts +122 -0
- package/store/agent/index.ts +9 -0
- package/store/agent/storeSurface.ts +28 -0
- package/store/agent/types.ts +50 -0
- package/store/agentic/StToolBuilder.ts +199 -0
- package/store/agentic/attachAgentic.ts +22 -0
- package/store/agentic/index.ts +5 -0
- package/store/agentic/readableValue.ts +17 -0
- package/store/agentic/useStExpose.ts +18 -0
- package/store/agentic/useStState.ts +35 -0
- package/store/baseSt.ts +7 -0
- package/store/databaseStateNames.ts +31 -0
- package/store/formSetterNames.ts +21 -0
- package/store/index.ts +8 -0
- package/store/rootStore.ts +3 -1
- package/store/sliceRole.ts +36 -0
- package/store/state.ts +2 -12
- package/store/store.ts +19 -1
- package/store/storeInstance.ts +165 -20
- package/store/storeRegistry.ts +16 -1
- package/store/types.ts +18 -0
- package/store/withSelector.ts +7 -8
- package/test/registerDom.ts +4 -0
- package/types/base/symbols.d.ts +4 -0
- package/types/client/cn.d.ts +5 -0
- package/types/client/csrTypes.d.ts +2 -0
- package/types/common/Logger.d.ts +2 -0
- package/types/common/deepObjectify.d.ts +4 -2
- package/types/common/index.d.ts +2 -1
- package/types/common/mcpExposure.d.ts +60 -0
- package/types/common/routeConvention.d.ts +8 -0
- package/types/constant/fieldInfo.d.ts +1 -0
- package/types/constant/getDefault.d.ts +5 -0
- package/types/constant/index.d.ts +2 -0
- package/types/constant/labelOf.d.ts +6 -0
- package/types/constant/mask.d.ts +34 -0
- package/types/constant/purify.d.ts +2 -2
- package/types/constant/types.d.ts +2 -2
- package/types/dictionary/DictionaryLookup.d.ts +14 -0
- package/types/dictionary/agent.dictionary.d.ts +1 -0
- package/types/dictionary/agentTurn.dictionary.d.ts +1 -0
- package/types/dictionary/base.dictionary.d.ts +1 -1
- package/types/dictionary/dictInfo.d.ts +6 -0
- package/types/dictionary/dictionary.d.ts +11 -9
- package/types/dictionary/index.d.ts +1 -0
- package/types/document/database.d.ts +20 -0
- package/types/document/filterMeta.d.ts +1 -0
- package/types/document/index.d.ts +1 -0
- package/types/document/into.d.ts +6 -1
- package/types/document/noDocumentError.d.ts +12 -0
- package/types/fetch/agentTurn.d.ts +593 -0
- package/types/fetch/client/fetchClient.d.ts +10 -0
- package/types/fetch/fetchType/endpointFetch.type.d.ts +5 -3
- package/types/fetch/index.d.ts +1 -0
- package/types/server/akanOption.d.ts +35 -7
- package/types/server/akanServer.d.ts +44 -1
- package/types/server/animatedImage.d.ts +7 -0
- package/types/server/artifact/routeClientCache.d.ts +6 -0
- package/types/server/assetEncoding.d.ts +7 -0
- package/types/server/devtools/types.d.ts +2 -2
- package/types/server/di/predefinedAdaptor.d.ts +4 -1
- package/types/server/index.d.ts +0 -2
- package/types/server/mcp/McpAuth.d.ts +62 -0
- package/types/server/mcp/McpDispatcher.d.ts +54 -0
- package/types/server/mcp/McpEventStream.d.ts +18 -0
- package/types/server/mcp/McpExecutionContext.d.ts +44 -0
- package/types/server/mcp/McpRouter.d.ts +60 -0
- package/types/server/mcp/index.d.ts +5 -0
- package/types/server/mcp.d.ts +1 -0
- package/types/server/subRouteIndexDocument.d.ts +8 -0
- package/types/server/systemPageDocument.d.ts +1 -0
- package/types/server/systemPages.d.ts +5 -0
- package/types/server/vendor/akanjs-fetch.d.ts +1 -0
- package/types/service/agent.service.d.ts +12 -0
- package/types/service/base.service.d.ts +4 -0
- package/types/service/index.d.ts +1 -0
- package/types/service/predefinedAdaptor/database.adaptor.d.ts +24 -2
- package/types/service/predefinedAdaptor/deepseekLlm.d.ts +64 -0
- package/types/service/predefinedAdaptor/index.d.ts +3 -0
- package/types/service/predefinedAdaptor/insightQuery.d.ts +50 -0
- package/types/service/predefinedAdaptor/llm.adaptor.d.ts +68 -0
- package/types/service/predefinedAdaptor/role.adaptor.d.ts +2 -0
- package/types/service/predefinedAdaptor/schedule.adaptor.d.ts +10 -6
- package/types/service/types.d.ts +4 -1
- package/types/signal/agent/AgentCatalogue.d.ts +100 -0
- package/types/signal/agent/index.d.ts +1 -0
- package/types/signal/agent.d.ts +1 -0
- package/types/signal/agent.signal.d.ts +29 -0
- package/types/signal/agentTurn.d.ts +9 -0
- package/types/signal/agentTurnStream.d.ts +16 -0
- package/types/signal/base.signal.d.ts +1 -1
- package/types/signal/endpointInfo.d.ts +21 -3
- package/types/signal/guard.d.ts +11 -0
- package/types/signal/guards.d.ts +22 -1
- package/types/signal/index.d.ts +6 -0
- package/types/signal/mcp/McpDocument.d.ts +65 -0
- package/types/signal/mcp/McpProgress.d.ts +40 -0
- package/types/signal/mcp/McpUriTemplate.d.ts +26 -0
- package/types/signal/mcp/Msg.d.ts +143 -0
- package/types/signal/mcp/index.d.ts +5 -0
- package/types/signal/mcp/mcpProtocol.d.ts +107 -0
- package/types/signal/mcp.d.ts +1 -0
- package/types/signal/openapi/openapi.d.ts +3 -3
- package/types/signal/schema/JsonSchemaBuilder.d.ts +47 -0
- package/types/signal/schema/index.d.ts +1 -0
- package/types/signal/schema.d.ts +1 -0
- package/types/signal/signalContext.d.ts +16 -1
- package/types/signal/slice.d.ts +8 -8
- package/types/signal/types.d.ts +1 -1
- package/types/store/actionTag.d.ts +21 -0
- package/types/store/agent/AgentBridge.d.ts +77 -0
- package/types/store/agent/AgentContext.d.ts +16 -0
- package/types/store/agent/AgentPrompts.d.ts +31 -0
- package/types/store/agent/AgentVisibility.d.ts +21 -0
- package/types/store/agent/ScreenReader.d.ts +11 -0
- package/types/store/agent/StoreCatalogue.d.ts +26 -0
- package/types/store/agent/StoreSurfaceSource.d.ts +18 -0
- package/types/store/agent/index.d.ts +9 -0
- package/types/store/agent/storeSurface.d.ts +14 -0
- package/types/store/agent/types.d.ts +49 -0
- package/types/store/agent.d.ts +1 -0
- package/types/store/agentic/StToolBuilder.d.ts +41 -0
- package/types/store/agentic/attachAgentic.d.ts +14 -0
- package/types/store/agentic/index.d.ts +5 -0
- package/types/store/agentic/readableValue.d.ts +3 -0
- package/types/store/agentic/useStExpose.d.ts +8 -0
- package/types/store/agentic/useStState.d.ts +12 -0
- package/types/store/agentic.d.ts +1 -0
- package/types/store/baseSt.d.ts +29 -22
- package/types/store/databaseStateNames.d.ts +25 -0
- package/types/store/formSetterNames.d.ts +16 -0
- package/types/store/index.d.ts +7 -0
- package/types/store/rootStore.d.ts +5 -1
- package/types/store/sliceRole.d.ts +25 -0
- package/types/store/store.d.ts +7 -2
- package/types/store/storeInstance.d.ts +33 -1
- package/types/store/storeRegistry.d.ts +5 -0
- package/types/store/types.d.ts +15 -0
- package/types/store/withSelector.d.ts +7 -8
- package/types/test/registerDom.d.ts +1 -0
- package/types/ui/Agent/Approval.d.ts +7 -0
- package/types/ui/Agent/Bubble.d.ts +13 -0
- package/types/ui/Agent/Chat.d.ts +24 -0
- package/types/ui/Agent/Context.d.ts +9 -0
- package/types/ui/Agent/Dock.d.ts +16 -0
- package/types/ui/Agent/Guide.d.ts +9 -0
- package/types/ui/Agent/Section.d.ts +10 -0
- package/types/ui/Agent/StateKey.d.ts +16 -0
- package/types/ui/Agent/Tool.d.ts +15 -0
- package/types/ui/Agent/Transcript.d.ts +13 -0
- package/types/ui/Agent/Zone.d.ts +23 -0
- package/types/ui/Agent/fetchRunner.d.ts +12 -0
- package/types/ui/Agent/index.d.ts +17 -0
- package/types/ui/Agent/index_.d.ts +1 -0
- package/types/ui/Agent/sessionHistory.d.ts +12 -0
- package/types/ui/Agent.d.ts +1 -0
- package/types/ui/Badge.d.ts +1 -1
- package/types/ui/Button.d.ts +1 -1
- package/types/ui/CsrImage.d.ts +1 -1
- package/types/ui/Dropdown.d.ts +2 -0
- package/types/ui/Image.d.ts +3 -3
- package/types/ui/Layout/BottomInset.d.ts +2 -1
- package/types/ui/Layout/index.d.ts +1 -1
- package/types/ui/Signal/style.d.ts +4 -1
- package/types/ui/UiOverride/context.d.ts +14 -2
- package/types/ui/agentAttrs.d.ts +14 -0
- package/types/ui/index.d.ts +4 -1
- package/types/ui/overlayLayer.d.ts +24 -0
- package/types/ui/recipe/badgeRecipe.d.ts +7 -3
- package/types/ui/recipe/buttonRecipe.d.ts +7 -3
- package/types/ui/recipe/inputRecipe.d.ts +5 -1
- package/types/vendor/use-agentic/AgentProvider.d.ts +18 -0
- package/types/vendor/use-agentic/AgentScope.d.ts +9 -0
- package/types/vendor/use-agentic/AgentSession.d.ts +50 -0
- package/types/vendor/use-agentic/Agentic.d.ts +21 -0
- package/types/vendor/use-agentic/AgenticSurface.d.ts +44 -0
- package/types/vendor/use-agentic/httpRunner.d.ts +15 -0
- package/types/vendor/use-agentic/index.d.ts +13 -0
- package/types/vendor/use-agentic/surfaceContext.d.ts +5 -0
- package/types/vendor/use-agentic/types.d.ts +133 -0
- package/types/vendor/use-agentic/useAgent.d.ts +4 -0
- package/types/vendor/use-agentic/useAgentGuide.d.ts +6 -0
- package/types/vendor/use-agentic/useAgentResource.d.ts +7 -0
- package/types/vendor/use-agentic/useAgentState.d.ts +13 -0
- package/types/vendor/use-agentic/useAgentTool.d.ts +16 -0
- package/types/vendor/use-agentic.d.ts +1 -0
- package/types/webkit/index.d.ts +3 -0
- package/types/webkit/lazy.d.ts +12 -0
- package/types/webkit/useCsrValues.d.ts +3 -3
- package/types/webkit/useEscapeKey.d.ts +5 -0
- package/types/webkit/useFrameRuntime.d.ts +6 -1
- package/types/webkit/useScreenScope.d.ts +18 -0
- package/ui/Agent/Approval.tsx +29 -0
- package/ui/Agent/Bubble.tsx +74 -0
- package/ui/Agent/Chat.tsx +225 -0
- package/ui/Agent/Context.tsx +38 -0
- package/ui/Agent/Dock.tsx +72 -0
- package/ui/Agent/Guide.tsx +16 -0
- package/ui/Agent/Section.tsx +24 -0
- package/ui/Agent/StateKey.tsx +44 -0
- package/ui/Agent/Tool.tsx +66 -0
- package/ui/Agent/Transcript.tsx +33 -0
- package/ui/Agent/Zone.tsx +48 -0
- package/ui/Agent/fetchRunner.ts +30 -0
- package/ui/Agent/index.ts +12 -0
- package/ui/Agent/index_.tsx +4 -0
- package/ui/Agent/sessionHistory.ts +33 -0
- package/ui/Badge.tsx +3 -3
- package/ui/BottomSheet.tsx +5 -0
- package/ui/Button.tsx +6 -2
- package/ui/Constant/Doc.tsx +1 -1
- package/ui/CsrImage.tsx +1 -1
- package/ui/Data/ListContainer.tsx +1 -1
- package/ui/DatePicker.tsx +5 -4
- package/ui/Dialog/Modal.tsx +12 -15
- package/ui/DraggableList.tsx +3 -1
- package/ui/Dropdown.tsx +33 -11
- package/ui/Field.tsx +30 -16
- package/ui/Image.tsx +3 -3
- package/ui/Input.tsx +14 -5
- package/ui/Layout/BottomInset.tsx +6 -1
- package/ui/Load/Units.tsx +11 -2
- package/ui/Load/View.tsx +7 -2
- package/ui/Loading/ProgressBar.tsx +8 -1
- package/ui/Menu.tsx +7 -8
- package/ui/Model/EditModal.tsx +37 -2
- package/ui/Model/SureToRemove.tsx +2 -1
- package/ui/Model/index_.tsx +42 -15
- package/ui/ObjectId.tsx +3 -4
- package/ui/Pagination.tsx +3 -4
- package/ui/Popconfirm.tsx +8 -7
- package/ui/Select.tsx +6 -4
- package/ui/Signal/RestApi.tsx +43 -16
- package/ui/Signal/WebSocket.tsx +1 -1
- package/ui/Signal/style.ts +6 -1
- package/ui/Switch.tsx +2 -0
- package/ui/System/CSR.tsx +6 -2
- package/ui/System/ThemeToggle.tsx +55 -33
- package/ui/ToggleSelect.tsx +4 -3
- package/ui/Tooltip.tsx +2 -1
- package/ui/UiOverride/context.ts +14 -2
- package/ui/agentAttrs.ts +19 -0
- package/ui/index.ts +10 -1
- package/ui/overlayLayer.ts +39 -0
- package/ui/recipe/badgeRecipe.ts +22 -3
- package/ui/recipe/buttonRecipe.ts +51 -2
- package/ui/recipe/factory.ts +2 -2
- package/ui/recipe/inputRecipe.ts +18 -4
- package/vendor/use-agentic/AgentProvider.tsx +39 -0
- package/vendor/use-agentic/AgentScope.tsx +20 -0
- package/vendor/use-agentic/AgentSession.ts +292 -0
- package/vendor/use-agentic/Agentic.tsx +44 -0
- package/vendor/use-agentic/AgenticSurface.ts +294 -0
- package/vendor/use-agentic/WIRE.md +57 -0
- package/vendor/use-agentic/httpRunner.ts +115 -0
- package/vendor/use-agentic/index.ts +14 -0
- package/vendor/use-agentic/surfaceContext.ts +9 -0
- package/vendor/use-agentic/types.ts +144 -0
- package/vendor/use-agentic/useAgent.ts +17 -0
- package/vendor/use-agentic/useAgentGuide.ts +16 -0
- package/vendor/use-agentic/useAgentResource.ts +30 -0
- package/vendor/use-agentic/useAgentState.ts +61 -0
- package/vendor/use-agentic/useAgentTool.ts +55 -0
- package/webkit/bootCsr.tsx +2 -33
- package/webkit/index.ts +3 -0
- package/webkit/lazy.tsx +27 -3
- package/webkit/useCsrValues.ts +121 -4
- package/webkit/useEscapeKey.tsx +42 -0
- package/webkit/useFrameRuntime.ts +65 -32
- package/webkit/useScreenScope.tsx +49 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useScopePath, useSurface } from "./surfaceContext";
|
|
4
|
+
|
|
5
|
+
export interface AgentResourceMeta {
|
|
6
|
+
description?: string;
|
|
7
|
+
serialize?: (value: unknown) => unknown;
|
|
8
|
+
report?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Publishes a read-only view of any value the component already holds — derived totals, statuses, labels. */
|
|
12
|
+
export const useAgentResource = (name: string, value: unknown, meta: AgentResourceMeta = {}): void => {
|
|
13
|
+
const surface = useSurface();
|
|
14
|
+
const scope = useScopePath();
|
|
15
|
+
const live = useRef({ value, meta });
|
|
16
|
+
live.current = { value, meta };
|
|
17
|
+
const scopeKey = scope.join(".");
|
|
18
|
+
useEffect(() => {
|
|
19
|
+
const { meta: declared } = live.current;
|
|
20
|
+
return surface.registerResource(scope, {
|
|
21
|
+
name,
|
|
22
|
+
description: declared.description,
|
|
23
|
+
report: declared.report,
|
|
24
|
+
read: () => {
|
|
25
|
+
const { value: current, meta: currentMeta } = live.current;
|
|
26
|
+
return currentMeta.serialize ? currentMeta.serialize(current) : current;
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
}, [surface, scopeKey, name]);
|
|
30
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { type Dispatch, type SetStateAction, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { useScopePath, useSurface } from "./surfaceContext";
|
|
4
|
+
import type { JsonSchema } from "./types";
|
|
5
|
+
|
|
6
|
+
export interface AgentStateMeta<T> {
|
|
7
|
+
description?: string;
|
|
8
|
+
serialize?: (value: T) => unknown;
|
|
9
|
+
report?: boolean;
|
|
10
|
+
/** Schema of the setter's `value` argument. Without it the state is published read-only. */
|
|
11
|
+
set?: JsonSchema;
|
|
12
|
+
/** Validates or coerces the setter's incoming `value` — throw to refuse the write. Nothing else enforces `set`. */
|
|
13
|
+
parse?: (value: unknown) => T;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/** `useState` the agent can read — and write only through a named setter tool, and only when `set` declares one. */
|
|
17
|
+
export const useAgentState = <T>(
|
|
18
|
+
name: string,
|
|
19
|
+
initial: T | (() => T),
|
|
20
|
+
meta: AgentStateMeta<T> = {},
|
|
21
|
+
): [T, Dispatch<SetStateAction<T>>] => {
|
|
22
|
+
const surface = useSurface();
|
|
23
|
+
const scope = useScopePath();
|
|
24
|
+
const [value, setValue] = useState(initial);
|
|
25
|
+
const live = useRef({ value, meta });
|
|
26
|
+
live.current = { value, meta };
|
|
27
|
+
const scopeKey = scope.join(".");
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
const { meta: declared } = live.current;
|
|
30
|
+
return surface.registerResource(scope, {
|
|
31
|
+
name,
|
|
32
|
+
description: declared.description,
|
|
33
|
+
report: declared.report,
|
|
34
|
+
read: () => {
|
|
35
|
+
const { value: current, meta: currentMeta } = live.current;
|
|
36
|
+
return currentMeta.serialize ? currentMeta.serialize(current) : current;
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
}, [surface, scopeKey, name]);
|
|
40
|
+
const writable = !!meta.set;
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
const { meta: declared } = live.current;
|
|
43
|
+
if (!declared.set) return;
|
|
44
|
+
return surface.registerTool(scope, {
|
|
45
|
+
name: `set${name.charAt(0).toUpperCase()}${name.slice(1)}`,
|
|
46
|
+
description: `Set ${name}.${declared.description ? ` ${declared.description}` : ""}`,
|
|
47
|
+
parameters: {
|
|
48
|
+
type: "object",
|
|
49
|
+
properties: { value: declared.set },
|
|
50
|
+
required: ["value"],
|
|
51
|
+
additionalProperties: false,
|
|
52
|
+
},
|
|
53
|
+
effect: "state",
|
|
54
|
+
run: (args) => {
|
|
55
|
+
const parse = live.current.meta.parse;
|
|
56
|
+
setValue(parse ? parse(args.value) : (args.value as T));
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
}, [surface, scopeKey, name, writable]);
|
|
60
|
+
return [value, setValue];
|
|
61
|
+
};
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
import { useScopePath, useSurface } from "./surfaceContext";
|
|
4
|
+
import type { JsonSchema, ToolConfirm, ToolEffect, ToolGuard } from "./types";
|
|
5
|
+
|
|
6
|
+
export interface AgentToolMeta {
|
|
7
|
+
description?: string;
|
|
8
|
+
parameters?: JsonSchema;
|
|
9
|
+
effect?: ToolEffect;
|
|
10
|
+
confirm?: ToolConfirm;
|
|
11
|
+
guard?: ToolGuard;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Publishes one tool for as long as the component is mounted.
|
|
16
|
+
*
|
|
17
|
+
* Identity is the scoped name, not the callback — `run`, `guard`, and `confirm` read the latest render through a
|
|
18
|
+
* ref, so there is no dependency array and no stale closure for the agent to hit. `description` and `parameters`
|
|
19
|
+
* are declaration data, read once at mount.
|
|
20
|
+
*/
|
|
21
|
+
export const useAgentTool = <Args extends Record<string, unknown> = Record<string, unknown>, Result = unknown>(
|
|
22
|
+
name: string,
|
|
23
|
+
meta: AgentToolMeta,
|
|
24
|
+
run: (args: Args) => Result | Promise<Result>,
|
|
25
|
+
): ((args?: Args) => Promise<Result>) => {
|
|
26
|
+
const surface = useSurface();
|
|
27
|
+
const scope = useScopePath();
|
|
28
|
+
const live = useRef({ meta, run });
|
|
29
|
+
live.current = { meta, run };
|
|
30
|
+
const call = useRef<((args?: Args) => Promise<Result>) | null>(null);
|
|
31
|
+
if (!call.current) call.current = async (args = {} as Args) => await live.current.run(args);
|
|
32
|
+
const scopeKey = scope.join(".");
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
const { meta: declared } = live.current;
|
|
35
|
+
return surface.registerTool(scope, {
|
|
36
|
+
name,
|
|
37
|
+
description: declared.description,
|
|
38
|
+
parameters: declared.parameters,
|
|
39
|
+
effect: declared.effect,
|
|
40
|
+
...(declared.confirm === undefined
|
|
41
|
+
? {}
|
|
42
|
+
: {
|
|
43
|
+
confirm: (args: Record<string, unknown>) => {
|
|
44
|
+
const confirm = live.current.meta.confirm;
|
|
45
|
+
return typeof confirm === "function" ? confirm(args) : (confirm ?? false);
|
|
46
|
+
},
|
|
47
|
+
}),
|
|
48
|
+
...(declared.guard === undefined
|
|
49
|
+
? {}
|
|
50
|
+
: { guard: (args: Record<string, unknown>) => live.current.meta.guard?.(args) ?? true }),
|
|
51
|
+
run: (args) => live.current.run(args as Args),
|
|
52
|
+
});
|
|
53
|
+
}, [surface, scopeKey, name]);
|
|
54
|
+
return call.current;
|
|
55
|
+
};
|
package/webkit/bootCsr.tsx
CHANGED
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
} from "akanjs/client";
|
|
19
19
|
import {
|
|
20
20
|
assertUniqueRoutePatterns,
|
|
21
|
+
getRouteExports,
|
|
21
22
|
Logger,
|
|
22
23
|
parseAkanI18nEnv,
|
|
23
24
|
parseBasePaths,
|
|
@@ -303,39 +304,7 @@ function validateRouteModuleExports(key: string, mod: RouteModule) {
|
|
|
303
304
|
if (!mod.default) throw new Error(`[route-convention] ${key} generated override wrapper has no default export`);
|
|
304
305
|
return;
|
|
305
306
|
}
|
|
306
|
-
const allowed =
|
|
307
|
-
parsed.kind === "page"
|
|
308
|
-
? new Set(["default", "pageConfig", "head", "metadata", "generateHead", "generateMetadata", "Loading"])
|
|
309
|
-
: parsed.isInternalRootLayout
|
|
310
|
-
? new Set([
|
|
311
|
-
"default",
|
|
312
|
-
"pageConfig",
|
|
313
|
-
"head",
|
|
314
|
-
"metadata",
|
|
315
|
-
"generateHead",
|
|
316
|
-
"generateMetadata",
|
|
317
|
-
"fonts",
|
|
318
|
-
"manifest",
|
|
319
|
-
"theme",
|
|
320
|
-
"reconnect",
|
|
321
|
-
"wsConnect",
|
|
322
|
-
"layoutStyle",
|
|
323
|
-
"gaTrackingId",
|
|
324
|
-
"Loading",
|
|
325
|
-
"NotFound",
|
|
326
|
-
"Error",
|
|
327
|
-
])
|
|
328
|
-
: new Set([
|
|
329
|
-
"default",
|
|
330
|
-
"pageConfig",
|
|
331
|
-
"head",
|
|
332
|
-
"metadata",
|
|
333
|
-
"generateHead",
|
|
334
|
-
"generateMetadata",
|
|
335
|
-
"Loading",
|
|
336
|
-
"NotFound",
|
|
337
|
-
"Error",
|
|
338
|
-
]);
|
|
307
|
+
const allowed = getRouteExports(parsed.kind, { rootLayout: parsed.isInternalRootLayout });
|
|
339
308
|
for (const exportName of Object.keys(mod)) {
|
|
340
309
|
if (!allowed.has(exportName)) {
|
|
341
310
|
throw new Error(`[route-convention] unsupported export "${exportName}" in ${key}`);
|
package/webkit/index.ts
CHANGED
|
@@ -8,9 +8,12 @@ export { useCodepush } from "./useCodepush";
|
|
|
8
8
|
export { useContact } from "./useContact";
|
|
9
9
|
export { useCsrValues } from "./useCsrValues";
|
|
10
10
|
export { useDebounce } from "./useDebounce";
|
|
11
|
+
export { useEscapeKey } from "./useEscapeKey";
|
|
11
12
|
export { useFetch, useFetchFn } from "./useFetch";
|
|
12
13
|
export { useGeoLocation } from "./useGeoLocation";
|
|
13
14
|
export { useHistory } from "./useHistory";
|
|
14
15
|
export { useInterval } from "./useInterval";
|
|
15
16
|
export { useLocation } from "./useLocation";
|
|
17
|
+
export type { ScreenScopeItem } from "./useScreenScope";
|
|
18
|
+
export { useScreenScope } from "./useScreenScope";
|
|
16
19
|
export { useThrottle } from "./useThrottle";
|
package/webkit/lazy.tsx
CHANGED
|
@@ -5,7 +5,18 @@ import { forwardRef, lazy as reactLazy } from "react";
|
|
|
5
5
|
|
|
6
6
|
const isServer = typeof window === "undefined";
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
/**
|
|
9
|
+
* `suspense` puts a Suspense boundary around the lazy component, so a chunk that resolves *after* the
|
|
10
|
+
* page is painted — a modal body, a dropdown menu — suspends only itself. Without one the suspension
|
|
11
|
+
* travels up to the nearest boundary, which is the route (`server/routeElementComposer.tsx`), and the
|
|
12
|
+
* whole page repaints as its loading fallback on the first open.
|
|
13
|
+
*
|
|
14
|
+
* It is opt-in rather than the default because a boundary also changes server rendering: under the
|
|
15
|
+
* default `renderMode: "stream"` the boundary's subtree leaves the shell as `loading` and arrives later
|
|
16
|
+
* in the stream. That is fine for an interaction shell and wrong for a page body, which SEO snapshots,
|
|
17
|
+
* prerendering and pre-hydration E2E read out of the shell.
|
|
18
|
+
*/
|
|
19
|
+
type LazyOption = { ssr?: boolean; suspense?: boolean; loading?: () => ReactNode };
|
|
9
20
|
type LazyProps = Record<string, unknown>;
|
|
10
21
|
type LoadedOf<Loaded> = Loaded extends { default: infer T } ? T : Loaded;
|
|
11
22
|
type LazyModule = { default: ComponentType<LazyProps> };
|
|
@@ -27,7 +38,16 @@ export const lazy = <Loaded,>(loader: () => Promise<Loaded>, option?: LazyOption
|
|
|
27
38
|
const LazyInner = reactLazy(async () => normalizeLazyModule(await loader()));
|
|
28
39
|
|
|
29
40
|
if (!ssrFalse) {
|
|
30
|
-
|
|
41
|
+
|
|
42
|
+
const Wrapper = forwardRef<unknown, LazyProps>((props, ref) =>
|
|
43
|
+
option?.suspense ? (
|
|
44
|
+
<React.Suspense fallback={renderFallback()}>
|
|
45
|
+
<LazyInner {...props} ref={ref as never} />
|
|
46
|
+
</React.Suspense>
|
|
47
|
+
) : (
|
|
48
|
+
<LazyInner {...props} ref={ref as never} />
|
|
49
|
+
),
|
|
50
|
+
);
|
|
31
51
|
Wrapper.displayName = "LazyWrapper";
|
|
32
52
|
return Wrapper as unknown as LoadedOf<Loaded>;
|
|
33
53
|
}
|
|
@@ -36,7 +56,11 @@ export const lazy = <Loaded,>(loader: () => Promise<Loaded>, option?: LazyOption
|
|
|
36
56
|
const [mounted, setMounted] = React.useState(false);
|
|
37
57
|
React.useEffect(() => setMounted(true), []);
|
|
38
58
|
if (!mounted) return <>{renderFallback()}</>;
|
|
39
|
-
return
|
|
59
|
+
return (
|
|
60
|
+
<React.Suspense fallback={renderFallback()}>
|
|
61
|
+
<LazyInner {...props} ref={ref as never} />
|
|
62
|
+
</React.Suspense>
|
|
63
|
+
);
|
|
40
64
|
});
|
|
41
65
|
Gate.displayName = "LazySsrFalseGate";
|
|
42
66
|
return Gate as unknown as LoadedOf<Loaded>;
|
package/webkit/useCsrValues.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type NavigationIntent,
|
|
14
14
|
normalizeDeepLinkHref,
|
|
15
15
|
type PageState,
|
|
16
|
+
type PageTransition,
|
|
16
17
|
type PathRoute,
|
|
17
18
|
type RouteGuide,
|
|
18
19
|
type RouteOptions,
|
|
@@ -23,13 +24,14 @@ import {
|
|
|
23
24
|
} from "akanjs/client";
|
|
24
25
|
import { loadCapacitorApp } from "akanjs/client/capacitor";
|
|
25
26
|
import { parseAkanI18nEnv, parseBasePaths } from "akanjs/common";
|
|
26
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
27
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
27
28
|
import {
|
|
28
29
|
createFrameSnapshot,
|
|
29
30
|
createTransitionPlan,
|
|
30
31
|
FRAME_Z_INDEX,
|
|
31
32
|
getFramePlatformProfile,
|
|
32
33
|
getFrameSlotsForSnapshot,
|
|
34
|
+
hasBottomAnchoredKeyboardSlot,
|
|
33
35
|
hasKeyboardStickySlot,
|
|
34
36
|
isPendingFrameReady,
|
|
35
37
|
PENDING_FRAME_READY_TIMEOUT_MS,
|
|
@@ -492,7 +494,6 @@ const useStackTrans = (routeState: RouteState): UseCsrTransition => {
|
|
|
492
494
|
gestureIntent.current = "pending";
|
|
493
495
|
stackBackConfigRef.current = null;
|
|
494
496
|
unlockPageScroll();
|
|
495
|
-
void Device.getDevice().hideKeyboard();
|
|
496
497
|
}
|
|
497
498
|
if (ix > initThreshold) {
|
|
498
499
|
gestureIntent.current = "scroll";
|
|
@@ -511,6 +512,7 @@ const useStackTrans = (routeState: RouteState): UseCsrTransition => {
|
|
|
511
512
|
if (absX < GESTURE_INTENT_THRESHOLD || absX <= absY * GESTURE_AXIS_LOCK_RATIO) return;
|
|
512
513
|
gestureIntent.current = "gesture";
|
|
513
514
|
lockPageScroll();
|
|
515
|
+
void Device.getDevice().hideKeyboard();
|
|
514
516
|
}
|
|
515
517
|
if (gestureIntent.current !== "gesture") {
|
|
516
518
|
cancel();
|
|
@@ -668,11 +670,11 @@ const useBottomUpTrans = (routeState: RouteState): UseCsrTransition => {
|
|
|
668
670
|
|
|
669
671
|
const pageBind = useDrag(
|
|
670
672
|
({ first, last, movement: [, my], initial: [, iy], cancel }) => {
|
|
671
|
-
if (first) void Device.getDevice().hideKeyboard();
|
|
672
673
|
if (iy > initThreshold) {
|
|
673
674
|
cancel();
|
|
674
675
|
return;
|
|
675
676
|
}
|
|
677
|
+
if (first) void Device.getDevice().hideKeyboard();
|
|
676
678
|
if (my < transUnitRange[1]) void transUnit.start(transUnitRange[1], { immediate: true });
|
|
677
679
|
else if (my > transUnitRange[0]) void transUnit.start(transUnitRange[0], { immediate: true });
|
|
678
680
|
else if (!last) void transUnit.start(my, { immediate: true });
|
|
@@ -864,6 +866,7 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
|
|
|
864
866
|
const resolvedPendingLocation = resolveLocationWithFrameState(pendingLocation, resolvedPathRouteMap);
|
|
865
867
|
const platformProfile = getFramePlatformProfile();
|
|
866
868
|
const accessoryHeight = resolveKeyboardAccessoryHeight(resolvedLocation.pathRoute.path, frameSlots);
|
|
869
|
+
const shouldAnchorContentBottom = hasBottomAnchoredKeyboardSlot(resolvedLocation.pathRoute.path, frameSlots);
|
|
867
870
|
const keyboardFrame = useKeyboardFrame({
|
|
868
871
|
bottomSafeArea: resolvedLocation.pathRoute.pageState.bottomSafeArea,
|
|
869
872
|
sticky: hasKeyboardStickySlot(resolvedLocation.pathRoute.path, frameSlots),
|
|
@@ -881,6 +884,103 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
|
|
|
881
884
|
}),
|
|
882
885
|
[viewport, keyboardFrame, accessoryHeight, resolvedLocation.pathRoute.pageState.bottomSafeArea],
|
|
883
886
|
);
|
|
887
|
+
const contentBottomAnchorRef = useRef<{
|
|
888
|
+
path: string;
|
|
889
|
+
contentViewportHeight: number;
|
|
890
|
+
bottomDistance: number;
|
|
891
|
+
} | null>(null);
|
|
892
|
+
const contentBottomAnchorRafRef = useRef<number | null>(null);
|
|
893
|
+
useLayoutEffect(() => {
|
|
894
|
+
const element = pageContentRef.current;
|
|
895
|
+
const path = resolvedLocation.pathRoute.path;
|
|
896
|
+
if (!element || !shouldAnchorContentBottom || !keyboardFrame.sticky) {
|
|
897
|
+
contentBottomAnchorRef.current = null;
|
|
898
|
+
if (contentBottomAnchorRafRef.current !== null) {
|
|
899
|
+
cancelAnimationFrame(contentBottomAnchorRafRef.current);
|
|
900
|
+
contentBottomAnchorRafRef.current = null;
|
|
901
|
+
}
|
|
902
|
+
return;
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
const previous = contentBottomAnchorRef.current;
|
|
906
|
+
const currentBottomDistance = Math.max(0, element.scrollHeight - element.scrollTop - element.clientHeight);
|
|
907
|
+
if (!previous || previous.path !== path) {
|
|
908
|
+
contentBottomAnchorRef.current = {
|
|
909
|
+
path,
|
|
910
|
+
contentViewportHeight: keyboardLayout.contentViewport.height,
|
|
911
|
+
bottomDistance: currentBottomDistance,
|
|
912
|
+
};
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
const shouldRestoreBottom = previous.contentViewportHeight !== keyboardLayout.contentViewport.height;
|
|
917
|
+
const bottomDistance = previous.bottomDistance;
|
|
918
|
+
if (shouldRestoreBottom) {
|
|
919
|
+
const nextScrollTop = Math.max(0, element.scrollHeight - element.clientHeight - bottomDistance);
|
|
920
|
+
if (Math.abs(element.scrollTop - nextScrollTop) > 1) {
|
|
921
|
+
debugFrame("keyboard.contentAnchor", {
|
|
922
|
+
path,
|
|
923
|
+
from: element.scrollTop,
|
|
924
|
+
to: nextScrollTop,
|
|
925
|
+
bottomDistance,
|
|
926
|
+
contentViewportHeight: keyboardLayout.contentViewport.height,
|
|
927
|
+
});
|
|
928
|
+
element.scrollTop = nextScrollTop;
|
|
929
|
+
}
|
|
930
|
+
const startedAt = performance.now();
|
|
931
|
+
const duration = (keyboardFrame.animationDuration ?? 0) + 50;
|
|
932
|
+
const keepBottomAnchored = () => {
|
|
933
|
+
const scrollTop = Math.max(0, element.scrollHeight - element.clientHeight - bottomDistance);
|
|
934
|
+
if (Math.abs(element.scrollTop - scrollTop) > 1) element.scrollTop = scrollTop;
|
|
935
|
+
if (performance.now() - startedAt < duration) {
|
|
936
|
+
contentBottomAnchorRafRef.current = requestAnimationFrame(keepBottomAnchored);
|
|
937
|
+
} else {
|
|
938
|
+
contentBottomAnchorRafRef.current = null;
|
|
939
|
+
}
|
|
940
|
+
};
|
|
941
|
+
if (contentBottomAnchorRafRef.current !== null) cancelAnimationFrame(contentBottomAnchorRafRef.current);
|
|
942
|
+
contentBottomAnchorRafRef.current = requestAnimationFrame(keepBottomAnchored);
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
contentBottomAnchorRef.current = {
|
|
946
|
+
path,
|
|
947
|
+
contentViewportHeight: keyboardLayout.contentViewport.height,
|
|
948
|
+
bottomDistance: Math.max(0, element.scrollHeight - element.scrollTop - element.clientHeight),
|
|
949
|
+
};
|
|
950
|
+
return () => {
|
|
951
|
+
if (contentBottomAnchorRafRef.current !== null) {
|
|
952
|
+
cancelAnimationFrame(contentBottomAnchorRafRef.current);
|
|
953
|
+
contentBottomAnchorRafRef.current = null;
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
}, [
|
|
957
|
+
pageContentRef,
|
|
958
|
+
resolvedLocation.pathRoute.path,
|
|
959
|
+
shouldAnchorContentBottom,
|
|
960
|
+
keyboardFrame.sticky,
|
|
961
|
+
keyboardFrame.animationDuration,
|
|
962
|
+
keyboardLayout.contentViewport.height,
|
|
963
|
+
]);
|
|
964
|
+
useEffect(() => {
|
|
965
|
+
const element = pageContentRef.current;
|
|
966
|
+
const path = resolvedLocation.pathRoute.path;
|
|
967
|
+
if (!element || !shouldAnchorContentBottom || !keyboardFrame.sticky) return;
|
|
968
|
+
const updateBottomDistance = () => {
|
|
969
|
+
contentBottomAnchorRef.current = {
|
|
970
|
+
path,
|
|
971
|
+
contentViewportHeight: keyboardLayout.contentViewport.height,
|
|
972
|
+
bottomDistance: Math.max(0, element.scrollHeight - element.scrollTop - element.clientHeight),
|
|
973
|
+
};
|
|
974
|
+
};
|
|
975
|
+
element.addEventListener("scroll", updateBottomDistance, { passive: true });
|
|
976
|
+
return () => element.removeEventListener("scroll", updateBottomDistance);
|
|
977
|
+
}, [
|
|
978
|
+
pageContentRef,
|
|
979
|
+
resolvedLocation.pathRoute.path,
|
|
980
|
+
shouldAnchorContentBottom,
|
|
981
|
+
keyboardFrame.sticky,
|
|
982
|
+
keyboardLayout.contentViewport.height,
|
|
983
|
+
]);
|
|
884
984
|
useFrameRuntimeResync({ updateViewport });
|
|
885
985
|
const shouldPrepareFrameTransition = useCallback(
|
|
886
986
|
(nextHref?: string) => {
|
|
@@ -1245,6 +1345,7 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
|
|
|
1245
1345
|
keyboard: keyboardFrame,
|
|
1246
1346
|
contentViewport: keyboardLayout.contentViewport,
|
|
1247
1347
|
keyboardAccessory: keyboardLayout.keyboardAccessory,
|
|
1348
|
+
contentAnchor: shouldAnchorContentBottom ? "bottom" : undefined,
|
|
1248
1349
|
platformProfile,
|
|
1249
1350
|
zIndex: FRAME_Z_INDEX,
|
|
1250
1351
|
pageStateByPath: effectivePageStateByPath,
|
|
@@ -1262,6 +1363,21 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
|
|
|
1262
1363
|
bottomUp: useBottomUpTransition,
|
|
1263
1364
|
scaleOut: useScaleOutTransition,
|
|
1264
1365
|
};
|
|
1366
|
+
|
|
1367
|
+
const contentResizeStyle = useMemo(() => {
|
|
1368
|
+
if (!shouldAnchorContentBottom || !keyboardFrame.sticky) return null;
|
|
1369
|
+
const duration = keyboardFrame.animationDuration ?? 420;
|
|
1370
|
+
const easing = keyboardFrame.animationEasing ?? "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
1371
|
+
return {
|
|
1372
|
+
transition: `height ${duration}ms ${easing}, padding-bottom ${duration}ms ${easing}`,
|
|
1373
|
+
willChange: "height, padding-bottom",
|
|
1374
|
+
};
|
|
1375
|
+
}, [shouldAnchorContentBottom, keyboardFrame.sticky, keyboardFrame.animationDuration, keyboardFrame.animationEasing]);
|
|
1376
|
+
const csrTransition = useCsrTransitionMap[resolvedLocation.pathRoute.pageState.transition];
|
|
1377
|
+
const page: PageTransition | null =
|
|
1378
|
+
contentResizeStyle && csrTransition.page
|
|
1379
|
+
? { ...csrTransition.page, contentStyle: { ...csrTransition.page.contentStyle, ...contentResizeStyle } }
|
|
1380
|
+
: csrTransition.page;
|
|
1265
1381
|
const nativeBackStateRef = useRef({
|
|
1266
1382
|
path: resolvedLocation.pathRoute.path,
|
|
1267
1383
|
keyboardHeight: keyboardFrame.height,
|
|
@@ -1409,6 +1525,7 @@ export const useCsrValues = (rootRouteGuide: RouteGuide, pathRoutes: PathRoute[]
|
|
|
1409
1525
|
|
|
1410
1526
|
return {
|
|
1411
1527
|
...routeState,
|
|
1412
|
-
...
|
|
1528
|
+
...csrTransition,
|
|
1529
|
+
page,
|
|
1413
1530
|
} satisfies CsrContextType;
|
|
1414
1531
|
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useEffect, useRef } from "react";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Escape belongs to the topmost dismissable surface only.
|
|
6
|
+
*
|
|
7
|
+
* A per-component `window` listener cannot know that: every open surface hears the same keydown, so a
|
|
8
|
+
* dialog opened from another dialog took both down at once, and a confirm popover inside a modal would
|
|
9
|
+
* have closed the modal under it. One shared stack keeps the order surfaces actually opened in and
|
|
10
|
+
* hands the key to the last one.
|
|
11
|
+
*/
|
|
12
|
+
const stack: (() => void)[] = [];
|
|
13
|
+
|
|
14
|
+
const onKeyDown = (event: KeyboardEvent) => {
|
|
15
|
+
if (event.key !== "Escape") return;
|
|
16
|
+
const topmost = stack.at(-1);
|
|
17
|
+
if (!topmost) return;
|
|
18
|
+
event.preventDefault();
|
|
19
|
+
topmost();
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Close `onEscape` on Escape while `active`, but only while this surface is the topmost active one.
|
|
24
|
+
* The callback is read through a ref, so a fresh closure per render does not reshuffle the stack.
|
|
25
|
+
*/
|
|
26
|
+
export const useEscapeKey = (active: boolean, onEscape: () => void) => {
|
|
27
|
+
const callbackRef = useRef(onEscape);
|
|
28
|
+
useEffect(() => {
|
|
29
|
+
callbackRef.current = onEscape;
|
|
30
|
+
});
|
|
31
|
+
useEffect(() => {
|
|
32
|
+
if (!active) return;
|
|
33
|
+
const entry = () => callbackRef.current();
|
|
34
|
+
stack.push(entry);
|
|
35
|
+
if (stack.length === 1) window.addEventListener("keydown", onKeyDown);
|
|
36
|
+
return () => {
|
|
37
|
+
const index = stack.lastIndexOf(entry);
|
|
38
|
+
if (index !== -1) stack.splice(index, 1);
|
|
39
|
+
if (stack.length === 0) window.removeEventListener("keydown", onKeyDown);
|
|
40
|
+
};
|
|
41
|
+
}, [active]);
|
|
42
|
+
};
|
|
@@ -25,7 +25,10 @@ export type FrameSlotBucket = "active" | "pending";
|
|
|
25
25
|
export type FrameSlotMapByBucket = Record<FrameSlotBucket, FrameSlotMap>;
|
|
26
26
|
export const PENDING_FRAME_READY_TIMEOUT_MS = 80;
|
|
27
27
|
export const PENDING_FRAME_READY_MAX_TIMEOUT_MS = 120;
|
|
28
|
-
export const
|
|
28
|
+
export const KEYBOARD_SHOW_ANIMATION_DURATION_MS = 420;
|
|
29
|
+
export const KEYBOARD_HIDE_ANIMATION_DURATION_MS = 90;
|
|
30
|
+
export const KEYBOARD_SHOW_ANIMATION_EASING = "cubic-bezier(0.16, 1, 0.3, 1)";
|
|
31
|
+
export const KEYBOARD_HIDE_ANIMATION_EASING = "cubic-bezier(0.7, 0, 0.84, 0)";
|
|
29
32
|
|
|
30
33
|
export const FRAME_Z_INDEX = {
|
|
31
34
|
page: 10,
|
|
@@ -204,29 +207,37 @@ export function useFrameViewport() {
|
|
|
204
207
|
visualOffsetTop: window.visualViewport?.offsetTop ?? 0,
|
|
205
208
|
}));
|
|
206
209
|
|
|
207
|
-
const updateViewport = useCallback(
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
210
|
+
const updateViewport = useCallback(
|
|
211
|
+
(reason = "viewport.change") => {
|
|
212
|
+
const visualViewport = window.visualViewport;
|
|
213
|
+
const platform = getCurrentPlatform();
|
|
214
|
+
const usesAkanKeyboardResize = platform === "ios" || platform === "android";
|
|
215
|
+
const width = Math.round(visualViewport?.width ?? window.innerWidth);
|
|
216
|
+
const currentHeight = Math.round(window.innerHeight);
|
|
217
|
+
const stableHeight =
|
|
218
|
+
usesAkanKeyboardResize && width === viewport.width ? Math.max(viewport.height, currentHeight) : currentHeight;
|
|
219
|
+
const nextViewport = {
|
|
220
|
+
width,
|
|
221
|
+
height: usesAkanKeyboardResize ? stableHeight : Math.round(visualViewport?.height ?? window.innerHeight),
|
|
222
|
+
visualWidth: Math.round(visualViewport?.width ?? window.innerWidth),
|
|
223
|
+
visualHeight: Math.round(visualViewport?.height ?? window.innerHeight),
|
|
224
|
+
visualOffsetTop: Math.round(visualViewport?.offsetTop ?? 0),
|
|
225
|
+
};
|
|
226
|
+
setViewport((prev) => {
|
|
227
|
+
if (
|
|
228
|
+
prev.width === nextViewport.width &&
|
|
229
|
+
prev.height === nextViewport.height &&
|
|
230
|
+
prev.visualWidth === nextViewport.visualWidth &&
|
|
231
|
+
prev.visualHeight === nextViewport.visualHeight &&
|
|
232
|
+
prev.visualOffsetTop === nextViewport.visualOffsetTop
|
|
233
|
+
)
|
|
234
|
+
return prev;
|
|
235
|
+
debugFrame(reason, { from: prev, to: nextViewport, platform });
|
|
236
|
+
return nextViewport;
|
|
237
|
+
});
|
|
238
|
+
},
|
|
239
|
+
[viewport.height],
|
|
240
|
+
);
|
|
230
241
|
|
|
231
242
|
useEffect(() => {
|
|
232
243
|
updateViewport("viewport.init");
|
|
@@ -259,6 +270,10 @@ export function hasKeyboardStickySlot(path: string, frameSlots: FrameSlotMap) {
|
|
|
259
270
|
return getKeyboardAccessorySlots(path, frameSlots).length > 0;
|
|
260
271
|
}
|
|
261
272
|
|
|
273
|
+
export function hasBottomAnchoredKeyboardSlot(path: string, frameSlots: FrameSlotMap) {
|
|
274
|
+
return getKeyboardAccessorySlots(path, frameSlots).some((slot) => slot.contentAnchor === "bottom");
|
|
275
|
+
}
|
|
276
|
+
|
|
262
277
|
export function resolveKeyboardFrame({
|
|
263
278
|
keyboardHeight,
|
|
264
279
|
bottomSafeArea,
|
|
@@ -274,22 +289,36 @@ export function resolveKeyboardFrame({
|
|
|
274
289
|
sticky: boolean;
|
|
275
290
|
freeze?: boolean;
|
|
276
291
|
}): KeyboardFrameState {
|
|
292
|
+
const useVisualViewportHeight = platformProfile === "android" && visualViewportKeyboardHeight > 0;
|
|
277
293
|
const visualFallbackHeight =
|
|
278
294
|
keyboardHeight <= 0 && visualViewportKeyboardHeight > 0 ? visualViewportKeyboardHeight : 0;
|
|
279
|
-
const effectiveKeyboardHeight =
|
|
280
|
-
|
|
295
|
+
const effectiveKeyboardHeight = useVisualViewportHeight
|
|
296
|
+
? visualViewportKeyboardHeight
|
|
297
|
+
: keyboardHeight > 0
|
|
298
|
+
? keyboardHeight
|
|
299
|
+
: visualFallbackHeight;
|
|
300
|
+
const source = useVisualViewportHeight
|
|
301
|
+
? "visualViewport"
|
|
302
|
+
: keyboardHeight > 0
|
|
303
|
+
? "native"
|
|
304
|
+
: visualFallbackHeight > 0
|
|
305
|
+
? "visualViewport"
|
|
306
|
+
: "fallback";
|
|
281
307
|
const visualCompensation =
|
|
282
|
-
platformProfile === "
|
|
308
|
+
platformProfile === "web" || platformProfile === "mobileWeb"
|
|
309
|
+
? Math.min(visualViewportKeyboardHeight, effectiveKeyboardHeight)
|
|
310
|
+
: 0;
|
|
283
311
|
const offset = sticky && !freeze ? Math.max(0, effectiveKeyboardHeight - visualCompensation) : 0;
|
|
312
|
+
const isShowing = effectiveKeyboardHeight > 0 && !freeze;
|
|
284
313
|
return {
|
|
285
314
|
height: effectiveKeyboardHeight,
|
|
286
315
|
offset,
|
|
287
|
-
visible:
|
|
316
|
+
visible: isShowing,
|
|
288
317
|
sticky,
|
|
289
318
|
frozen: freeze,
|
|
290
319
|
source,
|
|
291
|
-
animationDuration:
|
|
292
|
-
animationEasing:
|
|
320
|
+
animationDuration: isShowing ? KEYBOARD_SHOW_ANIMATION_DURATION_MS : KEYBOARD_HIDE_ANIMATION_DURATION_MS,
|
|
321
|
+
animationEasing: isShowing ? KEYBOARD_SHOW_ANIMATION_EASING : KEYBOARD_HIDE_ANIMATION_EASING,
|
|
293
322
|
};
|
|
294
323
|
}
|
|
295
324
|
|
|
@@ -344,14 +373,18 @@ export function useKeyboardFrame({
|
|
|
344
373
|
}: {
|
|
345
374
|
bottomSafeArea: number;
|
|
346
375
|
sticky: boolean;
|
|
347
|
-
viewport: { visualHeight: number; visualOffsetTop: number };
|
|
376
|
+
viewport: { height: number; visualHeight: number; visualOffsetTop: number };
|
|
348
377
|
platformProfile: FramePlatformProfile;
|
|
349
378
|
freeze?: boolean;
|
|
350
379
|
}) {
|
|
351
380
|
const keyboardHeight = st.use.keyboardHeight();
|
|
381
|
+
const androidViewportKeyboardHeight =
|
|
382
|
+
platformProfile === "android" ? Math.max(0, Math.round(viewport.height - window.innerHeight)) : 0;
|
|
352
383
|
const visualViewportKeyboardHeight = Math.max(
|
|
353
384
|
0,
|
|
354
|
-
|
|
385
|
+
androidViewportKeyboardHeight > 0
|
|
386
|
+
? androidViewportKeyboardHeight
|
|
387
|
+
: Math.round(viewport.height - viewport.visualHeight - viewport.visualOffsetTop),
|
|
355
388
|
);
|
|
356
389
|
return useMemo(
|
|
357
390
|
() =>
|