@affectively/aeon-pages-runtime 0.1.0 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -28,7 +28,7 @@ var __export = (target, all) => {
28
28
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
29
29
  var __require = import.meta.require;
30
30
 
31
- // ../../../../node_modules/react/cjs/react.development.js
31
+ // ../../../../node_modules/.bun/react@19.2.3/node_modules/react/cjs/react.development.js
32
32
  var require_react_development = __commonJS((exports, module) => {
33
33
  (function() {
34
34
  function defineDeprecationWarning(methodName, info) {
@@ -851,7 +851,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
851
851
  })();
852
852
  });
853
853
 
854
- // ../../../../node_modules/react/index.js
854
+ // ../../../../node_modules/.bun/react@19.2.3/node_modules/react/index.js
855
855
  var require_react = __commonJS((exports, module) => {
856
856
  var react_development = __toESM(require_react_development());
857
857
  if (false) {} else {
@@ -859,7 +859,7 @@ var require_react = __commonJS((exports, module) => {
859
859
  }
860
860
  });
861
861
 
862
- // ../../../../node_modules/react/cjs/react-jsx-dev-runtime.development.js
862
+ // ../../../../node_modules/.bun/react@19.2.3/node_modules/react/cjs/react-jsx-dev-runtime.development.js
863
863
  var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
864
864
  var React = __toESM(require_react());
865
865
  (function() {
@@ -1074,7 +1074,7 @@ React keys must be passed directly to JSX without using spread:
1074
1074
  })();
1075
1075
  });
1076
1076
 
1077
- // ../../../../node_modules/react/jsx-dev-runtime.js
1077
+ // ../../../../node_modules/.bun/react@19.2.3/node_modules/react/jsx-dev-runtime.js
1078
1078
  var require_jsx_dev_runtime = __commonJS((exports, module) => {
1079
1079
  var react_jsx_dev_runtime_development = __toESM(require_react_jsx_dev_runtime_development());
1080
1080
  if (false) {} else {
@@ -1082,7 +1082,7 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
1082
1082
  }
1083
1083
  });
1084
1084
 
1085
- // ../../../../node_modules/@affectively/aeon/node_modules/eventemitter3/index.js
1085
+ // ../../../../node_modules/.bun/eventemitter3@5.0.4/node_modules/eventemitter3/index.js
1086
1086
  var require_eventemitter3 = __commonJS((exports, module) => {
1087
1087
  var has = Object.prototype.hasOwnProperty;
1088
1088
  var prefix = "~";
@@ -1260,13 +1260,13 @@ var require_eventemitter3 = __commonJS((exports, module) => {
1260
1260
  }
1261
1261
  });
1262
1262
 
1263
- // ../../../../node_modules/@affectively/aeon/node_modules/eventemitter3/index.mjs
1263
+ // ../../../../node_modules/.bun/eventemitter3@5.0.4/node_modules/eventemitter3/index.mjs
1264
1264
  var import__;
1265
1265
  var init_eventemitter3 = __esm(() => {
1266
1266
  import__ = __toESM(require_eventemitter3(), 1);
1267
1267
  });
1268
1268
 
1269
- // ../../../../node_modules/@affectively/aeon/dist/index.js
1269
+ // ../../../aeon/dist/index.js
1270
1270
  var exports_dist = {};
1271
1271
  __export(exports_dist, {
1272
1272
  setLogger: () => setLogger,
@@ -5062,6 +5062,307 @@ var DEFAULT_ESI_CONFIG = {
5062
5062
  }
5063
5063
  }
5064
5064
  };
5065
+ // src/router/esi-cyrano.ts
5066
+ function esiContext(context, options = {}) {
5067
+ const { emitExhaust = true, id } = options;
5068
+ return {
5069
+ id: id || `esi-context-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
5070
+ params: {
5071
+ model: "custom",
5072
+ custom: {
5073
+ type: "context-drop",
5074
+ emitExhaust
5075
+ }
5076
+ },
5077
+ content: {
5078
+ type: "json",
5079
+ value: JSON.stringify(context)
5080
+ },
5081
+ contextAware: true,
5082
+ signals: ["emotion", "preferences", "history", "time", "device"]
5083
+ };
5084
+ }
5085
+ function esiCyrano(config, options = {}) {
5086
+ const {
5087
+ intent,
5088
+ tone = "warm",
5089
+ trigger = "always",
5090
+ fallback,
5091
+ suggestTool,
5092
+ suggestRoute,
5093
+ autoAcceptNavigation = false,
5094
+ priority = 1,
5095
+ maxTriggersPerSession,
5096
+ cooldownSeconds,
5097
+ speak = false,
5098
+ showCaption = true,
5099
+ requiredTier
5100
+ } = config;
5101
+ const systemPrompt = buildCyranoSystemPrompt(intent, tone);
5102
+ return {
5103
+ id: `esi-cyrano-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
5104
+ params: {
5105
+ model: "llm",
5106
+ system: systemPrompt,
5107
+ temperature: 0.7,
5108
+ maxTokens: 150,
5109
+ fallback,
5110
+ custom: {
5111
+ type: "cyrano-whisper",
5112
+ intent,
5113
+ tone,
5114
+ trigger,
5115
+ suggestTool,
5116
+ suggestRoute,
5117
+ autoAcceptNavigation,
5118
+ priority,
5119
+ maxTriggersPerSession,
5120
+ cooldownSeconds,
5121
+ speak,
5122
+ showCaption
5123
+ },
5124
+ ...options
5125
+ },
5126
+ content: {
5127
+ type: "template",
5128
+ value: buildCyranoPrompt(intent, trigger),
5129
+ variables: {
5130
+ intent,
5131
+ tone,
5132
+ trigger
5133
+ }
5134
+ },
5135
+ contextAware: true,
5136
+ signals: ["emotion", "preferences", "history", "time"],
5137
+ requiredTier
5138
+ };
5139
+ }
5140
+ function buildCyranoSystemPrompt(intent, tone) {
5141
+ const toneGuide = {
5142
+ warm: "Be warm, caring, and approachable. Use gentle language.",
5143
+ calm: "Be calm, measured, and reassuring. Use a steady pace.",
5144
+ encouraging: "Be supportive and uplifting. Celebrate small wins.",
5145
+ playful: "Be light-hearted and fun. Use appropriate humor.",
5146
+ professional: "Be clear and direct. Maintain professionalism.",
5147
+ empathetic: "Show deep understanding. Validate feelings.",
5148
+ neutral: "Be balanced and objective. Provide information."
5149
+ };
5150
+ const intentGuide = {
5151
+ greeting: "Welcome the user. Make them feel at home.",
5152
+ "proactive-check-in": "Check in gently. Ask how they are doing.",
5153
+ "supportive-presence": "Simply acknowledge. Let them know you are here.",
5154
+ "gentle-nudge": "Suggest an action softly. No pressure.",
5155
+ "tool-suggestion": "Recommend a tool that might help.",
5156
+ "navigation-hint": "Suggest exploring another area.",
5157
+ intervention: "Step in supportively. Offer help.",
5158
+ celebration: "Celebrate their progress. Be genuinely happy for them.",
5159
+ reflection: "Invite them to reflect. Ask thoughtful questions.",
5160
+ guidance: "Offer helpful guidance. Be a trusted advisor.",
5161
+ farewell: "Wish them well. Leave the door open.",
5162
+ custom: "Respond appropriately to the context."
5163
+ };
5164
+ return `You are Cyrano, an ambient AI companion. ${toneGuide[tone]} ${intentGuide[intent]}
5165
+
5166
+ Keep responses brief (1-2 sentences). Be natural and conversational.
5167
+ Never start with "I" - use "You" or the situation as the subject.
5168
+ Never say "As an AI" or similar phrases.
5169
+ Respond to the emotional context provided.`;
5170
+ }
5171
+ function buildCyranoPrompt(intent, trigger) {
5172
+ const prompts = {
5173
+ greeting: "Generate a warm greeting based on the time of day and user context.",
5174
+ "proactive-check-in": "Check in with the user based on their emotional state and behavior.",
5175
+ "supportive-presence": "Acknowledge the user's presence and current activity.",
5176
+ "gentle-nudge": "Gently suggest the user might benefit from a particular action.",
5177
+ "tool-suggestion": "Suggest a specific tool that could help with the user's current state.",
5178
+ "navigation-hint": "Suggest the user might want to explore a different area.",
5179
+ intervention: "Offer supportive intervention based on detected stress or difficulty.",
5180
+ celebration: "Celebrate the user's progress or achievement.",
5181
+ reflection: "Invite the user to reflect on their current experience.",
5182
+ guidance: "Offer helpful guidance for the user's current situation.",
5183
+ farewell: "Say goodbye warmly, acknowledging the session.",
5184
+ custom: "Respond appropriately to the context provided."
5185
+ };
5186
+ let prompt = prompts[intent] || prompts.custom;
5187
+ if (trigger !== "always" && trigger !== "never") {
5188
+ prompt += ` The trigger condition is: ${trigger}.`;
5189
+ }
5190
+ return prompt;
5191
+ }
5192
+ function esiHalo(config, options = {}) {
5193
+ const {
5194
+ observe,
5195
+ window: window2 = "session",
5196
+ action = "whisper-to-cyrano",
5197
+ sensitivity = 0.5,
5198
+ crisisLevel = false,
5199
+ parameters = {}
5200
+ } = config;
5201
+ return {
5202
+ id: `esi-halo-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`,
5203
+ params: {
5204
+ model: "custom",
5205
+ custom: {
5206
+ type: "halo-insight",
5207
+ observe,
5208
+ window: window2,
5209
+ action,
5210
+ sensitivity,
5211
+ crisisLevel,
5212
+ parameters
5213
+ },
5214
+ ...options
5215
+ },
5216
+ content: {
5217
+ type: "json",
5218
+ value: JSON.stringify({
5219
+ observation: observe,
5220
+ window: window2,
5221
+ action,
5222
+ sensitivity,
5223
+ crisisLevel
5224
+ })
5225
+ },
5226
+ contextAware: true,
5227
+ signals: ["emotion", "history", "time"]
5228
+ };
5229
+ }
5230
+ function evaluateTrigger(trigger, context, sessionContext) {
5231
+ if (trigger === "always")
5232
+ return true;
5233
+ if (trigger === "never")
5234
+ return false;
5235
+ const [type, condition] = trigger.split(":");
5236
+ switch (type) {
5237
+ case "dwell": {
5238
+ const match = condition?.match(/>(\d+)s/);
5239
+ if (!match)
5240
+ return false;
5241
+ const threshold = parseInt(match[1], 10) * 1000;
5242
+ const dwellTime = sessionContext?.behavior?.dwellTime || 0;
5243
+ return dwellTime > threshold;
5244
+ }
5245
+ case "scroll": {
5246
+ const match = condition?.match(/>(\d+\.?\d*)/);
5247
+ if (!match)
5248
+ return false;
5249
+ const threshold = parseFloat(match[1]);
5250
+ const scrollDepth = sessionContext?.behavior?.scrollDepth || 0;
5251
+ return scrollDepth > threshold;
5252
+ }
5253
+ case "emotion": {
5254
+ const targetEmotion = condition;
5255
+ return sessionContext?.emotion?.primary === targetEmotion || context.emotionState?.primary === targetEmotion;
5256
+ }
5257
+ case "behavior": {
5258
+ if (condition === "aimless") {
5259
+ return sessionContext?.behavior?.isAimlessClicking === true;
5260
+ }
5261
+ if (condition === "hesitation") {
5262
+ return sessionContext?.behavior?.hesitationDetected === true;
5263
+ }
5264
+ return false;
5265
+ }
5266
+ case "hrv": {
5267
+ const match = condition?.match(/<(\d+)/);
5268
+ if (!match)
5269
+ return false;
5270
+ const threshold = parseInt(match[1], 10);
5271
+ const hrv = sessionContext?.biometric?.hrv || 100;
5272
+ return hrv < threshold;
5273
+ }
5274
+ case "stress": {
5275
+ const match = condition?.match(/>(\d+)/);
5276
+ if (!match)
5277
+ return false;
5278
+ const threshold = parseInt(match[1], 10);
5279
+ const stress = sessionContext?.biometric?.stressScore || 0;
5280
+ return stress > threshold;
5281
+ }
5282
+ case "session": {
5283
+ if (condition === "start") {
5284
+ return context.isNewSession;
5285
+ }
5286
+ const idleMatch = condition?.match(/idle:(\d+)m/);
5287
+ if (idleMatch) {
5288
+ return false;
5289
+ }
5290
+ return false;
5291
+ }
5292
+ case "navigation": {
5293
+ const targetRoute = condition?.replace("to:", "");
5294
+ return sessionContext?.currentRoute === targetRoute;
5295
+ }
5296
+ case "time": {
5297
+ const hour = context.localHour;
5298
+ if (condition === "morning")
5299
+ return hour >= 6 && hour < 12;
5300
+ if (condition === "afternoon")
5301
+ return hour >= 12 && hour < 18;
5302
+ if (condition === "evening")
5303
+ return hour >= 18 && hour < 22;
5304
+ if (condition === "night")
5305
+ return hour >= 22 || hour < 6;
5306
+ return false;
5307
+ }
5308
+ default:
5309
+ return false;
5310
+ }
5311
+ }
5312
+ function createExhaustEntry(directive, result, type) {
5313
+ return {
5314
+ type,
5315
+ timestamp: Date.now(),
5316
+ content: {
5317
+ directiveId: directive.id,
5318
+ output: result.output,
5319
+ model: result.model,
5320
+ success: result.success,
5321
+ latencyMs: result.latencyMs
5322
+ },
5323
+ visible: type === "cyrano" || type === "user",
5324
+ source: directive.params.model
5325
+ };
5326
+ }
5327
+ var CYRANO_TOOL_SUGGESTIONS = {
5328
+ breathing: {
5329
+ triggers: ["stress:>70", "hrv:<40", "emotion:anxious"],
5330
+ tool: "breathing/4-7-8",
5331
+ reason: "You seem stressed - a breathing exercise might help"
5332
+ },
5333
+ grounding: {
5334
+ triggers: ["emotion:overwhelmed", "behavior:aimless"],
5335
+ tool: "grounding/5-4-3-2-1",
5336
+ reason: "A grounding exercise can help center you"
5337
+ },
5338
+ journaling: {
5339
+ triggers: ["dwell:>120s", "emotion:reflective"],
5340
+ tool: "journaling/freeform",
5341
+ reason: "Would you like to write about what's on your mind?"
5342
+ },
5343
+ insights: {
5344
+ triggers: ["navigation:to:/insights", "dwell:>60s"],
5345
+ tool: "insights/dashboard",
5346
+ reason: "Your recent patterns are ready to explore"
5347
+ }
5348
+ };
5349
+ function getToolSuggestions(context, sessionContext) {
5350
+ const suggestions = [];
5351
+ for (const [, config] of Object.entries(CYRANO_TOOL_SUGGESTIONS)) {
5352
+ for (const trigger of config.triggers) {
5353
+ if (evaluateTrigger(trigger, context, sessionContext)) {
5354
+ suggestions.push({
5355
+ tool: config.tool,
5356
+ reason: config.reason,
5357
+ priority: trigger.startsWith("stress") || trigger.startsWith("hrv") ? 2 : 1
5358
+ });
5359
+ break;
5360
+ }
5361
+ }
5362
+ }
5363
+ return suggestions.sort((a, b) => b.priority - a.priority);
5364
+ }
5365
+
5065
5366
  // src/router/esi.ts
5066
5367
  var esiCache = new Map;
5067
5368
  function getCacheKey(directive, context) {
@@ -5792,6 +6093,69 @@ function useESIInfer(options = {}) {
5792
6093
  }, []);
5793
6094
  return { run, result, isLoading, error, reset };
5794
6095
  }
6096
+ var DEFAULT_ESI_STATE = {
6097
+ userTier: "free",
6098
+ emotionState: null,
6099
+ preferences: {
6100
+ theme: "auto",
6101
+ reducedMotion: false
6102
+ },
6103
+ localHour: new Date().getHours(),
6104
+ timezone: "UTC",
6105
+ features: {
6106
+ aiInference: true,
6107
+ emotionTracking: true,
6108
+ collaboration: false,
6109
+ advancedInsights: false,
6110
+ customThemes: false,
6111
+ voiceSynthesis: false,
6112
+ imageAnalysis: false
6113
+ },
6114
+ isNewSession: true,
6115
+ recentPages: [],
6116
+ viewport: { width: 1920, height: 1080 },
6117
+ connection: "4g"
6118
+ };
6119
+ function useGlobalESIState() {
6120
+ const [state, setState] = import_react.useState(() => {
6121
+ if (typeof window !== "undefined" && window.__AEON_ESI_STATE__) {
6122
+ return window.__AEON_ESI_STATE__;
6123
+ }
6124
+ return DEFAULT_ESI_STATE;
6125
+ });
6126
+ import_react.useEffect(() => {
6127
+ if (typeof window !== "undefined" && window.__AEON_ESI_STATE__?.subscribe) {
6128
+ const unsubscribe = window.__AEON_ESI_STATE__.subscribe((newState) => {
6129
+ setState(newState);
6130
+ });
6131
+ return unsubscribe;
6132
+ }
6133
+ }, []);
6134
+ return state;
6135
+ }
6136
+ function useESIFeature(feature) {
6137
+ const { features } = useGlobalESIState();
6138
+ return features[feature] ?? false;
6139
+ }
6140
+ function useESITier() {
6141
+ const { userTier } = useGlobalESIState();
6142
+ return userTier;
6143
+ }
6144
+ function useESIEmotionState() {
6145
+ const { emotionState } = useGlobalESIState();
6146
+ return emotionState;
6147
+ }
6148
+ function useESIPreferences() {
6149
+ const { preferences } = useGlobalESIState();
6150
+ return preferences;
6151
+ }
6152
+ function updateGlobalESIState(partial) {
6153
+ if (typeof window !== "undefined" && window.__AEON_ESI_STATE__?.update) {
6154
+ window.__AEON_ESI_STATE__.update(partial);
6155
+ } else if (typeof window !== "undefined" && window.__AEON_ESI_STATE__) {
6156
+ Object.assign(window.__AEON_ESI_STATE__, partial);
6157
+ }
6158
+ }
5795
6159
  var ESI = {
5796
6160
  Provider: ESIProvider,
5797
6161
  Infer: ESIInfer,
@@ -6283,6 +6647,80 @@ function addSpeculationHeaders(response, prefetch, prerender) {
6283
6647
  headers
6284
6648
  });
6285
6649
  }
6650
+ function serializeToESIState(context) {
6651
+ const tierFeatures = {
6652
+ free: {
6653
+ aiInference: true,
6654
+ emotionTracking: true,
6655
+ collaboration: false,
6656
+ advancedInsights: false,
6657
+ customThemes: false,
6658
+ voiceSynthesis: false,
6659
+ imageAnalysis: false
6660
+ },
6661
+ starter: {
6662
+ aiInference: true,
6663
+ emotionTracking: true,
6664
+ collaboration: false,
6665
+ advancedInsights: true,
6666
+ customThemes: true,
6667
+ voiceSynthesis: false,
6668
+ imageAnalysis: false
6669
+ },
6670
+ pro: {
6671
+ aiInference: true,
6672
+ emotionTracking: true,
6673
+ collaboration: true,
6674
+ advancedInsights: true,
6675
+ customThemes: true,
6676
+ voiceSynthesis: true,
6677
+ imageAnalysis: true
6678
+ },
6679
+ enterprise: {
6680
+ aiInference: true,
6681
+ emotionTracking: true,
6682
+ collaboration: true,
6683
+ advancedInsights: true,
6684
+ customThemes: true,
6685
+ voiceSynthesis: true,
6686
+ imageAnalysis: true
6687
+ }
6688
+ };
6689
+ return {
6690
+ userTier: context.tier,
6691
+ emotionState: context.emotionState ? {
6692
+ primary: context.emotionState.primary,
6693
+ valence: context.emotionState.valence,
6694
+ arousal: context.emotionState.arousal,
6695
+ confidence: context.emotionState.confidence
6696
+ } : undefined,
6697
+ preferences: {
6698
+ theme: context.preferences.theme,
6699
+ reducedMotion: context.reducedMotion,
6700
+ language: context.preferences.language
6701
+ },
6702
+ sessionId: context.sessionId,
6703
+ localHour: context.localHour,
6704
+ timezone: context.timezone,
6705
+ features: tierFeatures[context.tier],
6706
+ userId: context.userId,
6707
+ isNewSession: context.isNewSession,
6708
+ recentPages: context.recentPages.slice(-10),
6709
+ viewport: {
6710
+ width: context.viewport.width,
6711
+ height: context.viewport.height
6712
+ },
6713
+ connection: context.connection
6714
+ };
6715
+ }
6716
+ function generateESIStateScript(esiState) {
6717
+ const stateJson = JSON.stringify(esiState);
6718
+ return `<script>window.__AEON_ESI_STATE__=${stateJson};</script>`;
6719
+ }
6720
+ function generateESIStateScriptFromContext(context) {
6721
+ const esiState = serializeToESIState(context);
6722
+ return generateESIStateScript(esiState);
6723
+ }
6286
6724
  // src/router/speculation.ts
6287
6725
  function supportsSpeculationRules() {
6288
6726
  if (typeof document === "undefined")
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Aeon Pages Service Worker Push Handler
3
+ *
4
+ * Optional push notification handler for service workers.
5
+ * Only loaded when push feature is enabled.
6
+ *
7
+ * Features:
8
+ * - Push event handling with notification display
9
+ * - Notification click handling with navigation
10
+ * - Background sync integration
11
+ * - Badge updates
12
+ */
13
+ export interface PushNotificationData {
14
+ title: string;
15
+ body?: string;
16
+ icon?: string;
17
+ badge?: string;
18
+ tag?: string;
19
+ data?: {
20
+ url?: string;
21
+ action?: string;
22
+ [key: string]: unknown;
23
+ };
24
+ requireInteraction?: boolean;
25
+ vibrate?: number[];
26
+ actions?: Array<{
27
+ action: string;
28
+ title: string;
29
+ icon?: string;
30
+ }>;
31
+ }
32
+ export interface PushHandlerConfig {
33
+ defaultIcon?: string;
34
+ defaultBadge?: string;
35
+ defaultVibrate?: number[];
36
+ onNotificationClick?: (data: PushNotificationData['data']) => string | undefined;
37
+ }
38
+ /**
39
+ * Handle incoming push event
40
+ */
41
+ export declare function handlePush(event: PushEvent, config?: PushHandlerConfig): void;
42
+ /**
43
+ * Handle notification click event
44
+ */
45
+ export declare function handleNotificationClick(event: NotificationEvent, config?: PushHandlerConfig): void;
46
+ /**
47
+ * Handle notification close event
48
+ */
49
+ export declare function handleNotificationClose(event: NotificationEvent): void;
50
+ /**
51
+ * Handle background sync event for offline queue
52
+ */
53
+ export declare function handleSync(event: ExtendableEvent, tag: string): void;
54
+ export interface ServiceWorkerMessage {
55
+ type: string;
56
+ payload?: unknown;
57
+ }
58
+ /**
59
+ * Handle messages from main thread
60
+ */
61
+ export declare function handleMessage(event: ExtendableMessageEvent, handlers: Record<string, (payload: unknown) => Promise<unknown> | unknown>): void;
62
+ /**
63
+ * Register push event handlers in service worker
64
+ */
65
+ export declare function registerPushHandlers(sw: ServiceWorkerGlobalScope, config?: PushHandlerConfig): void;
66
+ /**
67
+ * Register sync handlers in service worker
68
+ */
69
+ export declare function registerSyncHandlers(sw: ServiceWorkerGlobalScope): void;
70
+ /**
71
+ * Register message handlers in service worker
72
+ */
73
+ export declare function registerMessageHandlers(sw: ServiceWorkerGlobalScope, handlers: Record<string, (payload: unknown) => Promise<unknown> | unknown>): void;
74
+ /**
75
+ * Convert VAPID public key from base64url to Uint8Array
76
+ */
77
+ export declare function urlBase64ToUint8Array(base64String: string): Uint8Array;
78
+ /**
79
+ * Serialize push subscription for sending to server
80
+ */
81
+ export declare function serializePushSubscription(subscription: PushSubscription): {
82
+ endpoint: string;
83
+ keys: {
84
+ p256dh: string;
85
+ auth: string;
86
+ };
87
+ };
@@ -8,5 +8,10 @@
8
8
  *
9
9
  * With 8.4KB framework + ~2-5KB per page session, we can preload EVERYTHING.
10
10
  * A site with 100 pages = ~315KB total (smaller than one hero image!)
11
+ *
12
+ * Optional Push & Offline Features:
13
+ * - Push notification handling (when enabled)
14
+ * - Background sync for offline queue
15
+ * - Notification click/close handlers
11
16
  */
12
17
  export {};
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Skeleton Hydration - Client-Side Swap
3
+ *
4
+ * Handles the smooth transition from skeleton to real content.
5
+ * Designed to be inlined in <head> for instant execution.
6
+ */
7
+ /** Skeleton swap options */
8
+ export interface SkeletonSwapOptions {
9
+ /** Enable cross-fade animation */
10
+ fade?: boolean;
11
+ /** Fade duration in milliseconds */
12
+ duration?: number;
13
+ /** Callback when swap completes */
14
+ onComplete?: () => void;
15
+ }
16
+ /**
17
+ * Initialize skeleton system
18
+ * Called immediately in <head> before body renders
19
+ */
20
+ export declare function initSkeleton(): void;
21
+ /**
22
+ * Swap skeleton with real content
23
+ * Called when content is ready to render
24
+ */
25
+ export declare function swapToContent(options?: SkeletonSwapOptions): void;
26
+ /**
27
+ * Check if skeleton is still visible
28
+ */
29
+ export declare function isSkeletonVisible(): boolean;
30
+ /**
31
+ * Generate minified inline init script for <head>
32
+ * This script executes before body renders, ensuring skeleton shows first
33
+ */
34
+ export declare function generateSkeletonInitScript(): string;
35
+ /**
36
+ * Generate the complete HTML structure for skeleton-first rendering
37
+ */
38
+ export declare function generateSkeletonPageStructure(options: {
39
+ title: string;
40
+ description?: string;
41
+ skeletonHtml: string;
42
+ skeletonCss: string;
43
+ contentHtml: string;
44
+ contentCss: string;
45
+ headExtra?: string;
46
+ bodyExtra?: string;
47
+ }): string;
48
+ /**
49
+ * Generate skeleton swap script for async content loading
50
+ * Use this when content loads after initial page render
51
+ */
52
+ export declare function generateAsyncSwapScript(): string;
53
+ declare global {
54
+ interface Window {
55
+ __AEON_SKELETON__?: {
56
+ swap: (options?: SkeletonSwapOptions) => void;
57
+ isVisible: () => boolean;
58
+ done: boolean;
59
+ };
60
+ }
61
+ }