@reforgium/presentia 1.4.4 → 2.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/CHANGELOG.md +144 -0
- package/LICENSE +21 -0
- package/README.md +346 -35
- package/bin/presentia-gen-lang-keys.mjs +1 -1
- package/bin/presentia-gen-namespaces.mjs +1248 -0
- package/fesm2022/reforgium-presentia.mjs +750 -197
- package/package.json +27 -7
- package/types/reforgium-presentia.d.ts +292 -89
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
## [2.0.0]: 04.04.2026
|
|
2
|
+
### Feat:
|
|
3
|
+
- added grouped `providePresentia(...)` v2 entry point and marked `provideReInit(...)` as legacy in dev mode
|
|
4
|
+
- extended `ThemeService` with DOM strategies, theme registry validation, and configurable persistence adapter support
|
|
5
|
+
- extended `AdaptiveService` with named breakpoint helpers (`breakpoint`, `is`, `isAtLeast`, `isBetween`, `isMobile`, `isTablet`, `isDesktopSmall`)
|
|
6
|
+
- extended `IfDeviceDirective` with `reIfDeviceAtLeast` and `reIfDeviceBetween`
|
|
7
|
+
- extended `RouteWatcher` with `deepestParams`, `mergedData`, `routePattern`, `selectParam`, and path matching helpers
|
|
8
|
+
- aligned `SeoRouteListener` with merged route data and normalized route-tree path resolution
|
|
9
|
+
|
|
10
|
+
### Docs:
|
|
11
|
+
- documented grouped v2 config, theme DOM/persistence options, and adaptive breakpoint helpers
|
|
12
|
+
- documented recommended v2 setup, extended route watcher state, and adaptive range directives
|
|
13
|
+
- promoted `V2-MIGRATION.md` from draft to working migration guide and refreshed `V2-CONFIG.md` / `ROADMAP-2.0.0.md`
|
|
14
|
+
- moved compatibility-only surface out of primary README export lists and into explicit legacy notes
|
|
15
|
+
|
|
16
|
+
### Test:
|
|
17
|
+
- added provider and runtime coverage for grouped theme config, persistence adapters, theme registry constraints, and adaptive helper methods
|
|
18
|
+
- added route watcher coverage for merged/deepest selectors and adaptive directive coverage for range-based rendering
|
|
19
|
+
- added SEO listener regression coverage for merged route-data application and canonical path normalization
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## [1.6.0]: 03.04.2026
|
|
24
|
+
### Feat:
|
|
25
|
+
- added route-aware namespace preload via `LocaleConfig.routeNamespacePreload` with `blocking` / `lazy` modes
|
|
26
|
+
- added strict preload failure handling via `routeNamespacePreload.onError = 'throw'`
|
|
27
|
+
- added route namespace generator CLI `presentia-gen-namespaces`
|
|
28
|
+
- added generator report mode (`--report`, `--print-report`) for unresolved lazy imports and unsupported static-analysis cases
|
|
29
|
+
- extended generator extraction for route data namespaces, `reLangAttrs`, object-style `reLang`, standalone imports, `loadComponent`, and `loadChildren`
|
|
30
|
+
- added route preload diagnostics for late namespace loads after navigation
|
|
31
|
+
- added batch namespace diagnostics and `maxBatchSize` chunking in `LangService.loadNamespaces(...)`
|
|
32
|
+
|
|
33
|
+
### Docs:
|
|
34
|
+
- documented route preload modes, manifest generation, report mode, and consumer CLI defaults in `README`
|
|
35
|
+
- clarified `LangPipe.placeholder` as the loading-time value while `LocaleConfig.defaultValue` remains a missing-key fallback
|
|
36
|
+
|
|
37
|
+
### Test:
|
|
38
|
+
- added integration coverage for real router navigation with route preload
|
|
39
|
+
- added regression coverage for custom `dataKey`, `mergeStrategy: 'replace'`, manifest parent/child merge, strict preload failures, batch diagnostics, and `maxBatchSize`
|
|
40
|
+
- extended generator smoke coverage for report output, `reLangAttrs`, and const-based `presentiaNamespaces`
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## [1.5.0]: 16.03.2026
|
|
45
|
+
### Fix:
|
|
46
|
+
- `SeoRouteListener.init(...)` no longer duplicates router subscriptions on repeated calls and now reuses normalized `baseUrl`
|
|
47
|
+
- `ThemeService` now normalizes persisted/requested theme values before switching
|
|
48
|
+
- `LangPipe` now warns once in dev mode when a namespace is loaded but the requested key is unresolved
|
|
49
|
+
|
|
50
|
+
### Feat:
|
|
51
|
+
- added `LangService.has(...)` for explicit translation key existence checks
|
|
52
|
+
- `SeoService` is now provided in root
|
|
53
|
+
|
|
54
|
+
### Refactor:
|
|
55
|
+
- removed lightweight `rxjs` usage from `AdaptiveService` resize listener and route event listeners (`RouteWatcher`, `SeoRouteListener`) in favor of native subscriptions/cleanup
|
|
56
|
+
- tightened `provideReInit(...)` factories for `CHANGE_LANG` / `CHANGE_THEME`
|
|
57
|
+
- improved presentia package metadata and test setup for storage-backed specs
|
|
58
|
+
|
|
59
|
+
### Test:
|
|
60
|
+
- added regression coverage for repeated `SeoRouteListener.init(...)`, `ThemeService` contract behavior, init provider wiring, and unresolved lang key warnings
|
|
61
|
+
- kept adaptive/seo regression coverage green after the refactor
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## [1.4.4]: 05.03.2026
|
|
66
|
+
### Fix:
|
|
67
|
+
- improved `RouteWatcher.params` to merge params from `pathFromRoot` (parent to deepest child), not only from deepest snapshot
|
|
68
|
+
|
|
69
|
+
### Test:
|
|
70
|
+
- added a regression test for parent-child params inheritance in nested routes with parent component
|
|
71
|
+
|
|
72
|
+
---
|
|
73
|
+
|
|
74
|
+
## [1.4.3]: 27.02.2026
|
|
75
|
+
### Fix:
|
|
76
|
+
- fixed `RouteWatcher.url` to build a full path from `pathFromRoot` instead of only deepest snapshot segments
|
|
77
|
+
|
|
78
|
+
### Test:
|
|
79
|
+
- added a regression test for nested route URL composition (`/orgs/:orgId/users/:id`)
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
## [1.4.2]: 16.02.2026
|
|
84
|
+
### Fix:
|
|
85
|
+
- fixed `LangService.getChainedValue` deep lookup for keys with flat namespace prefix (e.g. `common.some-key.sub-key.target`)
|
|
86
|
+
|
|
87
|
+
### Test:
|
|
88
|
+
- added a regression test for nested translation resolution via flat namespace prefix key
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## [1.4.0]: 15.02.2026
|
|
93
|
+
### Feat:
|
|
94
|
+
- added `LocaleConfig.requestBuilder` and `LocaleConfig.responseAdapter` for custom API url/payload mapping
|
|
95
|
+
- added `LocaleConfig.requestOptionsFactory` to pass `HttpClient` options (`headers`, `params`, `withCredentials`, `responseType`, `context`, `transferCache`)
|
|
96
|
+
- added batch namespace loading support: `loadNamespaces`, `batchRequestBuilder`, `batchResponseAdapter`
|
|
97
|
+
- added namespace cache controls: `namespaceCache.maxNamespaces`, `namespaceCache.ttlMs`
|
|
98
|
+
- added `LangService.evictNamespace` and `LangService.clearNamespaceCache`
|
|
99
|
+
- added typed lang key extension point (`LangKeyRegistry`, `LangKey`) for `LangService.get/observe`
|
|
100
|
+
- added package CLI `presentia-gen-lang-keys` for generating key unions from locale json
|
|
101
|
+
|
|
102
|
+
### Test:
|
|
103
|
+
- added integration test for language switch across multiple namespaces without page reload
|
|
104
|
+
- added smoke test for CLI generator output
|
|
105
|
+
- extended `LangService` tests for request options, batch mode, stale response protection, and namespace cache policies
|
|
106
|
+
|
|
107
|
+
### Docs:
|
|
108
|
+
- updated README for new `LocaleConfig` options, service methods, typed keys, and CLI usage
|
|
109
|
+
|
|
110
|
+
---
|
|
111
|
+
|
|
112
|
+
## [1.2.0]: 16.01.2026
|
|
113
|
+
### Feat:
|
|
114
|
+
- added implementation providers: `CHANGE_LANG` and `CHANGE_THEME`
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## [1.1.1]: 14.01.2026
|
|
119
|
+
|
|
120
|
+
### Feat:
|
|
121
|
+
- added `translate` method to `LangDirective`
|
|
122
|
+
|
|
123
|
+
### Fix:
|
|
124
|
+
- fixed `reIfDevice` directive name
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## [1.1.0]: 14.01.2026
|
|
129
|
+
|
|
130
|
+
### Feat:
|
|
131
|
+
- added `LangDirective`, auto translate
|
|
132
|
+
|
|
133
|
+
---
|
|
134
|
+
|
|
135
|
+
## [1.0.1]: 11.01.2026
|
|
136
|
+
|
|
137
|
+
### Chore:
|
|
138
|
+
- updated docs
|
|
139
|
+
|
|
140
|
+
---
|
|
141
|
+
|
|
142
|
+
## [1.0.0]: 26.12.2025
|
|
143
|
+
|
|
144
|
+
- Init
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 rtommievich
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -10,19 +10,13 @@ Infrastructure package for Angular applications:
|
|
|
10
10
|
- route state helper (`RouteWatcher`),
|
|
11
11
|
- SEO automation (`SeoService`, `SeoRouteListener`).
|
|
12
12
|
|
|
13
|
-
## Release Highlights (
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
`namespaceCache.maxNamespaces`, `namespaceCache.ttlMs`,
|
|
21
|
-
plus `evictNamespace` and `clearNamespaceCache`.
|
|
22
|
-
- Typed localization keys support:
|
|
23
|
-
`LangKeyRegistry` extension point for `get()` and `observe()`.
|
|
24
|
-
- Consumer CLI for key generation:
|
|
25
|
-
`presentia-gen-lang-keys`.
|
|
13
|
+
## Release Highlights (2.0.0)
|
|
14
|
+
|
|
15
|
+
- Grouped `v2` provider via `providePresentia(...)`.
|
|
16
|
+
- Route-aware namespace preload with generated manifests, diagnostics, and strict failure handling.
|
|
17
|
+
- Extended `ThemeService` with theme registry, configurable DOM strategy, and persistence adapter.
|
|
18
|
+
- Breakpoint-first adaptive helpers with `reIfDeviceAtLeast` and `reIfDeviceBetween`.
|
|
19
|
+
- Richer `RouteWatcher` state with merged/deepest selectors and route matching helpers.
|
|
26
20
|
|
|
27
21
|
## Install
|
|
28
22
|
|
|
@@ -30,30 +24,45 @@ plus `evictNamespace` and `clearNamespaceCache`.
|
|
|
30
24
|
npm i @reforgium/presentia
|
|
31
25
|
```
|
|
32
26
|
|
|
27
|
+
## Recommended Setup
|
|
28
|
+
|
|
29
|
+
For new integrations prefer:
|
|
30
|
+
- `providePresentia(...)`
|
|
31
|
+
- route namespace preload in `blocking` mode
|
|
32
|
+
- `theme.registry` even if you currently use only `light` / `dark`
|
|
33
|
+
- explicit `adaptive.breakpoints` when app breakpoints differ from defaults
|
|
34
|
+
|
|
35
|
+
Legacy integrations can keep `provideReInit(...)` temporarily, but it is now the compatibility path, not the recommended one.
|
|
36
|
+
|
|
33
37
|
## Quick Start
|
|
34
38
|
|
|
35
39
|
```ts
|
|
36
40
|
import { bootstrapApplication } from '@angular/platform-browser';
|
|
37
|
-
import {
|
|
41
|
+
import { providePresentia } from '@reforgium/presentia';
|
|
38
42
|
|
|
39
43
|
bootstrapApplication(AppComponent, {
|
|
40
44
|
providers: [
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
45
|
+
providePresentia({
|
|
46
|
+
lang: {
|
|
47
|
+
source: {
|
|
48
|
+
url: '/assets/i18n',
|
|
49
|
+
fromAssets: true,
|
|
50
|
+
defaultLang: 'ru',
|
|
51
|
+
fallbackLang: 'en',
|
|
52
|
+
supportedLangs: ['de'],
|
|
53
|
+
},
|
|
54
|
+
preload: {
|
|
55
|
+
global: ['layout', 'common'],
|
|
56
|
+
},
|
|
57
|
+
rendering: {
|
|
58
|
+
placeholder: '...',
|
|
59
|
+
},
|
|
60
|
+
missingKeyHandler: (key, ctx) => `[${ctx.lang}] ${key}`,
|
|
49
61
|
},
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
placeholder: '...',
|
|
62
|
+
theme: {
|
|
63
|
+
defaultTheme: 'light',
|
|
64
|
+
dom: { darkClassName: 'dark' },
|
|
54
65
|
},
|
|
55
|
-
langMissingKeyHandler: (key, ctx) => `[${ctx.lang}] ${key}`,
|
|
56
|
-
theme: { defaultTheme: 'light', darkThemePrefix: 'dark' },
|
|
57
66
|
}),
|
|
58
67
|
],
|
|
59
68
|
});
|
|
@@ -62,7 +71,58 @@ bootstrapApplication(AppComponent, {
|
|
|
62
71
|
## What You Get
|
|
63
72
|
|
|
64
73
|
Main provider:
|
|
65
|
-
- `
|
|
74
|
+
- `providePresentia(config: PresentiaConfig)`
|
|
75
|
+
|
|
76
|
+
`providePresentia(...)` is the new grouped config entry point.
|
|
77
|
+
`provideReInit(...)` remains available as the legacy `1.x` style provider.
|
|
78
|
+
|
|
79
|
+
## Migration Snapshot
|
|
80
|
+
|
|
81
|
+
Typical `1.x -> v2` provider migration:
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
// 1.x
|
|
85
|
+
provideReInit({
|
|
86
|
+
locale: {
|
|
87
|
+
url: '/assets/i18n',
|
|
88
|
+
isFromAssets: true,
|
|
89
|
+
preloadNamespaces: ['layout', 'common'],
|
|
90
|
+
},
|
|
91
|
+
theme: {
|
|
92
|
+
defaultTheme: 'light',
|
|
93
|
+
darkThemePrefix: 're-dark',
|
|
94
|
+
},
|
|
95
|
+
breakpoints: {
|
|
96
|
+
mobile: '(max-width: 719px)',
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// v2
|
|
101
|
+
providePresentia({
|
|
102
|
+
lang: {
|
|
103
|
+
source: {
|
|
104
|
+
url: '/assets/i18n',
|
|
105
|
+
fromAssets: true,
|
|
106
|
+
},
|
|
107
|
+
preload: {
|
|
108
|
+
global: ['layout', 'common'],
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
theme: {
|
|
112
|
+
defaultTheme: 'light',
|
|
113
|
+
dom: {
|
|
114
|
+
darkClassName: 're-dark',
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
adaptive: {
|
|
118
|
+
breakpoints: {
|
|
119
|
+
mobile: '(max-width: 719px)',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
});
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
For a fuller mapping see [`V2-MIGRATION.md`](./V2-MIGRATION.md).
|
|
66
126
|
|
|
67
127
|
Localization exports:
|
|
68
128
|
- `LangService`
|
|
@@ -71,12 +131,14 @@ Localization exports:
|
|
|
71
131
|
- `LANG_CONFIG`
|
|
72
132
|
- `LANG_PIPE_CONFIG`
|
|
73
133
|
- `LANG_MISSING_KEY_HANDLER`
|
|
134
|
+
- `PRESENTIA_ROUTE_NAMESPACES_DATA_KEY`
|
|
74
135
|
- Types: `LocaleConfig`, `LangModel`, `LangDto`, `LangParams`, `LangKey`, `LangKeyRegistry`, etc.
|
|
75
136
|
|
|
76
137
|
Theme exports:
|
|
77
138
|
- `ThemeService`
|
|
139
|
+
- `Theme`
|
|
78
140
|
- `THEME_CONFIG`
|
|
79
|
-
- `themes
|
|
141
|
+
- `themes`
|
|
80
142
|
|
|
81
143
|
Adaptive exports:
|
|
82
144
|
- `AdaptiveService`
|
|
@@ -88,7 +150,63 @@ Routes and SEO exports:
|
|
|
88
150
|
- `SeoService`
|
|
89
151
|
- `SeoRouteListener`
|
|
90
152
|
|
|
91
|
-
|
|
153
|
+
Legacy compatibility surface:
|
|
154
|
+
- `provideReInit(config: AppConfig)`
|
|
155
|
+
- `darkThemePrefix`
|
|
156
|
+
- `defaultThemeConfig`
|
|
157
|
+
- `defaultThemePersistenceAdapter`
|
|
158
|
+
- `RouteNamespaceDiagnosticsService`
|
|
159
|
+
|
|
160
|
+
## Provider Configuration
|
|
161
|
+
|
|
162
|
+
### `providePresentia`
|
|
163
|
+
|
|
164
|
+
```ts
|
|
165
|
+
providePresentia({
|
|
166
|
+
lang: {
|
|
167
|
+
source: {
|
|
168
|
+
url: '/assets/i18n',
|
|
169
|
+
fromAssets: true,
|
|
170
|
+
defaultLang: 'ru',
|
|
171
|
+
fallbackLang: 'en',
|
|
172
|
+
},
|
|
173
|
+
rendering: {
|
|
174
|
+
placeholder: '...',
|
|
175
|
+
missingValue: '--',
|
|
176
|
+
},
|
|
177
|
+
preload: {
|
|
178
|
+
global: ['layout', 'common'],
|
|
179
|
+
routes: {
|
|
180
|
+
mode: 'blocking',
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
theme: {
|
|
185
|
+
registry: ['light', 'dark'],
|
|
186
|
+
defaultTheme: 'dark',
|
|
187
|
+
persistence: 'localStorage',
|
|
188
|
+
dom: {
|
|
189
|
+
strategy: 'root-class',
|
|
190
|
+
darkClassName: 're-dark',
|
|
191
|
+
},
|
|
192
|
+
},
|
|
193
|
+
adaptive: {
|
|
194
|
+
breakpoints: {
|
|
195
|
+
mobile: '(max-width: 719px)',
|
|
196
|
+
tablet: '(min-width: 720px) and (max-width: 1399px)',
|
|
197
|
+
'desktop-s': '(min-width: 1400px) and (max-width: 1919px)',
|
|
198
|
+
desktop: '(min-width: 1920px)',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Use `providePresentia(...)` for new integrations.
|
|
205
|
+
Use `provideReInit(...)` only when you need the legacy flat config shape.
|
|
206
|
+
|
|
207
|
+
### `provideReInit`
|
|
208
|
+
|
|
209
|
+
Legacy compatibility provider. Prefer `providePresentia(...)` for new integrations.
|
|
92
210
|
|
|
93
211
|
`AppConfig` includes:
|
|
94
212
|
- `locale: LocaleConfig` (required)
|
|
@@ -158,6 +276,14 @@ type LocaleConfig = {
|
|
|
158
276
|
maxNamespaces?: number;
|
|
159
277
|
ttlMs?: number;
|
|
160
278
|
};
|
|
279
|
+
routeNamespacePreload?: {
|
|
280
|
+
mode?: 'blocking' | 'lazy';
|
|
281
|
+
dataKey?: string;
|
|
282
|
+
manifest?: Record<string, readonly string[]>;
|
|
283
|
+
mergeStrategy?: 'append' | 'replace';
|
|
284
|
+
onError?: 'continue' | 'throw';
|
|
285
|
+
diagnostics?: boolean;
|
|
286
|
+
};
|
|
161
287
|
};
|
|
162
288
|
```
|
|
163
289
|
|
|
@@ -167,6 +293,8 @@ type LocaleConfig = {
|
|
|
167
293
|
- Alias `ky` is accepted and normalized to internal `kg`.
|
|
168
294
|
- `supportedLangs` lets you add custom language codes.
|
|
169
295
|
- If key is missing, `defaultValue` is used (or missing key handler result).
|
|
296
|
+
- `defaultValue` is a missing-key fallback, not a loading placeholder.
|
|
297
|
+
- `LangPipe.placeholder` is used while a namespace is still loading.
|
|
170
298
|
- Stale HTTP responses are ignored when language changes mid-flight.
|
|
171
299
|
|
|
172
300
|
### Basic Usage
|
|
@@ -216,6 +344,135 @@ const lang = inject(LangService);
|
|
|
216
344
|
await lang.loadNamespaces(['layout', 'common', 'breadcrumbs']);
|
|
217
345
|
```
|
|
218
346
|
|
|
347
|
+
### Route Namespace Preload
|
|
348
|
+
|
|
349
|
+
Use this when first-paint localization must be ready before page activation.
|
|
350
|
+
`presentia` patches router config automatically via `providePresentia(...)` or `provideReInit(...)`,
|
|
351
|
+
so no extra resolver wiring is required in the consumer app.
|
|
352
|
+
|
|
353
|
+
Behavior summary:
|
|
354
|
+
- `mode: 'blocking'` waits for route namespaces before route activation.
|
|
355
|
+
- `mode: 'lazy'` starts namespace loading without delaying navigation.
|
|
356
|
+
- `onError: 'continue'` keeps navigation alive if preload fails.
|
|
357
|
+
- `onError: 'throw'` cancels navigation by rethrowing the preload error.
|
|
358
|
+
|
|
359
|
+
Route data mode:
|
|
360
|
+
|
|
361
|
+
```ts
|
|
362
|
+
providePresentia({
|
|
363
|
+
lang: {
|
|
364
|
+
source: {
|
|
365
|
+
url: '/assets/i18n',
|
|
366
|
+
fromAssets: true,
|
|
367
|
+
},
|
|
368
|
+
preload: {
|
|
369
|
+
routes: {},
|
|
370
|
+
},
|
|
371
|
+
},
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
export const routes: Routes = [
|
|
375
|
+
{
|
|
376
|
+
path: 'profile',
|
|
377
|
+
component: ProfilePage,
|
|
378
|
+
data: {
|
|
379
|
+
presentiaNamespaces: ['profile', 'layout', 'common'],
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
];
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
Manifest mode:
|
|
386
|
+
|
|
387
|
+
```ts
|
|
388
|
+
const PRESENTIA_ROUTE_NAMESPACES = {
|
|
389
|
+
'/': ['home', 'layout', 'common'],
|
|
390
|
+
'/profile': ['profile', 'layout', 'common'],
|
|
391
|
+
'/users/:id': ['users', 'layout', 'common'],
|
|
392
|
+
} as const;
|
|
393
|
+
|
|
394
|
+
providePresentia({
|
|
395
|
+
lang: {
|
|
396
|
+
source: {
|
|
397
|
+
url: '/assets/i18n',
|
|
398
|
+
fromAssets: true,
|
|
399
|
+
},
|
|
400
|
+
preload: {
|
|
401
|
+
routes: {
|
|
402
|
+
manifest: PRESENTIA_ROUTE_NAMESPACES,
|
|
403
|
+
mode: 'blocking',
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
diagnostics: {
|
|
407
|
+
lateNamespaceLoads: true,
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
Legacy `1.x` projects can keep using `provideReInit(...)`,
|
|
414
|
+
but new integrations should prefer `providePresentia(...)`.
|
|
415
|
+
|
|
416
|
+
Generate the manifest in a consumer app:
|
|
417
|
+
|
|
418
|
+
```bash
|
|
419
|
+
npx presentia-gen-namespaces
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
Default paths:
|
|
423
|
+
- `--routes src/app/app.routes.ts`
|
|
424
|
+
- `--project src`
|
|
425
|
+
- `--tsconfig tsconfig.json`
|
|
426
|
+
- `--out src/app/presentia-route-namespaces.ts`
|
|
427
|
+
|
|
428
|
+
Explicit paths:
|
|
429
|
+
|
|
430
|
+
```bash
|
|
431
|
+
npx presentia-gen-namespaces \
|
|
432
|
+
--routes src/app/app.routes.ts \
|
|
433
|
+
--project src \
|
|
434
|
+
--tsconfig tsconfig.json \
|
|
435
|
+
--locales src/assets/locales \
|
|
436
|
+
--out src/app/presentia-route-namespaces.ts
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
The public CLI is `presentia-gen-namespaces`.
|
|
440
|
+
Repository scripts such as `libs/presentia/scripts/generate-sandbox-namespaces.mjs`
|
|
441
|
+
are internal helpers for local demo verification only.
|
|
442
|
+
|
|
443
|
+
Best-effort extraction sources:
|
|
444
|
+
- route `data` lang keys such as `title: 'profile.title'`
|
|
445
|
+
- route `data.presentiaNamespaces`
|
|
446
|
+
- `{{ 'namespace.key' | lang }}`
|
|
447
|
+
- `reLang` / `reLangKey`
|
|
448
|
+
- `reLangAttrs`
|
|
449
|
+
- `lang.get('namespace.key')`
|
|
450
|
+
- `lang.observe('namespace.key')`
|
|
451
|
+
- imported local standalone components from component `imports: [...]`
|
|
452
|
+
|
|
453
|
+
When `--locales` is provided, generator validates discovered keys and namespaces
|
|
454
|
+
against real locale JSON files to reduce false positives.
|
|
455
|
+
|
|
456
|
+
Optional report mode:
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
npx presentia-gen-namespaces \
|
|
460
|
+
--locales src/assets/locales \
|
|
461
|
+
--report src/app/presentia-route-namespaces.report.json \
|
|
462
|
+
--print-report
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
This emits a JSON report with static-analysis gaps such as unresolved lazy imports
|
|
466
|
+
or unsupported `presentiaNamespaces` expressions.
|
|
467
|
+
|
|
468
|
+
Options:
|
|
469
|
+
- `mode: 'blocking' | 'lazy'` - waits for namespaces before route activation, or starts loading without blocking.
|
|
470
|
+
- `dataKey` - custom route-data field name instead of default `presentiaNamespaces`.
|
|
471
|
+
- `manifest` - generated or handwritten `route -> namespaces[]` map.
|
|
472
|
+
- `mergeStrategy: 'append' | 'replace'` - merge manifest and route-data namespaces, or let route data override manifest entries.
|
|
473
|
+
- `onError: 'continue' | 'throw'` - in blocking mode either keep navigation alive on preload failure, or cancel it by rethrowing the load error.
|
|
474
|
+
- `diagnostics` - in dev mode warns when a namespace starts loading only after route activation.
|
|
475
|
+
|
|
219
476
|
### Namespace Cache Policy
|
|
220
477
|
|
|
221
478
|
```ts
|
|
@@ -387,8 +644,10 @@ After generation, invalid keys in `get()`/`observe()` are compile-time errors.
|
|
|
387
644
|
## Theme
|
|
388
645
|
|
|
389
646
|
`ThemeService`:
|
|
390
|
-
- `theme()` current theme (`
|
|
647
|
+
- `theme()` current theme (`Theme = BaseTheme | string`)
|
|
391
648
|
- `isLight()`
|
|
649
|
+
- `isDark()`
|
|
650
|
+
- `is(theme | theme[])`
|
|
392
651
|
- `switch(theme?)` explicit switch or toggle
|
|
393
652
|
|
|
394
653
|
```ts
|
|
@@ -397,17 +656,60 @@ theme.switch('dark');
|
|
|
397
656
|
```
|
|
398
657
|
|
|
399
658
|
`ThemeConfig`:
|
|
400
|
-
- `
|
|
659
|
+
- `registry?: readonly Theme[]`
|
|
660
|
+
- `defaultTheme?: Theme`
|
|
401
661
|
- `darkThemePrefix?: string`
|
|
662
|
+
- `dom?: { strategy?: 'root-class' | 'data-attribute'; rootSelector?: string; darkThemePrefix?: string; attributeName?: string; themeClassPrefix?: string; classNameBuilder?: (theme) => string }`
|
|
663
|
+
|
|
664
|
+
`providePresentia(...).theme` additionally supports:
|
|
665
|
+
- `persistence?: 'localStorage' | 'none'`
|
|
666
|
+
- `persistenceAdapter?: PersistenceAdapter`
|
|
667
|
+
- `dom.darkClassName` as a consumer-friendly alias for `darkThemePrefix`
|
|
668
|
+
- `dom.classPrefix` as a consumer-friendly alias for `themeClassPrefix`
|
|
669
|
+
|
|
670
|
+
Example:
|
|
671
|
+
|
|
672
|
+
```ts
|
|
673
|
+
providePresentia({
|
|
674
|
+
lang: {
|
|
675
|
+
source: {
|
|
676
|
+
url: '/assets/i18n',
|
|
677
|
+
fromAssets: true,
|
|
678
|
+
},
|
|
679
|
+
},
|
|
680
|
+
theme: {
|
|
681
|
+
registry: ['light', 'dark', 'sepia'],
|
|
682
|
+
defaultTheme: 'light',
|
|
683
|
+
persistence: 'localStorage',
|
|
684
|
+
dom: {
|
|
685
|
+
strategy: 'root-class',
|
|
686
|
+
darkClassName: 're-dark',
|
|
687
|
+
classPrefix: 're-theme-',
|
|
688
|
+
},
|
|
689
|
+
},
|
|
690
|
+
});
|
|
691
|
+
```
|
|
692
|
+
|
|
693
|
+
With that config:
|
|
694
|
+
- `dark` still applies the legacy dark class (`re-dark`)
|
|
695
|
+
- every registered theme also gets a prefixed class such as `re-theme-light`, `re-theme-dark`, `re-theme-sepia`
|
|
696
|
+
- for fully custom naming, use `dom.classNameBuilder`
|
|
402
697
|
|
|
403
698
|
## Adaptive
|
|
404
699
|
|
|
405
700
|
`AdaptiveService` provides reactive signals:
|
|
406
|
-
- `device()` -> `'desktop' | 'tablet' | 'mobile'`
|
|
701
|
+
- `device()` -> `'desktop' | 'desktop-s' | 'tablet' | 'mobile'`
|
|
702
|
+
- `breakpoint()` alias for the current device bucket
|
|
407
703
|
- `width()`
|
|
408
704
|
- `height()`
|
|
705
|
+
- `isMobile()`
|
|
706
|
+
- `isTablet()`
|
|
707
|
+
- `isDesktopSmall()`
|
|
409
708
|
- `isDesktop()`
|
|
410
709
|
- `isPortrait()`
|
|
710
|
+
- `is(device | device[])`
|
|
711
|
+
- `isAtLeast(device)`
|
|
712
|
+
- `isBetween(min, max)`
|
|
411
713
|
|
|
412
714
|
`*reIfDevice` structural directive:
|
|
413
715
|
|
|
@@ -415,20 +717,29 @@ theme.switch('dark');
|
|
|
415
717
|
<div *reIfDevice="'desktop'">Desktop only</div>
|
|
416
718
|
<div *reIfDevice="['mobile', 'tablet']">Mobile and tablet</div>
|
|
417
719
|
<div *reIfDevice="'mobile'; inverse: true">Hidden on mobile</div>
|
|
720
|
+
<div *reIfDeviceAtLeast="'tablet'">Tablet and larger</div>
|
|
721
|
+
<div *reIfDeviceBetween="['tablet', 'desktop']">Tablet to desktop</div>
|
|
418
722
|
```
|
|
419
723
|
|
|
420
724
|
Breakpoints are configurable via `DeviceBreakpoints`.
|
|
421
725
|
|
|
422
726
|
## Route State Helper
|
|
423
727
|
|
|
424
|
-
`RouteWatcher` gives reactive
|
|
728
|
+
`RouteWatcher` gives reactive route state:
|
|
425
729
|
- `params()`
|
|
730
|
+
- `deepestParams()`
|
|
426
731
|
- `query()`
|
|
427
732
|
- `data()`
|
|
733
|
+
- `mergedData()`
|
|
428
734
|
- `url()`
|
|
735
|
+
- `routePattern()`
|
|
429
736
|
- `fragment()`
|
|
430
737
|
- `state()`
|
|
431
738
|
- `selectData<T>(key)`
|
|
739
|
+
- `selectData<T>(key, 'merged')`
|
|
740
|
+
- `selectParam(key)`
|
|
741
|
+
- `selectParam(key, 'deepest')`
|
|
742
|
+
- `matchesPath(path | regexp)`
|
|
432
743
|
|
|
433
744
|
Use when you want route-derived UI state without manual router subscriptions.
|
|
434
745
|
|