@hawsen-the-first/interactiv 0.0.1 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -253,6 +253,65 @@ eventManager.drag(".draggable", {
253
253
  });
254
254
  ```
255
255
 
256
+ ### Screensaver Manager
257
+
258
+ The screensaver manager monitors user activity and triggers actions after a configurable timeout period. It supports two modes:
259
+
260
+ #### Standard Screensaver Mode
261
+
262
+ Navigate to a dedicated screensaver page after inactivity. User activity exits the screensaver and returns to the previous or starting page.
263
+
264
+ ```typescript
265
+ // Create a screensaver page with views
266
+ const screensaverPage = new Page("screensaver-page", orchestrator, false);
267
+ const screensaverView = new View("screensaver-view", orchestrator, false,
268
+ html`<div class="screensaver">...</div>`,
269
+ css`.screensaver { /* styles */ }`
270
+ );
271
+ screensaverPage.addView(screensaverView);
272
+
273
+ // Add screensaver with standard behavior
274
+ app.addScreensaver(screensaverPage, {
275
+ timeoutSeconds: 30,
276
+ defaultViewId: "screensaver-view",
277
+ exitBehavior: "return", // Return to where the user was before
278
+ transitionConfig: { type: "fade", duration: 500 },
279
+ });
280
+ ```
281
+
282
+ #### Return-to-Home Mode
283
+
284
+ Instead of showing a screensaver, navigate back to a specified home page/view after inactivity. No screensaver page is needed — the app simply redirects to the home screen. The screensaver never enters an "active" state, so user activity just resets the inactivity timer.
285
+
286
+ ```typescript
287
+ // No screensaver page needed — pass null
288
+ app.addScreensaver(null, {
289
+ timeoutSeconds: 60,
290
+ screensaverViewBehavior: "returnHome",
291
+ startingPageId: "home-page", // The home page to navigate to
292
+ defaultViewId: "home-view", // Optional: the home view to show
293
+ transitionConfig: { type: "fade", duration: 500 },
294
+ });
295
+ ```
296
+
297
+ #### Configuration Options
298
+
299
+ | Option | Type | Description |
300
+ |--------|------|-------------|
301
+ | `timeoutSeconds` | `number` | Seconds of inactivity before activation |
302
+ | `page` | `Page` | Screensaver page (required unless using `returnHome`) |
303
+ | `screensaverViewBehavior` | `"default" \| "specific" \| "return" \| "returnHome"` | How to handle the view on activation |
304
+ | `defaultViewId` | `string` | Default view to show (or home view for `returnHome`) |
305
+ | `exitBehavior` | `"reset" \| "return"` | Where to go when exiting the screensaver |
306
+ | `startingPageId` | `string` | Page to navigate to on exit/reset (or home page for `returnHome`) |
307
+ | `startingViewId` | `string` | View within the starting page on exit |
308
+ | `transitionConfig` | `TransitionConfig` | Transition animation configuration |
309
+ | `activateCallback` | `() => void` | Called when screensaver activates or returns home |
310
+ | `deactivateCallback` | `() => void` | Called when screensaver deactivates |
311
+ | `blockerCallback` | `() => boolean` | Return `true` to prevent activation |
312
+ | `rebootTimeout` | `number \| null` | Minutes before triggering a reboot callback |
313
+ | `rebootCallback` | `() => void` | Called when reboot timeout elapses |
314
+
256
315
  ### Settings Manager
257
316
 
258
317
  Create hidden settings pages with corner touch activation. See [SETTINGS_MANAGER.md](./SETTINGS_MANAGER.md) for detailed documentation.
package/dist/index.d.ts CHANGED
@@ -15,6 +15,7 @@ import { Logger } from "./utils/logger";
15
15
  import { useAnimations, AnimationManager } from "./src/animationBus";
16
16
  import { configureLogger } from "./src/logger";
17
17
  import { GarbageCollector, createGarbageCollector, type GarbageCollectionStats } from "./src/garbageCollector";
18
- export { createOrchestrator, EventOrchestrator, EventBus, AppBuilder, Page, View, Component, NavigationManager, ScreensaverManager, SettingsManager, EventManager, AnimationManager, stateManager, getGlobalState, setGlobalState, subscribeToGlobalState, useGlobalStateExternal, useAnimations, ExternalStateManager, ComponentStateManager, GarbageCollector, createGarbageCollector, css, html, Logger, configureLogger, };
19
- export type { PointerEventData, DragCallbacks, HoverCallbacks, SwipeCallbacks, StateSubscription, ScreensaverConfig, SettingsConfig, PageProps, ViewProps, ComponentProps, GarbageCollectionStats, };
18
+ import { TransitionOverlay, transitionWithOverlay, type TransitionOverlayConfig, type TransitionRequestPayload } from "./src/transitionOverlay";
19
+ export { createOrchestrator, EventOrchestrator, EventBus, AppBuilder, Page, View, Component, NavigationManager, ScreensaverManager, SettingsManager, EventManager, AnimationManager, stateManager, getGlobalState, setGlobalState, subscribeToGlobalState, useGlobalStateExternal, useAnimations, ExternalStateManager, ComponentStateManager, GarbageCollector, createGarbageCollector, TransitionOverlay, transitionWithOverlay, css, html, Logger, configureLogger, };
20
+ export type { PointerEventData, DragCallbacks, HoverCallbacks, SwipeCallbacks, StateSubscription, ScreensaverConfig, SettingsConfig, PageProps, ViewProps, ComponentProps, GarbageCollectionStats, TransitionOverlayConfig, TransitionRequestPayload, };
20
21
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,cAAc,EACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAEhB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EAErB,gBAAgB,EAChB,sBAAsB,EACtB,GAAG,EACH,IAAI,EACJ,MAAM,EACN,eAAe,GAChB,CAAC;AAEF,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,SAAS,EACT,cAAc,EACd,sBAAsB,GACvB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACrE,OAAO,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACjF,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,qBAAqB,EACtB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EACV,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,cAAc,EACf,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxE,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AACrE,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EACL,gBAAgB,EAChB,sBAAsB,EACtB,KAAK,sBAAsB,EAC5B,MAAM,wBAAwB,CAAC;AAChC,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,KAAK,uBAAuB,EAC5B,KAAK,wBAAwB,EAC9B,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EACL,kBAAkB,EAClB,iBAAiB,EACjB,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,IAAI,EACJ,SAAS,EACT,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,gBAAgB,EAEhB,YAAY,EACZ,cAAc,EACd,cAAc,EACd,sBAAsB,EACtB,sBAAsB,EACtB,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EAErB,gBAAgB,EAChB,sBAAsB,EAEtB,iBAAiB,EACjB,qBAAqB,EACrB,GAAG,EACH,IAAI,EACJ,MAAM,EACN,eAAe,GAChB,CAAC;AAEF,YAAY,EACV,gBAAgB,EAChB,aAAa,EACb,cAAc,EACd,cAAc,EACd,iBAAiB,EACjB,iBAAiB,EACjB,cAAc,EACd,SAAS,EACT,SAAS,EACT,cAAc,EACd,sBAAsB,EACtB,uBAAuB,EACvB,wBAAwB,GACzB,CAAC"}
package/dist/index.js CHANGED
@@ -10,11 +10,15 @@ import { Logger } from "./utils/logger";
10
10
  import { useAnimations, AnimationManager } from "./src/animationBus";
11
11
  import { configureLogger } from "./src/logger";
12
12
  import { GarbageCollector, createGarbageCollector, } from "./src/garbageCollector";
13
+ import { TransitionOverlay, transitionWithOverlay, } from "./src/transitionOverlay";
14
+ 5;
13
15
  export { createOrchestrator, EventOrchestrator, EventBus, AppBuilder, Page, View, Component, NavigationManager, ScreensaverManager, SettingsManager, EventManager, AnimationManager,
14
16
  // External State Management
15
17
  stateManager, getGlobalState, setGlobalState, subscribeToGlobalState, useGlobalStateExternal, useAnimations, ExternalStateManager, ComponentStateManager,
16
18
  // Garbage Collection
17
- GarbageCollector, createGarbageCollector, css, html, Logger, configureLogger, };
19
+ GarbageCollector, createGarbageCollector,
20
+ // Transition Overlay System
21
+ TransitionOverlay, transitionWithOverlay, css, html, Logger, configureLogger, };
18
22
  // Named exports for direct import
19
23
  // export {
20
24
  // stateManager,
@@ -5,6 +5,7 @@ import { EventManager } from "./eventManager";
5
5
  import { ComponentStateManager } from "./stateManager";
6
6
  import type { PointerEventData, DragCallbacks, HoverCallbacks, SwipeCallbacks } from "./eventManager";
7
7
  import type { RenderOptions } from "./types";
8
+ import { TransitionOverlay, type TransitionOverlayConfig } from "./transitionOverlay";
8
9
  declare abstract class RenderableComponent {
9
10
  componentId: string;
10
11
  state: any;
@@ -128,26 +129,61 @@ declare class AppBuilder extends RenderableComponent {
128
129
  isTransitioning(): boolean;
129
130
  /**
130
131
  * Add a screensaver page with the specified configuration
131
- * The screensaver page can contain multiple views for rich interactive experiences
132
- * @param screensaverPage The page to use as the screensaver (can contain multiple views)
132
+ * The screensaver page can contain multiple views for rich interactive experiences.
133
+ * Pass `null` for screensaverPage when using `screensaverViewBehavior: "returnHome"` mode,
134
+ * which navigates back to a home page/view on timeout instead of showing a screensaver.
135
+ * @param screensaverPage The page to use as the screensaver (can contain multiple views), or null for returnHome mode
133
136
  * @param config Configuration object for the screensaver (excluding 'page' property)
134
137
  */
135
- addScreensaver(screensaverPage: Page, config: Omit<ScreensaverConfig, 'page'>): void;
138
+ addScreensaver(screensaverPage: Page | null, config: Omit<ScreensaverConfig, 'page'>): void;
136
139
  attachToDom(): void;
137
140
  }
138
141
  declare class Page extends RenderableComponent {
139
142
  private views;
143
+ private transitionOverlay;
144
+ private transitionBus;
140
145
  constructor(id: string, orchestrator: EventOrchestrator, bubbleChanges?: boolean, customTemplate?: string, customStyles?: string);
141
146
  protected defineTemplate(): void;
142
147
  protected defineStyles(): void;
143
148
  addView(view: View): void;
144
149
  removeView(viewId: string): void;
145
150
  /**
146
- * Add a screensaver page with the specified configuration
147
- * @param screensaverPage The page to use as the screensaver (can contain multiple views)
151
+ * Enable transition overlay for this page
152
+ *
153
+ * Creates a full-screen overlay that fades in/out to hide content changes during state updates.
154
+ * Once enabled, components can request transitions via the "page-transition-overlay" event bus.
155
+ *
156
+ * @param config - Configuration for the overlay appearance and timing
157
+ */
158
+ useTransitionOverlay(config: TransitionOverlayConfig): void;
159
+ /**
160
+ * Execute a callback wrapped in the transition overlay lifecycle
161
+ *
162
+ * This is a convenience method for direct Page-level access to the overlay.
163
+ * For deeply nested components, prefer using the standalone `transitionWithOverlay` utility
164
+ * or emitting to the "page-transition-overlay" event bus.
165
+ *
166
+ * @param callback - Function to execute while overlay is opaque (state changes happen here)
167
+ * @returns Promise that resolves after the full transition completes
168
+ * @throws Error if useTransitionOverlay() hasn't been called
169
+ */
170
+ transitionWithOverlay(callback: () => void | Promise<void>): Promise<void>;
171
+ /**
172
+ * Check if this page has a transition overlay enabled
173
+ */
174
+ hasTransitionOverlay(): boolean;
175
+ /**
176
+ * Get the TransitionOverlay instance for this page (for NavigationManager integration)
177
+ */
178
+ getTransitionOverlay(): TransitionOverlay | null;
179
+ /**
180
+ * Add a screensaver page with the specified configuration.
181
+ * Pass `null` for screensaverPage when using `screensaverViewBehavior: "returnHome"` mode,
182
+ * which navigates back to a home page/view on timeout instead of showing a screensaver.
183
+ * @param screensaverPage The page to use as the screensaver (can contain multiple views), or null for returnHome mode
148
184
  * @param config Configuration object for the screensaver (excluding 'page' property)
149
185
  */
150
- addScreensaver(screensaverPage: Page, config: Omit<ScreensaverConfig, 'page'>): void;
186
+ addScreensaver(screensaverPage: Page | null, config: Omit<ScreensaverConfig, 'page'>): void;
151
187
  /**
152
188
  * Add a hidden settings page to this page with the specified configuration
153
189
  * The settings page is activated by touching corners in sequence: top-left, top-right, bottom-right
@@ -1 +1 @@
1
- {"version":3,"file":"appBuilder.d.ts","sourceRoot":"","sources":["../../src/appBuilder.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"appBuilder.d.ts","sourceRoot":"","sources":["../../src/appBuilder.ts"],"names":[],"mappings":"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"}
@@ -6,6 +6,7 @@ import { SettingsManager } from "./settingsManager";
6
6
  import { EventManager } from "./eventManager";
7
7
  import { ComponentStateManager } from "./stateManager";
8
8
  import { logger } from "./logger";
9
+ import { TransitionOverlay, stateChangesToCallback, } from "./transitionOverlay";
9
10
  class RenderableComponent {
10
11
  componentId;
11
12
  state;
@@ -499,20 +500,24 @@ class AppBuilder extends RenderableComponent {
499
500
  }
500
501
  /**
501
502
  * Add a screensaver page with the specified configuration
502
- * The screensaver page can contain multiple views for rich interactive experiences
503
- * @param screensaverPage The page to use as the screensaver (can contain multiple views)
503
+ * The screensaver page can contain multiple views for rich interactive experiences.
504
+ * Pass `null` for screensaverPage when using `screensaverViewBehavior: "returnHome"` mode,
505
+ * which navigates back to a home page/view on timeout instead of showing a screensaver.
506
+ * @param screensaverPage The page to use as the screensaver (can contain multiple views), or null for returnHome mode
504
507
  * @param config Configuration object for the screensaver (excluding 'page' property)
505
508
  */
506
509
  addScreensaver(screensaverPage, config) {
507
- // Add the screensaver page to the app
508
- this.addPage(screensaverPage);
510
+ // Add the screensaver page to the app (only if provided)
511
+ if (screensaverPage) {
512
+ this.addPage(screensaverPage);
513
+ }
509
514
  // Register the screensaver with the screensaver manager via event bus
510
515
  const screensaverBus = this.orchestrator.getEventBus("screensaver-manager");
511
516
  if (screensaverBus) {
512
517
  screensaverBus.emit("register-screensaver", {
513
518
  config: {
514
519
  ...config,
515
- page: screensaverPage,
520
+ page: screensaverPage || undefined,
516
521
  },
517
522
  });
518
523
  }
@@ -534,6 +539,8 @@ class AppBuilder extends RenderableComponent {
534
539
  // Page - Contains Views
535
540
  class Page extends RenderableComponent {
536
541
  views = [];
542
+ transitionOverlay = null;
543
+ transitionBus = null;
537
544
  constructor(id, orchestrator, bubbleChanges = true, customTemplate, customStyles) {
538
545
  super(id, orchestrator, bubbleChanges);
539
546
  // Set custom template/styles before initial render if provided
@@ -644,8 +651,100 @@ class Page extends RenderableComponent {
644
651
  }
645
652
  }
646
653
  /**
647
- * Add a screensaver page with the specified configuration
648
- * @param screensaverPage The page to use as the screensaver (can contain multiple views)
654
+ * Enable transition overlay for this page
655
+ *
656
+ * Creates a full-screen overlay that fades in/out to hide content changes during state updates.
657
+ * Once enabled, components can request transitions via the "page-transition-overlay" event bus.
658
+ *
659
+ * @param config - Configuration for the overlay appearance and timing
660
+ */
661
+ useTransitionOverlay(config) {
662
+ if (this.transitionOverlay) {
663
+ logger.warn(`Page ${this.componentId} already has a transition overlay configured`);
664
+ return;
665
+ }
666
+ // Create the transition overlay
667
+ this.transitionOverlay = new TransitionOverlay(config, this.shadowRoot);
668
+ // Register the event bus for transition requests
669
+ this.transitionBus = this.orchestrator.registerEventBus("page-transition-overlay");
670
+ // Listen for transition requests
671
+ this.transitionBus.on("request-transition", async (e) => {
672
+ const payload = e.detail;
673
+ try {
674
+ // Build the callback from state changes or use provided callback
675
+ let callback;
676
+ if (payload.callback) {
677
+ // Direct callback provided (from standalone utility)
678
+ callback = payload.callback;
679
+ }
680
+ else if (payload.stateChanges) {
681
+ // State changes array provided (from consumer usage)
682
+ callback = async () => {
683
+ stateChangesToCallback(payload.stateChanges)();
684
+ // Execute afterStateChange if provided
685
+ if (payload.afterStateChange) {
686
+ await Promise.resolve(payload.afterStateChange());
687
+ }
688
+ };
689
+ }
690
+ else {
691
+ throw new Error("Transition request must include either 'callback' or 'stateChanges'");
692
+ }
693
+ // Execute the transition with config overrides
694
+ await this.transitionOverlay.executeTransition(callback, {
695
+ fadeInDuration: payload.fadeInDuration,
696
+ holdDuration: payload.holdDuration,
697
+ fadeOutDuration: payload.fadeOutDuration,
698
+ });
699
+ // Resolve the promise if provided (from standalone utility)
700
+ if (payload._resolve) {
701
+ payload._resolve();
702
+ }
703
+ }
704
+ catch (error) {
705
+ logger.error("Transition overlay error:", error);
706
+ // Reject the promise if provided (from standalone utility)
707
+ if (payload._reject) {
708
+ payload._reject(error);
709
+ }
710
+ }
711
+ });
712
+ logger.trace(`Transition overlay enabled for page ${this.componentId}`);
713
+ }
714
+ /**
715
+ * Execute a callback wrapped in the transition overlay lifecycle
716
+ *
717
+ * This is a convenience method for direct Page-level access to the overlay.
718
+ * For deeply nested components, prefer using the standalone `transitionWithOverlay` utility
719
+ * or emitting to the "page-transition-overlay" event bus.
720
+ *
721
+ * @param callback - Function to execute while overlay is opaque (state changes happen here)
722
+ * @returns Promise that resolves after the full transition completes
723
+ * @throws Error if useTransitionOverlay() hasn't been called
724
+ */
725
+ async transitionWithOverlay(callback) {
726
+ if (!this.transitionOverlay) {
727
+ throw new Error(`Page ${this.componentId} does not have a transition overlay. Call useTransitionOverlay() first.`);
728
+ }
729
+ return this.transitionOverlay.executeTransition(callback);
730
+ }
731
+ /**
732
+ * Check if this page has a transition overlay enabled
733
+ */
734
+ hasTransitionOverlay() {
735
+ return this.transitionOverlay !== null;
736
+ }
737
+ /**
738
+ * Get the TransitionOverlay instance for this page (for NavigationManager integration)
739
+ */
740
+ getTransitionOverlay() {
741
+ return this.transitionOverlay;
742
+ }
743
+ /**
744
+ * Add a screensaver page with the specified configuration.
745
+ * Pass `null` for screensaverPage when using `screensaverViewBehavior: "returnHome"` mode,
746
+ * which navigates back to a home page/view on timeout instead of showing a screensaver.
747
+ * @param screensaverPage The page to use as the screensaver (can contain multiple views), or null for returnHome mode
649
748
  * @param config Configuration object for the screensaver (excluding 'page' property)
650
749
  */
651
750
  addScreensaver(screensaverPage, config) {
@@ -655,7 +754,7 @@ class Page extends RenderableComponent {
655
754
  screensaverBus.emit("register-screensaver", {
656
755
  config: {
657
756
  ...config,
658
- page: screensaverPage,
757
+ page: screensaverPage || undefined,
659
758
  },
660
759
  });
661
760
  }
@@ -18,8 +18,7 @@ export declare class EventBus<DetailType = any> {
18
18
  * Destroy this event bus and clean up all listeners
19
19
  */
20
20
  destroy(): void;
21
- emit(type: string, detail?: DetailType): boolean | undefined;
22
- private validateEventDispatch;
21
+ emit(type: string, detail?: DetailType): boolean;
23
22
  }
24
23
  export declare class EventOrchestrator {
25
24
  private eventBuses;
@@ -1 +1 @@
1
- {"version":3,"file":"eventBus.d.ts","sourceRoot":"","sources":["../../src/eventBus.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"eventBus.d.ts","sourceRoot":"","sources":["../../src/eventBus.ts"],"names":[],"mappings":"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"}
@@ -86,24 +86,12 @@ export class EventBus {
86
86
  log.trace(`Event Bus ${this.id} destroyed`);
87
87
  }
88
88
  emit(type, detail) {
89
- try {
90
- this.validateEventDispatch(type);
91
- return this.eventTarget.dispatchEvent(new CustomEvent(type, { detail }));
92
- }
93
- catch (error) {
94
- log.error(error.message, error);
95
- }
96
- }
97
- // private validateNewListener(type: string): void {
98
- // // Allow multiple listeners for the same event type
99
- // // This validation is too restrictive for our use case
100
- // // Components may need to register multiple listeners for the same event
101
- // return;
102
- // }
103
- validateEventDispatch(type) {
89
+ // Check if there are any listeners before dispatching
104
90
  if (this.activeListeners.filter((l) => l.eventName === type).length === 0) {
105
- throw new Error(`Failed to dispatch event. Event Bus with Id ${this.id} does not contain a listener for an event with the name ${type}`);
91
+ log.trace(`Event Bus [${this.id}]: No listeners registered for event "${type}", skipping dispatch.`);
92
+ return true; // Return true to indicate the event was "handled" (just not dispatched)
106
93
  }
94
+ return this.eventTarget.dispatchEvent(new CustomEvent(type, { detail }));
107
95
  }
108
96
  }
109
97
  export class EventOrchestrator {
@@ -66,6 +66,10 @@ export declare class NavigationManager {
66
66
  private performPageNavigationInternal;
67
67
  private performViewNavigationInternal;
68
68
  private performPageTransition;
69
+ /**
70
+ * Perform an immediate view swap (no animation) - used when transition overlay is handling the fade
71
+ */
72
+ private performViewSwapImmediate;
69
73
  private performViewTransition;
70
74
  private animateOut;
71
75
  private animateIn;
@@ -1 +1 @@
1
- {"version":3,"file":"navigationManager.d.ts","sourceRoot":"","sources":["../../src/navigationManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAKtE,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,OAAO,GAAG,MAAM,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC9D,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,MAAM,CAAC;IAC7C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAkC;IACzD,OAAO,CAAC,MAAM,CAAC,WAAW,CAAkB;IAE5C,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,KAAK,CAAgC;IAC7C,OAAO,CAAC,kBAAkB,CAA2B;IACrD,OAAO,CAAC,wBAAwB,CAA2C;IAE3E;;;OAGG;WACW,WAAW,IAAI,iBAAiB,GAAG,IAAI;gBAIzC,YAAY,EAAE,iBAAiB;IA4B3C,OAAO,CAAC,qBAAqB;IAa7B,OAAO,CAAC,mBAAmB;IAsBpB,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAa9B,YAAY,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAQrC,OAAO,CAAC,kBAAkB;IAM1B,OAAO,CAAC,kBAAkB;IAMb,cAAc,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,gBAAmC,EAC3C,QAAQ,GAAE,MAAoB,GAC7B,OAAO,CAAC,IAAI,CAAC;IAYH,cAAc,CACzB,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,gBAAmC,EAC3C,QAAQ,GAAE,MAAoB,GAC7B,OAAO,CAAC,IAAI,CAAC;YAYF,6BAA6B;YA6B7B,6BAA6B;YAgC7B,qBAAqB;YAmBrB,qBAAqB;YAqBrB,UAAU;YAuEV,SAAS;IA2FvB,OAAO,CAAC,yBAAyB;IAkBjC,OAAO,CAAC,qBAAqB;IAwB7B,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,QAAQ;IAchB,OAAO,CAAC,QAAQ;IAgBhB,OAAO,CAAC,QAAQ;IAUhB,OAAO,CAAC,YAAY;IAQb,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC,gBAAgB,IAAI,MAAM,GAAG,IAAI;IAIjC,eAAe,IAAI,OAAO;IAI1B,kBAAkB,IAAI,MAAM,EAAE;IAI9B,kBAAkB,IAAI,MAAM,EAAE;IAK9B,sBAAsB,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,iBAAiB;IAIpF,sBAAsB,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,iBAAiB;IAIpF,0BAA0B,CAAC,QAAQ,EAAE,CAAC,eAAe,EAAE,OAAO,KAAK,IAAI,GAAG,iBAAiB;IAIlG;;;OAGG;IACI,0BAA0B,IAAI,IAAI;IAezC;;OAEG;IACI,wBAAwB,IAAI,MAAM;IAIzC;;;;;;OAMG;IACI,OAAO,IAAI,IAAI;CAoBvB"}
1
+ {"version":3,"file":"navigationManager.d.ts","sourceRoot":"","sources":["../../src/navigationManager.ts"],"names":[],"mappings":"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"}
@@ -181,13 +181,35 @@ export class NavigationManager {
181
181
  log.trace(`Starting navigation from ${previousViewId} to ${viewId}`);
182
182
  stateManager.set("navigation.isTransitioning", true);
183
183
  try {
184
- await this.performViewTransition(viewId, config);
185
- stateManager.set("navigation.currentViewId", viewId);
186
- this.eventBus.emit("view-changed", {
187
- newViewId: viewId,
188
- previousViewId,
189
- });
190
- log.trace(`Navigation to ${viewId} completed successfully`);
184
+ // Check if current page has a transition overlay
185
+ const currentPageId = stateManager.get("navigation.currentPageId");
186
+ const currentPage = currentPageId ? this.pages.get(currentPageId) : null;
187
+ const hasOverlay = currentPage && currentPage.hasTransitionOverlay();
188
+ if (hasOverlay) {
189
+ // Use the overlay to wrap the view transition
190
+ log.trace(`Using transition overlay for view navigation to ${viewId}`);
191
+ const overlay = currentPage.getTransitionOverlay();
192
+ await overlay.executeTransition(async () => {
193
+ // Perform the view swap while overlay is opaque
194
+ await this.performViewSwapImmediate(viewId, previousViewId);
195
+ stateManager.set("navigation.currentViewId", viewId);
196
+ });
197
+ this.eventBus.emit("view-changed", {
198
+ newViewId: viewId,
199
+ previousViewId,
200
+ });
201
+ log.trace(`Navigation to ${viewId} completed successfully with overlay`);
202
+ }
203
+ else {
204
+ // No overlay - use standard transition
205
+ await this.performViewTransition(viewId, config);
206
+ stateManager.set("navigation.currentViewId", viewId);
207
+ this.eventBus.emit("view-changed", {
208
+ newViewId: viewId,
209
+ previousViewId,
210
+ });
211
+ log.trace(`Navigation to ${viewId} completed successfully`);
212
+ }
191
213
  }
192
214
  catch (error) {
193
215
  log.error(`Navigation to ${viewId} failed:`, error);
@@ -211,6 +233,23 @@ export class NavigationManager {
211
233
  }
212
234
  await this.animateIn(targetPage.getHostElement(), normalizedConfig);
213
235
  }
236
+ /**
237
+ * Perform an immediate view swap (no animation) - used when transition overlay is handling the fade
238
+ */
239
+ async performViewSwapImmediate(targetViewId, currentViewId) {
240
+ const currentView = currentViewId ? this.views.get(currentViewId) : null;
241
+ const targetView = this.views.get(targetViewId);
242
+ // Hide current view immediately
243
+ if (currentView) {
244
+ this.hideView(currentViewId);
245
+ }
246
+ // Show target view immediately (no animation since overlay handles the fade)
247
+ this.showView(targetViewId, false);
248
+ const element = targetView.getHostElement();
249
+ element.style.opacity = "1";
250
+ element.style.visibility = "visible";
251
+ element.style.transform = "none";
252
+ }
214
253
  async performViewTransition(targetViewId, config) {
215
254
  const currentViewId = stateManager.get("navigation.currentViewId");
216
255
  const currentView = currentViewId ? this.views.get(currentViewId) : null;
@@ -3,9 +3,9 @@ import { Page } from "./appBuilder";
3
3
  import { NavigationManager, type TransitionConfig } from "./navigationManager";
4
4
  export interface ScreensaverConfig {
5
5
  timeoutSeconds: number;
6
- page: Page;
6
+ page?: Page;
7
7
  defaultViewId?: string;
8
- screensaverViewBehavior?: "default" | "specific" | "return";
8
+ screensaverViewBehavior?: "default" | "specific" | "return" | "returnHome";
9
9
  specificViewId?: string;
10
10
  transitionConfig?: TransitionConfig;
11
11
  exitBehavior?: "reset" | "return";
@@ -38,6 +38,7 @@ export declare class ScreensaverManager {
38
38
  private initializeGlobalState;
39
39
  private setupEventListeners;
40
40
  registerScreensaver(config: ScreensaverConfig): void;
41
+ private isReturnHomeMode;
41
42
  private validateConfig;
42
43
  private setupGlobalActivityListeners;
43
44
  private shouldIgnoreActivity;
@@ -45,6 +46,12 @@ export declare class ScreensaverManager {
45
46
  private pauseActivityTimer;
46
47
  private clearActivityTimer;
47
48
  private activateScreensaver;
49
+ /**
50
+ * Return-to-home activation: navigates to the configured home page/view
51
+ * without entering "screensaver active" state. The timer resets immediately
52
+ * for the next inactivity cycle.
53
+ */
54
+ private activateReturnHome;
48
55
  private determineScreensaverView;
49
56
  private handleScreensaverExit;
50
57
  private deactivateScreensaver;
@@ -1 +1 @@
1
- {"version":3,"file":"screensaverManager.d.ts","sourceRoot":"","sources":["../../src/screensaverManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAM/E,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,IAAI,CAAC;IACX,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uBAAuB,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,CAAC;IAC5D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,eAAe,CAIf;IAGR,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAG1C,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAStC;gBAEU,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB;IASjF,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,mBAAmB;IAwCpB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAiC3D,OAAO,CAAC,cAAc;IA0BtB,OAAO,CAAC,4BAA4B;IAmDpC,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,kBAAkB;YAOZ,mBAAmB;IAkDjC,OAAO,CAAC,wBAAwB;YAclB,qBAAqB;YAmDrB,qBAAqB;IAI5B,QAAQ,IAAI,OAAO;IAInB,gBAAgB,IAAI,iBAAiB,GAAG,IAAI;IAI5C,mBAAmB,IAAI,MAAM,GAAG,IAAI;IAIpC,mBAAmB,IAAI,MAAM,GAAG,IAAI;IAIpC,wBAAwB,IAAI,MAAM,GAAG,IAAI;IAKzC,aAAa,IAAI,IAAI;IAKrB,eAAe,IAAI,IAAI;IAIvB,UAAU,IAAI,IAAI;IAMzB,OAAO,CAAC,OAAO;IAcR,OAAO,IAAI,IAAI;IAUtB,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,uBAAuB;CAOhC"}
1
+ {"version":3,"file":"screensaverManager.d.ts","sourceRoot":"","sources":["../../src/screensaverManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,KAAK,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AAM/E,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,uBAAuB,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC3E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC,YAAY,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAC;IAClC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,gBAAgB,CAAC,EAAE,MAAM,IAAI,CAAC;IAC9B,kBAAkB,CAAC,EAAE,MAAM,IAAI,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,cAAc,CAAC,EAAE,MAAM,IAAI,CAAC;CAC7B;AAED,qBAAa,kBAAkB;IAC7B,OAAO,CAAC,QAAQ,CAAW;IAC3B,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,iBAAiB,CAAoB;IAC7C,OAAO,CAAC,MAAM,CAAkC;IAChD,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,mBAAmB,CAAkB;IAC7C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,qBAAqB,CAAuB;IACpD,OAAO,CAAC,eAAe,CAIf;IAGR,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAQ;IAG1C,OAAO,CAAC,QAAQ,CAAC,uBAAuB,CAStC;gBAEU,YAAY,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,iBAAiB;IASjF,OAAO,CAAC,qBAAqB;IAkB7B,OAAO,CAAC,mBAAmB;IAyCpB,mBAAmB,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAyC3D,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,cAAc;IAkCtB,OAAO,CAAC,4BAA4B;IAmDpC,OAAO,CAAC,oBAAoB;IAgB5B,OAAO,CAAC,kBAAkB;IAe1B,OAAO,CAAC,kBAAkB;IAO1B,OAAO,CAAC,kBAAkB;YAOZ,mBAAmB;IA6DjC;;;;OAIG;YACW,kBAAkB;IAkDhC,OAAO,CAAC,wBAAwB;YAclB,qBAAqB;YAmDrB,qBAAqB;IAI5B,QAAQ,IAAI,OAAO;IAInB,gBAAgB,IAAI,iBAAiB,GAAG,IAAI;IAI5C,mBAAmB,IAAI,MAAM,GAAG,IAAI;IAIpC,mBAAmB,IAAI,MAAM,GAAG,IAAI;IAIpC,wBAAwB,IAAI,MAAM,GAAG,IAAI;IAKzC,aAAa,IAAI,IAAI;IAKrB,eAAe,IAAI,IAAI;IAIvB,UAAU,IAAI,IAAI;IAMzB,OAAO,CAAC,OAAO;IAcR,OAAO,IAAI,IAAI;IAUtB,OAAO,CAAC,uBAAuB;IAc/B,OAAO,CAAC,qBAAqB;IAc7B,OAAO,CAAC,wBAAwB;IAchC,OAAO,CAAC,uBAAuB;CAOhC"}
@@ -67,7 +67,8 @@ export class ScreensaverManager {
67
67
  if (navBus) {
68
68
  navBus.on("page-changed", (e) => {
69
69
  const { newPageId } = e.detail;
70
- if (!this.isScreensaverActive && newPageId !== this.config?.page.componentId) {
70
+ const screensaverPageId = this.config?.page?.componentId;
71
+ if (!this.isScreensaverActive && (!screensaverPageId || newPageId !== screensaverPageId)) {
71
72
  this.lastActivePageId = newPageId;
72
73
  stateManager.set("screensaver.lastActivePageId", newPageId);
73
74
  }
@@ -103,33 +104,52 @@ export class ScreensaverManager {
103
104
  type: "snap",
104
105
  },
105
106
  };
106
- // Register the screensaver page with navigation manager
107
- const navBus = this.orchestrator.getEventBus("navigation-manager");
108
- if (navBus) {
109
- navBus.emit("register-page", { page: this.config.page });
107
+ // Register the screensaver page with navigation manager (only in screensaver mode)
108
+ if (!this.isReturnHomeMode() && this.config.page) {
109
+ const navBus = this.orchestrator.getEventBus("navigation-manager");
110
+ if (navBus) {
111
+ navBus.emit("register-page", { page: this.config.page });
112
+ }
110
113
  }
111
114
  this.setupGlobalActivityListeners();
112
115
  this.resetActivityTimer();
113
- log.trace(`Screensaver registered with ${config.timeoutSeconds}s timeout and '${this.config.exitBehavior}' exit behavior`);
116
+ if (this.isReturnHomeMode()) {
117
+ log.trace(`Screensaver registered in returnHome mode with ${config.timeoutSeconds}s timeout, target: ${this.config.startingPageId}/${this.config.defaultViewId || "default"}`);
118
+ }
119
+ else {
120
+ log.trace(`Screensaver registered with ${config.timeoutSeconds}s timeout and '${this.config.exitBehavior}' exit behavior`);
121
+ }
122
+ }
123
+ isReturnHomeMode() {
124
+ return this.config?.screensaverViewBehavior === "returnHome";
114
125
  }
115
126
  validateConfig(config) {
116
- if (!config.page) {
117
- throw new Error("Screensaver page is required");
118
- }
119
127
  if (config.timeoutSeconds <= 0) {
120
128
  throw new Error("timeoutSeconds must be greater than 0");
121
129
  }
122
- if (config.exitBehavior === "reset" && !config.startingPageId) {
123
- throw new Error('startingPageId is required when exitBehavior is "reset"');
130
+ if (config.screensaverViewBehavior && !["default", "specific", "return", "returnHome"].includes(config.screensaverViewBehavior)) {
131
+ throw new Error('screensaverViewBehavior must be "default", "specific", "return", or "returnHome"');
124
132
  }
125
- if (config.exitBehavior && !["reset", "return"].includes(config.exitBehavior)) {
126
- throw new Error('exitBehavior must be either "reset" or "return"');
127
- }
128
- if (config.screensaverViewBehavior === "specific" && !config.specificViewId) {
129
- throw new Error('specificViewId is required when screensaverViewBehavior is "specific"');
133
+ if (config.screensaverViewBehavior === "returnHome") {
134
+ // In returnHome mode, startingPageId is required as the home page target
135
+ if (!config.startingPageId) {
136
+ throw new Error('startingPageId is required when screensaverViewBehavior is "returnHome"');
137
+ }
130
138
  }
131
- if (config.screensaverViewBehavior && !["default", "specific", "return"].includes(config.screensaverViewBehavior)) {
132
- throw new Error('screensaverViewBehavior must be "default", "specific", or "return"');
139
+ else {
140
+ // In screensaver modes, page is required
141
+ if (!config.page) {
142
+ throw new Error('Screensaver page is required when screensaverViewBehavior is not "returnHome"');
143
+ }
144
+ if (config.exitBehavior === "reset" && !config.startingPageId) {
145
+ throw new Error('startingPageId is required when exitBehavior is "reset"');
146
+ }
147
+ if (config.exitBehavior && !["reset", "return"].includes(config.exitBehavior)) {
148
+ throw new Error('exitBehavior must be either "reset" or "return"');
149
+ }
150
+ if (config.screensaverViewBehavior === "specific" && !config.specificViewId) {
151
+ throw new Error('specificViewId is required when screensaverViewBehavior is "specific"');
152
+ }
133
153
  }
134
154
  }
135
155
  setupGlobalActivityListeners() {
@@ -219,17 +239,26 @@ export class ScreensaverManager {
219
239
  }
220
240
  }
221
241
  async activateScreensaver() {
222
- if (!this.config || this.isScreensaverActive)
242
+ if (!this.config)
223
243
  return;
224
244
  if (this.config.blockerCallback && this.config.blockerCallback()) {
225
245
  this.resetActivityTimer();
226
246
  return;
227
247
  }
248
+ // Handle returnHome mode — navigate to home page/view without entering screensaver state
249
+ if (this.isReturnHomeMode()) {
250
+ await this.activateReturnHome();
251
+ return;
252
+ }
253
+ // Standard screensaver mode
254
+ if (this.isScreensaverActive)
255
+ return;
228
256
  log.trace("Activating screensaver");
229
257
  // Store the current page and view before switching to screensaver
230
258
  const currentPageId = this.navigationManager.getCurrentPageId();
231
259
  const currentViewId = this.navigationManager.getCurrentViewId();
232
- if (currentPageId && currentPageId !== this.config.page.componentId) {
260
+ const screensaverPageId = this.config.page.componentId;
261
+ if (currentPageId && currentPageId !== screensaverPageId) {
233
262
  this.lastActivePageId = currentPageId;
234
263
  this.lastActiveViewId = currentViewId;
235
264
  stateManager.set("screensaver.lastActivePageId", currentPageId);
@@ -239,14 +268,14 @@ export class ScreensaverManager {
239
268
  stateManager.set("screensaver.isActive", true);
240
269
  try {
241
270
  // Navigate to screensaver page
242
- await this.navigationManager.navigateToPage(this.config.page.componentId, this.config.transitionConfig);
271
+ await this.navigationManager.navigateToPage(screensaverPageId, this.config.transitionConfig);
243
272
  // Determine which view to show based on screensaverViewBehavior
244
273
  const targetViewId = this.determineScreensaverView();
245
274
  if (targetViewId) {
246
275
  await this.navigationManager.navigateToView(targetViewId, { type: "snap" });
247
276
  }
248
277
  this.eventBus.emit("screensaver-activated", {
249
- pageId: this.config.page.componentId,
278
+ pageId: screensaverPageId,
250
279
  viewId: targetViewId,
251
280
  previousPageId: this.lastActivePageId,
252
281
  previousViewId: this.lastActiveViewId,
@@ -263,6 +292,53 @@ export class ScreensaverManager {
263
292
  this.startRebootCheckInterval();
264
293
  this.checkAndPerformReboot();
265
294
  }
295
+ /**
296
+ * Return-to-home activation: navigates to the configured home page/view
297
+ * without entering "screensaver active" state. The timer resets immediately
298
+ * for the next inactivity cycle.
299
+ */
300
+ async activateReturnHome() {
301
+ if (!this.config)
302
+ return;
303
+ const targetPageId = this.config.startingPageId;
304
+ const targetViewId = this.config.defaultViewId || null;
305
+ const currentPageId = this.navigationManager.getCurrentPageId();
306
+ const currentViewId = this.navigationManager.getCurrentViewId();
307
+ // Skip navigation if already on the target page/view
308
+ const alreadyOnTargetPage = currentPageId === targetPageId;
309
+ const alreadyOnTargetView = !targetViewId || currentViewId === targetViewId;
310
+ if (alreadyOnTargetPage && alreadyOnTargetView) {
311
+ log.trace("ReturnHome: Already on home page/view, resetting timer");
312
+ this.resetActivityTimer();
313
+ return;
314
+ }
315
+ log.trace(`ReturnHome: Navigating to ${targetPageId}/${targetViewId || "default"}`);
316
+ try {
317
+ // Navigate to home page
318
+ if (!alreadyOnTargetPage) {
319
+ await this.navigationManager.navigateToPage(targetPageId, this.config.transitionConfig);
320
+ }
321
+ // Navigate to home view if specified
322
+ if (targetViewId && !alreadyOnTargetView) {
323
+ await this.navigationManager.navigateToView(targetViewId, { type: "snap" });
324
+ }
325
+ this.eventBus.emit("screensaver-returned-home", {
326
+ targetPageId,
327
+ targetViewId,
328
+ previousPageId: currentPageId,
329
+ previousViewId: currentViewId,
330
+ });
331
+ if (this.config.activateCallback)
332
+ this.config.activateCallback();
333
+ }
334
+ catch (error) {
335
+ log.error("Failed to return to home:", error);
336
+ }
337
+ // Reset timer for next inactivity cycle
338
+ this.resetActivityTimer();
339
+ // Check reboot timeout if configured
340
+ this.checkAndPerformReboot();
341
+ }
266
342
  determineScreensaverView() {
267
343
  if (!this.config)
268
344
  return null;
@@ -0,0 +1,96 @@
1
+ import { EventOrchestrator } from "./eventBus";
2
+ export interface TransitionOverlayConfig {
3
+ backgroundColor?: string;
4
+ fadeInDuration?: number;
5
+ holdDuration?: number;
6
+ fadeOutDuration?: number;
7
+ zIndex?: number;
8
+ }
9
+ export interface TransitionRequestPayload {
10
+ stateChanges?: Array<{
11
+ key: string;
12
+ value: any;
13
+ }>;
14
+ afterStateChange?: () => void | Promise<void>;
15
+ fadeInDuration?: number;
16
+ holdDuration?: number;
17
+ fadeOutDuration?: number;
18
+ }
19
+ type TransitionPhase = "idle" | "fade-in" | "hold" | "fade-out";
20
+ /**
21
+ * TransitionOverlay - Manages visual overlay transitions for seamless content updates
22
+ *
23
+ * This class provides a fade-to-opaque overlay that hides content changes during state updates.
24
+ *
25
+ * **Concurrency Strategy:**
26
+ * - If a transition is in the **fade-in or hold phase**, incoming requests queue their callbacks
27
+ * to be batched into the current cycle's hold phase
28
+ * - If a transition is in the **fade-out phase**, incoming requests start a new transition cycle
29
+ * after the current one completes
30
+ *
31
+ * This ensures smooth transitions without jarring interruptions or visible content changes.
32
+ */
33
+ export declare class TransitionOverlay {
34
+ private config;
35
+ private shadowRoot;
36
+ private overlayElement;
37
+ private currentPhase;
38
+ private queuedCallbacks;
39
+ private queuedRequests;
40
+ private activeTransitionPromise;
41
+ constructor(config: TransitionOverlayConfig, shadowRoot: ShadowRoot);
42
+ private injectOverlayElement;
43
+ /**
44
+ * Execute a transition with the overlay lifecycle
45
+ *
46
+ * @param callback - Function to execute while overlay is opaque (state changes happen here)
47
+ * @param configOverrides - Optional per-request config overrides
48
+ * @returns Promise that resolves after the full transition completes
49
+ */
50
+ executeTransition(callback: () => void | Promise<void>, configOverrides?: Partial<TransitionOverlayConfig>): Promise<void>;
51
+ private handleConcurrentRequest;
52
+ private performTransition;
53
+ private fadeIn;
54
+ private executeCallbacks;
55
+ private fadeOut;
56
+ private processQueuedRequests;
57
+ /**
58
+ * Update the overlay configuration
59
+ */
60
+ updateConfig(config: Partial<TransitionOverlayConfig>): void;
61
+ /**
62
+ * Get the current transition phase
63
+ */
64
+ getPhase(): TransitionPhase;
65
+ /**
66
+ * Check if a transition is currently active
67
+ */
68
+ isTransitioning(): boolean;
69
+ /**
70
+ * Clean up the overlay element and resources
71
+ */
72
+ destroy(): void;
73
+ }
74
+ /**
75
+ * Standalone utility for requesting overlay transitions via event bus
76
+ *
77
+ * This is the recommended way for components to request transitions, as it:
78
+ * 1. Works from anywhere in the component tree
79
+ * 2. Gracefully falls back to direct execution if no overlay is available
80
+ * 3. Supports state changes via the standardized event format
81
+ *
82
+ * @param orchestrator - The EventOrchestrator instance
83
+ * @param callback - Function to execute during the transition (while overlay is opaque)
84
+ * @param config - Optional per-request config overrides
85
+ * @returns Promise that resolves after the transition completes
86
+ */
87
+ export declare function transitionWithOverlay(orchestrator: EventOrchestrator, callback: () => void | Promise<void>, config?: Partial<TransitionOverlayConfig>): Promise<void>;
88
+ /**
89
+ * Helper to convert state changes array to callback function
90
+ */
91
+ export declare function stateChangesToCallback(stateChanges: Array<{
92
+ key: string;
93
+ value: any;
94
+ }>): () => void;
95
+ export {};
96
+ //# sourceMappingURL=transitionOverlay.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transitionOverlay.d.ts","sourceRoot":"","sources":["../../src/transitionOverlay.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAM/C,MAAM,WAAW,uBAAuB;IACtC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,wBAAwB;IACvC,YAAY,CAAC,EAAE,KAAK,CAAC;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IAClD,gBAAgB,CAAC,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AASD,KAAK,eAAe,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,UAAU,CAAC;AAEhE;;;;;;;;;;;;GAYG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,MAAM,CAAoC;IAClD,OAAO,CAAC,UAAU,CAAa;IAC/B,OAAO,CAAC,cAAc,CAA+B;IACrD,OAAO,CAAC,YAAY,CAA2B;IAC/C,OAAO,CAAC,eAAe,CAAyC;IAChE,OAAO,CAAC,cAAc,CAAiC;IACvD,OAAO,CAAC,uBAAuB,CAA8B;gBAEjD,MAAM,EAAE,uBAAuB,EAAE,UAAU,EAAE,UAAU;IAYnE,OAAO,CAAC,oBAAoB;IA8B5B;;;;;;OAMG;IACU,iBAAiB,CAC5B,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACpC,eAAe,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,GACjD,OAAO,CAAC,IAAI,CAAC;IAiBhB,OAAO,CAAC,uBAAuB;YAwBjB,iBAAiB;IAmC/B,OAAO,CAAC,MAAM;YAoDA,gBAAgB;IAsB9B,OAAO,CAAC,OAAO;IAmDf,OAAO,CAAC,qBAAqB;IAW7B;;OAEG;IACI,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,uBAAuB,CAAC,GAAG,IAAI;IAgBnE;;OAEG;IACI,QAAQ,IAAI,eAAe;IAIlC;;OAEG;IACI,eAAe,IAAI,OAAO;IAIjC;;OAEG;IACI,OAAO,IAAI,IAAI;CAUvB;AAED;;;;;;;;;;;;GAYG;AACH,wBAAsB,qBAAqB,CACzC,YAAY,EAAE,iBAAiB,EAC/B,QAAQ,EAAE,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,EACpC,MAAM,CAAC,EAAE,OAAO,CAAC,uBAAuB,CAAC,GACxC,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,YAAY,EAAE,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,GAAG,CAAA;CAAE,CAAC,GAC/C,MAAM,IAAI,CAMZ"}
@@ -0,0 +1,328 @@
1
+ import { setGlobalState } from "./stateManager";
2
+ import { logger } from "./logger";
3
+ const log = logger;
4
+ /**
5
+ * TransitionOverlay - Manages visual overlay transitions for seamless content updates
6
+ *
7
+ * This class provides a fade-to-opaque overlay that hides content changes during state updates.
8
+ *
9
+ * **Concurrency Strategy:**
10
+ * - If a transition is in the **fade-in or hold phase**, incoming requests queue their callbacks
11
+ * to be batched into the current cycle's hold phase
12
+ * - If a transition is in the **fade-out phase**, incoming requests start a new transition cycle
13
+ * after the current one completes
14
+ *
15
+ * This ensures smooth transitions without jarring interruptions or visible content changes.
16
+ */
17
+ export class TransitionOverlay {
18
+ config;
19
+ shadowRoot;
20
+ overlayElement = null;
21
+ currentPhase = "idle";
22
+ queuedCallbacks = [];
23
+ queuedRequests = [];
24
+ activeTransitionPromise = null;
25
+ constructor(config, shadowRoot) {
26
+ this.config = {
27
+ backgroundColor: config.backgroundColor ?? "#000",
28
+ fadeInDuration: config.fadeInDuration ?? 200,
29
+ holdDuration: config.holdDuration ?? 100,
30
+ fadeOutDuration: config.fadeOutDuration ?? 200,
31
+ zIndex: config.zIndex ?? 100,
32
+ };
33
+ this.shadowRoot = shadowRoot;
34
+ this.injectOverlayElement();
35
+ }
36
+ injectOverlayElement() {
37
+ this.overlayElement = document.createElement("div");
38
+ this.overlayElement.className = "transition-overlay";
39
+ this.overlayElement.style.cssText = `
40
+ position: absolute;
41
+ inset: 0;
42
+ background-color: ${this.config.backgroundColor};
43
+ opacity: 0;
44
+ pointer-events: none;
45
+ z-index: ${this.config.zIndex};
46
+ will-change: opacity;
47
+ transition: opacity ${this.config.fadeInDuration}ms ease-in-out;
48
+ `;
49
+ // Append to the shadow root or to .page if it exists
50
+ const pageElement = this.shadowRoot.querySelector(".page");
51
+ const container = pageElement || this.shadowRoot;
52
+ // Ensure the container has relative positioning for absolute overlay
53
+ if (container instanceof HTMLElement) {
54
+ const currentPosition = window.getComputedStyle(container).position;
55
+ if (currentPosition === "static") {
56
+ container.style.position = "relative";
57
+ }
58
+ }
59
+ container.appendChild(this.overlayElement);
60
+ log.trace("TransitionOverlay element injected into shadow DOM");
61
+ }
62
+ /**
63
+ * Execute a transition with the overlay lifecycle
64
+ *
65
+ * @param callback - Function to execute while overlay is opaque (state changes happen here)
66
+ * @param configOverrides - Optional per-request config overrides
67
+ * @returns Promise that resolves after the full transition completes
68
+ */
69
+ async executeTransition(callback, configOverrides) {
70
+ // Merge config overrides
71
+ const effectiveConfig = {
72
+ ...this.config,
73
+ ...configOverrides,
74
+ };
75
+ // Handle concurrency
76
+ if (this.currentPhase !== "idle") {
77
+ return this.handleConcurrentRequest(callback, effectiveConfig);
78
+ }
79
+ // Start new transition
80
+ this.activeTransitionPromise = this.performTransition(callback, effectiveConfig);
81
+ return this.activeTransitionPromise;
82
+ }
83
+ handleConcurrentRequest(callback, config) {
84
+ return new Promise((resolve, reject) => {
85
+ // If we're in fade-in or hold, batch this callback into the current cycle
86
+ if (this.currentPhase === "fade-in" || this.currentPhase === "hold") {
87
+ log.trace("Batching callback into current transition cycle", {
88
+ currentPhase: this.currentPhase
89
+ });
90
+ this.queuedCallbacks.push(callback);
91
+ // Resolve when the current transition completes
92
+ this.activeTransitionPromise?.then(resolve).catch(reject);
93
+ }
94
+ // If we're in fade-out, queue for the next cycle
95
+ else if (this.currentPhase === "fade-out") {
96
+ log.trace("Queueing request for next transition cycle", {
97
+ currentPhase: this.currentPhase
98
+ });
99
+ this.queuedRequests.push({ callback, config, resolve, reject });
100
+ }
101
+ });
102
+ }
103
+ async performTransition(callback, config) {
104
+ if (!this.overlayElement) {
105
+ throw new Error("Overlay element not initialized");
106
+ }
107
+ try {
108
+ // Phase 1: Fade in
109
+ this.currentPhase = "fade-in";
110
+ await this.fadeIn(config.fadeInDuration);
111
+ // Phase 2: Hold & execute callbacks
112
+ this.currentPhase = "hold";
113
+ await this.executeCallbacks(callback, config.holdDuration);
114
+ // Phase 3: Fade out
115
+ this.currentPhase = "fade-out";
116
+ await this.fadeOut(config.fadeOutDuration);
117
+ // Back to idle
118
+ this.currentPhase = "idle";
119
+ // Process any queued requests
120
+ this.processQueuedRequests();
121
+ }
122
+ catch (error) {
123
+ log.error("Transition error:", error);
124
+ this.currentPhase = "idle";
125
+ this.queuedCallbacks = [];
126
+ throw error;
127
+ }
128
+ }
129
+ fadeIn(duration) {
130
+ if (!this.overlayElement)
131
+ return Promise.resolve();
132
+ return new Promise((resolve) => {
133
+ const element = this.overlayElement;
134
+ // Update transition duration
135
+ element.style.transition = `opacity ${duration}ms ease-in-out`;
136
+ // Enable pointer events to block interaction
137
+ element.style.pointerEvents = "all";
138
+ let transitionEndFired = false;
139
+ let fallbackTimerId = null;
140
+ const cleanup = () => {
141
+ if (transitionEndFired)
142
+ return;
143
+ transitionEndFired = true;
144
+ element.removeEventListener("transitionend", transitionEndHandler);
145
+ if (fallbackTimerId !== null) {
146
+ clearTimeout(fallbackTimerId);
147
+ fallbackTimerId = null;
148
+ }
149
+ log.trace("Overlay fade-in complete");
150
+ resolve();
151
+ };
152
+ const transitionEndHandler = (e) => {
153
+ // Only respond to opacity transitions on this element
154
+ if (e.target === element && e.propertyName === "opacity") {
155
+ cleanup();
156
+ }
157
+ };
158
+ element.addEventListener("transitionend", transitionEndHandler);
159
+ // Trigger fade-in by setting opacity to 1
160
+ // Use requestAnimationFrame to ensure CSS transition is applied
161
+ requestAnimationFrame(() => {
162
+ element.style.opacity = "1";
163
+ });
164
+ // Fallback timeout
165
+ fallbackTimerId = window.setTimeout(() => {
166
+ log.trace("Overlay fade-in fallback timeout triggered");
167
+ cleanup();
168
+ }, duration + 50);
169
+ });
170
+ }
171
+ async executeCallbacks(callback, holdDuration) {
172
+ // Execute the main callback
173
+ await Promise.resolve(callback());
174
+ // Execute any batched callbacks
175
+ if (this.queuedCallbacks.length > 0) {
176
+ log.trace(`Executing ${this.queuedCallbacks.length} batched callbacks`);
177
+ for (const queuedCallback of this.queuedCallbacks) {
178
+ await Promise.resolve(queuedCallback());
179
+ }
180
+ this.queuedCallbacks = [];
181
+ }
182
+ // Hold duration
183
+ if (holdDuration > 0) {
184
+ await new Promise(resolve => setTimeout(resolve, holdDuration));
185
+ }
186
+ }
187
+ fadeOut(duration) {
188
+ if (!this.overlayElement)
189
+ return Promise.resolve();
190
+ return new Promise((resolve) => {
191
+ const element = this.overlayElement;
192
+ // Update transition duration
193
+ element.style.transition = `opacity ${duration}ms ease-in-out`;
194
+ let transitionEndFired = false;
195
+ let fallbackTimerId = null;
196
+ const cleanup = () => {
197
+ if (transitionEndFired)
198
+ return;
199
+ transitionEndFired = true;
200
+ element.removeEventListener("transitionend", transitionEndHandler);
201
+ if (fallbackTimerId !== null) {
202
+ clearTimeout(fallbackTimerId);
203
+ fallbackTimerId = null;
204
+ }
205
+ // Disable pointer events after fade-out
206
+ element.style.pointerEvents = "none";
207
+ log.trace("Overlay fade-out complete");
208
+ resolve();
209
+ };
210
+ const transitionEndHandler = (e) => {
211
+ // Only respond to opacity transitions on this element
212
+ if (e.target === element && e.propertyName === "opacity") {
213
+ cleanup();
214
+ }
215
+ };
216
+ element.addEventListener("transitionend", transitionEndHandler);
217
+ // Trigger fade-out by setting opacity to 0
218
+ requestAnimationFrame(() => {
219
+ element.style.opacity = "0";
220
+ });
221
+ // Fallback timeout
222
+ fallbackTimerId = window.setTimeout(() => {
223
+ log.trace("Overlay fade-out fallback timeout triggered");
224
+ cleanup();
225
+ }, duration + 50);
226
+ });
227
+ }
228
+ processQueuedRequests() {
229
+ if (this.queuedRequests.length > 0) {
230
+ log.trace(`Processing ${this.queuedRequests.length} queued transition requests`);
231
+ const request = this.queuedRequests.shift();
232
+ this.performTransition(request.callback, request.config)
233
+ .then(request.resolve)
234
+ .catch(request.reject);
235
+ }
236
+ }
237
+ /**
238
+ * Update the overlay configuration
239
+ */
240
+ updateConfig(config) {
241
+ this.config = {
242
+ ...this.config,
243
+ ...config,
244
+ };
245
+ if (this.overlayElement) {
246
+ if (config.backgroundColor) {
247
+ this.overlayElement.style.backgroundColor = config.backgroundColor;
248
+ }
249
+ if (config.zIndex !== undefined) {
250
+ this.overlayElement.style.zIndex = String(config.zIndex);
251
+ }
252
+ }
253
+ }
254
+ /**
255
+ * Get the current transition phase
256
+ */
257
+ getPhase() {
258
+ return this.currentPhase;
259
+ }
260
+ /**
261
+ * Check if a transition is currently active
262
+ */
263
+ isTransitioning() {
264
+ return this.currentPhase !== "idle";
265
+ }
266
+ /**
267
+ * Clean up the overlay element and resources
268
+ */
269
+ destroy() {
270
+ if (this.overlayElement && this.overlayElement.parentNode) {
271
+ this.overlayElement.parentNode.removeChild(this.overlayElement);
272
+ }
273
+ this.overlayElement = null;
274
+ this.queuedCallbacks = [];
275
+ this.queuedRequests = [];
276
+ this.currentPhase = "idle";
277
+ log.trace("TransitionOverlay destroyed");
278
+ }
279
+ }
280
+ /**
281
+ * Standalone utility for requesting overlay transitions via event bus
282
+ *
283
+ * This is the recommended way for components to request transitions, as it:
284
+ * 1. Works from anywhere in the component tree
285
+ * 2. Gracefully falls back to direct execution if no overlay is available
286
+ * 3. Supports state changes via the standardized event format
287
+ *
288
+ * @param orchestrator - The EventOrchestrator instance
289
+ * @param callback - Function to execute during the transition (while overlay is opaque)
290
+ * @param config - Optional per-request config overrides
291
+ * @returns Promise that resolves after the transition completes
292
+ */
293
+ export async function transitionWithOverlay(orchestrator, callback, config) {
294
+ const transitionBus = orchestrator.getEventBus("page-transition-overlay");
295
+ if (transitionBus) {
296
+ // Wrap in a promise that resolves when the transition completes
297
+ return new Promise((resolve, reject) => {
298
+ try {
299
+ transitionBus.emit("request-transition", {
300
+ callback,
301
+ ...config,
302
+ _resolve: resolve,
303
+ _reject: reject,
304
+ });
305
+ }
306
+ catch (error) {
307
+ // If the emit fails (no listeners), fall back to direct execution
308
+ log.warn("No transition overlay available, executing callback directly");
309
+ Promise.resolve(callback()).then(resolve).catch(reject);
310
+ }
311
+ });
312
+ }
313
+ else {
314
+ // Fallback: no overlay available, execute directly
315
+ log.trace("No transition overlay bus found, executing callback directly");
316
+ await Promise.resolve(callback());
317
+ }
318
+ }
319
+ /**
320
+ * Helper to convert state changes array to callback function
321
+ */
322
+ export function stateChangesToCallback(stateChanges) {
323
+ return () => {
324
+ stateChanges.forEach(({ key, value }) => {
325
+ setGlobalState(key, value);
326
+ });
327
+ };
328
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hawsen-the-first/interactiv",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "A TypeScript framework for building interactive applications with event management, state management, navigation, and screensaver functionality",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",