@smartsoft001-mobilems/claude-plugins 2.119.0 → 2.121.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
@@ -20,6 +20,7 @@ Run them in this order for a new museum app — `/smart:scaffold-nx-workspace`
20
20
  | `mobilems-project-init` | The MobilEMS foundation: deps, Tailwind, runtime env, `app.config` providers, shared lib, static pages, app shell, `.claude`. No feature modules. |
21
21
  | `mobilems-objects` | `libs/museum-objects` + the `/obiekty` route |
22
22
  | `mobilems-object-collections` | `libs/museum-collections` + the `/kolekcje` route |
23
+ | `mobilems-creators` | `libs/museum-creators` + the creators route (`/tworcy` or `/artysci`) and the creators search tab |
23
24
  | `mobilems-articles` | A parametrized (NAME + TEMPLATE) article module — news, education, … |
24
25
  | `mobilems-thematic-publications` | `libs/museum-thematic-paths` + the `/sciezki-tematyczne` route |
25
26
  | `mobilems-events` | `libs/museum-events` + the `/wydarzenia` route (booking flow) |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@smartsoft001-mobilems/claude-plugins",
3
- "version": "2.119.0",
3
+ "version": "2.121.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -13,6 +13,7 @@ Run order for a new museum app: `/smart:scaffold-nx-workspace` → `mobilems-pro
13
13
  | Skill | `mobilems-project-init` | The MobilEMS foundation — deps, Tailwind, SSR + translations runtime, `app.config` providers, shared lib, static pages, app shell, `.claude`. Produces NO feature modules |
14
14
  | Skill | `mobilems-objects` | Museum objects module — `libs/museum-objects` (domain + shell/angular) + the `/obiekty` route |
15
15
  | Skill | `mobilems-object-collections` | Collections module — `libs/museum-collections` + the `/kolekcje` route |
16
+ | Skill | `mobilems-creators` | Creators (artists) — `libs/museum-creators` + the creators route and search tab |
16
17
  | Skill | `mobilems-articles` | A parametrized (NAME + TEMPLATE) article-backed content module; run once per content type |
17
18
  | Skill | `mobilems-thematic-publications` | Thematic paths — `libs/museum-thematic-paths` + the `/sciezki-tematyczne` route |
18
19
  | Skill | `mobilems-events` | Events + booking flow — `libs/museum-events` + the `/wydarzenia` route |
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "mobilems",
3
3
  "description": "MobilEMS-specific Claude Code skills (MuzaCMS / MobileMS indexer API)",
4
- "version": "2.119.0"
4
+ "version": "2.121.0"
5
5
  }
@@ -0,0 +1,138 @@
1
+ ---
2
+ name: mobilems-creators
3
+ description: Add the museum CREATORS (artists) feature module to a MobilEMS app initialized with mobilems-project-init. Use whenever the user wants to add / scaffold / wire the creators module, the artists index (letters, groups, jump-to), the filtered artists list, the creator details page, the creator card, or the artists tab of the global search, or asks to "dodaj moduł twórców / artystów". Creates libs/museum-creators (or the museum's own name, e.g. museum-artists), registers the creators route (/tworcy or /artysci), and adds the creators npm packages. Requires the MobilEMS foundation to exist first.
4
+ user-invocable: true
5
+ allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion
6
+ ---
7
+
8
+ # MobilEMS creators module
9
+
10
+ Add the **creators** (twórcy / artyści) feature module to a MobilEMS app whose foundation was built by
11
+ `mobilems-project-init`. This skill builds the `libs/museum-creators` library (the museum may name it
12
+ `museum-artists`), its routes and its pages on top of the framework bases, and wires the creators path and the
13
+ search tab.
14
+
15
+ The framework shares **logic only**: every page is an `@Directive() abstract class` with no template. The museum
16
+ writes its own HTML and styles. There is no generator (one reference consumer, so YAGNI): the library is written by
17
+ hand from the reference below.
18
+
19
+ ## Precondition
20
+
21
+ The MobilEMS foundation must exist (`SharedModule.forRoot` in `app.config.ts`, a populated shared-lib barrel). If it
22
+ does not, tell the user to run `/mobilems:project-init` first. Ask the user for:
23
+
24
+ - the library name (default `museum-creators`) and the route (`/tworcy`, or `/artysci` as in sztuki);
25
+ - the museum's API host. **The index base needs the prefixes endpoint and `lastNameSw`, which only the staging
26
+ API has** (`api-muza.muzeumsztuki.pl` does not). Without them build the list on `CreatorsCatalogBaseComponent`
27
+ (see the README's "Which list base?").
28
+
29
+ The reference is the sztuki museum's `libs/museum-artists` (routes, pages, templates, search tab). Read it for the
30
+ templates; take the logic from the framework bases. Its artist page (MOB-1972/1963) was ported into
31
+ `CreatorDetailsBaseComponent` from sztuki `8f698f6`, but sztuki's own `item.component` does not extend the base yet:
32
+ copy its template, not its class.
33
+
34
+ ## Framework first — MANDATORY
35
+
36
+ Read these before writing ANY component, service, pipe, directive or helper, and reuse what they
37
+ list — re-implementing framework code is a defect, not a shortcut:
38
+
39
+ 0. [**The Framework API index**](https://github.com/smartsoft001/mobilems-framework/blob/main/README.md#-framework-api) — the map of every package to
40
+ its catalog. Start here when you do not already know which package owns what.
41
+ 1. The framework API catalog — `packages/shared/angular/README.md` in this repo, installed as
42
+ `node_modules/@smartsoft001-mobilems/angular/README.md` in a museum app ([browse](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/shared/angular/README.md)).
43
+ 2. [`packages/creators/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/creators/README.md) — this module's package catalog: services, URL tools, the bases,
44
+ the "Which list base?" table and the API notes
45
+ 3. `libs/shared/angular/README.md` — the app's own shared lib (built by `mobilems-project-init`)
46
+
47
+ | Do not hand-roll | Use |
48
+ | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
49
+ | the creators HTTP calls, letter filtering, id normalisation | `CreatorsService` (`getPage`, `getAll`, `getByLetter`, `getPrefixes`, `getByIndexPrefix`, `getById`, `getWorks`, `getRelated`) |
50
+ | the letter index: letters, two-letter groups, `#fragment`, focus | `CreatorsIndexBaseComponent` |
51
+ | the mobile "go to" panel | `CreatorsJumpToBaseComponent` |
52
+ | the filtered list (name, birth range, sort, paging) | `CreatorsCatalogBaseComponent` + `CreatorsFiltersBaseComponent` (modal) |
53
+ | the creator card (name, years, roles, image, link) | `CreatorCardBaseComponent` |
54
+ | the details page (biography fold, portrait, related strips, contents, share, prev/next, SEO) | `CreatorDetailsBaseComponent` |
55
+ | the search tab (phrase, paging, page size) | `CreatorsSearchListBaseComponent` |
56
+ | query urls, name/roles/years formatting, letter grouping, details link | `creatorsListUrl`, `creatorDisplayName`, `creatorRoles`, `groupCreatorsByLetter`, `creatorDetailsCommands` |
57
+ | a masonry grid | `<smart-mobilems-masonry-grid>` (`MasonryGridComponent`), with `provideBrowserMasonry()` |
58
+ | meta / OG tags, media urls | `MuseumSeoService` (the bases call it), `FileUrlService` (the card and details call it) |
59
+
60
+ Anything the framework genuinely lacks goes into `libs/shared/angular` **once** (and into its README) — never a
61
+ per-module copy.
62
+
63
+ ## Steps
64
+
65
+ 1. **npm deps** — add `@smartsoft001-mobilems/creators-domain`, `@smartsoft001-mobilems/creators-shell-angular`,
66
+ `@smartsoft001-mobilems/creators-shell-dtos` (match the installed mobilems line). The details base types works as
67
+ `MsObject`, so `@smartsoft001-mobilems/objects-domain` must be present too. Resolve versions at run time; do not
68
+ `npm install` unless asked.
69
+ 2. **Library** — create `libs/museum-creators` with a `shell/angular` project (sztuki's `museum-artists` has one) and the tsconfig path
70
+ `@<PREFIX>/museum-creators/shell/angular`, mirroring the other `libs/museum-*` libs. The models come from the
71
+ framework packages, so the museum needs no domain project of its own.
72
+ 3. **Routes** — the module routes, in this order (the details page takes the canonical `:id/:slug` and the bare `:id`):
73
+
74
+ ```ts
75
+ export const routes: Route[] = [
76
+ { path: ':id/:slug', component: ItemComponent },
77
+ { path: ':id', component: ItemComponent },
78
+ { path: '', component: ListComponent },
79
+ ];
80
+ ```
81
+
82
+ Register the lazy route in `apps/web/src/app/app.routes.ts` (`/tworcy` or `/artysci`).
83
+
84
+ 4. **Pages** — each extends a framework base; the museum supplies the template. Overridable members are read lazily,
85
+ so declare them as `protected override readonly`:
86
+ - `ListComponent extends CreatorsIndexBaseComponent`: override `creatorsIndexAnchorBase` (default `'tworcy'`; the
87
+ museum's route name, so `#Ba` links match) and `creatorsIndexSeo` (an `ICreatorsListSeo` with the museum's
88
+ translation keys). Put the button that opens the jump-to panel under the `#filtersButton` template ref. Its
89
+ template shows the results view while the base's `filtered()` is `true` and the letter index otherwise; do not
90
+ re-parse `?filter=` yourself.
91
+ - `ResultsComponent extends CreatorsCatalogBaseComponent` for `?filter=`: `super({ persistKey, listPath, seo })`.
92
+ Pair it with `FiltersComponent extends CreatorsFiltersBaseComponent` (do not bind Escape again, the base does).
93
+ Bind the modal's `(applied)` to `applyFilter($event)`: it navigates to `listPath?filter=…` (the url is the source
94
+ of truth); "reset filters" is `clearAllFilters()`, which navigates back to the index.
95
+ Offer only the filters the API supports: **name and birth date from-to** (no death date, collection or tags).
96
+ - `IndexFiltersMobileComponent extends CreatorsJumpToBaseComponent` (the jump-to panel; override
97
+ `creatorsJumpToHeaderSelectors` if the header tags differ from `app-header` / `app-nav-bar`).
98
+ - `ArtistCardComponent extends CreatorCardBaseComponent`: override `creatorCardPath` (default `'/tworcy'`) and
99
+ `creatorCardPlaceholder` (default `'/gfx/no-image.png'`).
100
+ - `ItemComponent extends CreatorDetailsBaseComponent`: override `creatorDetailsPath` (start it with `/`) and
101
+ `creatorDetailsKeys` (`{ ...CREATOR_DETAILS_KEYS, ... }` with the museum's label and SEO title keys); sanitize
102
+ the biography html where the template binds it. For result cards in the related strips, set the type parameter
103
+ (`CreatorDetailsBaseComponent<SearchResultCard>`) and override `creatorDetailsRelatedItem(item, key)`. Tailwind
104
+ class strings stay in the component; the base has none.
105
+ 5. **Search tab** — in `app.config.ts` set `SEARCH_CONFIG_TOKEN` to `{ apiUrl, creators: true }` (adds the creators
106
+ count; skip it when your `searchMap` already returns a `creators` url, as sztuki's does, since `searchMap` wins
107
+ anyway), add a `searchRoutes` module for `/wyszukaj/:text/artysci` whose `SearchListComponent` extends
108
+ `CreatorsSearchListBaseComponent` (the `#results` template ref is the scroll target), and register it as a child of
109
+ the `wyszukaj/:text` route (sztuki also lists it in its `SEARCH_SECTIONS`). See `mobilems-search` for the frame.
110
+ 6. **Section links** — provide `PAGE_SECTION_ROUTES_TOKEN` as `{ ...DEFAULT_PAGE_SECTION_ROUTES, creatorsPath: '/artysci' }`
111
+ so a CMS "creator" target goes to `[creatorsPath, id]` instead of the objects filter.
112
+ 7. **Translations** — add the keys the museum's templates and overrides use (`ARTISTS.*` / `CREATORS.*`, the SEO
113
+ keys) for BOTH `pl` and `en`; run `npm run check:translations`.
114
+ 8. **Verify** — `npx nx build web` (production), the lib's lint and tests, and open `/artysci`, a letter, a
115
+ `#fragment` link (`/artysci#Ba`), `?filter=`, a details page with prev/next, and the search tab.
116
+
117
+ ## Known pitfalls
118
+
119
+ - **Empty `q=` / `sw=` empties the list (404).** Always go through `creatorsListUrl` / the service.
120
+ - **Prefixes endpoint and `lastNameSw` are staging-only**; on `api-muza.muzeumsztuki.pl` the index base shows nothing
121
+ useful. Check the API before choosing the index base.
122
+ - **Death-date range does not work** (the API always answers 404); collection and tags filters are ignored. Leave
123
+ them out of the filters modal and report them.
124
+ - **Works 404 means none**: do not show an error for a creator without works.
125
+ - **`creator/{id}` is staging-only**: `getById` reads it and falls back to `dictionary/Creator/{id}` on a 404, so the
126
+ details page works on both hosts, but the portrait, lead artwork, tags, editorial biography and the exhibitions,
127
+ archives and MS books strips need the profile endpoint. Their parts simply do not render without it.
128
+ - **Prev/next** exist only after a list was shown (`CreatorsListContext`); a deep link has none. Hide the buttons.
129
+ - **Member name clashes**: the bases prefix their private members (`creators…`, `creator…`) so that a subclass can
130
+ keep its own `route`, `router`, `document`, `translate`. Keep your own names unprefixed and do not reuse a base
131
+ prefix; a clash shows up as TS2415 in `nx build`, not in Jest.
132
+ - **SSR**: scrolling, focus and the `#fragment` run only in the browser; use the injected `DOCUMENT`, never the
133
+ `document` global.
134
+
135
+ ## Notes
136
+
137
+ - This skill builds only the creators module. It never adds other feature modules and never re-runs the foundation.
138
+ - Bundled files ship automatically. Do not hand-edit `plugin.json`.
@@ -34,12 +34,14 @@ list — re-implementing framework code is a defect, not a shortcut:
34
34
  2. [`packages/public-collections/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/public-collections/README.md) — this module's package catalog
35
35
  3. `libs/shared/angular/README.md` — the app's own shared lib (built by `mobilems-project-init`)
36
36
 
37
- | Do not hand-roll | Use |
38
- | ------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------- |
39
- | a masonry grid (`new Masonry(...)`, per-page masonry JS/CSS) | `<smart-mobilems-masonry-grid>` (`MasonryGridComponent`) with a `#masonryContentTpl`; the `Masonry` provider only feeds it |
40
- | filter state / hand-built `query` arrays | `FiltersBaseComponent<PublicCollection>` + its `FiltersContext` accessors (`searchText`, `keywords`, `cycles`, `page`, `sort`, …) |
41
- | list/detail plumbing (facade wiring, id-from-route) | the `PublicCollectionsListComponent` / `PublicCollectionsItemComponent` base classes |
42
- | media URLs, meta/OG tags, filter dictionaries, modals, WCAG | `FileUrlService`, `SeoService`/`MetaService`, `DictionaryService`, `ModalService`, `WcagService` |
37
+ | Do not hand-roll | Use |
38
+ | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
39
+ | a masonry grid (`new Masonry(...)`, per-page masonry JS/CSS) | `<smart-mobilems-masonry-grid>` (`MasonryGridComponent`) with a `#masonryContentTpl`; the `Masonry` provider only feeds it |
40
+ | filter state / hand-built `query` arrays | `FiltersBaseComponent<PublicCollection>` + its `FiltersContext` accessors (`searchText`, `keywords`, `cycles`, `page`, `sort`, …) |
41
+ | list page logic (first read, persistence, SSR read, read guard, load-more, tag bar) | `CollectionsCatalogBaseComponent` (extends `PublicCollectionsListComponent`; the page keeps its own markup) |
42
+ | collection page logic (paged items, sort/limit, 404 vs error, SEO, list mode) | `CollectionDetailsBaseComponent` (extends `PublicCollectionsItemComponent`) + `PublicCollectionItemsService` (provided by `PublicCollectionsSharedModule`) |
43
+ | list/detail plumbing (facade wiring, id-from-route) | the `PublicCollectionsListComponent` / `PublicCollectionsItemComponent` base classes (already under the two catalog bases) |
44
+ | media URLs, meta/OG tags, filter dictionaries, modals, WCAG | `FileUrlService`, `SeoService`/`MetaService`, `DictionaryService`, `ModalService`, `WcagService` |
43
45
 
44
46
  Anything the framework genuinely lacks goes into `libs/shared/angular` **once** (and into its
45
47
  README) — never a per-module copy. `scripts/verify-module.sh` fails the run on hand-rolled masonry.
@@ -62,7 +64,7 @@ README) — never a per-module copy. `scripts/verify-module.sh` fails the run on
62
64
  `references/lib-contents.md`. The `museum-collections` commit scope registers automatically from the `libs/` dir name.
63
65
  3. **Module + routes** — see `references/module-and-routes.md`: `MuseumCollectionsModule` wraps
64
66
  `PublicCollectionsSharedModule.forFeature({ routing: false, config: { type: MuseumCollection, apiUrl, entity: 'publicCollections' } })`
65
- with the `Masonry` SSR factory and the `CrudService → CollectionsService` provider. Add the lazy
67
+ with `provideBrowserMasonry()`. No `CollectionsService` — the framework `PublicCollectionsSharedModule` provides `PublicCollectionItemsService`. Add the lazy
66
68
  `kolekcje` route to `appRoutes` in `apps/web/src/app/app.routes.ts` and a matching child under the
67
69
  `wyszukaj/:text` search route.
68
70
  4. **Collections shared component** — see `references/shared-components.md`: build
@@ -20,10 +20,13 @@ libs/museum-collections/
20
20
  ├── collections.module.ts # MuseumCollectionsModule (references/module-and-routes.md)
21
21
  ├── collections.routes.ts
22
22
  ├── components/ # filters (keywords/cycles) + index
23
- ├── pages/ # list (ListComponent), item (ItemComponent) + index
24
- └── services/ # collections.service.ts + index
23
+ └── pages/ # list (ListComponent), item (ItemComponent) + index
25
24
  ```
26
25
 
26
+ There is no `services/` dir: the module declares no collections service (`PublicCollectionsSharedModule`
27
+ provides `PublicCollectionItemsService`). `pages/list` extends `CollectionsCatalogBaseComponent` and
28
+ `pages/item` extends `CollectionDetailsBaseComponent`; each keeps the app's own markup (the bases hold logic only).
29
+
27
30
  There are no `enums/`, `specifications/`, or `value-objects/` dirs in the domain — the connection
28
31
  shape (keywords/cycles) is inlined on the entity below.
29
32
 
@@ -3,15 +3,14 @@
3
3
  ## `libs/museum-collections/shell/angular/src/lib/collections.module.ts`
4
4
 
5
5
  Thread `<PREFIX>` (the `environment` + domain imports). `PublicCollectionsSharedModule.forFeature`
6
- provides the list/detail behaviour over `MuseumCollection`; `CollectionsService` is provided as the
7
- `CrudService`; `provideBrowserMasonry()` supplies the real library in the browser and `null` during
6
+ provides the list/detail behaviour over `MuseumCollection` (including `PublicCollectionItemsService`, so the
7
+ module declares no collections service of its own); `provideBrowserMasonry()` supplies the real library in the browser and `null` during
8
8
  SSR (the grid then skips layout). Note: unlike the objects module this one does NOT import `SmartSharedModule`.
9
9
 
10
10
  ```ts
11
11
  import { NgModule } from '@angular/core';
12
12
  import { FormsModule } from '@angular/forms';
13
13
  import { RouterModule } from '@angular/router';
14
- import { CrudService } from '@smartsoft001/crud-shell-angular';
15
14
  import { PublicCollectionsSharedModule } from '@smartsoft001-mobilems/public-collections-shell-angular';
16
15
  import { provideBrowserMasonry } from '@smartsoft001-mobilems/angular';
17
16
 
@@ -19,17 +18,9 @@ import { environment } from '@<PREFIX>/angular';
19
18
  import { MuseumCollection } from '@<PREFIX>/museum-collections/domain';
20
19
 
21
20
  import { routes } from './collections.routes';
22
- import { CollectionsService } from './services';
23
21
 
24
22
  @NgModule({
25
- providers: [
26
- CollectionsService,
27
- {
28
- provide: CrudService,
29
- useClass: CollectionsService,
30
- },
31
- provideBrowserMasonry(),
32
- ],
23
+ providers: [provideBrowserMasonry()],
33
24
  imports: [
34
25
  FormsModule,
35
26
  PublicCollectionsSharedModule.forFeature({
@@ -102,6 +102,27 @@ else
102
102
  pass "no filters component in this module"
103
103
  fi
104
104
 
105
+ # 5. page logic lives in the framework catalog bases — the pages only add markup
106
+ for pair in "list:CollectionsCatalogBaseComponent" "item:CollectionDetailsBaseComponent"; do
107
+ pdir="${pair%%:*}"; base="${pair#*:}"
108
+ pf="$LIBDIR/pages/$pdir/$pdir.component.ts"
109
+ if [ -f "$pf" ]; then
110
+ if grep -qE "extends[[:space:]]+$base" "$pf"; then
111
+ pass "pages/$pdir extends $base"
112
+ else
113
+ bad "pages/$pdir/$pdir.component.ts does not extend $base — page logic belongs to the framework base ($pf)"
114
+ fi
115
+ fi
116
+ done
117
+ # 6. no module-level CollectionsService that only re-declares getItems
118
+ CS="$(grep -rlE "class[[:space:]]+CollectionsService" "$LIBDIR" 2>/dev/null | xargs grep -lE "getItems" 2>/dev/null || true)"
119
+ if [ -n "$CS" ]; then
120
+ bad "module-level CollectionsService re-declares getItems — drop it, PublicCollectionItemsService covers it:"
121
+ printf ' %s\n' $CS >&2
122
+ else
123
+ pass "no module-level CollectionsService re-declaring getItems"
124
+ fi
125
+
105
126
  echo "== Translations (REF_ROOT=${REF_ROOT:-unset}) =="
106
127
  # Groups: present in pl+en, no dead references, complete vs the reference repo
107
128
  # (export REF_ROOT=<reference-repo-root> to enable the completeness diff).
@@ -49,6 +49,11 @@ representations of the same object.
49
49
  `wyszukaj/:text` (its search module) and the page builds its tab list from
50
50
  what is registered. When a tab is missing, fix it in that module's skill
51
51
  scope, not by hardcoding the tab here.
52
+ **Creators tab.** Set `creators: true` in `SEARCH_CONFIG_TOKEN` (default `false`): `SearchService` then also counts
53
+ `creators` (`dictionary/Creator/search/page/1?maxPerPage=1&q=…`, `ISearchResults.creators`); `searchMap` still
54
+ overrides the url. The tab's page extends `CreatorsSearchListBaseComponent`
55
+ (`@smartsoft001-mobilems/creators-shell-angular`, route `wyszukaj/:text/artysci`); the museum writes the view.
56
+ Build the module with `mobilems-creators`.
52
57
  3. **Direct navigation to a tab must work.** Opening `/wyszukaj/wazy/aktualnosci`
53
58
  straight from a link — not by clicking through — has to land on that tab with
54
59
  results loaded. This broke repeatedly (MOB-1052…1056) because the active tab