@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,545 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Aeon Flux Navigation Tools
|
|
3
|
+
*
|
|
4
|
+
* MCP tools for Cyrano to navigate and personalize the site.
|
|
5
|
+
* Supports auto-accept mode for seamless navigation suggestions.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { Tool, TextContent } from '@modelcontextprotocol/sdk/types.js';
|
|
9
|
+
|
|
10
|
+
// ============================================================================
|
|
11
|
+
// State (would be connected to real site state in production)
|
|
12
|
+
// ============================================================================
|
|
13
|
+
|
|
14
|
+
interface NavigationState {
|
|
15
|
+
currentRoute: string;
|
|
16
|
+
previousRoutes: string[];
|
|
17
|
+
pendingSuggestions: Array<{
|
|
18
|
+
route: string;
|
|
19
|
+
reason: string;
|
|
20
|
+
autoAccept: boolean;
|
|
21
|
+
timestamp: number;
|
|
22
|
+
}>;
|
|
23
|
+
autoAcceptEnabled: boolean;
|
|
24
|
+
sessionId: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let state: NavigationState = {
|
|
28
|
+
currentRoute: '/',
|
|
29
|
+
previousRoutes: [],
|
|
30
|
+
pendingSuggestions: [],
|
|
31
|
+
autoAcceptEnabled: false,
|
|
32
|
+
sessionId: '',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
// Event emitter for navigation events (to be wired to site)
|
|
36
|
+
type NavigationEventHandler = (event: {
|
|
37
|
+
type: 'navigate' | 'suggest' | 'personalize' | 'invoke_tool';
|
|
38
|
+
data: unknown;
|
|
39
|
+
}) => void;
|
|
40
|
+
|
|
41
|
+
const eventHandlers: Set<NavigationEventHandler> = new Set();
|
|
42
|
+
|
|
43
|
+
export function onNavigationEvent(handler: NavigationEventHandler): () => void {
|
|
44
|
+
eventHandlers.add(handler);
|
|
45
|
+
return () => eventHandlers.delete(handler);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function emit(
|
|
49
|
+
type: NavigationEventHandler extends (e: infer E) => void ? E['type'] : never,
|
|
50
|
+
data: unknown,
|
|
51
|
+
): void {
|
|
52
|
+
for (const handler of eventHandlers) {
|
|
53
|
+
handler({ type, data });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Tool Definitions
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
export const navigateTool: Tool = {
|
|
62
|
+
name: 'navigate',
|
|
63
|
+
description: `Navigate to a route in the aeon-flux site. Use this to take the user directly to a page.
|
|
64
|
+
|
|
65
|
+
Examples:
|
|
66
|
+
- navigate({ route: '/breathing/4-7-8' }) - Go to breathing exercise
|
|
67
|
+
- navigate({ route: '/insights' }) - Go to insights dashboard
|
|
68
|
+
- navigate({ route: '/learning/emotions/joy' }) - Go to joy learning page`,
|
|
69
|
+
inputSchema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
route: {
|
|
73
|
+
type: 'string',
|
|
74
|
+
description:
|
|
75
|
+
'The route to navigate to (e.g., "/breathing/4-7-8", "/insights")',
|
|
76
|
+
},
|
|
77
|
+
autoAccept: {
|
|
78
|
+
type: 'boolean',
|
|
79
|
+
description:
|
|
80
|
+
'Whether to navigate immediately without user confirmation (default: false)',
|
|
81
|
+
default: false,
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
required: ['route'],
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export const suggestRouteTool: Tool = {
|
|
89
|
+
name: 'suggest_route',
|
|
90
|
+
description: `Suggest a route to the user. Shows a gentle suggestion with a reason.
|
|
91
|
+
The user can accept or dismiss the suggestion.
|
|
92
|
+
|
|
93
|
+
Use this when Cyrano wants to recommend a page based on context.
|
|
94
|
+
|
|
95
|
+
Examples:
|
|
96
|
+
- suggest_route({ route: '/breathing', reason: 'You seem stressed - this might help' })
|
|
97
|
+
- suggest_route({ route: '/insights/patterns', reason: 'Your weekly patterns are ready' })`,
|
|
98
|
+
inputSchema: {
|
|
99
|
+
type: 'object',
|
|
100
|
+
properties: {
|
|
101
|
+
route: {
|
|
102
|
+
type: 'string',
|
|
103
|
+
description: 'The route to suggest',
|
|
104
|
+
},
|
|
105
|
+
reason: {
|
|
106
|
+
type: 'string',
|
|
107
|
+
description: 'Why Cyrano is suggesting this route',
|
|
108
|
+
},
|
|
109
|
+
autoAccept: {
|
|
110
|
+
type: 'boolean',
|
|
111
|
+
description:
|
|
112
|
+
'If true and user has auto-accept enabled, navigate immediately',
|
|
113
|
+
default: false,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
required: ['route', 'reason'],
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
export const getCurrentRouteTool: Tool = {
|
|
121
|
+
name: 'get_current_route',
|
|
122
|
+
description: `Get the current route the user is on. Use this to understand context.`,
|
|
123
|
+
inputSchema: {
|
|
124
|
+
type: 'object',
|
|
125
|
+
properties: {},
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
export const getSitemapTool: Tool = {
|
|
130
|
+
name: 'get_sitemap',
|
|
131
|
+
description: `Get the site structure. Useful for finding relevant pages.
|
|
132
|
+
|
|
133
|
+
Examples:
|
|
134
|
+
- get_sitemap() - Get full sitemap
|
|
135
|
+
- get_sitemap({ filter: 'breathing' }) - Get pages related to breathing
|
|
136
|
+
- get_sitemap({ filter: 'tools' }) - Get all tool pages`,
|
|
137
|
+
inputSchema: {
|
|
138
|
+
type: 'object',
|
|
139
|
+
properties: {
|
|
140
|
+
filter: {
|
|
141
|
+
type: 'string',
|
|
142
|
+
description: 'Optional filter to narrow down results',
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
export const speculateTool: Tool = {
|
|
149
|
+
name: 'speculate',
|
|
150
|
+
description: `Get likely next routes for prefetching. Helps optimize navigation.
|
|
151
|
+
|
|
152
|
+
Based on current context and user patterns, returns probable next destinations.`,
|
|
153
|
+
inputSchema: {
|
|
154
|
+
type: 'object',
|
|
155
|
+
properties: {
|
|
156
|
+
depth: {
|
|
157
|
+
type: 'number',
|
|
158
|
+
description: 'How many routes to speculate (default: 3)',
|
|
159
|
+
default: 3,
|
|
160
|
+
},
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
export const personalizeTool: Tool = {
|
|
166
|
+
name: 'personalize',
|
|
167
|
+
description: `Apply personalization to the current page based on user context.
|
|
168
|
+
|
|
169
|
+
Examples:
|
|
170
|
+
- personalize({ theme: 'dark' }) - Switch to dark theme
|
|
171
|
+
- personalize({ accent: '#4A90D9' }) - Set accent color (e.g., calming blue)
|
|
172
|
+
- personalize({ density: 'comfortable' }) - More spacing for stressed users`,
|
|
173
|
+
inputSchema: {
|
|
174
|
+
type: 'object',
|
|
175
|
+
properties: {
|
|
176
|
+
theme: {
|
|
177
|
+
type: 'string',
|
|
178
|
+
enum: ['light', 'dark'],
|
|
179
|
+
description: 'Theme mode',
|
|
180
|
+
},
|
|
181
|
+
accent: {
|
|
182
|
+
type: 'string',
|
|
183
|
+
description: 'Accent color in hex format',
|
|
184
|
+
},
|
|
185
|
+
density: {
|
|
186
|
+
type: 'string',
|
|
187
|
+
enum: ['compact', 'normal', 'comfortable'],
|
|
188
|
+
description: 'Layout density',
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
export const invokeToolTool: Tool = {
|
|
195
|
+
name: 'invoke_tool',
|
|
196
|
+
description: `Invoke a specific tool in the application.
|
|
197
|
+
|
|
198
|
+
Cyrano can suggest and invoke tools based on user context.
|
|
199
|
+
250+ tools available including breathing exercises, journaling, insights, etc.
|
|
200
|
+
|
|
201
|
+
Examples:
|
|
202
|
+
- invoke_tool({ toolId: 'breathing/4-7-8' }) - Start 4-7-8 breathing
|
|
203
|
+
- invoke_tool({ toolId: 'grounding/5-4-3-2-1' }) - Start grounding exercise
|
|
204
|
+
- invoke_tool({ toolId: 'journaling/freeform' }) - Open journaling
|
|
205
|
+
- invoke_tool({ toolId: 'insights/patterns' }) - Show patterns analysis`,
|
|
206
|
+
inputSchema: {
|
|
207
|
+
type: 'object',
|
|
208
|
+
properties: {
|
|
209
|
+
toolId: {
|
|
210
|
+
type: 'string',
|
|
211
|
+
description: 'The tool ID to invoke (e.g., "breathing/4-7-8")',
|
|
212
|
+
},
|
|
213
|
+
params: {
|
|
214
|
+
type: 'object',
|
|
215
|
+
description: 'Optional parameters to pass to the tool',
|
|
216
|
+
additionalProperties: true,
|
|
217
|
+
},
|
|
218
|
+
},
|
|
219
|
+
required: ['toolId'],
|
|
220
|
+
},
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
// ============================================================================
|
|
224
|
+
// Tool Handlers
|
|
225
|
+
// ============================================================================
|
|
226
|
+
|
|
227
|
+
export async function handleNavigate(args: {
|
|
228
|
+
route: string;
|
|
229
|
+
autoAccept?: boolean;
|
|
230
|
+
}): Promise<{ content: TextContent[] }> {
|
|
231
|
+
const { route, autoAccept = false } = args;
|
|
232
|
+
|
|
233
|
+
// Update state
|
|
234
|
+
state.previousRoutes.push(state.currentRoute);
|
|
235
|
+
state.currentRoute = route;
|
|
236
|
+
|
|
237
|
+
// Emit navigation event
|
|
238
|
+
emit('navigate', { route, autoAccept });
|
|
239
|
+
|
|
240
|
+
return {
|
|
241
|
+
content: [
|
|
242
|
+
{
|
|
243
|
+
type: 'text',
|
|
244
|
+
text: autoAccept
|
|
245
|
+
? `Navigated to ${route}`
|
|
246
|
+
: `Navigation to ${route} initiated. Awaiting user confirmation.`,
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export async function handleSuggestRoute(args: {
|
|
253
|
+
route: string;
|
|
254
|
+
reason: string;
|
|
255
|
+
autoAccept?: boolean;
|
|
256
|
+
}): Promise<{ content: TextContent[] }> {
|
|
257
|
+
const { route, reason, autoAccept = false } = args;
|
|
258
|
+
|
|
259
|
+
// Add to pending suggestions
|
|
260
|
+
state.pendingSuggestions.push({
|
|
261
|
+
route,
|
|
262
|
+
reason,
|
|
263
|
+
autoAccept,
|
|
264
|
+
timestamp: Date.now(),
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
// Emit suggestion event
|
|
268
|
+
emit('suggest', { route, reason, autoAccept });
|
|
269
|
+
|
|
270
|
+
// If auto-accept is enabled and requested, navigate immediately
|
|
271
|
+
if (autoAccept && state.autoAcceptEnabled) {
|
|
272
|
+
state.previousRoutes.push(state.currentRoute);
|
|
273
|
+
state.currentRoute = route;
|
|
274
|
+
|
|
275
|
+
return {
|
|
276
|
+
content: [
|
|
277
|
+
{
|
|
278
|
+
type: 'text',
|
|
279
|
+
text: `Auto-navigated to ${route}: ${reason}`,
|
|
280
|
+
},
|
|
281
|
+
],
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
content: [
|
|
287
|
+
{
|
|
288
|
+
type: 'text',
|
|
289
|
+
text: `Suggested ${route}: ${reason}. Awaiting user response.`,
|
|
290
|
+
},
|
|
291
|
+
],
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
export async function handleGetCurrentRoute(): Promise<{
|
|
296
|
+
content: TextContent[];
|
|
297
|
+
}> {
|
|
298
|
+
return {
|
|
299
|
+
content: [
|
|
300
|
+
{
|
|
301
|
+
type: 'text',
|
|
302
|
+
text: JSON.stringify(
|
|
303
|
+
{
|
|
304
|
+
currentRoute: state.currentRoute,
|
|
305
|
+
previousRoutes: state.previousRoutes.slice(-5),
|
|
306
|
+
pendingSuggestions: state.pendingSuggestions,
|
|
307
|
+
},
|
|
308
|
+
null,
|
|
309
|
+
2,
|
|
310
|
+
),
|
|
311
|
+
},
|
|
312
|
+
],
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export async function handleGetSitemap(args: {
|
|
317
|
+
filter?: string;
|
|
318
|
+
}): Promise<{ content: TextContent[] }> {
|
|
319
|
+
const { filter } = args;
|
|
320
|
+
|
|
321
|
+
// Simplified sitemap structure (would be loaded from sitemapRAG in production)
|
|
322
|
+
const sitemap = [
|
|
323
|
+
// Core pages
|
|
324
|
+
{ route: '/', title: 'Home', category: 'core' },
|
|
325
|
+
{ route: '/dashboard', title: 'Dashboard', category: 'core' },
|
|
326
|
+
{ route: '/insights', title: 'Insights', category: 'core' },
|
|
327
|
+
{ route: '/chat', title: 'Chat with Cyrano', category: 'core' },
|
|
328
|
+
|
|
329
|
+
// Tools - Breathing
|
|
330
|
+
{ route: '/breathing', title: 'Breathing Exercises', category: 'tools' },
|
|
331
|
+
{ route: '/breathing/4-7-8', title: '4-7-8 Breathing', category: 'tools' },
|
|
332
|
+
{ route: '/breathing/box', title: 'Box Breathing', category: 'tools' },
|
|
333
|
+
{
|
|
334
|
+
route: '/breathing/coherent',
|
|
335
|
+
title: 'Coherent Breathing',
|
|
336
|
+
category: 'tools',
|
|
337
|
+
},
|
|
338
|
+
|
|
339
|
+
// Tools - Grounding
|
|
340
|
+
{ route: '/grounding', title: 'Grounding Exercises', category: 'tools' },
|
|
341
|
+
{
|
|
342
|
+
route: '/grounding/5-4-3-2-1',
|
|
343
|
+
title: '5-4-3-2-1 Grounding',
|
|
344
|
+
category: 'tools',
|
|
345
|
+
},
|
|
346
|
+
{ route: '/grounding/body-scan', title: 'Body Scan', category: 'tools' },
|
|
347
|
+
|
|
348
|
+
// Tools - Journaling
|
|
349
|
+
{ route: '/journaling', title: 'Journaling', category: 'tools' },
|
|
350
|
+
{
|
|
351
|
+
route: '/journaling/freeform',
|
|
352
|
+
title: 'Freeform Journaling',
|
|
353
|
+
category: 'tools',
|
|
354
|
+
},
|
|
355
|
+
{
|
|
356
|
+
route: '/journaling/gratitude',
|
|
357
|
+
title: 'Gratitude Journal',
|
|
358
|
+
category: 'tools',
|
|
359
|
+
},
|
|
360
|
+
|
|
361
|
+
// Learning
|
|
362
|
+
{ route: '/learning', title: 'Learning', category: 'learning' },
|
|
363
|
+
{ route: '/learning/emotions', title: 'Emotions', category: 'learning' },
|
|
364
|
+
{ route: '/learning/emotions/joy', title: 'Joy', category: 'learning' },
|
|
365
|
+
{
|
|
366
|
+
route: '/learning/emotions/sadness',
|
|
367
|
+
title: 'Sadness',
|
|
368
|
+
category: 'learning',
|
|
369
|
+
},
|
|
370
|
+
{ route: '/learning/emotions/anger', title: 'Anger', category: 'learning' },
|
|
371
|
+
{ route: '/learning/emotions/fear', title: 'Fear', category: 'learning' },
|
|
372
|
+
|
|
373
|
+
// Settings
|
|
374
|
+
{ route: '/settings', title: 'Settings', category: 'settings' },
|
|
375
|
+
{
|
|
376
|
+
route: '/settings/privacy',
|
|
377
|
+
title: 'Privacy Settings',
|
|
378
|
+
category: 'settings',
|
|
379
|
+
},
|
|
380
|
+
{
|
|
381
|
+
route: '/settings/notifications',
|
|
382
|
+
title: 'Notification Settings',
|
|
383
|
+
category: 'settings',
|
|
384
|
+
},
|
|
385
|
+
|
|
386
|
+
// Account
|
|
387
|
+
{ route: '/account', title: 'Account', category: 'account' },
|
|
388
|
+
{ route: '/account/profile', title: 'Profile', category: 'account' },
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
// Apply filter if provided
|
|
392
|
+
const filteredSitemap = filter
|
|
393
|
+
? sitemap.filter(
|
|
394
|
+
(page) =>
|
|
395
|
+
page.route.includes(filter) ||
|
|
396
|
+
page.title.toLowerCase().includes(filter.toLowerCase()) ||
|
|
397
|
+
page.category.includes(filter),
|
|
398
|
+
)
|
|
399
|
+
: sitemap;
|
|
400
|
+
|
|
401
|
+
return {
|
|
402
|
+
content: [
|
|
403
|
+
{
|
|
404
|
+
type: 'text',
|
|
405
|
+
text: JSON.stringify(filteredSitemap, null, 2),
|
|
406
|
+
},
|
|
407
|
+
],
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
export async function handleSpeculate(args: {
|
|
412
|
+
depth?: number;
|
|
413
|
+
}): Promise<{ content: TextContent[] }> {
|
|
414
|
+
const { depth = 3 } = args;
|
|
415
|
+
|
|
416
|
+
// Speculation based on current route (simplified - would use heuristic adapter in production)
|
|
417
|
+
const speculations: Record<string, string[]> = {
|
|
418
|
+
'/': ['/dashboard', '/breathing', '/insights'],
|
|
419
|
+
'/dashboard': ['/insights', '/breathing', '/chat'],
|
|
420
|
+
'/breathing': ['/breathing/4-7-8', '/breathing/box', '/grounding'],
|
|
421
|
+
'/breathing/4-7-8': ['/dashboard', '/breathing', '/insights'],
|
|
422
|
+
'/insights': ['/dashboard', '/learning', '/settings'],
|
|
423
|
+
'/learning': ['/learning/emotions', '/dashboard', '/insights'],
|
|
424
|
+
'/settings': ['/settings/privacy', '/account', '/dashboard'],
|
|
425
|
+
};
|
|
426
|
+
|
|
427
|
+
const likelyRoutes = speculations[state.currentRoute] || [
|
|
428
|
+
'/dashboard',
|
|
429
|
+
'/insights',
|
|
430
|
+
'/breathing',
|
|
431
|
+
];
|
|
432
|
+
|
|
433
|
+
return {
|
|
434
|
+
content: [
|
|
435
|
+
{
|
|
436
|
+
type: 'text',
|
|
437
|
+
text: JSON.stringify(
|
|
438
|
+
{
|
|
439
|
+
currentRoute: state.currentRoute,
|
|
440
|
+
likelyNextRoutes: likelyRoutes.slice(0, depth),
|
|
441
|
+
confidence: 0.7,
|
|
442
|
+
},
|
|
443
|
+
null,
|
|
444
|
+
2,
|
|
445
|
+
),
|
|
446
|
+
},
|
|
447
|
+
],
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
export async function handlePersonalize(args: {
|
|
452
|
+
theme?: 'light' | 'dark';
|
|
453
|
+
accent?: string;
|
|
454
|
+
density?: 'compact' | 'normal' | 'comfortable';
|
|
455
|
+
}): Promise<{ content: TextContent[] }> {
|
|
456
|
+
// Emit personalization event
|
|
457
|
+
emit('personalize', args);
|
|
458
|
+
|
|
459
|
+
const applied: string[] = [];
|
|
460
|
+
if (args.theme) applied.push(`theme: ${args.theme}`);
|
|
461
|
+
if (args.accent) applied.push(`accent: ${args.accent}`);
|
|
462
|
+
if (args.density) applied.push(`density: ${args.density}`);
|
|
463
|
+
|
|
464
|
+
return {
|
|
465
|
+
content: [
|
|
466
|
+
{
|
|
467
|
+
type: 'text',
|
|
468
|
+
text: `Applied personalization: ${applied.join(', ')}`,
|
|
469
|
+
},
|
|
470
|
+
],
|
|
471
|
+
};
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
export async function handleInvokeTool(args: {
|
|
475
|
+
toolId: string;
|
|
476
|
+
params?: Record<string, unknown>;
|
|
477
|
+
}): Promise<{ content: TextContent[] }> {
|
|
478
|
+
const { toolId, params } = args;
|
|
479
|
+
|
|
480
|
+
// Emit tool invocation event
|
|
481
|
+
emit('invoke_tool', { toolId, params });
|
|
482
|
+
|
|
483
|
+
// Parse tool category and name
|
|
484
|
+
const [category, name] = toolId.split('/');
|
|
485
|
+
|
|
486
|
+
return {
|
|
487
|
+
content: [
|
|
488
|
+
{
|
|
489
|
+
type: 'text',
|
|
490
|
+
text: JSON.stringify(
|
|
491
|
+
{
|
|
492
|
+
status: 'invoked',
|
|
493
|
+
toolId,
|
|
494
|
+
category,
|
|
495
|
+
name,
|
|
496
|
+
params: params || {},
|
|
497
|
+
timestamp: Date.now(),
|
|
498
|
+
},
|
|
499
|
+
null,
|
|
500
|
+
2,
|
|
501
|
+
),
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
};
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
// ============================================================================
|
|
508
|
+
// State Management
|
|
509
|
+
// ============================================================================
|
|
510
|
+
|
|
511
|
+
export function setNavigationState(newState: Partial<NavigationState>): void {
|
|
512
|
+
state = { ...state, ...newState };
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
export function getNavigationState(): NavigationState {
|
|
516
|
+
return { ...state };
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
export function enableAutoAccept(enabled: boolean): void {
|
|
520
|
+
state.autoAcceptEnabled = enabled;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
export function clearPendingSuggestions(): void {
|
|
524
|
+
state.pendingSuggestions = [];
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export function acceptSuggestion(route: string): boolean {
|
|
528
|
+
const index = state.pendingSuggestions.findIndex((s) => s.route === route);
|
|
529
|
+
if (index === -1) return false;
|
|
530
|
+
|
|
531
|
+
state.pendingSuggestions.splice(index, 1);
|
|
532
|
+
state.previousRoutes.push(state.currentRoute);
|
|
533
|
+
state.currentRoute = route;
|
|
534
|
+
|
|
535
|
+
emit('navigate', { route, fromSuggestion: true });
|
|
536
|
+
return true;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
export function dismissSuggestion(route: string): boolean {
|
|
540
|
+
const index = state.pendingSuggestions.findIndex((s) => s.route === route);
|
|
541
|
+
if (index === -1) return false;
|
|
542
|
+
|
|
543
|
+
state.pendingSuggestions.splice(index, 1);
|
|
544
|
+
return true;
|
|
545
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "NodeNext",
|
|
5
|
+
"moduleResolution": "NodeNext",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"declaration": true,
|
|
8
|
+
"declarationMap": true,
|
|
9
|
+
"sourceMap": true,
|
|
10
|
+
"outDir": "./dist",
|
|
11
|
+
"rootDir": "./src",
|
|
12
|
+
"strict": true,
|
|
13
|
+
"esModuleInterop": true,
|
|
14
|
+
"skipLibCheck": true,
|
|
15
|
+
"forceConsistentCasingInFileNames": true,
|
|
16
|
+
"resolveJsonModule": true,
|
|
17
|
+
"isolatedModules": true
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*"],
|
|
20
|
+
"exclude": ["node_modules", "dist"]
|
|
21
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@affectively/aeon-pages-react",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "React bindings for @affectively/aeon-pages",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"types": "./dist/index.d.ts"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc --declaration --emitDeclarationOnly",
|
|
17
|
+
"dev": "bun --watch ./src/index.ts",
|
|
18
|
+
"test": "bun test",
|
|
19
|
+
"prepublishOnly": "npm run build"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"dependencies": {
|
|
26
|
+
"@affectively/aeon-pages-runtime": "^0.3.0"
|
|
27
|
+
},
|
|
28
|
+
"peerDependencies": {
|
|
29
|
+
"react": ">=18.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"typescript": "^5.7.0",
|
|
33
|
+
"@types/react": "^18.0.0"
|
|
34
|
+
},
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/affectively/aeon-pages"
|
|
39
|
+
}
|
|
40
|
+
}
|