@sero-ai/app-runtime 0.1.1 → 0.1.3
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/package.json +4 -3
- package/src/index.ts +1 -1
- package/src/sero-bridge.ts +1 -1
- package/src/use-app-state.ts +30 -3
- package/src/use-app-tools.ts +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sero-ai/app-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Hooks for Sero federated app modules — useAppState, useAppInfo, useAgentPrompt, useAppTools",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.ts",
|
|
@@ -20,10 +20,11 @@
|
|
|
20
20
|
"access": "public"
|
|
21
21
|
},
|
|
22
22
|
"peerDependencies": {
|
|
23
|
-
"
|
|
23
|
+
"@sero-ai/common": ">=0.3.1",
|
|
24
|
+
"react": "^19.2.4"
|
|
24
25
|
},
|
|
25
26
|
"devDependencies": {
|
|
26
|
-
"@sero/common": "
|
|
27
|
+
"@sero-ai/common": "workspace:*",
|
|
27
28
|
"@types/react": "^19.2.13",
|
|
28
29
|
"typescript": "^5.9.3"
|
|
29
30
|
}
|
package/src/index.ts
CHANGED
|
@@ -15,7 +15,7 @@ export { useAvailableModels, type UseAvailableModelsResult } from './use-availab
|
|
|
15
15
|
export { useTheme, type UseThemeResult } from './use-theme';
|
|
16
16
|
export { getSeroApi } from './sero-bridge';
|
|
17
17
|
export type { AppModelInfo, AppModelGroup } from './sero-bridge';
|
|
18
|
-
export type { AppToolContentBlock, AppToolImageContent, AppToolResult, AppToolTextContent } from '@sero/common';
|
|
18
|
+
export type { AppToolContentBlock, AppToolImageContent, AppToolResult, AppToolTextContent } from '@sero-ai/common';
|
|
19
19
|
export { registerWidget, getRuntimeWidgets, onWidgetRegistryChange } from './widget-registry';
|
|
20
20
|
export type { RuntimeWidget } from './widget-registry';
|
|
21
21
|
export { useWidgetRegistration } from './use-widget-registration';
|
package/src/sero-bridge.ts
CHANGED
package/src/use-app-state.ts
CHANGED
|
@@ -16,6 +16,33 @@ import { getSeroApi, type SeroWindowAppStateBridge } from './sero-bridge';
|
|
|
16
16
|
* @param defaultState — returned while the file is being read (or if missing)
|
|
17
17
|
* @returns [state, updateState] — updateState accepts an updater function
|
|
18
18
|
*/
|
|
19
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
20
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function normalizeStateValue(defaultValue: unknown, currentValue: unknown): unknown {
|
|
24
|
+
if (Array.isArray(defaultValue)) {
|
|
25
|
+
return Array.isArray(currentValue) ? currentValue : defaultValue;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (isPlainObject(defaultValue)) {
|
|
29
|
+
if (!isPlainObject(currentValue)) return defaultValue;
|
|
30
|
+
const merged: Record<string, unknown> = { ...currentValue };
|
|
31
|
+
for (const [key, childDefault] of Object.entries(defaultValue)) {
|
|
32
|
+
merged[key] = normalizeStateValue(childDefault, currentValue[key]);
|
|
33
|
+
}
|
|
34
|
+
return merged;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (currentValue === undefined) return defaultValue;
|
|
38
|
+
if (defaultValue === null) return currentValue ?? null;
|
|
39
|
+
return typeof currentValue === typeof defaultValue ? currentValue : defaultValue;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function applyDefaultState<T>(defaultState: T, current: unknown): T {
|
|
43
|
+
return normalizeStateValue(defaultState, current) as T;
|
|
44
|
+
}
|
|
45
|
+
|
|
19
46
|
export function useAppState<T>(defaultState: T): [T, (updater: (prev: T) => T) => void] {
|
|
20
47
|
const ctx = useContext(AppContext);
|
|
21
48
|
if (!ctx) {
|
|
@@ -43,7 +70,7 @@ export function useAppState<T>(defaultState: T): [T, (updater: (prev: T) => T) =
|
|
|
43
70
|
try {
|
|
44
71
|
const current = await api.read<T | null>(stateFilePath);
|
|
45
72
|
if (writeId !== latestWriteIdRef.current) return;
|
|
46
|
-
applyState(current
|
|
73
|
+
applyState(current == null ? fallbackState : applyDefaultState(fallbackState, current));
|
|
47
74
|
} catch {
|
|
48
75
|
if (writeId !== latestWriteIdRef.current) return;
|
|
49
76
|
applyState(fallbackState);
|
|
@@ -65,12 +92,12 @@ export function useAppState<T>(defaultState: T): [T, (updater: (prev: T) => T) =
|
|
|
65
92
|
|
|
66
93
|
const unsubscribe = api.appState.onChange<T | null>((filePath, data) => {
|
|
67
94
|
if (filePath !== stateFilePath || data == null) return;
|
|
68
|
-
applyIfActive(data);
|
|
95
|
+
applyIfActive(applyDefaultState(defaultStateRef.current, data));
|
|
69
96
|
});
|
|
70
97
|
|
|
71
98
|
void api.appState.watch<T | null>(stateFilePath).then((current) => {
|
|
72
99
|
if (current == null) return;
|
|
73
|
-
applyIfActive(current);
|
|
100
|
+
applyIfActive(applyDefaultState(defaultStateRef.current, current));
|
|
74
101
|
});
|
|
75
102
|
|
|
76
103
|
return () => {
|
package/src/use-app-tools.ts
CHANGED