@smartsoft001-mobilems/claude-plugins 2.86.0 → 2.87.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.
Files changed (46) hide show
  1. package/package.json +1 -1
  2. package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
  3. package/plugins/mobilems/skills/mobilems-articles/SKILL.md +84 -0
  4. package/plugins/mobilems/skills/mobilems-articles/references/lib-contents.md +164 -0
  5. package/plugins/mobilems/skills/mobilems-articles/references/module-and-routes.md +146 -0
  6. package/plugins/mobilems/skills/mobilems-articles/references/shared-components.md +56 -0
  7. package/plugins/mobilems/skills/mobilems-articles/scripts/detect-init.sh +29 -0
  8. package/plugins/mobilems/skills/mobilems-articles/scripts/verify-module.sh +61 -0
  9. package/plugins/mobilems/skills/mobilems-events/SKILL.md +65 -0
  10. package/plugins/mobilems/skills/mobilems-events/references/lib-contents.md +212 -0
  11. package/plugins/mobilems/skills/mobilems-events/references/module-and-routes.md +113 -0
  12. package/plugins/mobilems/skills/mobilems-events/scripts/detect-init.sh +29 -0
  13. package/plugins/mobilems/skills/mobilems-events/scripts/verify-module.sh +59 -0
  14. package/plugins/mobilems/skills/mobilems-object-collections/SKILL.md +52 -0
  15. package/plugins/mobilems/skills/mobilems-object-collections/references/lib-contents.md +76 -0
  16. package/plugins/mobilems/skills/mobilems-object-collections/references/module-and-routes.md +116 -0
  17. package/plugins/mobilems/skills/mobilems-object-collections/references/shared-components.md +61 -0
  18. package/plugins/mobilems/skills/mobilems-object-collections/scripts/detect-init.sh +29 -0
  19. package/plugins/mobilems/skills/mobilems-object-collections/scripts/verify-module.sh +44 -0
  20. package/plugins/mobilems/skills/mobilems-objects/SKILL.md +52 -0
  21. package/plugins/mobilems/skills/mobilems-objects/references/lib-contents.md +68 -0
  22. package/plugins/mobilems/skills/mobilems-objects/references/module-and-routes.md +108 -0
  23. package/plugins/mobilems/skills/mobilems-objects/references/shared-components.md +69 -0
  24. package/plugins/mobilems/skills/mobilems-objects/scripts/detect-init.sh +29 -0
  25. package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +51 -0
  26. package/plugins/mobilems/skills/mobilems-partners/SKILL.md +65 -0
  27. package/plugins/mobilems/skills/mobilems-partners/references/lib-contents.md +160 -0
  28. package/plugins/mobilems/skills/mobilems-partners/references/module-and-routes.md +125 -0
  29. package/plugins/mobilems/skills/mobilems-partners/scripts/detect-init.sh +29 -0
  30. package/plugins/mobilems/skills/mobilems-partners/scripts/verify-module.sh +59 -0
  31. package/plugins/mobilems/skills/mobilems-project-init/SKILL.md +134 -0
  32. package/plugins/mobilems/skills/mobilems-project-init/references/app-shell.md +107 -0
  33. package/plugins/mobilems/skills/mobilems-project-init/references/claude-config.md +44 -0
  34. package/plugins/mobilems/skills/mobilems-project-init/references/config-providers.md +203 -0
  35. package/plugins/mobilems/skills/mobilems-project-init/references/npm-deps.md +66 -0
  36. package/plugins/mobilems/skills/mobilems-project-init/references/server-env.md +89 -0
  37. package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +125 -0
  38. package/plugins/mobilems/skills/mobilems-project-init/references/tailwind-styles.md +106 -0
  39. package/plugins/mobilems/skills/mobilems-project-init/scripts/classify-shared-components.py +114 -0
  40. package/plugins/mobilems/skills/mobilems-project-init/scripts/detect-base.sh +49 -0
  41. package/plugins/mobilems/skills/mobilems-project-init/scripts/verify-init.sh +73 -0
  42. package/plugins/mobilems/skills/mobilems-thematic-publications/SKILL.md +60 -0
  43. package/plugins/mobilems/skills/mobilems-thematic-publications/references/lib-contents.md +134 -0
  44. package/plugins/mobilems/skills/mobilems-thematic-publications/references/module-and-routes.md +107 -0
  45. package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/detect-init.sh +29 -0
  46. package/plugins/mobilems/skills/mobilems-thematic-publications/scripts/verify-module.sh +56 -0
@@ -0,0 +1,203 @@
1
+ # app.config.ts — the MobilEMS provider set
2
+
3
+ Overwrite `apps/web/src/app/app.config.ts` with the providers below (the base shipped a minimal
4
+ version). Thread `<PREFIX>` (reference uses `@msr`). `app.config.server.ts` and
5
+ `app.routes.server.ts` stay as the base emitted them.
6
+
7
+ The imports from `@<PREFIX>/angular` (`LanguageInterceptor`, `PreviewAuthInterceptor`,
8
+ `SsrTimeoutInterceptor`, `LocalStorageService`, `EnvironmentService`) come from the shared lib
9
+ populated in step 7 — so in **foundation-only** mode, omit the three interceptor providers and the
10
+ `@<PREFIX>/angular` interceptor imports (they don't exist yet).
11
+
12
+ ```ts
13
+ import {
14
+ HTTP_INTERCEPTORS,
15
+ HttpClient,
16
+ provideHttpClient,
17
+ withFetch,
18
+ withInterceptorsFromDi,
19
+ } from '@angular/common/http';
20
+ import {
21
+ ApplicationConfig,
22
+ Injectable,
23
+ provideBrowserGlobalErrorListeners,
24
+ provideZonelessChangeDetection,
25
+ importProvidersFrom,
26
+ inject,
27
+ provideEnvironmentInitializer,
28
+ isDevMode,
29
+ provideAppInitializer,
30
+ } from '@angular/core';
31
+ import {
32
+ provideClientHydration,
33
+ withEventReplay,
34
+ withI18nSupport,
35
+ } from '@angular/platform-browser';
36
+ import { provideRouter, withInMemoryScrolling } from '@angular/router';
37
+ import { EffectsModule, provideEffects } from '@ngrx/effects';
38
+ import { provideStore, StoreModule } from '@ngrx/store';
39
+ import { provideStoreDevtools } from '@ngrx/store-devtools';
40
+ import {
41
+ InterpolatableTranslationObject,
42
+ TranslateModule,
43
+ TranslateService,
44
+ } from '@ngx-translate/core';
45
+ import {
46
+ AppService,
47
+ ErrorService,
48
+ NgrxSharedModule,
49
+ StyleService,
50
+ } from '@smartsoft001/angular';
51
+ import {
52
+ ISearchData,
53
+ SEARCH_CONFIG_TOKEN,
54
+ SharedModule,
55
+ } from '@smartsoft001-mobilems/angular';
56
+ import { MasonryModule } from '@thisissoon/angular-masonry';
57
+ import { catchError, firstValueFrom, map, of, timeout } from 'rxjs';
58
+
59
+ import {
60
+ environment,
61
+ EnvironmentService,
62
+ LanguageInterceptor,
63
+ LocalStorageService,
64
+ PreviewAuthInterceptor,
65
+ SsrTimeoutInterceptor,
66
+ } from '@<PREFIX>/angular';
67
+
68
+ import { appRoutes } from './app.routes';
69
+
70
+ @Injectable()
71
+ class NoOpAppService extends AppService {
72
+ override initTitle(): void {
73
+ // No-op: disable AppService.initTitle() which overrides
74
+ // page titles set by components via SeoService.updateTitle()
75
+ }
76
+ }
77
+
78
+ export const appConfig: ApplicationConfig = {
79
+ providers: [
80
+ provideClientHydration(withEventReplay(), withI18nSupport()),
81
+ provideBrowserGlobalErrorListeners(),
82
+ provideZonelessChangeDetection(),
83
+ provideRouter(
84
+ appRoutes,
85
+ withInMemoryScrolling({ scrollPositionRestoration: 'top' }),
86
+ ),
87
+ provideHttpClient(withFetch(), withInterceptorsFromDi()),
88
+ { provide: HTTP_INTERCEPTORS, useClass: LanguageInterceptor, multi: true },
89
+ {
90
+ provide: HTTP_INTERCEPTORS,
91
+ useClass: PreviewAuthInterceptor,
92
+ multi: true,
93
+ },
94
+ {
95
+ provide: HTTP_INTERCEPTORS,
96
+ useClass: SsrTimeoutInterceptor,
97
+ multi: true,
98
+ },
99
+ importProvidersFrom(
100
+ StoreModule.forRoot(
101
+ {},
102
+ {
103
+ runtimeChecks: {
104
+ strictStateImmutability: false,
105
+ strictActionImmutability: false,
106
+ },
107
+ },
108
+ ),
109
+ MasonryModule.forRoot(),
110
+ TranslateModule.forRoot(),
111
+ EffectsModule.forRoot([]),
112
+ NgrxSharedModule,
113
+ SharedModule.forRoot({
114
+ apiUrl: environment.apiUrl,
115
+ multimediaUrl: environment.multimediaUrl,
116
+ mainLogoPath: 'logo.png',
117
+ styleVariables: {},
118
+ }),
119
+ ),
120
+ { provide: StyleService, useClass: StyleService },
121
+ { provide: AppService, useClass: NoOpAppService },
122
+ {
123
+ provide: ErrorService,
124
+ useValue: {
125
+ log: (err: any) => {
126
+ console.error('Global error handler:', err);
127
+ },
128
+ },
129
+ },
130
+ provideEnvironmentInitializer(() => {
131
+ const envService = inject(EnvironmentService);
132
+ envService.init();
133
+ }),
134
+ provideStore(),
135
+ provideEffects(),
136
+ provideStoreDevtools({
137
+ maxAge: 25,
138
+ logOnly: !isDevMode(),
139
+ autoPause: true,
140
+ trace: true,
141
+ traceLimit: 75,
142
+ }),
143
+ {
144
+ provide: SEARCH_CONFIG_TOKEN,
145
+ useValue: {
146
+ apiUrl: environment.apiUrl,
147
+ searchMap: (txt: string, data: ISearchData) => {
148
+ return {
149
+ news: `${environment.apiUrl}search/Article/page/1?maxPerPage=1&filter[phrase]=${encodeURIComponent(txt)}&filter[template]=AKTUALNO%C5%9ACI`,
150
+ articles: `${environment.apiUrl}search/Article/page/1?maxPerPage=1&filter[phrase]=${encodeURIComponent(txt)}&filter[template]=POZOSTAŁE`,
151
+ partners: `${environment.apiUrl}dictionary/Department/search/page/1?maxPerPage=999&sort=random&_type=query&q=${encodeURIComponent(txt)}`,
152
+ };
153
+ },
154
+ },
155
+ },
156
+ provideAppInitializer(async () => {
157
+ const httpClient = inject(HttpClient);
158
+ const translateService = inject(TranslateService);
159
+ const localStorageService = inject(LocalStorageService);
160
+
161
+ // Initialize language before fetching translations so the
162
+ // language interceptor sends the correct Accept-Language header.
163
+ localStorageService.initLang();
164
+
165
+ async function fetchTranslations() {
166
+ const url = environment.production
167
+ ? environment.apiUrl + 'setting/theme'
168
+ : '/api/translations';
169
+
170
+ const translations = (await firstValueFrom(
171
+ httpClient.get<{ data: any }>(url).pipe(
172
+ timeout(5000),
173
+ map((response) => {
174
+ return response?.data?.translations ?? { en: {}, pl: {} };
175
+ }),
176
+ catchError(() => of(null)),
177
+ ),
178
+ )) as {
179
+ en: InterpolatableTranslationObject;
180
+ pl: InterpolatableTranslationObject;
181
+ } | null;
182
+
183
+ if (!translations || !translations.en || !translations.pl) {
184
+ console.warn('Translations are missing or malformed:', translations);
185
+ return;
186
+ }
187
+
188
+ translateService.setTranslation('en', translations.en);
189
+ translateService.setTranslation('pl', translations.pl);
190
+ }
191
+
192
+ await fetchTranslations();
193
+ }),
194
+ ],
195
+ };
196
+ ```
197
+
198
+ ## Notes
199
+
200
+ - `searchMap` is plain URL-string config — keep it verbatim even though it references `partners`
201
+ (out of scope) and `news`/`articles` (added later). It's runtime config, not an import dependency.
202
+ - The `news`/`articles` template values (`AKTUALNO%C5%9ACI` = AKTUALNOŚCI, `POZOSTAŁE`) match the
203
+ `baseQuery` the `mobilems-articles` skill threads into `ArticlesSharedModule.forFeature`.
@@ -0,0 +1,66 @@
1
+ # npm dependencies — the MobilEMS foundation set
2
+
3
+ Add these to the workspace root `package.json` `dependencies`. **Resolve every version at run
4
+ time — never hardcode.** Anchor on the installed `@smartsoft001/angular` line (the same anchor
5
+ `scaffold-nx-workspace` used): read its installed `package.json` version, or
6
+ `npm view @smartsoft001/<pkg> version`. The `@smartsoft001*` libs move together; match their lines.
7
+
8
+ ## Add in project-init (foundation — used by the shared lib + app.config)
9
+
10
+ **Framework (smartsoft001 + MobilEMS shared):**
11
+
12
+ - `@smartsoft001/angular`
13
+ - `@smartsoft001/crud-shell-angular`
14
+ - `@smartsoft001/domain-core`
15
+ - `@smartsoft001/models`
16
+ - `@smartsoft001/utils`
17
+ - `@smartsoft001-mobilems/angular` (provides `SharedModule`, `SeoService`, `ConfigsFacade`, `SEARCH_CONFIG_TOKEN`)
18
+ - `@smartsoft001-mobilems/models` (provides `Config`, `Page`, `PageSlide`, `MsFile`)
19
+
20
+ **State + i18n:**
21
+
22
+ - `@ngrx/store`, `@ngrx/effects`, `@ngrx/store-devtools`, `@ngrx/signals`
23
+ - `@ngx-translate/core`
24
+
25
+ **Shared-UI / runtime libraries** (used by **generic** shared-lib components, so they are
26
+ foundation deps — verified against the reference):
27
+
28
+ - `@thisissoon/angular-masonry` + `masonry-layout` (masonry grids; `MasonryModule.forRoot()` in app.config)
29
+ - `swiper` (generic `slider` component)
30
+ - `leaflet` + `@types/leaflet` (generic `map` component + `styles.scss` `leaflet.css` import)
31
+ - `openseadragon` + `@types/openseadragon` (generic `gallery/gallery-fullscreen` deep-zoom)
32
+ - `@angular/cdk` (`BreakpointObserver` in the home carousels; the `orientation` service)
33
+ - `moment` + `moment-timezone` (date handling across shared components)
34
+
35
+ The runtime libraries go in `dependencies`; their `@types/*` packages (`@types/leaflet`,
36
+ `@types/openseadragon`) go in `devDependencies`. Pin `@angular/cdk` to the workspace's Angular line
37
+ (`@angular/core` version). `@angular/forms` is used by the shared filter components — it normally
38
+ comes with the Angular base; ensure it's present (same Angular line) and add it if the base lacks it.
39
+
40
+ > `express` + `dotenv` are already present from the base `server.ts` — verify, don't re-add.
41
+
42
+ ## Feature packages — added by the module skills, not here
43
+
44
+ Each module skill adds its own feature packages when it builds that module:
45
+
46
+ | Package(s) | Added by |
47
+ | -------------------------------------------------------------------------------------- | ----------------------------- |
48
+ | `@smartsoft001-mobilems/objects-{domain,shell-angular,shell-dtos}`, `online-3d-viewer` | `mobilems-objects` |
49
+ | `@smartsoft001-mobilems/public-collections-{domain,shell-angular,shell-dtos}` | `mobilems-object-collections` |
50
+ | `@smartsoft001-mobilems/articles-{domain,shell-angular,shell-dtos}` | `mobilems-articles` |
51
+
52
+ Keep these out of the foundation: a feature package belongs with the feature shared components that
53
+ use it, so the foundation library builds on its own.
54
+
55
+ ## Resolution recipe
56
+
57
+ ```bash
58
+ # anchor line (e.g. 2.122.0 → use ^2.122.0 for @smartsoft001/*, the matching mobilems line for @smartsoft001-mobilems/*)
59
+ npm view @smartsoft001/angular version
60
+ npm view @smartsoft001-mobilems/angular version
61
+ # third-party: take the current published line unless the base already pinned a compatible one
62
+ npm view <pkg> version
63
+ ```
64
+
65
+ Write `^<resolved>` ranges (matching the reference's style). Do not run `npm install` unless the
66
+ user approves (skip during eval).
@@ -0,0 +1,89 @@
1
+ # Runtime env delta — multimediaUrl + /api/translations
2
+
3
+ The base shipped `environment.{ts,prod.ts}`, `EnvironmentService`, a `server.ts` with `/env.js`, and
4
+ the SSRF allowed-hosts list — all keyed on `API_URL` / `SITE_URL` / `APP_VERSION` only. MobilEMS adds
5
+ a **`multimediaUrl`** key (media/CDN host) and an **`/api/translations`** dev endpoint. These are the
6
+ only env changes; do not otherwise rewrite the base SSR infra.
7
+
8
+ ## 1. `libs/shared/angular/src/lib/environments/environment.ts` + `environment.prod.ts`
9
+
10
+ Add the `multimediaUrl` field to **both** files, mirroring the existing keys:
11
+
12
+ ```ts
13
+ multimediaUrl:
14
+ typeof window !== 'undefined' && (window as any)['env']
15
+ ? (window as any)['env']['multimediaUrl']
16
+ : process.env['MULTIMEDIA_URL'],
17
+ ```
18
+
19
+ (Append after `siteUrl`. In reference mode the copied shared lib already carries this — verify it's
20
+ present rather than duplicating.)
21
+
22
+ ## 2. `EnvironmentService` (shared lib)
23
+
24
+ In `EnvironmentService.initFromProcessEnv()` and `init()`, propagate `multimediaUrl` alongside
25
+ `apiUrl`/`siteUrl`/`version` (set from `process.env['MULTIMEDIA_URL']` on the server; copy from the
26
+ transfer-state / `window.env` on the client). In reference mode this comes with the copied service —
27
+ just confirm it.
28
+
29
+ ## 3. `apps/web/src/server.ts` — two additions
30
+
31
+ **a) Allowed-hosts:** after the `SITE_URL` block and before the `API_URL` block, add a
32
+ `MULTIMEDIA_URL` host so SSR can fetch media during render:
33
+
34
+ ```ts
35
+ const multimediaUrl = process.env['MULTIMEDIA_URL'];
36
+ if (multimediaUrl) {
37
+ try {
38
+ allowedHosts.push(
39
+ new URL(
40
+ multimediaUrl.startsWith('http')
41
+ ? multimediaUrl
42
+ : `https://${multimediaUrl}`,
43
+ ).hostname,
44
+ );
45
+ } catch {
46
+ // ignore invalid URL
47
+ }
48
+ }
49
+ ```
50
+
51
+ **b) `/env.js` template:** add the `multimediaUrl` line + its replacement:
52
+
53
+ ```ts
54
+ window['env']['multimediaUrl'] = '${MULTIMEDIA_URL}';
55
+ ```
56
+
57
+ ```ts
58
+ .replace(/\$\{MULTIMEDIA_URL\}/g, process.env['MULTIMEDIA_URL'] || '')
59
+ ```
60
+
61
+ **c) `/api/translations` endpoint** (needs `import * as fs from 'fs'` and `import * as path from 'path'`
62
+ at the top): serve the translations JSON from dist, falling back to the public source. Register it
63
+ **before** the static-file middleware and the catch-all `app.use('/**', …)`:
64
+
65
+ ```ts
66
+ app.get('/api/translations', (req, res) => {
67
+ let translationPath = path.join(
68
+ process.cwd(),
69
+ 'dist/apps/web/browser/translations.json',
70
+ );
71
+ if (!fs.existsSync(translationPath)) {
72
+ translationPath = path.join(
73
+ process.cwd(),
74
+ 'apps/web/public/translations.json',
75
+ );
76
+ }
77
+ if (fs.existsSync(translationPath)) {
78
+ const translations = JSON.parse(fs.readFileSync(translationPath, 'utf8'));
79
+ res.json(translations);
80
+ } else {
81
+ console.error('Translation file not found at:', translationPath);
82
+ res.status(404).json({ error: 'Translation file not found' });
83
+ }
84
+ });
85
+ ```
86
+
87
+ The `app.config.ts` `provideAppInitializer` hits `/api/translations` in dev and `apiUrl + 'setting/theme'`
88
+ in prod (see `references/config-providers.md`). Optionally seed an empty
89
+ `apps/web/public/translations.json` (`{"en":{},"pl":{}}`) so dev SSR doesn't 404.
@@ -0,0 +1,125 @@
1
+ # Build the foundation shared library
2
+
3
+ The shared library `libs/shared/angular` (alias `@<PREFIX>/angular`) holds the museum's shared
4
+ Angular building blocks: the `App` shell, header, footer, the generic page renderer, filters,
5
+ gallery, map, slider, carousels, content widgets, WCAG + SEO services, interceptors, pipes,
6
+ directives, tools, models, and `configs.module.ts`. Build it by copying from the reference repo's
7
+ `libs/shared/angular/src/lib` and threading `<PREFIX>` (the reference uses `@msr`).
8
+
9
+ The library starts as the base shell that exports only `environment` + `EnvironmentService` with
10
+ empty `lib/` placeholder dirs — fill those dirs with the **foundation shared components**.
11
+
12
+ ## Foundation shared components vs feature shared components
13
+
14
+ The shared library contains two kinds of shared component, separated by a simple structural rule:
15
+
16
+ - A **foundation shared component** depends only on Angular, the smartsoft framework packages, and
17
+ other foundation shared components. **Build these here.**
18
+ - A **feature shared component** imports a feature domain package
19
+ (`@smartsoft001-mobilems/objects-domain`, `…/articles-domain`, `…/public-collections-domain`) to
20
+ render that entity, e.g. the per-entity grid/list cards and the object/article gallery widgets.
21
+ Each of these belongs to its feature's module skill (`mobilems-objects`,
22
+ `mobilems-object-collections`, `mobilems-articles`), which builds it alongside the feature's npm
23
+ package. The foundation does not build them, because the foundation does not install those
24
+ packages.
25
+
26
+ This rule keeps the layering clean and lets the foundation library build on its own. The home-page
27
+ carousels (`new-objects-carousel`, `news-carousel`, `partners-carousel`) and the home page are
28
+ **foundation** shared components — they take `IObject` / `IArticle` / `IPartner` from the shared
29
+ `models/data.interface.ts` and never import a feature package, so build them here.
30
+
31
+ ## Identify the foundation set
32
+
33
+ Run `python3 scripts/classify-shared-components.py <reference-repo>/libs/shared/angular/src/lib/components`.
34
+ It prints two lists — **FOUNDATION** (build here) and **FEATURE-OWNED** (built by a module skill) —
35
+ by following imports: any component that imports a feature package, or a component that does, is
36
+ FEATURE-OWNED. A typical FEATURE-OWNED set (confirm with the classifier on your reference app) is:
37
+
38
+ | Feature shared component | Built by |
39
+ | ------------------------------------------ | ------------------------------ |
40
+ | `objects-grid-item`, `objects-list-item` | `mobilems-objects` |
41
+ | `widget/gallery-object` | `mobilems-objects` |
42
+ | `collections-grid-item` | `mobilems-object-collections` |
43
+ | `articles-grid-item`, `articles-list-item` | `mobilems-articles` (articles) |
44
+ | `news-grid-item`, `news-list-item` | `mobilems-articles` (news) |
45
+ | `widget/gallery-article` | `mobilems-articles` |
46
+
47
+ Build everything else — `app`, `header`, `footer`, `page`, the `filter*` family, `breadcrumbs`,
48
+ `pagination`, `searchbar`, `loader`, `error-retry`, `scroll-top`, `skip-links`, `wcag`, `gallery`,
49
+ `map`, `slider`, `carousel`, the three carousels above, `category-dropdown`, `dropdown`, `menu-item`,
50
+ `next-page`, `calendar`, `confirmation-modal`, `newsletter-*`, `image-info-panel`, `icons`, `tabs`,
51
+ and the `widget` container with its foundation widgets.
52
+
53
+ > Trust the script over this table for the **leaf** components (the grid/list cards and the
54
+ > `widget/gallery-*` sub-widgets) — if the reference diverges, the script is authoritative there.
55
+ > **One expected exception:** the script also lists the `widget` **container** (`components/widget`)
56
+ > as FEATURE-OWNED, because the reference container imports the feature gallery sub-widgets. That is
57
+ > not a signal to skip it — the container is foundation infrastructure. Build it (see "The widget
58
+ > container" below) registering only the foundation widgets.
59
+
60
+ ## The widget container
61
+
62
+ `components/widget/widget.component.ts` renders content blocks by mapping an `ArticleWidgetType` to a
63
+ component. The classifier flags this container FEATURE-OWNED (the reference one imports the feature
64
+ gallery sub-widgets), but build it here — it is foundation infrastructure the page renderer needs.
65
+ Build it registering only the **foundation widgets** — `text`, `image`, `video`, `audio`,
66
+ `gallery-image`, `media` — and their type-map entries; do not import or register the
67
+ `gallery-object` / `gallery-article` sub-widgets (those don't exist in the foundation). The
68
+ `mobilems-objects` / `mobilems-articles` skills add their `GALLERY_OBJECT` / `GALLERY_ARTICLE` imports
69
+ and type-map entries when they build those widgets. Build the widget barrel
70
+ (`components/widget/index.ts`) exporting the foundation widgets only.
71
+
72
+ ## models/objects.model.ts
73
+
74
+ This file holds foundation model types the shared components use (`SortType`, `KeyValue` → `dropdown`,
75
+ `AdvancedFilterType` → `filter-dictionary-input`, `FileType`, `MULTIMEDIA_DICTIONARY`,
76
+ `IModel3DObject`, `IAnimationObject`, …). Build it with those types. The one object-specific type,
77
+ `ISpecificObject extends MsObject`, is a feature type — the `mobilems-objects` skill adds it (with
78
+ the `MsObject` import) when it builds the objects module. Keep `models/index.ts` exporting
79
+ `./objects.model`.
80
+
81
+ ## The rest of the lib
82
+
83
+ Build `services/`, `pipes/`, `directives/`, `tools/`, `models/` (incl. `data.interface.ts` with
84
+ `IObject`/`IArticle`/`IPartner`), `interceptors/`, `environments/` (with the `multimediaUrl` field —
85
+ see `server-env.md`), and `configs.module.ts`. These are foundation code. Confirm with the
86
+ build-safety grep below; if any of these files imports a feature package on your reference, it is
87
+ feature code and belongs to a module skill — leave it out.
88
+
89
+ ## Barrels
90
+
91
+ - `src/index.ts` — the full barrel: re-export `./lib/components`, `./lib/services`,
92
+ `./lib/configs.module`, `./lib/models`, `./lib/pipes`, `./lib/tools`, `./lib/interceptors`,
93
+ `./lib/directives`, `./lib/environments`.
94
+ - `components/index.ts` — export the foundation shared components (and keep the `COMPONENTS` array,
95
+ which holds `AppComponent`/`FooterComponent`/`HeaderComponent`). Module skills add their feature
96
+ shared components' exports when they build them.
97
+
98
+ ## Build-safety invariant
99
+
100
+ The foundation library must build with no feature package installed. The check is positive and
101
+ authoritative — this grep over the workspace's shared lib returns nothing:
102
+
103
+ ```bash
104
+ grep -rlnE "from '@[^']*/(objects|articles|public-collections)-(domain|shell-angular|shell-dtos)'" \
105
+ libs/shared/angular/src
106
+ ```
107
+
108
+ `verify-init.sh` runs it, plus checks the `widget` container imports no gallery-object/article and
109
+ `objects.model.ts` carries no `MsObject` import.
110
+
111
+ ## project.json / build config
112
+
113
+ Keep the base's normalized `libs/shared/angular/project.json`, `ng-package.json`, `jest.config.ts`,
114
+ and tsconfigs (a full base already fixed the generator's `packages/shared/angular` leftover). Verify
115
+ `tsconfig.lib.json` keeps `"types": ["node"]` (the env code uses `process.env`). If the base's shared
116
+ lib is minimal and ships only `src/` (no `project.json`/`ng-package.json`/`jest.config.ts`/tsconfigs),
117
+ copy those from the reference and normalize their `dest`/`coverageDirectory`/paths to
118
+ `libs/shared/angular/...`.
119
+
120
+ ## Thread the prefix everywhere — including the lib's own package.json
121
+
122
+ After copying, the only reference-prefix string left should be gone. Beyond the import statements,
123
+ remember to thread `<PREFIX>` in `libs/shared/angular/package.json` `"name"` (it reads
124
+ `@<reference-prefix>/angular` in the reference → set it to `@<PREFIX>/angular`). A workspace-wide grep
125
+ for the reference prefix (`@msr/`) should return nothing.
@@ -0,0 +1,106 @@
1
+ # Tailwind (smart- prefix) layered onto scss
2
+
3
+ The base is scss-only. MobilEMS adds Tailwind with the **`smart-`** class prefix (so utilities read
4
+ `smart-flex`, `smart-container`, …) and a museum global stylesheet. Tailwind processes the scss via
5
+ postcss, so scss and Tailwind coexist — no migration.
6
+
7
+ ## 1. `tailwind.config.js` (workspace root — create verbatim, no placeholders)
8
+
9
+ ```js
10
+ /** @type {import('tailwindcss').Config} */
11
+ module.exports = {
12
+ content: ['./apps/web/src/**/*.{html,ts}', './libs/**/*.{html,ts}'],
13
+ prefix: 'smart-',
14
+ darkMode: ['class', '[class~="contrast-high"]'],
15
+ theme: {
16
+ extend: {
17
+ colors: {
18
+ primary: {
19
+ DEFAULT: '#414141',
20
+ active: 'rgb(223 223 223)',
21
+ red: 'rgb(193 18 31)',
22
+ },
23
+ dark: {
24
+ DEFAULT: '#100f0d',
25
+ yellow: '#ffff00',
26
+ },
27
+ },
28
+ gridTemplateColumns: {
29
+ 'auto-fit': 'repeat(auto-fit, minmax(min(350px, 100%), 1fr))',
30
+ },
31
+ boxShadow: {
32
+ custom:
33
+ '0px 5px 5px rgba(0, 0, 0, 0.2), 0 10px 10px rgba(0, 0, 0, 0.1), 0 20px 20px rgba(0, 0, 0, 0.15)',
34
+ },
35
+ fontFamily: {
36
+ satoshi: [
37
+ 'Satoshi',
38
+ 'Satoshi Fallback',
39
+ 'Satoshi Fallback Android',
40
+ 'sans-serif',
41
+ ],
42
+ oswald: [
43
+ 'Oswald',
44
+ 'Oswald Fallback',
45
+ 'Oswald Fallback Android',
46
+ 'sans-serif',
47
+ ],
48
+ 'fira-sans': [
49
+ 'Fira Sans',
50
+ 'Fira Sans Fallback',
51
+ 'Fira Sans Fallback Android',
52
+ 'sans-serif',
53
+ ],
54
+ },
55
+ fontWeight: { extralight: '200' },
56
+ },
57
+ },
58
+ plugins: [],
59
+ };
60
+ ```
61
+
62
+ > The `content` globs already cover `libs/**`, so feature libs added later are picked up — no edit
63
+ > needed when modules arrive. `darkMode` keys off `contrast-high` for the WCAG high-contrast toggle.
64
+
65
+ ## 2. postcss
66
+
67
+ Add Tailwind + autoprefixer to postcss. If the base has a `postcss.config.js`/`.json`, add the
68
+ plugins; otherwise create `postcss.config.js`:
69
+
70
+ ```js
71
+ module.exports = {
72
+ plugins: {
73
+ tailwindcss: {},
74
+ autoprefixer: {},
75
+ },
76
+ };
77
+ ```
78
+
79
+ Add `tailwindcss` + `autoprefixer` to `devDependencies` (resolve current lines via `npm view`). The
80
+ base `.eslintignore` already ignores `**/tailwind.config.js`.
81
+
82
+ ## 3. `apps/web/src/styles.scss` (overwrite)
83
+
84
+ **Reference mode:** copy `apps/web/src/styles.scss` from the reference repo verbatim. It is ~500
85
+ lines of pure CSS/scss — font-face metric-override fallbacks (CLS mitigation), the Tailwind
86
+ directives (`@tailwind base/components/utilities`), the swiper + leaflet css imports, `.smart-container*`
87
+ layout utilities, WCAG body classes (`text-small`/`text-large`/letter+word spacing/grayscale),
88
+ masonry column widths, fullscreen-`dialog` rules, and the Windows `forced-colors` block. It targets
89
+ some feature selectors (e.g. `app-thematic-paths-menu`) but that is harmless — CSS for absent
90
+ elements is a no-op, so copy it whole.
91
+
92
+ **Foundation-only mode:** write a minimal `styles.scss` with just the Tailwind directives + the
93
+ `@import` lines for swiper/leaflet and the `.smart-container` utility; note the rest in the TODO doc.
94
+
95
+ The structure is:
96
+
97
+ ```scss
98
+ @import 'swiper/swiper-bundle.css';
99
+ @import 'leaflet/dist/leaflet.css';
100
+
101
+ @tailwind base;
102
+ @tailwind components;
103
+ @tailwind utilities;
104
+
105
+ /* font-face fallbacks, @layer utilities { .smart-container … }, WCAG, masonry, dialogs, forced-colors */
106
+ ```