@affectively/aeon-pages 1.3.0
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 +112 -0
- package/README.md +625 -0
- package/examples/basic/aeon.config.ts +39 -0
- package/examples/basic/components/Cursor.tsx +86 -0
- package/examples/basic/components/OfflineIndicator.tsx +103 -0
- package/examples/basic/components/PresenceBar.tsx +77 -0
- package/examples/basic/package.json +20 -0
- package/examples/basic/pages/index.tsx +80 -0
- package/package.json +101 -0
- package/packages/analytics/README.md +309 -0
- package/packages/analytics/build.ts +35 -0
- package/packages/analytics/package.json +50 -0
- package/packages/analytics/src/click-tracker.ts +368 -0
- package/packages/analytics/src/context-bridge.ts +319 -0
- package/packages/analytics/src/data-layer.ts +302 -0
- package/packages/analytics/src/gtm-loader.ts +239 -0
- package/packages/analytics/src/index.ts +230 -0
- package/packages/analytics/src/merkle-tree.ts +489 -0
- package/packages/analytics/src/provider.tsx +300 -0
- package/packages/analytics/src/types.ts +320 -0
- package/packages/analytics/src/use-analytics.ts +296 -0
- package/packages/analytics/tsconfig.json +19 -0
- package/packages/benchmarks/src/benchmark.test.ts +691 -0
- package/packages/cli/dist/index.js +61899 -0
- package/packages/cli/package.json +43 -0
- package/packages/cli/src/commands/build.test.ts +682 -0
- package/packages/cli/src/commands/build.ts +890 -0
- package/packages/cli/src/commands/dev.ts +473 -0
- package/packages/cli/src/commands/init.ts +409 -0
- package/packages/cli/src/commands/start.ts +297 -0
- package/packages/cli/src/index.ts +105 -0
- package/packages/directives/src/use-aeon.ts +272 -0
- package/packages/mcp-server/package.json +51 -0
- package/packages/mcp-server/src/index.ts +178 -0
- package/packages/mcp-server/src/resources.ts +346 -0
- package/packages/mcp-server/src/tools/index.ts +36 -0
- package/packages/mcp-server/src/tools/navigation.ts +545 -0
- package/packages/mcp-server/tsconfig.json +21 -0
- package/packages/react/package.json +40 -0
- package/packages/react/src/Link.tsx +388 -0
- package/packages/react/src/components/InstallPrompt.tsx +286 -0
- package/packages/react/src/components/OfflineDiagnostics.tsx +677 -0
- package/packages/react/src/components/PushNotifications.tsx +453 -0
- package/packages/react/src/hooks/useAeonNavigation.ts +219 -0
- package/packages/react/src/hooks/useConflicts.ts +277 -0
- package/packages/react/src/hooks/useNetworkState.ts +209 -0
- package/packages/react/src/hooks/usePilotNavigation.ts +254 -0
- package/packages/react/src/hooks/useServiceWorker.ts +278 -0
- package/packages/react/src/hooks.ts +195 -0
- package/packages/react/src/index.ts +151 -0
- package/packages/react/src/provider.tsx +467 -0
- package/packages/react/tsconfig.json +19 -0
- package/packages/runtime/README.md +399 -0
- package/packages/runtime/build.ts +48 -0
- package/packages/runtime/package.json +71 -0
- package/packages/runtime/schema.sql +40 -0
- package/packages/runtime/src/api-routes.ts +465 -0
- package/packages/runtime/src/benchmark.ts +171 -0
- package/packages/runtime/src/cache.ts +479 -0
- package/packages/runtime/src/durable-object.ts +1341 -0
- package/packages/runtime/src/index.ts +360 -0
- package/packages/runtime/src/navigation.test.ts +421 -0
- package/packages/runtime/src/navigation.ts +422 -0
- package/packages/runtime/src/nextjs-adapter.ts +272 -0
- package/packages/runtime/src/offline/encrypted-queue.test.ts +607 -0
- package/packages/runtime/src/offline/encrypted-queue.ts +478 -0
- package/packages/runtime/src/offline/encryption.test.ts +412 -0
- package/packages/runtime/src/offline/encryption.ts +397 -0
- package/packages/runtime/src/offline/types.ts +465 -0
- package/packages/runtime/src/predictor.ts +371 -0
- package/packages/runtime/src/registry.ts +351 -0
- package/packages/runtime/src/router/context-extractor.ts +661 -0
- package/packages/runtime/src/router/esi-control-react.tsx +2053 -0
- package/packages/runtime/src/router/esi-control.ts +541 -0
- package/packages/runtime/src/router/esi-cyrano.ts +779 -0
- package/packages/runtime/src/router/esi-format-react.tsx +1744 -0
- package/packages/runtime/src/router/esi-react.tsx +1065 -0
- package/packages/runtime/src/router/esi-translate-observer.ts +476 -0
- package/packages/runtime/src/router/esi-translate-react.tsx +556 -0
- package/packages/runtime/src/router/esi-translate.ts +503 -0
- package/packages/runtime/src/router/esi.ts +666 -0
- package/packages/runtime/src/router/heuristic-adapter.test.ts +295 -0
- package/packages/runtime/src/router/heuristic-adapter.ts +557 -0
- package/packages/runtime/src/router/index.ts +298 -0
- package/packages/runtime/src/router/merkle-capability.ts +473 -0
- package/packages/runtime/src/router/speculation.ts +451 -0
- package/packages/runtime/src/router/types.ts +630 -0
- package/packages/runtime/src/router.test.ts +470 -0
- package/packages/runtime/src/router.ts +302 -0
- package/packages/runtime/src/server.ts +481 -0
- package/packages/runtime/src/service-worker-push.ts +319 -0
- package/packages/runtime/src/service-worker.ts +553 -0
- package/packages/runtime/src/skeleton-hydrate.ts +237 -0
- package/packages/runtime/src/speculation.test.ts +389 -0
- package/packages/runtime/src/speculation.ts +486 -0
- package/packages/runtime/src/storage.test.ts +1297 -0
- package/packages/runtime/src/storage.ts +1048 -0
- package/packages/runtime/src/sync/conflict-resolver.test.ts +528 -0
- package/packages/runtime/src/sync/conflict-resolver.ts +565 -0
- package/packages/runtime/src/sync/coordinator.test.ts +608 -0
- package/packages/runtime/src/sync/coordinator.ts +596 -0
- package/packages/runtime/src/tree-compiler.ts +295 -0
- package/packages/runtime/src/types.ts +728 -0
- package/packages/runtime/src/worker.ts +327 -0
- package/packages/runtime/tsconfig.json +20 -0
- package/packages/runtime/wasm/aeon_pages_runtime.d.ts +504 -0
- package/packages/runtime/wasm/aeon_pages_runtime.js +1657 -0
- package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm +0 -0
- package/packages/runtime/wasm/aeon_pages_runtime_bg.wasm.d.ts +196 -0
- package/packages/runtime/wasm/package.json +21 -0
- package/packages/runtime/wrangler.toml +41 -0
- package/packages/runtime-wasm/Cargo.lock +436 -0
- package/packages/runtime-wasm/Cargo.toml +29 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime.d.ts +480 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime.js +1568 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm +0 -0
- package/packages/runtime-wasm/pkg/aeon_pages_runtime_bg.wasm.d.ts +192 -0
- package/packages/runtime-wasm/pkg/package.json +21 -0
- package/packages/runtime-wasm/src/hydrate.rs +352 -0
- package/packages/runtime-wasm/src/lib.rs +191 -0
- package/packages/runtime-wasm/src/render.rs +629 -0
- package/packages/runtime-wasm/src/router.rs +298 -0
- package/packages/runtime-wasm/src/skeleton.rs +430 -0
- package/rfcs/RFC-001-ZERO-DEPENDENCY-RENDERING.md +1446 -0
|
@@ -0,0 +1,1065 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ESI React Components
|
|
3
|
+
*
|
|
4
|
+
* Bring AI to templates with declarative components.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```tsx
|
|
8
|
+
* import { ESI } from '@affectively/aeon-flux/esi';
|
|
9
|
+
*
|
|
10
|
+
* function PersonalizedGreeting() {
|
|
11
|
+
* return (
|
|
12
|
+
* <ESI.Infer
|
|
13
|
+
* model="llm"
|
|
14
|
+
* contextAware
|
|
15
|
+
* signals={['emotion', 'time']}
|
|
16
|
+
* >
|
|
17
|
+
* Generate a warm greeting for the user
|
|
18
|
+
* </ESI.Infer>
|
|
19
|
+
* );
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* function EmotionAwareContent({ content }) {
|
|
23
|
+
* return (
|
|
24
|
+
* <ESI.Infer
|
|
25
|
+
* model="llm"
|
|
26
|
+
* temperature={0.7}
|
|
27
|
+
* fallback="Here's today's content..."
|
|
28
|
+
* >
|
|
29
|
+
* {`Adapt this content to be emotionally supportive: ${content}`}
|
|
30
|
+
* </ESI.Infer>
|
|
31
|
+
* );
|
|
32
|
+
* }
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
import {
|
|
37
|
+
createContext,
|
|
38
|
+
useContext,
|
|
39
|
+
useEffect,
|
|
40
|
+
useState,
|
|
41
|
+
useCallback,
|
|
42
|
+
type ReactNode,
|
|
43
|
+
type FC,
|
|
44
|
+
} from 'react';
|
|
45
|
+
import type {
|
|
46
|
+
ESIConfig,
|
|
47
|
+
ESIDirective,
|
|
48
|
+
ESIModel,
|
|
49
|
+
ESIParams,
|
|
50
|
+
ESIProcessor,
|
|
51
|
+
ESIResult,
|
|
52
|
+
UserContext,
|
|
53
|
+
} from './types';
|
|
54
|
+
import {
|
|
55
|
+
EdgeWorkersESIProcessor,
|
|
56
|
+
esiInfer,
|
|
57
|
+
esiEmbed,
|
|
58
|
+
esiEmotion,
|
|
59
|
+
esiVision,
|
|
60
|
+
esiWithContext,
|
|
61
|
+
} from './esi';
|
|
62
|
+
|
|
63
|
+
// Import control components for ESI namespace extension
|
|
64
|
+
import {
|
|
65
|
+
ESIStructured,
|
|
66
|
+
ESIIf,
|
|
67
|
+
ESIShow,
|
|
68
|
+
ESIHide,
|
|
69
|
+
ESIWhen,
|
|
70
|
+
ESIUnless,
|
|
71
|
+
ESIMatch,
|
|
72
|
+
ESICase,
|
|
73
|
+
ESIDefault,
|
|
74
|
+
ESIFirst,
|
|
75
|
+
ESITierGate,
|
|
76
|
+
ESIEmotionGate,
|
|
77
|
+
ESITimeGate,
|
|
78
|
+
ESIForEach,
|
|
79
|
+
ESISelect,
|
|
80
|
+
ESIABTest,
|
|
81
|
+
ESIClamp,
|
|
82
|
+
ESIScore,
|
|
83
|
+
ESICollaborative,
|
|
84
|
+
ESIReflect,
|
|
85
|
+
ESIOptimize,
|
|
86
|
+
ESIAuto,
|
|
87
|
+
} from './esi-control-react';
|
|
88
|
+
|
|
89
|
+
// Import format components for ESI namespace extension
|
|
90
|
+
import {
|
|
91
|
+
ESIMarkdown,
|
|
92
|
+
ESILatex,
|
|
93
|
+
ESIJson,
|
|
94
|
+
ESIPlaintext,
|
|
95
|
+
ESICode,
|
|
96
|
+
ESISemantic,
|
|
97
|
+
} from './esi-format-react';
|
|
98
|
+
|
|
99
|
+
// Import translation components for ESI namespace extension
|
|
100
|
+
import { ESITranslate, TranslationProvider } from './esi-translate-react';
|
|
101
|
+
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// ESI Context
|
|
104
|
+
// ============================================================================
|
|
105
|
+
|
|
106
|
+
interface ESIContextValue {
|
|
107
|
+
processor: ESIProcessor;
|
|
108
|
+
userContext: UserContext | null;
|
|
109
|
+
enabled: boolean;
|
|
110
|
+
process: (directive: ESIDirective) => Promise<ESIResult>;
|
|
111
|
+
processWithStream: (
|
|
112
|
+
directive: ESIDirective,
|
|
113
|
+
onChunk: (chunk: string) => void,
|
|
114
|
+
) => Promise<ESIResult>;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const ESIContext = createContext<ESIContextValue | null>(null);
|
|
118
|
+
|
|
119
|
+
export interface ESIProviderProps {
|
|
120
|
+
children: ReactNode;
|
|
121
|
+
config?: Partial<ESIConfig>;
|
|
122
|
+
userContext?: UserContext;
|
|
123
|
+
processor?: ESIProcessor;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* ESI Provider - enables ESI components in the tree
|
|
128
|
+
*/
|
|
129
|
+
export const ESIProvider: FC<ESIProviderProps> = ({
|
|
130
|
+
children,
|
|
131
|
+
config,
|
|
132
|
+
userContext,
|
|
133
|
+
processor: customProcessor,
|
|
134
|
+
}) => {
|
|
135
|
+
const [processor] = useState(
|
|
136
|
+
() => customProcessor || new EdgeWorkersESIProcessor(config),
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
useEffect(() => {
|
|
140
|
+
processor.warmup?.();
|
|
141
|
+
}, [processor]);
|
|
142
|
+
|
|
143
|
+
const process = useCallback(
|
|
144
|
+
async (directive: ESIDirective) => {
|
|
145
|
+
if (!userContext) {
|
|
146
|
+
return {
|
|
147
|
+
id: directive.id,
|
|
148
|
+
success: false,
|
|
149
|
+
error: 'No user context available',
|
|
150
|
+
latencyMs: 0,
|
|
151
|
+
cached: false,
|
|
152
|
+
model: directive.params.model,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return processor.process(directive, userContext);
|
|
156
|
+
},
|
|
157
|
+
[processor, userContext],
|
|
158
|
+
);
|
|
159
|
+
|
|
160
|
+
const processWithStream = useCallback(
|
|
161
|
+
async (directive: ESIDirective, onChunk: (chunk: string) => void) => {
|
|
162
|
+
if (!userContext) {
|
|
163
|
+
return {
|
|
164
|
+
id: directive.id,
|
|
165
|
+
success: false,
|
|
166
|
+
error: 'No user context available',
|
|
167
|
+
latencyMs: 0,
|
|
168
|
+
cached: false,
|
|
169
|
+
model: directive.params.model,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
if (!processor.stream) {
|
|
173
|
+
return processor.process(directive, userContext);
|
|
174
|
+
}
|
|
175
|
+
return processor.stream(directive, userContext, onChunk);
|
|
176
|
+
},
|
|
177
|
+
[processor, userContext],
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
return (
|
|
181
|
+
<ESIContext.Provider
|
|
182
|
+
value={{
|
|
183
|
+
processor,
|
|
184
|
+
userContext: userContext || null,
|
|
185
|
+
enabled: config?.enabled ?? true,
|
|
186
|
+
process,
|
|
187
|
+
processWithStream,
|
|
188
|
+
}}
|
|
189
|
+
>
|
|
190
|
+
{children}
|
|
191
|
+
</ESIContext.Provider>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Hook to access ESI context
|
|
197
|
+
*/
|
|
198
|
+
export function useESI() {
|
|
199
|
+
const ctx = useContext(ESIContext);
|
|
200
|
+
if (!ctx) {
|
|
201
|
+
throw new Error('useESI must be used within an ESIProvider');
|
|
202
|
+
}
|
|
203
|
+
return ctx;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// ============================================================================
|
|
207
|
+
// ESI Components
|
|
208
|
+
// ============================================================================
|
|
209
|
+
|
|
210
|
+
export interface ESIInferProps {
|
|
211
|
+
/** The prompt - can be children or explicit prop */
|
|
212
|
+
children?: ReactNode;
|
|
213
|
+
prompt?: string;
|
|
214
|
+
|
|
215
|
+
/** Model to use */
|
|
216
|
+
model?: ESIModel;
|
|
217
|
+
|
|
218
|
+
/** Model variant */
|
|
219
|
+
variant?: string;
|
|
220
|
+
|
|
221
|
+
/** Temperature for generation */
|
|
222
|
+
temperature?: number;
|
|
223
|
+
|
|
224
|
+
/** Maximum tokens */
|
|
225
|
+
maxTokens?: number;
|
|
226
|
+
|
|
227
|
+
/** System prompt */
|
|
228
|
+
system?: string;
|
|
229
|
+
|
|
230
|
+
/** Enable streaming */
|
|
231
|
+
stream?: boolean;
|
|
232
|
+
|
|
233
|
+
/** Fallback content if inference fails */
|
|
234
|
+
fallback?: ReactNode;
|
|
235
|
+
|
|
236
|
+
/** Loading content */
|
|
237
|
+
loading?: ReactNode;
|
|
238
|
+
|
|
239
|
+
/** Inject user context */
|
|
240
|
+
contextAware?: boolean;
|
|
241
|
+
|
|
242
|
+
/** Context signals to include */
|
|
243
|
+
signals?: Array<'emotion' | 'preferences' | 'history' | 'time' | 'device'>;
|
|
244
|
+
|
|
245
|
+
/** Cache TTL in seconds */
|
|
246
|
+
cacheTtl?: number;
|
|
247
|
+
|
|
248
|
+
/** Custom render function */
|
|
249
|
+
render?: (result: ESIResult) => ReactNode;
|
|
250
|
+
|
|
251
|
+
/** Class name for wrapper */
|
|
252
|
+
className?: string;
|
|
253
|
+
|
|
254
|
+
/** Callback when inference completes */
|
|
255
|
+
onComplete?: (result: ESIResult) => void;
|
|
256
|
+
|
|
257
|
+
/** Callback on error */
|
|
258
|
+
onError?: (error: string) => void;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* ESI Inference Component
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```tsx
|
|
266
|
+
* <ESI.Infer model="llm" temperature={0.7}>
|
|
267
|
+
* Write a haiku about React
|
|
268
|
+
* </ESI.Infer>
|
|
269
|
+
* ```
|
|
270
|
+
*/
|
|
271
|
+
export const ESIInfer: FC<ESIInferProps> = ({
|
|
272
|
+
children,
|
|
273
|
+
prompt,
|
|
274
|
+
model = 'llm',
|
|
275
|
+
variant,
|
|
276
|
+
temperature,
|
|
277
|
+
maxTokens,
|
|
278
|
+
system,
|
|
279
|
+
stream = false,
|
|
280
|
+
fallback,
|
|
281
|
+
loading = '...',
|
|
282
|
+
contextAware = false,
|
|
283
|
+
signals,
|
|
284
|
+
cacheTtl,
|
|
285
|
+
render,
|
|
286
|
+
className,
|
|
287
|
+
onComplete,
|
|
288
|
+
onError,
|
|
289
|
+
}) => {
|
|
290
|
+
const { process, processWithStream, enabled } = useESI();
|
|
291
|
+
const [output, setOutput] = useState<string>('');
|
|
292
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
293
|
+
const [error, setError] = useState<string | null>(null);
|
|
294
|
+
|
|
295
|
+
const promptText =
|
|
296
|
+
prompt ||
|
|
297
|
+
(typeof children === 'string' ? children : String(children || ''));
|
|
298
|
+
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
const fetchData = async () => {
|
|
301
|
+
if (!enabled) {
|
|
302
|
+
setOutput(typeof fallback === 'string' ? fallback : '');
|
|
303
|
+
setIsLoading(false);
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const directive: ESIDirective = contextAware
|
|
308
|
+
? esiWithContext(promptText, signals, {
|
|
309
|
+
model,
|
|
310
|
+
variant,
|
|
311
|
+
temperature,
|
|
312
|
+
maxTokens,
|
|
313
|
+
system,
|
|
314
|
+
cacheTtl,
|
|
315
|
+
fallback: typeof fallback === 'string' ? fallback : undefined,
|
|
316
|
+
})
|
|
317
|
+
: esiInfer(promptText, {
|
|
318
|
+
model,
|
|
319
|
+
variant,
|
|
320
|
+
temperature,
|
|
321
|
+
maxTokens,
|
|
322
|
+
system,
|
|
323
|
+
cacheTtl,
|
|
324
|
+
fallback: typeof fallback === 'string' ? fallback : undefined,
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
if (stream) {
|
|
328
|
+
setOutput('');
|
|
329
|
+
await processWithStream(directive, (chunk) => {
|
|
330
|
+
setOutput((prev) => prev + chunk);
|
|
331
|
+
}).then((result) => {
|
|
332
|
+
setIsLoading(false);
|
|
333
|
+
if (!result.success) {
|
|
334
|
+
setError(result.error || 'Inference failed');
|
|
335
|
+
onError?.(result.error || 'Inference failed');
|
|
336
|
+
}
|
|
337
|
+
onComplete?.(result);
|
|
338
|
+
});
|
|
339
|
+
} else {
|
|
340
|
+
await process(directive).then((result) => {
|
|
341
|
+
setIsLoading(false);
|
|
342
|
+
if (result.success && result.output) {
|
|
343
|
+
setOutput(result.output);
|
|
344
|
+
} else {
|
|
345
|
+
setError(result.error || 'Inference failed');
|
|
346
|
+
onError?.(result.error || 'Inference failed');
|
|
347
|
+
}
|
|
348
|
+
onComplete?.(result);
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
fetchData();
|
|
354
|
+
|
|
355
|
+
return () => {};
|
|
356
|
+
}, [
|
|
357
|
+
promptText,
|
|
358
|
+
model,
|
|
359
|
+
variant,
|
|
360
|
+
temperature,
|
|
361
|
+
maxTokens,
|
|
362
|
+
system,
|
|
363
|
+
contextAware,
|
|
364
|
+
stream,
|
|
365
|
+
enabled,
|
|
366
|
+
fallback,
|
|
367
|
+
processWithStream,
|
|
368
|
+
process,
|
|
369
|
+
onComplete,
|
|
370
|
+
onError,
|
|
371
|
+
]);
|
|
372
|
+
|
|
373
|
+
if (isLoading && !stream) {
|
|
374
|
+
return <span className={className}>{loading}</span>;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
if (error && fallback) {
|
|
378
|
+
return <span className={className}>{fallback}</span>;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
if (render) {
|
|
382
|
+
return (
|
|
383
|
+
<span className={className}>
|
|
384
|
+
{render({
|
|
385
|
+
id: '',
|
|
386
|
+
success: !error,
|
|
387
|
+
output,
|
|
388
|
+
error: error || undefined,
|
|
389
|
+
latencyMs: 0,
|
|
390
|
+
cached: false,
|
|
391
|
+
model,
|
|
392
|
+
})}
|
|
393
|
+
</span>
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return (
|
|
398
|
+
<span className={className}>{output || (isLoading ? loading : '')}</span>
|
|
399
|
+
);
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
export interface ESIEmbedProps {
|
|
403
|
+
children: ReactNode;
|
|
404
|
+
onComplete?: (embedding: number[]) => void;
|
|
405
|
+
onError?: (error: string) => void;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
/**
|
|
409
|
+
* ESI Embed Component - generates embeddings
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```tsx
|
|
413
|
+
* <ESI.Embed onComplete={(embedding) => console.log(embedding)}>
|
|
414
|
+
* Text to embed
|
|
415
|
+
* </ESI.Embed>
|
|
416
|
+
* ```
|
|
417
|
+
*/
|
|
418
|
+
export const ESIEmbed: FC<ESIEmbedProps> = ({
|
|
419
|
+
children,
|
|
420
|
+
onComplete,
|
|
421
|
+
onError,
|
|
422
|
+
}) => {
|
|
423
|
+
const { process, enabled } = useESI();
|
|
424
|
+
const text = typeof children === 'string' ? children : String(children || '');
|
|
425
|
+
|
|
426
|
+
useEffect(() => {
|
|
427
|
+
if (!enabled) return;
|
|
428
|
+
|
|
429
|
+
const directive = esiEmbed(text);
|
|
430
|
+
process(directive).then((result) => {
|
|
431
|
+
if (result.success && result.embedding) {
|
|
432
|
+
onComplete?.(result.embedding);
|
|
433
|
+
} else {
|
|
434
|
+
onError?.(result.error || 'Embedding failed');
|
|
435
|
+
}
|
|
436
|
+
});
|
|
437
|
+
}, [text, enabled]);
|
|
438
|
+
|
|
439
|
+
// Embed component is invisible - just triggers the embedding
|
|
440
|
+
return null;
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
export interface ESIEmotionProps {
|
|
444
|
+
children: ReactNode;
|
|
445
|
+
contextAware?: boolean;
|
|
446
|
+
onComplete?: (result: { emotion: string; confidence: number }) => void;
|
|
447
|
+
onError?: (error: string) => void;
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* ESI Emotion Component - detects emotion from text
|
|
452
|
+
*
|
|
453
|
+
* @example
|
|
454
|
+
* ```tsx
|
|
455
|
+
* <ESI.Emotion onComplete={({ emotion }) => setMood(emotion)}>
|
|
456
|
+
* {userInput}
|
|
457
|
+
* </ESI.Emotion>
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
export const ESIEmotion: FC<ESIEmotionProps> = ({
|
|
461
|
+
children,
|
|
462
|
+
contextAware = true,
|
|
463
|
+
onComplete,
|
|
464
|
+
onError,
|
|
465
|
+
}) => {
|
|
466
|
+
const { process, enabled } = useESI();
|
|
467
|
+
const text = typeof children === 'string' ? children : String(children || '');
|
|
468
|
+
|
|
469
|
+
useEffect(() => {
|
|
470
|
+
if (!enabled) return;
|
|
471
|
+
|
|
472
|
+
const directive = esiEmotion(text, contextAware);
|
|
473
|
+
process(directive).then((result) => {
|
|
474
|
+
if (result.success && result.output) {
|
|
475
|
+
try {
|
|
476
|
+
const parsed = JSON.parse(result.output);
|
|
477
|
+
onComplete?.(parsed);
|
|
478
|
+
} catch {
|
|
479
|
+
onComplete?.({ emotion: result.output, confidence: 1 });
|
|
480
|
+
}
|
|
481
|
+
} else {
|
|
482
|
+
onError?.(result.error || 'Emotion detection failed');
|
|
483
|
+
}
|
|
484
|
+
});
|
|
485
|
+
}, [text, contextAware, enabled]);
|
|
486
|
+
|
|
487
|
+
return null;
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
export interface ESIVisionProps {
|
|
491
|
+
src: string; // base64 image
|
|
492
|
+
prompt: string;
|
|
493
|
+
fallback?: ReactNode;
|
|
494
|
+
loading?: ReactNode;
|
|
495
|
+
className?: string;
|
|
496
|
+
onComplete?: (result: ESIResult) => void;
|
|
497
|
+
onError?: (error: string) => void;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/**
|
|
501
|
+
* ESI Vision Component - analyzes images
|
|
502
|
+
*
|
|
503
|
+
* @example
|
|
504
|
+
* ```tsx
|
|
505
|
+
* <ESI.Vision
|
|
506
|
+
* src={base64Image}
|
|
507
|
+
* prompt="Describe what you see"
|
|
508
|
+
* fallback="Unable to analyze image"
|
|
509
|
+
* />
|
|
510
|
+
* ```
|
|
511
|
+
*/
|
|
512
|
+
export const ESIVision: FC<ESIVisionProps> = ({
|
|
513
|
+
src,
|
|
514
|
+
prompt,
|
|
515
|
+
fallback,
|
|
516
|
+
loading = '...',
|
|
517
|
+
className,
|
|
518
|
+
onComplete,
|
|
519
|
+
onError,
|
|
520
|
+
}) => {
|
|
521
|
+
const { process, enabled } = useESI();
|
|
522
|
+
const [output, setOutput] = useState<string>('');
|
|
523
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
524
|
+
const [error, setError] = useState<string | null>(null);
|
|
525
|
+
|
|
526
|
+
useEffect(() => {
|
|
527
|
+
if (!enabled) {
|
|
528
|
+
setOutput(typeof fallback === 'string' ? fallback : '');
|
|
529
|
+
setIsLoading(false);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const directive = esiVision(src, prompt);
|
|
534
|
+
process(directive).then((result) => {
|
|
535
|
+
setIsLoading(false);
|
|
536
|
+
if (result.success && result.output) {
|
|
537
|
+
setOutput(result.output);
|
|
538
|
+
} else {
|
|
539
|
+
setError(result.error || 'Vision analysis failed');
|
|
540
|
+
onError?.(result.error || 'Vision analysis failed');
|
|
541
|
+
}
|
|
542
|
+
onComplete?.(result);
|
|
543
|
+
});
|
|
544
|
+
}, [src, prompt, enabled]);
|
|
545
|
+
|
|
546
|
+
if (isLoading) {
|
|
547
|
+
return <span className={className}>{loading}</span>;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (error && fallback) {
|
|
551
|
+
return <span className={className}>{fallback}</span>;
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
return <span className={className}>{output}</span>;
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
// ============================================================================
|
|
558
|
+
// Hook for programmatic ESI
|
|
559
|
+
// ============================================================================
|
|
560
|
+
|
|
561
|
+
export interface UseESIInferOptions
|
|
562
|
+
extends Omit<
|
|
563
|
+
ESIInferProps,
|
|
564
|
+
'children' | 'loading' | 'fallback' | 'render' | 'className'
|
|
565
|
+
> {
|
|
566
|
+
autoRun?: boolean;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
/**
|
|
570
|
+
* Hook for programmatic ESI inference
|
|
571
|
+
*
|
|
572
|
+
* @example
|
|
573
|
+
* ```tsx
|
|
574
|
+
* function MyComponent() {
|
|
575
|
+
* const { run, result, isLoading, error } = useESIInfer({
|
|
576
|
+
* model: 'llm',
|
|
577
|
+
* contextAware: true,
|
|
578
|
+
* });
|
|
579
|
+
*
|
|
580
|
+
* return (
|
|
581
|
+
* <button onClick={() => run('Generate a greeting')}>
|
|
582
|
+
* {isLoading ? 'Generating...' : result?.output || 'Click to generate'}
|
|
583
|
+
* </button>
|
|
584
|
+
* );
|
|
585
|
+
* }
|
|
586
|
+
* ```
|
|
587
|
+
*/
|
|
588
|
+
export function useESIInfer(options: UseESIInferOptions = {}) {
|
|
589
|
+
const { process, processWithStream, enabled } = useESI();
|
|
590
|
+
const [result, setResult] = useState<ESIResult | null>(null);
|
|
591
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
592
|
+
const [error, setError] = useState<string | null>(null);
|
|
593
|
+
|
|
594
|
+
const run = useCallback(
|
|
595
|
+
async (prompt: string) => {
|
|
596
|
+
if (!enabled) {
|
|
597
|
+
setError('ESI is disabled');
|
|
598
|
+
return null;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
setIsLoading(true);
|
|
602
|
+
setError(null);
|
|
603
|
+
|
|
604
|
+
const directive: ESIDirective = options.contextAware
|
|
605
|
+
? esiWithContext(prompt, options.signals, {
|
|
606
|
+
model: options.model,
|
|
607
|
+
variant: options.variant,
|
|
608
|
+
temperature: options.temperature,
|
|
609
|
+
maxTokens: options.maxTokens,
|
|
610
|
+
system: options.system,
|
|
611
|
+
cacheTtl: options.cacheTtl,
|
|
612
|
+
})
|
|
613
|
+
: esiInfer(prompt, {
|
|
614
|
+
model: options.model,
|
|
615
|
+
variant: options.variant,
|
|
616
|
+
temperature: options.temperature,
|
|
617
|
+
maxTokens: options.maxTokens,
|
|
618
|
+
system: options.system,
|
|
619
|
+
cacheTtl: options.cacheTtl,
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
try {
|
|
623
|
+
let inferenceResult: ESIResult;
|
|
624
|
+
|
|
625
|
+
if (options.stream) {
|
|
626
|
+
let output = '';
|
|
627
|
+
inferenceResult = await processWithStream(directive, (chunk) => {
|
|
628
|
+
output += chunk;
|
|
629
|
+
setResult((prev) => ({
|
|
630
|
+
...prev!,
|
|
631
|
+
output,
|
|
632
|
+
}));
|
|
633
|
+
});
|
|
634
|
+
} else {
|
|
635
|
+
inferenceResult = await process(directive);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
setResult(inferenceResult);
|
|
639
|
+
setIsLoading(false);
|
|
640
|
+
|
|
641
|
+
if (!inferenceResult.success) {
|
|
642
|
+
setError(inferenceResult.error || 'Inference failed');
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
options.onComplete?.(inferenceResult);
|
|
646
|
+
return inferenceResult;
|
|
647
|
+
} catch (err) {
|
|
648
|
+
const errorMsg = err instanceof Error ? err.message : 'Unknown error';
|
|
649
|
+
setError(errorMsg);
|
|
650
|
+
setIsLoading(false);
|
|
651
|
+
options.onError?.(errorMsg);
|
|
652
|
+
return null;
|
|
653
|
+
}
|
|
654
|
+
},
|
|
655
|
+
[process, processWithStream, enabled, options],
|
|
656
|
+
);
|
|
657
|
+
|
|
658
|
+
const reset = useCallback(() => {
|
|
659
|
+
setResult(null);
|
|
660
|
+
setError(null);
|
|
661
|
+
setIsLoading(false);
|
|
662
|
+
}, []);
|
|
663
|
+
|
|
664
|
+
return { run, result, isLoading, error, reset };
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
// ============================================================================
|
|
668
|
+
// Global ESI State Hook (consumes window.__AEON_ESI_STATE__)
|
|
669
|
+
// ============================================================================
|
|
670
|
+
|
|
671
|
+
/**
|
|
672
|
+
* Global ESI State type (matches ESIState from context-extractor)
|
|
673
|
+
*/
|
|
674
|
+
export interface GlobalESIState {
|
|
675
|
+
userTier: 'free' | 'starter' | 'pro' | 'enterprise' | 'admin';
|
|
676
|
+
/** Admin flag - bypasses ALL tier restrictions */
|
|
677
|
+
isAdmin?: boolean;
|
|
678
|
+
emotionState?: {
|
|
679
|
+
primary: string;
|
|
680
|
+
valence: number;
|
|
681
|
+
arousal: number;
|
|
682
|
+
confidence?: number;
|
|
683
|
+
} | null;
|
|
684
|
+
preferences: {
|
|
685
|
+
theme?: 'light' | 'dark' | 'auto';
|
|
686
|
+
reducedMotion: boolean;
|
|
687
|
+
language?: string;
|
|
688
|
+
};
|
|
689
|
+
sessionId?: string;
|
|
690
|
+
localHour: number;
|
|
691
|
+
timezone: string;
|
|
692
|
+
features: {
|
|
693
|
+
aiInference: boolean;
|
|
694
|
+
emotionTracking: boolean;
|
|
695
|
+
collaboration: boolean;
|
|
696
|
+
advancedInsights: boolean;
|
|
697
|
+
customThemes: boolean;
|
|
698
|
+
voiceSynthesis: boolean;
|
|
699
|
+
imageAnalysis: boolean;
|
|
700
|
+
};
|
|
701
|
+
userId?: string;
|
|
702
|
+
isNewSession: boolean;
|
|
703
|
+
recentPages: string[];
|
|
704
|
+
viewport: {
|
|
705
|
+
width: number;
|
|
706
|
+
height: number;
|
|
707
|
+
};
|
|
708
|
+
connection: string;
|
|
709
|
+
// Runtime methods added by prerender script
|
|
710
|
+
update?: (partial: Partial<GlobalESIState>) => void;
|
|
711
|
+
subscribe?: (listener: (state: GlobalESIState) => void) => () => void;
|
|
712
|
+
}
|
|
713
|
+
|
|
714
|
+
declare global {
|
|
715
|
+
interface Window {
|
|
716
|
+
__AEON_ESI_STATE__?: GlobalESIState;
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
/**
|
|
721
|
+
* Default ESI state for SSR or when global state is not available
|
|
722
|
+
*/
|
|
723
|
+
const DEFAULT_ESI_STATE: GlobalESIState = {
|
|
724
|
+
userTier: 'free',
|
|
725
|
+
emotionState: null,
|
|
726
|
+
preferences: {
|
|
727
|
+
theme: 'auto',
|
|
728
|
+
reducedMotion: false,
|
|
729
|
+
},
|
|
730
|
+
localHour: new Date().getHours(),
|
|
731
|
+
timezone: 'UTC',
|
|
732
|
+
features: {
|
|
733
|
+
aiInference: true,
|
|
734
|
+
emotionTracking: true,
|
|
735
|
+
collaboration: false,
|
|
736
|
+
advancedInsights: false,
|
|
737
|
+
customThemes: false,
|
|
738
|
+
voiceSynthesis: false,
|
|
739
|
+
imageAnalysis: false,
|
|
740
|
+
},
|
|
741
|
+
isNewSession: true,
|
|
742
|
+
recentPages: [],
|
|
743
|
+
viewport: { width: 1920, height: 1080 },
|
|
744
|
+
connection: '4g',
|
|
745
|
+
};
|
|
746
|
+
|
|
747
|
+
/**
|
|
748
|
+
* Hook to consume global ESI state from window.__AEON_ESI_STATE__
|
|
749
|
+
*
|
|
750
|
+
* This state is injected in the <head> at pre-render time and hydrated
|
|
751
|
+
* with actual user context at runtime. Components can use this to
|
|
752
|
+
* access tier, emotion state, preferences, etc. before full React hydration.
|
|
753
|
+
*
|
|
754
|
+
* @example
|
|
755
|
+
* ```tsx
|
|
756
|
+
* function TierGatedFeature() {
|
|
757
|
+
* const { userTier, features } = useGlobalESIState();
|
|
758
|
+
*
|
|
759
|
+
* if (!features.advancedInsights) {
|
|
760
|
+
* return <UpgradePrompt />;
|
|
761
|
+
* }
|
|
762
|
+
*
|
|
763
|
+
* return <AdvancedInsightsPanel />;
|
|
764
|
+
* }
|
|
765
|
+
* ```
|
|
766
|
+
*/
|
|
767
|
+
export function useGlobalESIState(): GlobalESIState {
|
|
768
|
+
const [state, setState] = useState<GlobalESIState>(() => {
|
|
769
|
+
if (typeof window !== 'undefined' && window.__AEON_ESI_STATE__) {
|
|
770
|
+
return window.__AEON_ESI_STATE__;
|
|
771
|
+
}
|
|
772
|
+
return DEFAULT_ESI_STATE;
|
|
773
|
+
});
|
|
774
|
+
|
|
775
|
+
useEffect(() => {
|
|
776
|
+
// Subscribe to state updates if available
|
|
777
|
+
if (typeof window !== 'undefined' && window.__AEON_ESI_STATE__?.subscribe) {
|
|
778
|
+
const unsubscribe = window.__AEON_ESI_STATE__.subscribe((newState) => {
|
|
779
|
+
setState(newState);
|
|
780
|
+
});
|
|
781
|
+
return unsubscribe;
|
|
782
|
+
}
|
|
783
|
+
return () => {}; // Return a no-op cleanup function if subscribe is not available
|
|
784
|
+
}, []);
|
|
785
|
+
|
|
786
|
+
return state;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
/**
|
|
790
|
+
* Hook to check if user has a specific feature enabled based on tier
|
|
791
|
+
*
|
|
792
|
+
* @example
|
|
793
|
+
* ```tsx
|
|
794
|
+
* function VoiceButton() {
|
|
795
|
+
* const hasVoice = useESIFeature('voiceSynthesis');
|
|
796
|
+
*
|
|
797
|
+
* return hasVoice ? <VoiceControl /> : <UpgradeToProBanner />;
|
|
798
|
+
* }
|
|
799
|
+
* ```
|
|
800
|
+
*/
|
|
801
|
+
export function useESIFeature(
|
|
802
|
+
feature: keyof GlobalESIState['features'],
|
|
803
|
+
): boolean {
|
|
804
|
+
const { features, isAdmin, userTier } = useGlobalESIState();
|
|
805
|
+
// Admins bypass ALL tier restrictions
|
|
806
|
+
if (isAdmin === true || userTier === 'admin') {
|
|
807
|
+
return true;
|
|
808
|
+
}
|
|
809
|
+
return features[feature] ?? false;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* Hook to check if current user is an admin
|
|
814
|
+
* Admins bypass ALL tier restrictions
|
|
815
|
+
*/
|
|
816
|
+
export function useIsAdmin(): boolean {
|
|
817
|
+
const { isAdmin, userTier } = useGlobalESIState();
|
|
818
|
+
return isAdmin === true || userTier === 'admin';
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
/**
|
|
822
|
+
* Hook to get user tier
|
|
823
|
+
*
|
|
824
|
+
* @example
|
|
825
|
+
* ```tsx
|
|
826
|
+
* function PricingBanner() {
|
|
827
|
+
* const tier = useESITier();
|
|
828
|
+
*
|
|
829
|
+
* if (tier === 'pro' || tier === 'enterprise') {
|
|
830
|
+
* return null; // Already on paid tier
|
|
831
|
+
* }
|
|
832
|
+
*
|
|
833
|
+
* return <UpgradeBanner currentTier={tier} />;
|
|
834
|
+
* }
|
|
835
|
+
* ```
|
|
836
|
+
*/
|
|
837
|
+
export function useESITier(): GlobalESIState['userTier'] {
|
|
838
|
+
const { userTier } = useGlobalESIState();
|
|
839
|
+
return userTier;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
/**
|
|
843
|
+
* Tier level ordering for comparison
|
|
844
|
+
*/
|
|
845
|
+
const TIER_ORDER: Record<GlobalESIState['userTier'], number> = {
|
|
846
|
+
free: 0,
|
|
847
|
+
starter: 1,
|
|
848
|
+
pro: 2,
|
|
849
|
+
enterprise: 3,
|
|
850
|
+
admin: 999,
|
|
851
|
+
};
|
|
852
|
+
|
|
853
|
+
/**
|
|
854
|
+
* Hook to check if user meets minimum tier requirement
|
|
855
|
+
* Admins bypass ALL tier restrictions
|
|
856
|
+
*
|
|
857
|
+
* @example
|
|
858
|
+
* ```tsx
|
|
859
|
+
* function ProFeature() {
|
|
860
|
+
* const hasPro = useMeetsTierRequirement('pro');
|
|
861
|
+
*
|
|
862
|
+
* if (!hasPro) {
|
|
863
|
+
* return <UpgradePrompt tier="pro" />;
|
|
864
|
+
* }
|
|
865
|
+
*
|
|
866
|
+
* return <ProFeatureContent />;
|
|
867
|
+
* }
|
|
868
|
+
* ```
|
|
869
|
+
*/
|
|
870
|
+
export function useMeetsTierRequirement(
|
|
871
|
+
requiredTier: GlobalESIState['userTier'],
|
|
872
|
+
): boolean {
|
|
873
|
+
const { userTier, isAdmin } = useGlobalESIState();
|
|
874
|
+
|
|
875
|
+
// Admins bypass ALL tier restrictions
|
|
876
|
+
if (isAdmin === true || userTier === 'admin') {
|
|
877
|
+
return true;
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
const userLevel = TIER_ORDER[userTier] ?? 0;
|
|
881
|
+
const requiredLevel = TIER_ORDER[requiredTier] ?? 0;
|
|
882
|
+
|
|
883
|
+
return userLevel >= requiredLevel;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
/**
|
|
887
|
+
* Hook to get current emotion state
|
|
888
|
+
*
|
|
889
|
+
* @example
|
|
890
|
+
* ```tsx
|
|
891
|
+
* function EmotionAwareUI() {
|
|
892
|
+
* const emotion = useESIEmotion();
|
|
893
|
+
*
|
|
894
|
+
* if (emotion?.valence < -0.3) {
|
|
895
|
+
* return <SupportiveContent />;
|
|
896
|
+
* }
|
|
897
|
+
*
|
|
898
|
+
* return <RegularContent />;
|
|
899
|
+
* }
|
|
900
|
+
* ```
|
|
901
|
+
*/
|
|
902
|
+
export function useESIEmotionState(): GlobalESIState['emotionState'] {
|
|
903
|
+
const { emotionState } = useGlobalESIState();
|
|
904
|
+
return emotionState;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Hook to get user preferences
|
|
909
|
+
*/
|
|
910
|
+
export function useESIPreferences(): GlobalESIState['preferences'] {
|
|
911
|
+
const { preferences } = useGlobalESIState();
|
|
912
|
+
return preferences;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/**
|
|
916
|
+
* Update global ESI state at runtime (e.g., after fetching user context)
|
|
917
|
+
*
|
|
918
|
+
* @example
|
|
919
|
+
* ```tsx
|
|
920
|
+
* useEffect(() => {
|
|
921
|
+
* fetchUserContext().then((ctx) => {
|
|
922
|
+
* updateGlobalESIState({
|
|
923
|
+
* userTier: ctx.tier,
|
|
924
|
+
* userId: ctx.id,
|
|
925
|
+
* emotionState: ctx.emotion,
|
|
926
|
+
* });
|
|
927
|
+
* });
|
|
928
|
+
* }, []);
|
|
929
|
+
* ```
|
|
930
|
+
*/
|
|
931
|
+
export function updateGlobalESIState(partial: Partial<GlobalESIState>): void {
|
|
932
|
+
if (typeof window !== 'undefined' && window.__AEON_ESI_STATE__?.update) {
|
|
933
|
+
window.__AEON_ESI_STATE__.update(partial);
|
|
934
|
+
} else if (typeof window !== 'undefined' && window.__AEON_ESI_STATE__) {
|
|
935
|
+
Object.assign(window.__AEON_ESI_STATE__, partial);
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
// ============================================================================
|
|
940
|
+
// Navigation Hook (edge-ready, no Next.js dependency)
|
|
941
|
+
// ============================================================================
|
|
942
|
+
|
|
943
|
+
import { getNavigator } from '../navigation.js';
|
|
944
|
+
|
|
945
|
+
export interface NavigationRouter {
|
|
946
|
+
push: (url: string) => void;
|
|
947
|
+
replace: (url: string) => void;
|
|
948
|
+
back: () => void;
|
|
949
|
+
prefetch: (url: string) => void;
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
/**
|
|
953
|
+
* Edge-ready navigation hook - works in both Next.js and edge environments
|
|
954
|
+
*
|
|
955
|
+
* @example
|
|
956
|
+
* ```tsx
|
|
957
|
+
* function MyComponent() {
|
|
958
|
+
* const router = useNavigation();
|
|
959
|
+
*
|
|
960
|
+
* return (
|
|
961
|
+
* <button onClick={() => router.push('/dashboard')}>
|
|
962
|
+
* Go to Dashboard
|
|
963
|
+
* </button>
|
|
964
|
+
* );
|
|
965
|
+
* }
|
|
966
|
+
* ```
|
|
967
|
+
*/
|
|
968
|
+
export function useNavigation(): NavigationRouter {
|
|
969
|
+
// Try to get the Aeon navigation engine
|
|
970
|
+
const aeonNavigator = typeof window !== 'undefined' ? getNavigator() : null;
|
|
971
|
+
|
|
972
|
+
const push = useCallback(
|
|
973
|
+
(url: string) => {
|
|
974
|
+
if (aeonNavigator) {
|
|
975
|
+
aeonNavigator.navigate(url);
|
|
976
|
+
} else if (typeof window !== 'undefined') {
|
|
977
|
+
window.location.href = url;
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
[aeonNavigator],
|
|
981
|
+
);
|
|
982
|
+
|
|
983
|
+
const replace = useCallback(
|
|
984
|
+
(url: string) => {
|
|
985
|
+
if (aeonNavigator) {
|
|
986
|
+
aeonNavigator.navigate(url, { replace: true });
|
|
987
|
+
} else if (typeof window !== 'undefined') {
|
|
988
|
+
window.location.replace(url);
|
|
989
|
+
}
|
|
990
|
+
},
|
|
991
|
+
[aeonNavigator],
|
|
992
|
+
);
|
|
993
|
+
|
|
994
|
+
const back = useCallback(() => {
|
|
995
|
+
if (aeonNavigator) {
|
|
996
|
+
aeonNavigator.back();
|
|
997
|
+
} else if (typeof window !== 'undefined') {
|
|
998
|
+
window.history.back();
|
|
999
|
+
}
|
|
1000
|
+
}, [aeonNavigator]);
|
|
1001
|
+
|
|
1002
|
+
const prefetch = useCallback(
|
|
1003
|
+
(url: string) => {
|
|
1004
|
+
if (aeonNavigator) {
|
|
1005
|
+
aeonNavigator.prefetch(url);
|
|
1006
|
+
}
|
|
1007
|
+
// No-op in fallback mode
|
|
1008
|
+
},
|
|
1009
|
+
[aeonNavigator],
|
|
1010
|
+
);
|
|
1011
|
+
|
|
1012
|
+
return { push, replace, back, prefetch };
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// ============================================================================
|
|
1016
|
+
// ESI Namespace Export
|
|
1017
|
+
// ============================================================================
|
|
1018
|
+
|
|
1019
|
+
export const ESI = {
|
|
1020
|
+
// Basic components
|
|
1021
|
+
Provider: ESIProvider,
|
|
1022
|
+
Infer: ESIInfer,
|
|
1023
|
+
Embed: ESIEmbed,
|
|
1024
|
+
Emotion: ESIEmotion,
|
|
1025
|
+
Vision: ESIVision,
|
|
1026
|
+
// Translation components (from esi-translate-react)
|
|
1027
|
+
Translate: ESITranslate,
|
|
1028
|
+
TranslationProvider: TranslationProvider,
|
|
1029
|
+
// Control flow components (from esi-control-react)
|
|
1030
|
+
Structured: ESIStructured,
|
|
1031
|
+
If: ESIIf,
|
|
1032
|
+
Show: ESIShow,
|
|
1033
|
+
Hide: ESIHide,
|
|
1034
|
+
When: ESIWhen,
|
|
1035
|
+
Unless: ESIUnless,
|
|
1036
|
+
Match: ESIMatch,
|
|
1037
|
+
Case: ESICase,
|
|
1038
|
+
Default: ESIDefault,
|
|
1039
|
+
First: ESIFirst,
|
|
1040
|
+
// Gates
|
|
1041
|
+
TierGate: ESITierGate,
|
|
1042
|
+
EmotionGate: ESIEmotionGate,
|
|
1043
|
+
TimeGate: ESITimeGate,
|
|
1044
|
+
// Iteration & Selection
|
|
1045
|
+
ForEach: ESIForEach,
|
|
1046
|
+
Select: ESISelect,
|
|
1047
|
+
ABTest: ESIABTest,
|
|
1048
|
+
// Numeric
|
|
1049
|
+
Clamp: ESIClamp,
|
|
1050
|
+
Score: ESIScore,
|
|
1051
|
+
// Advanced
|
|
1052
|
+
Collaborative: ESICollaborative,
|
|
1053
|
+
Reflect: ESIReflect,
|
|
1054
|
+
Optimize: ESIOptimize,
|
|
1055
|
+
Auto: ESIAuto,
|
|
1056
|
+
// Format transformations (from esi-format-react)
|
|
1057
|
+
Markdown: ESIMarkdown,
|
|
1058
|
+
Latex: ESILatex,
|
|
1059
|
+
Json: ESIJson,
|
|
1060
|
+
Plaintext: ESIPlaintext,
|
|
1061
|
+
Code: ESICode,
|
|
1062
|
+
Semantic: ESISemantic,
|
|
1063
|
+
};
|
|
1064
|
+
|
|
1065
|
+
export default ESI;
|