ngx-theme-stack 0.0.15 → 1.0.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/README.md CHANGED
@@ -1,64 +1,131 @@
1
- # NgxThemeStack
1
+ # ngx-theme-stack 🎨
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 21.2.0.
3
+ A complete and lightweight solution for managing themes (light, dark, system, and custom) in **Angular** applications. Built with performance, accessibility, and SSR (Server-Side Rendering) support in mind.
4
4
 
5
- ## Code scaffolding
5
+ ## 🚀 Features
6
6
 
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
7
+ - **Single Command Installation**: Automatic configuration via `ng add`.
8
+ - 🌓 **System Preference Detection**: Automatic synchronization with OS settings (`prefers-color-scheme`).
9
+ - 🔄 **Dynamic Switching**: Multiple ways to toggle themes (toggle, cycle, select).
10
+ - 🛠️ **Highly Customizable**: Support for custom themes, class prefixes, and configurable storage.
11
+ - 🧱 **Modern Architecture**: Powered by Angular Signals for maximum reactivity and performance.
12
+ - 🌍 **SSR Ready**: Safe to use in Server-Side Rendering environments.
8
13
 
9
- ```bash
10
- ng generate component component-name
11
- ```
14
+ ## 📦 Installation
12
15
 
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
16
+ To install the library and configure it automatically in your project, run:
14
17
 
15
18
  ```bash
16
- ng generate --help
19
+ ng add ngx-theme-stack
17
20
  ```
18
21
 
19
- ## Building
22
+ ### Installation Modes
20
23
 
21
- To build the library, run:
24
+ When running `ng add`, you will be presented with two configuration options:
22
25
 
23
- ```bash
24
- ng build ngx-theme-stack
25
- ```
26
+ 1. **Quick Mode**:
27
+ - Applies default configuration instantly.
28
+ - Initial theme: `system`.
29
+ - Apply mode: `class` (adds the theme class to the `<html>` element).
30
+ - Available themes: `['light', 'dark', 'system']`.
26
31
 
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
32
+ 2. **Custom Mode**:
33
+ - Choose which themes to include (e.g., if you have a `blue` or `high-contrast` theme).
34
+ - Configure the default theme upon app startup.
35
+ - Change the `localStorage` key where the theme choice is saved.
36
+ - Decide how to apply themes: via classes (`class`), attributes (`data-theme`), or both.
28
37
 
29
- ### Publishing the Library
38
+ ## 🏗️ Architecture & Extensibility
30
39
 
31
- Once the project is built, you can publish your library by following these steps:
40
+ The library is designed to be flexible. The **`CoreThemeService`** is the foundation:
32
41
 
33
- 1. Navigate to the `dist` directory:
42
+ - **Solid Base:** Manages state (`Signal`), persistence (`localStorage`), system detection (`matchMedia`), and safe DOM manipulation (SSR compatible).
43
+ - **Extensibility:** You can inject `CoreThemeService` to build your own custom services or components with specific business logic.
34
44
 
35
- ```bash
36
- cd dist/ngx-theme-stack
37
- ```
45
+ ### Utility Services (Ready to Use)
46
+ For common use cases, we include three services with predefined logic:
38
47
 
39
- 2. Run the `npm publish` command to publish your library to the npm registry:
40
- ```bash
41
- npm publish
42
- ```
48
+ 1. **`ThemeToggleService`**: A simple binary switch between `light` and `dark`.
49
+ 2. **`ThemeSelectService`**: Exposes the full list of themes and methods to select them.
50
+ 3. **`ThemeCycleService`**: A circular function to cycle through all available themes with a single click.
43
51
 
44
- ## Running unit tests
52
+ ---
45
53
 
46
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
54
+ ## ⚙️ Supported Versions
47
55
 
48
- ```bash
49
- ng test
56
+ | Angular Version | Support |
57
+ | :--- | :--- |
58
+ | **Angular 21** | ✅ Stable |
59
+ | **Angular 20** | ✅ Stable |
60
+ | **Angular 19** | ✅ Stable |
61
+ | **Angular 18** | ✅ Stable |
62
+
63
+ ## 🛠️ Basic Usage
64
+
65
+ ### CoreThemeService
66
+
67
+ This is the main service managing the theme state.
68
+
69
+ ```typescript
70
+ import { inject } from '@angular/core';
71
+ import { CoreThemeService } from 'ngx-theme-stack';
72
+
73
+ @Component({ ... })
74
+ export class MyComponent {
75
+ private themeService = inject(CoreThemeService);
76
+
77
+ // Reactive signals
78
+ isDark = this.themeService.isDark; // boolean (true/false)
79
+ selectedTheme = this.themeService.selectedTheme; // 'light' | 'dark' | 'system' | ...
80
+
81
+ changeTheme(theme: string) {
82
+ this.themeService.setTheme(theme);
83
+ }
84
+ }
50
85
  ```
51
86
 
52
- ## Running end-to-end tests
87
+ ### Utility Services Examples
53
88
 
54
- For end-to-end (e2e) testing, run:
89
+ #### ThemeToggleService usage:
55
90
 
56
- ```bash
57
- ng e2e
91
+ ```typescript
92
+ import { ThemeToggleService } from 'ngx-theme-stack';
93
+
94
+ @Component({
95
+ selector: 'app-theme-toggle',
96
+ template: `
97
+ <button (click)="toggle.toggle()">
98
+ Switch to {{ toggle.isDark() ? 'Light' : 'Dark' }}
99
+ </button>
100
+ `
101
+ })
102
+ export class ThemeToggleComponent {
103
+ protected toggle = inject(ThemeToggleService);
104
+ }
58
105
  ```
59
106
 
60
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
107
+ ## 🎨 Styling
108
+
109
+ By default, the library adds the theme name as a class or attribute to the `<html>` element. Use this in your global styles:
110
+
111
+ ```css
112
+ /* Using Classes (Default) */
113
+ html.dark {
114
+ background-color: #121212;
115
+ color: white;
116
+ }
117
+
118
+ html.light {
119
+ background-color: #ffffff;
120
+ color: #333;
121
+ }
122
+
123
+ /* Using Attributes */
124
+ [data-theme='blue'] {
125
+ --primary-color: #0000ff;
126
+ }
127
+ ```
61
128
 
62
- ## Additional Resources
129
+ ## 📄 License
63
130
 
64
- For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
131
+ [MIT](./LICENSE)
@@ -3,8 +3,17 @@ import * as i0 from '@angular/core';
3
3
  import { InjectionToken, inject, DestroyRef, DOCUMENT, PLATFORM_ID, signal, computed, effect, Injectable } from '@angular/core';
4
4
 
5
5
  /** Built-in themes. All other values are considered custom themes. */
6
- const DEFAULT_THEMES = ['light', 'dark', 'system'];
6
+ const DEFAULT_THEMES = ['system', 'light', 'dark'];
7
7
 
8
+ /**
9
+ * ⚠ ATTENTION: SHARED CONFIGURATION VALUES
10
+ *
11
+ * These values MUST match the schematic defaults in:
12
+ * projects/ngx-theme-stack/schematics/ng-add/index.ts
13
+ *
14
+ * If you change any of these, you MUST also update the schematic's DEFAULTS
15
+ * constant so 'ng add' continues to provide correct hints and clean code.
16
+ */
8
17
  const DEFAULT_NG_CONFIG = {
9
18
  theme: 'system',
10
19
  storageKey: 'ngx-theme-stack-theme',
@@ -1 +1 @@
1
- {"version":3,"file":"ngx-theme-stack.mjs","sources":["../../../projects/ngx-theme-stack/src/lib/types.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-stack.config.ts","../../../projects/ngx-theme-stack/src/lib/core/core-theme.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-cycle.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-select.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-toggle.service.ts","../../../projects/ngx-theme-stack/src/public-api.ts","../../../projects/ngx-theme-stack/src/ngx-theme-stack.ts"],"sourcesContent":["/** Built-in themes. All other values are considered custom themes. */\nexport const DEFAULT_THEMES = ['light', 'dark', 'system'] as const;\n\n/** String union with autocompletion for defaults + any string support for customization. */\nexport type NgTheme = (typeof DEFAULT_THEMES)[number] | (string & {});\n\nexport type NgSystemTheme = Exclude<NgTheme, 'system'>;\nexport type NgMode = 'attribute' | 'class' | 'both';\n\nexport interface NgConfig {\n theme: NgTheme;\n storageKey: string;\n mode: NgMode;\n themes: NgTheme[];\n}\n","import { InjectionToken } from '@angular/core';\nimport { DEFAULT_THEMES, NgConfig } from '../types';\n\nexport const DEFAULT_NG_CONFIG = {\n theme: 'system',\n storageKey: 'ngx-theme-stack-theme',\n mode: 'class',\n themes: [...DEFAULT_THEMES],\n} satisfies NgConfig;\n\nexport const NGX_THEME_STACK_CONFIG = new InjectionToken<NgConfig>('NGX_THEME_STACK_CONFIG', {\n factory: () => DEFAULT_NG_CONFIG,\n});\n\n/**\n * Helper function to provide Theme Stack configuration.\n */\nexport function provideThemeStack(config: Partial<NgConfig> = {}) {\n return {\n provide: NGX_THEME_STACK_CONFIG,\n useValue: {\n ...DEFAULT_NG_CONFIG,\n ...config,\n } satisfies NgConfig,\n };\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n DOCUMENT,\n effect,\n inject,\n Injectable,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { NGX_THEME_STACK_CONFIG } from '../services/theme-stack.config';\nimport { NgTheme, NgSystemTheme } from '../types';\n\n\n/**\n * Core service for managing the application's color theme.\n *\n * Handles theme persistence, system preference detection, and DOM updates.\n * Supports built-in themes ('dark', 'light', 'system') and custom extensions.\n */\n@Injectable({ providedIn: 'root' })\nexport class CoreThemeService {\n // ── Dependencies ──────────────────────────────────────────────────────────\n\n readonly #config = inject(NGX_THEME_STACK_CONFIG);\n readonly #destroyRef = inject(DestroyRef);\n readonly #document = inject(DOCUMENT);\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n // ── Theme configuration ───────────────────────────────────────────────────\n\n /** List of available themes for Select/Cycle services. Defaults to ['light', 'dark', 'system']. */\n readonly availableThemes = this.#config.themes;\n\n /** Internal Set for O(1) existence checks. */\n readonly #validThemes = new Set<NgTheme>(this.availableThemes);\n\n // ── System preference ─────────────────────────────────────────────────────\n\n /** MediaQueryList for OS color scheme, created once and reused. Null in SSR. */\n readonly #mediaQuery: MediaQueryList | null = this.#isBrowser\n ? (this.#document.defaultView?.matchMedia('(prefers-color-scheme: dark)') ?? null)\n : null;\n\n readonly #systemPreference = signal<NgSystemTheme>(this.resolveSystemPreference());\n\n // ── Theme state ───────────────────────────────────────────────────────────\n\n readonly #selectedTheme = signal<NgTheme>(this.resolveInitialTheme());\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#selectedTheme.asReadonly();\n\n /** Resolved theme applied to the DOM. Always `'dark'` or `'light'` (or custom) — never `'system'`. */\n readonly userTheme = computed(() => {\n const theme = this.#selectedTheme();\n return theme === 'system' ? this.#systemPreference() : theme;\n });\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = computed(() => this.userTheme() === 'dark');\n\n /** Whether the currently applied theme is light. */\n readonly isLight = computed(() => this.userTheme() === 'light');\n\n // ── Event handler ─────────────────────────────────────────────────────────\n\n readonly #onSystemPreferenceChange = () =>\n this.#systemPreference.set(this.resolveSystemPreference());\n\n // ── Lifecycle ─────────────────────────────────────────────────────────────\n\n constructor() {\n if (this.#isBrowser && this.#selectedTheme() === 'system') {\n this.startSystemThemeListener();\n }\n\n effect(() => this.applyThemeToDOM(this.userTheme()));\n\n this.#destroyRef.onDestroy(() => this.stopSystemThemeListener());\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Changes the active theme.\n *\n * Persists the choice explicitly so that switching e.g. from `'system'` to\n * `'light'` is saved even when the resolved `userTheme` did not change\n * (system preference was already `'light'`).\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or a custom theme name.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n public setTheme(theme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n if (!this.#validThemes.has(theme)) {\n throw new Error(\n `[ngx-theme-stack] Invalid theme: \"${theme}\". Valid values are: ${[...this.#validThemes].join(', ')}.`,\n );\n }\n\n if (theme === 'system') {\n this.#systemPreference.set(this.resolveSystemPreference());\n this.startSystemThemeListener();\n } else {\n this.stopSystemThemeListener();\n }\n\n this.#selectedTheme.set(theme);\n this.saveTheme(theme);\n }\n\n // ── Private ───────────────────────────────────────────────────────────────\n\n private resolveSystemPreference(): NgSystemTheme {\n return this.#mediaQuery?.matches ? 'dark' : 'light';\n }\n\n private resolveInitialTheme(): NgTheme {\n if (!this.#isBrowser) return this.#config.theme;\n return this.readStoredTheme() ?? this.#config.theme;\n }\n\n private startSystemThemeListener(): void {\n if (!this.#mediaQuery) return;\n this.stopSystemThemeListener();\n this.#mediaQuery.addEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private stopSystemThemeListener(): void {\n this.#mediaQuery?.removeEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private applyThemeToDOM(userTheme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n const host = this.#document.documentElement;\n const { mode } = this.#config;\n\n if (mode === 'attribute' || mode === 'both') {\n this.applyThemeAttribute(host, userTheme);\n }\n\n if (mode === 'class' || mode === 'both') {\n this.applyThemeClasses(host, userTheme);\n }\n\n this.applyColorSchemeHint(host, userTheme);\n }\n\n private applyThemeAttribute(host: HTMLElement, theme: NgTheme): void {\n host.setAttribute('data-theme', theme);\n }\n\n private applyThemeClasses(host: HTMLElement, theme: NgTheme): void {\n for (const t of this.availableThemes) {\n host.classList.remove(t);\n }\n\n host.classList.add(theme);\n }\n\n private applyColorSchemeHint(host: HTMLElement, theme: NgTheme): void {\n if (theme === 'dark' || theme === 'light') {\n host.style.setProperty('color-scheme', theme);\n return;\n }\n\n host.style.removeProperty('color-scheme');\n }\n\n private readStoredTheme(): NgTheme | null {\n try {\n const stored = localStorage.getItem(this.#config.storageKey);\n if (stored && this.#validThemes.has(stored as NgTheme)) {\n return stored as NgTheme;\n }\n return null;\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not read theme from localStorage.', e);\n return null;\n }\n }\n\n private saveTheme(theme: NgTheme): void {\n try {\n localStorage.setItem(this.#config.storageKey, theme);\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not save theme to localStorage.', e);\n }\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for cycling through themes in a fixed order.\n *\n * Default cycle: `'light'` → `'dark'` → `'system'` → `'light'` → ...\n *\n * Use this when you want to offer users a single button that rotates\n * through all available theme options.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeCycleService {\n\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes for cycling. Defaults to ['light', 'dark', 'system']. */\n readonly #cycle = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Advances to the next theme in the cycle.\n *\n * Cycle order is determined by the configured `themes` property in `NgConfig`.\n *\n * If the current theme is not found in the cycle (e.g. set externally),\n * the cycle restarts from the first theme.\n */\n cycle(): void {\n const current = this.#core.selectedTheme();\n const index = this.#cycle.indexOf(current);\n const next = this.#cycle[(index + 1) % this.#cycle.length];\n this.#core.setTheme(next);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\nimport { NgTheme } from '../types';\n\n\n/**\n * Convenience service for selecting a theme from a list.\n *\n * Use this when you want to bind a `<select>` or a group of radio/tab\n * buttons to the full set of available themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeSelectService {\n\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes. Defaults to ['light', 'dark', 'system']. */\n readonly availableThemes = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Applies the given theme.\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or custom.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n select(theme: NgTheme): void {\n this.#core.setTheme(theme);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for toggling between `'dark'` and `'light'`.\n *\n * Use this when you only need a simple on/off switch and do not\n * need to manage `'system'` or cycle through themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeToggleService {\n\n readonly #core = inject(CoreThemeService);\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Toggles between `'dark'` and `'light'`.\n *\n * If the selected theme is explicitly `'dark'`, switches to `'light'`.\n * Otherwise (including `'system'`), switches to `'dark'`.\n */\n toggle(): void {\n const next = this.#core.selectedTheme() === 'dark' ? 'light' : 'dark';\n this.#core.setTheme(next);\n }\n}\n","/*\n * Public API Surface of ngx-theme-stack\n */\n\nexport * from './lib/core/core-theme.service';\nexport * from './lib/services/theme-stack.config';\nexport * from './lib/services/theme-cycle.service';\nexport * from './lib/services/theme-select.service';\nexport * from './lib/services/theme-toggle.service';\nexport * from './lib/types';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ngx-theme-stack.mjs","sources":["../../../projects/ngx-theme-stack/src/lib/types.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-stack.config.ts","../../../projects/ngx-theme-stack/src/lib/core/core-theme.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-cycle.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-select.service.ts","../../../projects/ngx-theme-stack/src/lib/services/theme-toggle.service.ts","../../../projects/ngx-theme-stack/src/public-api.ts","../../../projects/ngx-theme-stack/src/ngx-theme-stack.ts"],"sourcesContent":["/** Built-in themes. All other values are considered custom themes. */\nexport const DEFAULT_THEMES = ['system', 'light', 'dark'] as const;\n\n/** String union with autocompletion for defaults + any string support for customization. */\nexport type NgTheme = (typeof DEFAULT_THEMES)[number] | (string & {});\n\nexport type NgSystemTheme = Exclude<NgTheme, 'system'>;\nexport type NgMode = 'attribute' | 'class' | 'both';\n\nexport interface NgConfig {\n theme: NgTheme;\n storageKey: string;\n mode: NgMode;\n themes: NgTheme[];\n}\n","import { InjectionToken } from '@angular/core';\nimport { DEFAULT_THEMES, NgConfig } from '../types';\n\n/**\n * ⚠ ATTENTION: SHARED CONFIGURATION VALUES\n *\n * These values MUST match the schematic defaults in:\n * projects/ngx-theme-stack/schematics/ng-add/index.ts\n *\n * If you change any of these, you MUST also update the schematic's DEFAULTS\n * constant so 'ng add' continues to provide correct hints and clean code.\n */\nexport const DEFAULT_NG_CONFIG = {\n theme: 'system',\n storageKey: 'ngx-theme-stack-theme',\n mode: 'class',\n themes: [...DEFAULT_THEMES],\n} satisfies NgConfig;\n\nexport const NGX_THEME_STACK_CONFIG = new InjectionToken<NgConfig>('NGX_THEME_STACK_CONFIG', {\n factory: () => DEFAULT_NG_CONFIG,\n});\n\n/**\n * Helper function to provide Theme Stack configuration.\n */\nexport function provideThemeStack(config: Partial<NgConfig> = {}) {\n return {\n provide: NGX_THEME_STACK_CONFIG,\n useValue: {\n ...DEFAULT_NG_CONFIG,\n ...config,\n } satisfies NgConfig,\n };\n}\n","import { isPlatformBrowser } from '@angular/common';\nimport {\n computed,\n DestroyRef,\n DOCUMENT,\n effect,\n inject,\n Injectable,\n PLATFORM_ID,\n signal,\n} from '@angular/core';\nimport { NGX_THEME_STACK_CONFIG } from '../services/theme-stack.config';\nimport { NgTheme, NgSystemTheme } from '../types';\n\n\n/**\n * Core service for managing the application's color theme.\n *\n * Handles theme persistence, system preference detection, and DOM updates.\n * Supports built-in themes ('dark', 'light', 'system') and custom extensions.\n */\n@Injectable({ providedIn: 'root' })\nexport class CoreThemeService {\n // ── Dependencies ──────────────────────────────────────────────────────────\n\n readonly #config = inject(NGX_THEME_STACK_CONFIG);\n readonly #destroyRef = inject(DestroyRef);\n readonly #document = inject(DOCUMENT);\n readonly #isBrowser = isPlatformBrowser(inject(PLATFORM_ID));\n\n // ── Theme configuration ───────────────────────────────────────────────────\n\n /** List of available themes for Select/Cycle services. Defaults to ['light', 'dark', 'system']. */\n readonly availableThemes = this.#config.themes;\n\n /** Internal Set for O(1) existence checks. */\n readonly #validThemes = new Set<NgTheme>(this.availableThemes);\n\n // ── System preference ─────────────────────────────────────────────────────\n\n /** MediaQueryList for OS color scheme, created once and reused. Null in SSR. */\n readonly #mediaQuery: MediaQueryList | null = this.#isBrowser\n ? (this.#document.defaultView?.matchMedia('(prefers-color-scheme: dark)') ?? null)\n : null;\n\n readonly #systemPreference = signal<NgSystemTheme>(this.resolveSystemPreference());\n\n // ── Theme state ───────────────────────────────────────────────────────────\n\n readonly #selectedTheme = signal<NgTheme>(this.resolveInitialTheme());\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#selectedTheme.asReadonly();\n\n /** Resolved theme applied to the DOM. Always `'dark'` or `'light'` (or custom) — never `'system'`. */\n readonly userTheme = computed(() => {\n const theme = this.#selectedTheme();\n return theme === 'system' ? this.#systemPreference() : theme;\n });\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = computed(() => this.userTheme() === 'dark');\n\n /** Whether the currently applied theme is light. */\n readonly isLight = computed(() => this.userTheme() === 'light');\n\n // ── Event handler ─────────────────────────────────────────────────────────\n\n readonly #onSystemPreferenceChange = () =>\n this.#systemPreference.set(this.resolveSystemPreference());\n\n // ── Lifecycle ─────────────────────────────────────────────────────────────\n\n constructor() {\n if (this.#isBrowser && this.#selectedTheme() === 'system') {\n this.startSystemThemeListener();\n }\n\n effect(() => this.applyThemeToDOM(this.userTheme()));\n\n this.#destroyRef.onDestroy(() => this.stopSystemThemeListener());\n }\n\n // ── Public API ────────────────────────────────────────────────────────────\n\n /**\n * Changes the active theme.\n *\n * Persists the choice explicitly so that switching e.g. from `'system'` to\n * `'light'` is saved even when the resolved `userTheme` did not change\n * (system preference was already `'light'`).\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or a custom theme name.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n public setTheme(theme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n if (!this.#validThemes.has(theme)) {\n throw new Error(\n `[ngx-theme-stack] Invalid theme: \"${theme}\". Valid values are: ${[...this.#validThemes].join(', ')}.`,\n );\n }\n\n if (theme === 'system') {\n this.#systemPreference.set(this.resolveSystemPreference());\n this.startSystemThemeListener();\n } else {\n this.stopSystemThemeListener();\n }\n\n this.#selectedTheme.set(theme);\n this.saveTheme(theme);\n }\n\n // ── Private ───────────────────────────────────────────────────────────────\n\n private resolveSystemPreference(): NgSystemTheme {\n return this.#mediaQuery?.matches ? 'dark' : 'light';\n }\n\n private resolveInitialTheme(): NgTheme {\n if (!this.#isBrowser) return this.#config.theme;\n return this.readStoredTheme() ?? this.#config.theme;\n }\n\n private startSystemThemeListener(): void {\n if (!this.#mediaQuery) return;\n this.stopSystemThemeListener();\n this.#mediaQuery.addEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private stopSystemThemeListener(): void {\n this.#mediaQuery?.removeEventListener('change', this.#onSystemPreferenceChange);\n }\n\n private applyThemeToDOM(userTheme: NgTheme): void {\n if (!this.#isBrowser) return;\n\n const host = this.#document.documentElement;\n const { mode } = this.#config;\n\n if (mode === 'attribute' || mode === 'both') {\n this.applyThemeAttribute(host, userTheme);\n }\n\n if (mode === 'class' || mode === 'both') {\n this.applyThemeClasses(host, userTheme);\n }\n\n this.applyColorSchemeHint(host, userTheme);\n }\n\n private applyThemeAttribute(host: HTMLElement, theme: NgTheme): void {\n host.setAttribute('data-theme', theme);\n }\n\n private applyThemeClasses(host: HTMLElement, theme: NgTheme): void {\n for (const t of this.availableThemes) {\n host.classList.remove(t);\n }\n\n host.classList.add(theme);\n }\n\n private applyColorSchemeHint(host: HTMLElement, theme: NgTheme): void {\n if (theme === 'dark' || theme === 'light') {\n host.style.setProperty('color-scheme', theme);\n return;\n }\n\n host.style.removeProperty('color-scheme');\n }\n\n private readStoredTheme(): NgTheme | null {\n try {\n const stored = localStorage.getItem(this.#config.storageKey);\n if (stored && this.#validThemes.has(stored as NgTheme)) {\n return stored as NgTheme;\n }\n return null;\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not read theme from localStorage.', e);\n return null;\n }\n }\n\n private saveTheme(theme: NgTheme): void {\n try {\n localStorage.setItem(this.#config.storageKey, theme);\n } catch (e) {\n console.warn('[ngx-theme-stack] Could not save theme to localStorage.', e);\n }\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for cycling through themes in a fixed order.\n *\n * Default cycle: `'light'` → `'dark'` → `'system'` → `'light'` → ...\n *\n * Use this when you want to offer users a single button that rotates\n * through all available theme options.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeCycleService {\n\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes for cycling. Defaults to ['light', 'dark', 'system']. */\n readonly #cycle = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Advances to the next theme in the cycle.\n *\n * Cycle order is determined by the configured `themes` property in `NgConfig`.\n *\n * If the current theme is not found in the cycle (e.g. set externally),\n * the cycle restarts from the first theme.\n */\n cycle(): void {\n const current = this.#core.selectedTheme();\n const index = this.#cycle.indexOf(current);\n const next = this.#cycle[(index + 1) % this.#cycle.length];\n this.#core.setTheme(next);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\nimport { NgTheme } from '../types';\n\n\n/**\n * Convenience service for selecting a theme from a list.\n *\n * Use this when you want to bind a `<select>` or a group of radio/tab\n * buttons to the full set of available themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeSelectService {\n\n readonly #core = inject(CoreThemeService);\n\n /** List of all configured themes. Defaults to ['light', 'dark', 'system']. */\n readonly availableThemes = this.#core.availableThemes;\n\n /** The theme explicitly selected by the user. May be `'system'`. */\n readonly selectedTheme = this.#core.selectedTheme;\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Applies the given theme.\n *\n * @param theme - The theme to apply: `'dark'`, `'light'`, `'system'`, or custom.\n * @throws If `theme` is not a valid theme according to library configuration.\n */\n select(theme: NgTheme): void {\n this.#core.setTheme(theme);\n }\n}\n","import { inject, Injectable } from '@angular/core';\nimport { CoreThemeService } from '../core/core-theme.service';\n\n/**\n * Convenience service for toggling between `'dark'` and `'light'`.\n *\n * Use this when you only need a simple on/off switch and do not\n * need to manage `'system'` or cycle through themes.\n */\n@Injectable({ providedIn: 'root' })\nexport class ThemeToggleService {\n\n readonly #core = inject(CoreThemeService);\n\n /** Resolved theme applied to the DOM. Always concrete — never `'system'`. */\n readonly userTheme = this.#core.userTheme;\n\n /** Whether the currently applied theme is dark. */\n readonly isDark = this.#core.isDark;\n\n /** Whether the currently applied theme is light. */\n readonly isLight = this.#core.isLight;\n\n /**\n * Toggles between `'dark'` and `'light'`.\n *\n * If the selected theme is explicitly `'dark'`, switches to `'light'`.\n * Otherwise (including `'system'`), switches to `'dark'`.\n */\n toggle(): void {\n const next = this.#core.selectedTheme() === 'dark' ? 'light' : 'dark';\n this.#core.setTheme(next);\n }\n}\n","/*\n * Public API Surface of ngx-theme-stack\n */\n\nexport * from './lib/core/core-theme.service';\nexport * from './lib/services/theme-stack.config';\nexport * from './lib/services/theme-cycle.service';\nexport * from './lib/services/theme-select.service';\nexport * from './lib/services/theme-toggle.service';\nexport * from './lib/types';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "ngx-theme-stack",
3
- "version": "0.0.15",
3
+ "version": "1.0.0",
4
4
  "description": "A stack of themes for Angular applications.",
5
5
  "schematics": "./schematics/collection.json",
6
6
  "ng-add": {
7
7
  "save": "devDependencies"
8
8
  },
9
9
  "peerDependencies": {
10
- "@angular/common": "^21.2.0",
11
- "@angular/core": "^21.2.0"
10
+ "@angular/common": "^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0",
11
+ "@angular/core": "^18.0.0 || ^19.0.0 || ^20.0.0 || ^21.0.0"
12
12
  },
13
13
  "dependencies": {
14
14
  "tslib": "^2.3.0"
@@ -25,6 +25,5 @@
25
25
  "types": "./types/ngx-theme-stack.d.ts",
26
26
  "default": "./fesm2022/ngx-theme-stack.mjs"
27
27
  }
28
- },
29
- "type": "module"
28
+ }
30
29
  }
@@ -3,7 +3,7 @@
3
3
  "schematics": {
4
4
  "ng-add": {
5
5
  "description": "Add my library to the project.",
6
- "factory": "./ng-add/index.cjs#ngAdd",
6
+ "factory": "./ng-add/index#ngAdd",
7
7
  "schema": "./ng-add/schema.json"
8
8
  }
9
9
  }
@@ -0,0 +1,11 @@
1
+ import { SchematicContext, Tree } from '@angular-devkit/schematics';
2
+ /**
3
+ * Attempts to automatically locate and update the application configuration files.
4
+ * It searches for standard Angular file paths like 'app.config.ts', 'main.ts',
5
+ * and 'app.module.ts', injecting the proper import and provider call.
6
+ *
7
+ * @param tree The virtual file tree of the project.
8
+ * @param context The schematic's execution context.
9
+ * @param provideCall The pre-formatted code string for provideThemeStack().
10
+ */
11
+ export declare function patchAppConfig(tree: Tree, context: SchematicContext, provideCall: string): void;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.patchAppConfig = patchAppConfig;
4
+ /**
5
+ * Attempts to automatically locate and update the application configuration files.
6
+ * It searches for standard Angular file paths like 'app.config.ts', 'main.ts',
7
+ * and 'app.module.ts', injecting the proper import and provider call.
8
+ *
9
+ * @param tree The virtual file tree of the project.
10
+ * @param context The schematic's execution context.
11
+ * @param provideCall The pre-formatted code string for provideThemeStack().
12
+ */
13
+ function patchAppConfig(tree, context, provideCall) {
14
+ const candidates = ['src/app/app.config.ts', 'src/main.ts', 'src/app/app.module.ts'];
15
+ for (const filePath of candidates) {
16
+ if (!tree.exists(filePath))
17
+ continue;
18
+ const content = tree.readText(filePath);
19
+ if (content.includes('provideThemeStack')) {
20
+ context.logger.info(`✔ ngx-theme-stack already configured in ${filePath}`);
21
+ return;
22
+ }
23
+ let updated = content;
24
+ // Add import if missing
25
+ if (!updated.includes("from 'ngx-theme-stack'")) {
26
+ const lastImport = updated.lastIndexOf('import ');
27
+ const eol = updated.indexOf('\n', lastImport);
28
+ updated =
29
+ updated.slice(0, eol + 1) +
30
+ `import { provideThemeStack } from 'ngx-theme-stack';\n` +
31
+ updated.slice(eol + 1);
32
+ }
33
+ // Inject into providers array
34
+ if (updated.includes('providers:')) {
35
+ updated = updated.replace(/providers:\s*\[([\s\S]*?)\]/, (_m, inner) => {
36
+ const trimmed = inner.trimEnd();
37
+ const sep = trimmed.length > 0 && !trimmed.endsWith(',') ? ',\n ' : '\n ';
38
+ return `providers: [${trimmed}${sep}${provideCall}\n ]`;
39
+ });
40
+ }
41
+ else if (updated.includes('bootstrapApplication')) {
42
+ updated = updated.replace(/bootstrapApplication\s*\(\s*([^,)]+)\s*\)/, `bootstrapApplication($1, {\n providers: [\n ${provideCall}\n ]\n})`);
43
+ }
44
+ tree.overwrite(filePath, updated);
45
+ context.logger.info(`✔ Added ${provideCall} to ${filePath}`);
46
+ return;
47
+ }
48
+ context.logger.warn(`⚠ Could not find app.config.ts / main.ts / app.module.ts.\n` +
49
+ ` Add manually:\n\n` +
50
+ ` import { provideThemeStack } from 'ngx-theme-stack';\n\n` +
51
+ ` providers: [ ${provideCall} ]`);
52
+ }
53
+ //# sourceMappingURL=app-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"app-config.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/app-config.ts"],"names":[],"mappings":";;AAWA,wCAqDC;AA9DD;;;;;;;;GAQG;AACH,SAAgB,cAAc,CAAC,IAAU,EAAE,OAAyB,EAAE,WAAmB;IACvF,MAAM,UAAU,GAAG,CAAC,uBAAuB,EAAE,aAAa,EAAE,uBAAuB,CAAC,CAAC;IAErF,KAAK,MAAM,QAAQ,IAAI,UAAU,EAAE,CAAC;QAClC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC;YAAE,SAAS;QAErC,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAExC,IAAI,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,CAAC;YAC1C,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,2CAA2C,QAAQ,EAAE,CAAC,CAAC;YAC3E,OAAO;QACT,CAAC;QAED,IAAI,OAAO,GAAG,OAAO,CAAC;QAEtB,wBAAwB;QACxB,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,wBAAwB,CAAC,EAAE,CAAC;YAChD,MAAM,UAAU,GAAG,OAAO,CAAC,WAAW,CAAC,SAAS,CAAC,CAAC;YAClD,MAAM,GAAG,GAAG,OAAO,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAC9C,OAAO;gBACL,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC;oBACzB,wDAAwD;oBACxD,OAAO,CAAC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC;QAC3B,CAAC;QAED,8BAA8B;QAC9B,IAAI,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACnC,OAAO,GAAG,OAAO,CAAC,OAAO,CACvB,6BAA6B,EAC7B,CAAC,EAAU,EAAE,KAAa,EAAE,EAAE;gBAC5B,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,EAAE,CAAC;gBAChC,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC;gBAChF,OAAO,eAAe,OAAO,GAAG,GAAG,GAAG,WAAW,OAAO,CAAC;YAC3D,CAAC,CACF,CAAC;QACJ,CAAC;aAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,sBAAsB,CAAC,EAAE,CAAC;YACpD,OAAO,GAAG,OAAO,CAAC,OAAO,CACvB,2CAA2C,EAC3C,mDAAmD,WAAW,WAAW,CAC1E,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;QAClC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,WAAW,OAAO,QAAQ,EAAE,CAAC,CAAC;QAC7D,OAAO;IACT,CAAC;IAED,OAAO,CAAC,MAAM,CAAC,IAAI,CACjB,6DAA6D;QAC3D,qBAAqB;QACrB,4DAA4D;QAC5D,kBAAkB,WAAW,IAAI,CACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * ⚠ ATTENTION: SHARED CONFIGURATION VALUES
3
+ *
4
+ * These values MUST match the library defaults in:
5
+ * projects/ngx-theme-stack/src/lib/services/theme-stack.config.ts
6
+ *
7
+ * Schematics run in Node.js (CommonJS) and cannot import from the library (ESM),
8
+ * so these defaults are intentionally duplicated here for:
9
+ * 1. Proposing hints/defaults in interactive prompts.
10
+ * 2. Deciding if a property can be omitted from the generated provideThemeStack() call.
11
+ */
12
+ export declare const DEFAULT_THEMES: readonly ["system", "light", "dark"];
13
+ export declare const DEFAULTS: {
14
+ readonly theme: "system";
15
+ readonly storageKey: "ngx-theme-stack-theme";
16
+ readonly mode: "class";
17
+ readonly themes: readonly ["system", "light", "dark"];
18
+ };
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DEFAULTS = exports.DEFAULT_THEMES = void 0;
4
+ /**
5
+ * ⚠ ATTENTION: SHARED CONFIGURATION VALUES
6
+ *
7
+ * These values MUST match the library defaults in:
8
+ * projects/ngx-theme-stack/src/lib/services/theme-stack.config.ts
9
+ *
10
+ * Schematics run in Node.js (CommonJS) and cannot import from the library (ESM),
11
+ * so these defaults are intentionally duplicated here for:
12
+ * 1. Proposing hints/defaults in interactive prompts.
13
+ * 2. Deciding if a property can be omitted from the generated provideThemeStack() call.
14
+ */
15
+ exports.DEFAULT_THEMES = ['system', 'light', 'dark'];
16
+ exports.DEFAULTS = {
17
+ theme: 'system',
18
+ storageKey: 'ngx-theme-stack-theme',
19
+ mode: 'class',
20
+ themes: [...exports.DEFAULT_THEMES],
21
+ };
22
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/constants.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;GAUG;AACU,QAAA,cAAc,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,MAAM,CAAU,CAAC;AAEtD,QAAA,QAAQ,GAAG;IACtB,KAAK,EAAE,QAAQ;IACf,UAAU,EAAE,uBAAuB;IACnC,IAAI,EAAE,OAAO;IACb,MAAM,EAAE,CAAC,GAAG,sBAAc,CAAC;CACnB,CAAC"}
@@ -1,3 +1,11 @@
1
1
  import { Rule } from '@angular-devkit/schematics';
2
2
  import { Schema } from './schema';
3
+ /**
4
+ * Main schematic factory for the 'ng-add' command.
5
+ * Depending on the 'mode' option, it either applies default library values ("quick")
6
+ * or starts an interactive session to collect personalized configuration ("custom").
7
+ *
8
+ * @param options The schema options provided by the Angular CLI.
9
+ * @returns A rule that modifies the project's setup.
10
+ */
3
11
  export declare function ngAdd(options: Schema): Rule;
@@ -0,0 +1,89 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.ngAdd = ngAdd;
13
+ const schematics_1 = require("@angular-devkit/schematics");
14
+ const constants_1 = require("./constants");
15
+ const utils_1 = require("./utils");
16
+ const app_config_1 = require("./app-config");
17
+ /**
18
+ * Interactively prompts the user for custom configuration options using readline.
19
+ * It allows defining additional themes, selecting a default theme from the expanded list,
20
+ * providing a custom localStorage key, and choosing a CSS application mode.
21
+ *
22
+ * @returns A promise resolving to the user's selected configuration options.
23
+ */
24
+ function collectCustomOptions() {
25
+ return __awaiter(this, void 0, void 0, function* () {
26
+ const rl = (0, utils_1.createRl)();
27
+ try {
28
+ process.stdout.write('\n');
29
+ const rawThemes = yield (0, utils_1.ask)(rl, ' Custom themes (comma-separated, Enter to skip): ');
30
+ const customThemes = rawThemes
31
+ ? rawThemes
32
+ .split(',')
33
+ .map((t) => t.trim())
34
+ .filter(Boolean)
35
+ : [];
36
+ const allThemes = [...constants_1.DEFAULT_THEMES, ...customThemes];
37
+ const theme = yield (0, utils_1.askList)(rl, 'Default theme:', allThemes, 0);
38
+ const rawKey = yield (0, utils_1.ask)(rl, ` localStorage key [${constants_1.DEFAULTS.storageKey}]: `);
39
+ const storageKey = rawKey || constants_1.DEFAULTS.storageKey;
40
+ const MODES = ['class', 'attribute', 'both'];
41
+ const mode = yield (0, utils_1.askList)(rl, 'How to apply theme on <html>:', MODES, 0);
42
+ process.stdout.write('\n');
43
+ return { theme, storageKey, mode, themes: allThemes };
44
+ }
45
+ finally {
46
+ rl.close();
47
+ }
48
+ });
49
+ }
50
+ /**
51
+ * Main schematic factory for the 'ng-add' command.
52
+ * Depending on the 'mode' option, it either applies default library values ("quick")
53
+ * or starts an interactive session to collect personalized configuration ("custom").
54
+ *
55
+ * @param options The schema options provided by the Angular CLI.
56
+ * @returns A rule that modifies the project's setup.
57
+ */
58
+ function ngAdd(options) {
59
+ return (_tree, context) => __awaiter(this, void 0, void 0, function* () {
60
+ context.logger.info('');
61
+ context.logger.info('🎨 ngx-theme-stack — setup');
62
+ context.logger.info('');
63
+ let provideCall;
64
+ if (options.mode === 'quick') {
65
+ provideCall = 'provideThemeStack()';
66
+ context.logger.info('⚡ Quick setup — defaults applied by the library (DEFAULT_NG_CONFIG).');
67
+ }
68
+ else {
69
+ context.logger.info('🛠 Custom setup — answer the prompts below:');
70
+ const opts = yield collectCustomOptions();
71
+ const { theme, storageKey, mode, themes } = opts;
72
+ context.logger.info(' Applying your configuration:');
73
+ context.logger.info(` theme : ${theme}`);
74
+ context.logger.info(` themes : [${themes.join(', ')}]`);
75
+ context.logger.info(` storageKey : ${storageKey}`);
76
+ context.logger.info(` mode : ${mode}`);
77
+ provideCall = (0, utils_1.buildProvideCall)(theme, storageKey, mode, themes);
78
+ }
79
+ return (0, schematics_1.chain)([
80
+ (t, ctx) => {
81
+ (0, app_config_1.patchAppConfig)(t, ctx, provideCall);
82
+ ctx.logger.info('');
83
+ ctx.logger.info('✅ Done! Run `ng serve` to see ngx-theme-stack in action.');
84
+ ctx.logger.info('');
85
+ },
86
+ ]);
87
+ });
88
+ }
89
+ //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/index.ts"],"names":[],"mappings":";;;;;;;;;;;AAwDA,sBAkCC;AA1FD,2DAAiF;AAEjF,2CAAuD;AACvD,mCAAmE;AACnE,6CAA8C;AAE9C;;;;;;GAMG;AACH,SAAe,oBAAoB;;QAMjC,MAAM,EAAE,GAAG,IAAA,gBAAQ,GAAE,CAAC;QAEtB,IAAI,CAAC;YACH,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAE3B,MAAM,SAAS,GAAG,MAAM,IAAA,WAAG,EAAC,EAAE,EAAE,oDAAoD,CAAC,CAAC;YACtF,MAAM,YAAY,GAAG,SAAS;gBAC5B,CAAC,CAAC,SAAS;qBACN,KAAK,CAAC,GAAG,CAAC;qBACV,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;qBACpB,MAAM,CAAC,OAAO,CAAC;gBACpB,CAAC,CAAC,EAAE,CAAC;YACP,MAAM,SAAS,GAAG,CAAC,GAAG,0BAAc,EAAE,GAAG,YAAY,CAAC,CAAC;YAEvD,MAAM,KAAK,GAAG,MAAM,IAAA,eAAO,EAAC,EAAE,EAAE,gBAAgB,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC;YAEhE,MAAM,MAAM,GAAG,MAAM,IAAA,WAAG,EAAC,EAAE,EAAE,uBAAuB,oBAAQ,CAAC,UAAU,KAAK,CAAC,CAAC;YAC9E,MAAM,UAAU,GAAG,MAAM,IAAI,oBAAQ,CAAC,UAAU,CAAC;YAEjD,MAAM,KAAK,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,CAAU,CAAC;YACtD,MAAM,IAAI,GAAG,MAAM,IAAA,eAAO,EAAC,EAAE,EAAE,+BAA+B,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC;YAE1E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC3B,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC;QACxD,CAAC;gBAAS,CAAC;YACT,EAAE,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;IACH,CAAC;CAAA;AAED;;;;;;;GAOG;AACH,SAAgB,KAAK,CAAC,OAAe;IACnC,OAAO,CAAO,KAAW,EAAE,OAAyB,EAAE,EAAE;QACtD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACxB,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;QACnD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAExB,IAAI,WAAmB,CAAC;QAExB,IAAI,OAAO,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC7B,WAAW,GAAG,qBAAqB,CAAC;YACpC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;QAC9F,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;YACpE,MAAM,IAAI,GAAG,MAAM,oBAAoB,EAAE,CAAC;YAC1C,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;YAEjD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,iCAAiC,CAAC,CAAC;YACvD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,KAAK,EAAE,CAAC,CAAC;YAChD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YAC9D,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,UAAU,EAAE,CAAC,CAAC;YACrD,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,mBAAmB,IAAI,EAAE,CAAC,CAAC;YAE/C,WAAW,GAAG,IAAA,wBAAgB,EAAC,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;QAClE,CAAC;QAED,OAAO,IAAA,kBAAK,EAAC;YACX,CAAC,CAAO,EAAE,GAAqB,EAAE,EAAE;gBACjC,IAAA,2BAAc,EAAC,CAAC,EAAE,GAAG,EAAE,WAAW,CAAC,CAAC;gBACpC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;gBACpB,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;gBAC7E,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACtB,CAAC;SACF,CAAC,CAAC;IACL,CAAC,CAAA,CAAC;AACJ,CAAC"}
@@ -1,17 +1,6 @@
1
- /** Options passed to the ng-add schematic by the Angular CLI. */
2
1
  export interface Schema {
3
2
  /** Name of the Angular project to configure. */
4
3
  project: string;
5
- /**
6
- * Installation mode:
7
- * - 'quick' → apply defaults immediately, no further questions
8
- * - 'custom' → prompt the user for each individual option
9
- */
4
+ /** 'quick' applies defaults silently. 'custom' prompts interactively. */
10
5
  mode: 'quick' | 'custom';
11
- /** Default theme applied on startup. Only used in custom mode. */
12
- theme: 'system' | 'light' | 'dark' | string;
13
- /** localStorage key used to persist the theme. Only used in custom mode. */
14
- storageKey: string;
15
- /** Strategy used to apply the theme to the <html> element. Only used in custom mode. */
16
- themeMode: 'class' | 'attribute' | 'both';
17
6
  }
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "mode": {
15
15
  "type": "string",
16
- "description": "Installation mode: quick uses defaults, custom asks each option.",
16
+ "description": "Installation mode.",
17
17
  "default": "quick",
18
18
  "enum": ["quick", "custom"],
19
19
  "x-prompt": {
@@ -22,49 +22,14 @@
22
22
  "items": [
23
23
  {
24
24
  "value": "quick",
25
- "label": "Quick – use default settings (theme: system, mode: class, storageKey: ngx-theme-stack-theme)"
25
+ "label": "Quick – apply defaults instantly (theme: system, mode: class)"
26
26
  },
27
27
  {
28
28
  "value": "custom",
29
- "label": "Custom – choose each option interactively"
29
+ "label": "Custom – choose themes, default theme, storage key and apply mode"
30
30
  }
31
31
  ]
32
32
  }
33
- },
34
- "theme": {
35
- "type": "string",
36
- "description": "Default theme to apply on startup.",
37
- "default": "system",
38
- "x-prompt": {
39
- "message": "Which default theme should be applied on startup?",
40
- "type": "list",
41
- "items": [
42
- { "value": "system", "label": "system – follow OS preference (recommended)" },
43
- { "value": "light", "label": "light – always light" },
44
- { "value": "dark", "label": "dark – always dark" }
45
- ]
46
- }
47
- },
48
- "storageKey": {
49
- "type": "string",
50
- "description": "localStorage key used to persist the selected theme.",
51
- "default": "ngx-theme-stack-theme",
52
- "x-prompt": "What localStorage key should be used to persist the theme? (default: ngx-theme-stack-theme)"
53
- },
54
- "themeMode": {
55
- "type": "string",
56
- "description": "Strategy used to apply the theme to the <html> element.",
57
- "default": "class",
58
- "enum": ["class", "attribute", "both"],
59
- "x-prompt": {
60
- "message": "How should the theme be applied to the <html> element?",
61
- "type": "list",
62
- "items": [
63
- { "value": "class", "label": "class – add/remove CSS class (e.g. .dark)" },
64
- { "value": "attribute", "label": "attribute – set data-theme attribute" },
65
- { "value": "both", "label": "both – apply class AND attribute simultaneously" }
66
- ]
67
- }
68
33
  }
69
34
  },
70
35
  "required": ["project"]
@@ -0,0 +1,20 @@
1
+ import * as readline from 'readline';
2
+ /**
3
+ * Creates a readline interface using the system's standard input and output.
4
+ */
5
+ export declare function createRl(): readline.Interface;
6
+ /**
7
+ * Prompts the user with a question and returns their trimmed answer.
8
+ */
9
+ export declare function ask(rl: readline.Interface, question: string): Promise<string>;
10
+ /**
11
+ * Displays a numbered list of items to the user and returns the selected item.
12
+ * If the input is invalid or ignored, the default index item is returned.
13
+ */
14
+ export declare function askList(rl: readline.Interface, label: string, items: readonly string[], defaultIndex?: number): Promise<string>;
15
+ /**
16
+ * Builds the 'provideThemeStack({...})' string representation.
17
+ * It compares the selected values with library defaults to generate a
18
+ * minimal call string (omitting properties that match defaults).
19
+ */
20
+ export declare function buildProvideCall(theme: string, storageKey: string, mode: string, themes: string[]): string;
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.createRl = createRl;
13
+ exports.ask = ask;
14
+ exports.askList = askList;
15
+ exports.buildProvideCall = buildProvideCall;
16
+ const readline = require("readline");
17
+ const constants_1 = require("./constants");
18
+ /**
19
+ * Creates a readline interface using the system's standard input and output.
20
+ */
21
+ function createRl() {
22
+ return readline.createInterface({ input: process.stdin, output: process.stdout });
23
+ }
24
+ /**
25
+ * Prompts the user with a question and returns their trimmed answer.
26
+ */
27
+ function ask(rl, question) {
28
+ return new Promise((resolve) => rl.question(question, (a) => resolve(a.trim())));
29
+ }
30
+ /**
31
+ * Displays a numbered list of items to the user and returns the selected item.
32
+ * If the input is invalid or ignored, the default index item is returned.
33
+ */
34
+ function askList(rl_1, label_1, items_1) {
35
+ return __awaiter(this, arguments, void 0, function* (rl, label, items, defaultIndex = 0) {
36
+ process.stdout.write(`\n ${label}\n`);
37
+ items.forEach((item, i) => process.stdout.write(` ${i + 1}) ${item}\n`));
38
+ const raw = yield ask(rl, ` Choice [${defaultIndex + 1}]: `);
39
+ const n = parseInt(raw, 10);
40
+ return isNaN(n) || n < 1 || n > items.length ? items[defaultIndex] : items[n - 1];
41
+ });
42
+ }
43
+ /**
44
+ * Builds the 'provideThemeStack({...})' string representation.
45
+ * It compares the selected values with library defaults to generate a
46
+ * minimal call string (omitting properties that match defaults).
47
+ */
48
+ function buildProvideCall(theme, storageKey, mode, themes) {
49
+ const defaultThemes = [...constants_1.DEFAULT_THEMES];
50
+ const isDefaultThemes = themes.length === defaultThemes.length && themes.every((t, i) => t === defaultThemes[i]);
51
+ const isAllDefault = theme === constants_1.DEFAULTS.theme &&
52
+ storageKey === constants_1.DEFAULTS.storageKey &&
53
+ mode === constants_1.DEFAULTS.mode &&
54
+ isDefaultThemes;
55
+ if (isAllDefault)
56
+ return `provideThemeStack()`;
57
+ const parts = [];
58
+ if (theme !== constants_1.DEFAULTS.theme)
59
+ parts.push(`theme: '${theme}'`);
60
+ if (storageKey !== constants_1.DEFAULTS.storageKey)
61
+ parts.push(`storageKey: '${storageKey}'`);
62
+ if (mode !== constants_1.DEFAULTS.mode)
63
+ parts.push(`mode: '${mode}'`);
64
+ if (!isDefaultThemes) {
65
+ const arr = themes.map((t) => `'${t}'`).join(', ');
66
+ parts.push(`themes: [${arr}]`);
67
+ }
68
+ return `provideThemeStack({ ${parts.join(', ')} })`;
69
+ }
70
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../../../projects/ngx-theme-stack/schematics/ng-add/utils.ts"],"names":[],"mappings":"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"}
@@ -3,7 +3,7 @@ import { InjectionToken } from '@angular/core';
3
3
  import * as ngx_theme_stack from 'ngx-theme-stack';
4
4
 
5
5
  /** Built-in themes. All other values are considered custom themes. */
6
- declare const DEFAULT_THEMES: readonly ["light", "dark", "system"];
6
+ declare const DEFAULT_THEMES: readonly ["system", "light", "dark"];
7
7
  /** String union with autocompletion for defaults + any string support for customization. */
8
8
  type NgTheme = (typeof DEFAULT_THEMES)[number] | (string & {});
9
9
  type NgSystemTheme = Exclude<NgTheme, 'system'>;
@@ -59,11 +59,20 @@ declare class CoreThemeService {
59
59
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<CoreThemeService>;
60
60
  }
61
61
 
62
+ /**
63
+ * ⚠ ATTENTION: SHARED CONFIGURATION VALUES
64
+ *
65
+ * These values MUST match the schematic defaults in:
66
+ * projects/ngx-theme-stack/schematics/ng-add/index.ts
67
+ *
68
+ * If you change any of these, you MUST also update the schematic's DEFAULTS
69
+ * constant so 'ng add' continues to provide correct hints and clean code.
70
+ */
62
71
  declare const DEFAULT_NG_CONFIG: {
63
72
  theme: "system";
64
73
  storageKey: string;
65
74
  mode: "class";
66
- themes: ("light" | "dark" | "system")[];
75
+ themes: ("system" | "light" | "dark")[];
67
76
  };
68
77
  declare const NGX_THEME_STACK_CONFIG: InjectionToken<NgConfig>;
69
78
  /**
@@ -1,157 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ngAdd = ngAdd;
4
- const schematics_1 = require("@angular-devkit/schematics");
5
- // ---------------------------------------------------------------------------
6
- // Default configuration (mirrors DEFAULT_NG_CONFIG from the library)
7
- // ---------------------------------------------------------------------------
8
- const DEFAULTS = {
9
- theme: 'system',
10
- storageKey: 'ngx-theme-stack-theme',
11
- themeMode: 'class',
12
- };
13
- // ---------------------------------------------------------------------------
14
- // Helper: build the provideThemeStack() call string
15
- // ---------------------------------------------------------------------------
16
- function buildProvideCall(theme, storageKey, themeMode) {
17
- const isDefault = theme === DEFAULTS.theme &&
18
- storageKey === DEFAULTS.storageKey &&
19
- themeMode === DEFAULTS.themeMode;
20
- if (isDefault) {
21
- // Clean call — no arguments needed, defaults will be used by the library
22
- return `provideThemeStack()`;
23
- }
24
- const parts = [];
25
- if (theme !== DEFAULTS.theme)
26
- parts.push(`theme: '${theme}'`);
27
- if (storageKey !== DEFAULTS.storageKey)
28
- parts.push(`storageKey: '${storageKey}'`);
29
- if (themeMode !== DEFAULTS.themeMode)
30
- parts.push(`mode: '${themeMode}'`);
31
- return `provideThemeStack({ ${parts.join(', ')} })`;
32
- }
33
- // ---------------------------------------------------------------------------
34
- // Helper: patch app.config.ts (or main.ts for bootstrapApplication)
35
- // ---------------------------------------------------------------------------
36
- function patchAppConfig(tree, context, provideCall) {
37
- // Try app.config.ts first (standalone Angular ≥ 17 default)
38
- const candidates = [
39
- 'src/app/app.config.ts',
40
- 'src/main.ts',
41
- 'src/app/app.module.ts',
42
- ];
43
- let patched = false;
44
- for (const filePath of candidates) {
45
- if (!tree.exists(filePath))
46
- continue;
47
- const content = tree.readText(filePath);
48
- // Already configured?
49
- if (content.includes('provideThemeStack')) {
50
- context.logger.info(`✔ ngx-theme-stack is already configured in ${filePath}`);
51
- patched = true;
52
- break;
53
- }
54
- // -----------------------------------------------------------------------
55
- // app.config.ts / standalone bootstrapApplication pattern
56
- // -----------------------------------------------------------------------
57
- if (filePath.endsWith('app.config.ts') || filePath.endsWith('main.ts')) {
58
- // Add import statement
59
- let updated = content;
60
- if (!updated.includes("from 'ngx-theme-stack'")) {
61
- const lastImportIndex = updated.lastIndexOf("import ");
62
- const endOfLine = updated.indexOf('\n', lastImportIndex);
63
- const importStatement = `import { provideThemeStack } from 'ngx-theme-stack';`;
64
- updated =
65
- updated.slice(0, endOfLine + 1) +
66
- importStatement + '\n' +
67
- updated.slice(endOfLine + 1);
68
- }
69
- // Inject into providers array
70
- if (updated.includes('providers:')) {
71
- // Insert before closing bracket of providers array
72
- updated = updated.replace(/providers:\s*\[([\s\S]*?)\]/, (_match, inner) => {
73
- const trimmed = inner.trimEnd();
74
- const separator = trimmed.length > 0 && !trimmed.endsWith(',') ? ',\n ' : '\n ';
75
- return `providers: [${trimmed}${separator}${provideCall}\n ]`;
76
- });
77
- }
78
- else if (updated.includes('bootstrapApplication')) {
79
- // No providers key yet — add it
80
- updated = updated.replace(/bootstrapApplication\s*\(\s*([^,)]+)\s*\)/, `bootstrapApplication($1, {\n providers: [\n ${provideCall}\n ]\n})`);
81
- }
82
- tree.overwrite(filePath, updated);
83
- context.logger.info(`✔ Added ${provideCall} to ${filePath}`);
84
- patched = true;
85
- break;
86
- }
87
- // -----------------------------------------------------------------------
88
- // app.module.ts (legacy NgModule pattern)
89
- // -----------------------------------------------------------------------
90
- if (filePath.endsWith('app.module.ts')) {
91
- let updated = content;
92
- if (!updated.includes("from 'ngx-theme-stack'")) {
93
- const lastImportIndex = updated.lastIndexOf("import ");
94
- const endOfLine = updated.indexOf('\n', lastImportIndex);
95
- const importStatement = `import { provideThemeStack } from 'ngx-theme-stack';`;
96
- updated =
97
- updated.slice(0, endOfLine + 1) +
98
- importStatement + '\n' +
99
- updated.slice(endOfLine + 1);
100
- }
101
- // Add to NgModule providers
102
- updated = updated.replace(/providers:\s*\[([\s\S]*?)\]/, (_match, inner) => {
103
- const trimmed = inner.trimEnd();
104
- const separator = trimmed.length > 0 && !trimmed.endsWith(',') ? ',\n ' : '\n ';
105
- return `providers: [${trimmed}${separator}${provideCall}\n ]`;
106
- });
107
- tree.overwrite(filePath, updated);
108
- context.logger.info(`✔ Added ${provideCall} to ${filePath}`);
109
- patched = true;
110
- break;
111
- }
112
- }
113
- if (!patched) {
114
- context.logger.warn(`⚠ Could not locate app.config.ts, main.ts, or app.module.ts.\n` +
115
- ` Please add the following manually to your providers array:\n\n` +
116
- ` import { provideThemeStack } from 'ngx-theme-stack';\n\n` +
117
- ` providers: [ ${provideCall} ]`);
118
- }
119
- }
120
- // ---------------------------------------------------------------------------
121
- // Schematic factory
122
- // ---------------------------------------------------------------------------
123
- function ngAdd(options) {
124
- return (_tree, context) => {
125
- var _a, _b, _c;
126
- context.logger.info('');
127
- context.logger.info('🎨 ngx-theme-stack — setup');
128
- context.logger.info('');
129
- // Resolve final config values
130
- // In 'quick' mode the x-prompt for theme/storageKey/themeMode are never shown
131
- // (they keep their schema defaults), so we always have usable values here.
132
- const theme = options.mode === 'quick' ? DEFAULTS.theme : ((_a = options.theme) !== null && _a !== void 0 ? _a : DEFAULTS.theme);
133
- const storageKey = options.mode === 'quick' ? DEFAULTS.storageKey : ((_b = options.storageKey) !== null && _b !== void 0 ? _b : DEFAULTS.storageKey);
134
- const themeMode = options.mode === 'quick' ? DEFAULTS.themeMode : ((_c = options.themeMode) !== null && _c !== void 0 ? _c : DEFAULTS.themeMode);
135
- if (options.mode === 'quick') {
136
- context.logger.info('⚡ Quick setup — applying default configuration:');
137
- }
138
- else {
139
- context.logger.info('🛠 Custom setup — applying your configuration:');
140
- }
141
- context.logger.info(` theme : ${theme}`);
142
- context.logger.info(` storageKey : ${storageKey}`);
143
- context.logger.info(` mode : ${themeMode}`);
144
- context.logger.info('');
145
- const provideCall = buildProvideCall(theme, storageKey, themeMode);
146
- return (0, schematics_1.chain)([
147
- (t, ctx) => {
148
- patchAppConfig(t, ctx, provideCall);
149
- ctx.logger.info('');
150
- ctx.logger.info('✅ ngx-theme-stack configured successfully!');
151
- ctx.logger.info(' Run `ng serve` to see your theme stack in action.');
152
- ctx.logger.info('');
153
- },
154
- ]);
155
- };
156
- }
157
- //# sourceMappingURL=index.js.map
File without changes