@smartsoft001-mobilems/claude-plugins 2.128.0 → 2.130.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/package.json +1 -1
- package/plugins/mobilems/.claude-plugin/plugin.json +1 -1
- package/plugins/mobilems/skills/mobilems-objects/SKILL.md +18 -12
- package/plugins/mobilems/skills/mobilems-objects/references/lib-contents.md +11 -4
- package/plugins/mobilems/skills/mobilems-objects/scripts/verify-module.sh +62 -0
- package/plugins/mobilems/skills/mobilems-project-init/references/shared-lib.md +2 -0
package/package.json
CHANGED
|
@@ -35,16 +35,22 @@ list — re-implementing framework code is a defect, not a shortcut:
|
|
|
35
35
|
2. [`packages/objects/README.md`](https://github.com/smartsoft001/mobilems-framework/blob/main/packages/objects/README.md) — this module's package catalog
|
|
36
36
|
3. `libs/shared/angular/README.md` — the app's own shared lib (built by `mobilems-project-init`)
|
|
37
37
|
|
|
38
|
-
| Do not hand-roll
|
|
39
|
-
|
|
|
40
|
-
| a masonry grid (`new Masonry(...)`, per-page masonry JS/CSS)
|
|
41
|
-
| filter state / hand-built `query` arrays
|
|
42
|
-
| list page logic (first read, persistence, SSR read, read guard, load-more, masonry lifecycle, list SEO)
|
|
43
|
-
| detail page logic (SSR SEO, scroll, visit counter, retry, opt-in related / related collections)
|
|
44
|
-
| object detail field helpers (title, tags, inventory, bibliography, description, related articles, ask-about link)
|
|
45
|
-
| gallery / media lists
|
|
46
|
-
| visit counter, related objects, collection / archaeological dictionaries
|
|
47
|
-
| media
|
|
38
|
+
| Do not hand-roll | Use |
|
|
39
|
+
| ------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
40
|
+
| a masonry grid (`new Masonry(...)`, per-page masonry JS/CSS) | `<smart-mobilems-masonry-grid>` (`MasonryGridComponent`) with a `#masonryContentTpl`; the `Masonry` provider in step 3 only feeds it |
|
|
41
|
+
| filter state / hand-built `query` arrays | `FiltersBaseComponent<MsObject>` + its `FiltersContext` accessors (`searchText`, `keywords`, `page`, `sort`, `onlyImage`, …) |
|
|
42
|
+
| list page logic (first read, persistence, SSR read, read guard, load-more, masonry lifecycle, list SEO) | `ObjectsCatalogBaseComponent` (`pages/list` extends it; the app keeps its own markup and `sortOptions` over `isNewType`) |
|
|
43
|
+
| detail page logic (SSR SEO, scroll, visit counter, retry, opt-in related / related collections) | `ObjectDetailsBaseComponent` (extends `ObjectsItemComponent`; `pages/item` extends it, `related` and `relatedCollections` are opt-in via the constructor options) |
|
|
44
|
+
| object detail field helpers (title, tags, inventory, bibliography, description, related articles, ask-about link) | the `objectTitle`, `tagValues`, `inventoryValues`, `bibliography`, `descriptionHtml`, `relatedArticles`, `askAboutObjectHref` tools |
|
|
45
|
+
| gallery / media lists | `ObjectMediaService` |
|
|
46
|
+
| visit counter, related objects, collection / archaeological dictionaries | `ObjectsDataService` (extends `ObjectsService`) |
|
|
47
|
+
| media tabs (images aside): map, audio, audio description, documents, animation, video, virtual walk, 3D model, download button, `media-sections` | `components/tabs/*` extend the `Object…TabBaseComponent` bases (`ObjectMapTabBaseComponent`, `ObjectAudioTabBaseComponent`, `ObjectAudioDescriptionTabBaseComponent`, `ObjectDocumentsTabBaseComponent`, `ObjectAnimationTabBaseComponent`, `ObjectVideoTabBaseComponent`, `ObjectVirtualWalkTabBaseComponent`, `ObjectModel3dTabBaseComponent`, `ObjectDownloadButtonBaseComponent`, `ObjectMediaSectionsBaseComponent`) from `objects-shell-angular`: logic in the framework, `selector`/`template`/`styles`/`imports` in the app. The 3D tab implements `loadModel3dLibrary()` as `import('online-3d-viewer')`; `playableOnly` (audio) and `loadableOnly` (3D) default to `true` (Podlasie), pass `false` to keep the unfiltered list |
|
|
48
|
+
| filter bar (sort / limit, chips, dictionary toggles, year range, theme tags) | `ObjectsFiltersBarBaseComponent` (`components/filters-top` extends it; the app keeps its markup; the cycle theme tags are opt-in with `cycleTags: true`, otherwise no `dictionary/Cycle` request) |
|
|
49
|
+
| search panel (phrase, tags, boolean filters) - Podlasie-style museums only | `ObjectsSearchPanelBaseComponent` (S/T-style museums have no search panel: they use the filters bar only) |
|
|
50
|
+
| advanced filters panel - Podlasie-style museums only | `ObjectsAdvancedFiltersBaseComponent` (the field config `IObjectsAdvancedFilterField[]` stays in the app, the framework exports only the types; S/T-style museums skip it) |
|
|
51
|
+
| share results link | `ObjectsShareResultsBaseComponent` (extends the shared `ShareLinkBaseComponent`; set `objectsShareResultsListPath` for another list) |
|
|
52
|
+
| sort / limit options, sort keys, results URL | `OBJECTS_SORT_OPTIONS`, `OBJECTS_NEW_TYPE_SORT_OPTIONS`, `OBJECTS_LIMIT_OPTIONS`, `objectsSortKey`, `objectsSortFromKey`, `buildObjectsResultsUrl` |
|
|
53
|
+
| media URLs, meta/OG tags, filter dictionaries, modals, WCAG | `FileUrlService`, `SeoService`/`MetaService`, `DictionaryService`, `ModalService`, `WcagService` |
|
|
48
54
|
|
|
49
55
|
Anything the framework genuinely lacks goes into `libs/shared/angular` **once** (and into its
|
|
50
56
|
README) — never a per-module copy. `scripts/verify-module.sh` fails the run on hand-rolled masonry.
|
|
@@ -75,7 +81,7 @@ tab, the grid/list item cards and the object gallery widget.
|
|
|
75
81
|
|
|
76
82
|
1. **npm deps** — add `@smartsoft001-mobilems/objects-domain`, `@smartsoft001-mobilems/objects-shell-angular`,
|
|
77
83
|
`@smartsoft001-mobilems/objects-shell-dtos` (match the installed mobilems line) and
|
|
78
|
-
`online-3d-viewer` (the 3D tab
|
|
84
|
+
`online-3d-viewer` (the 3D tab, loaded only through `loadModel3dLibrary()`; `leaflet`/`openseadragon`/`swiper` are already foundation deps).
|
|
79
85
|
Resolve versions at run time; do not `npm install` unless asked.
|
|
80
86
|
2. **Library** — scaffold `libs/museum-objects` with this package's Nx generator (deterministic — do
|
|
81
87
|
NOT copy files by hand when the generator is available):
|
|
@@ -109,7 +115,7 @@ tab, the grid/list item cards and the object gallery widget.
|
|
|
109
115
|
The cross-cutting rules live in `mobilems-project-init`
|
|
110
116
|
`references/qa-hardening.md`; these are the objects-specific ones:
|
|
111
117
|
|
|
112
|
-
- **Search debounce.**
|
|
118
|
+
- **Search debounce.** For apps that render the panel full-text search with `app-objects-searchbar variant="panel"` (a standalone component: a live debounced search through `searchEvent` / `closeEvent`, an `autofocus` input, never navigates - it does not extend `SearchbarBaseComponent`, which navigates to the search route; the filter bases do not need it), the host must
|
|
113
119
|
re-emit through a handler with `@debounce(250)` (`lodash-decorators`) — without it every keystroke
|
|
114
120
|
hits the API. Collections and news filters already do this; keep the modules consistent.
|
|
115
121
|
- **List page:** show the total result count (`facade.totalCount()`) and add the shared
|
|
@@ -20,8 +20,10 @@ libs/museum-objects/
|
|
|
20
20
|
└── lib/
|
|
21
21
|
├── objects.module.ts # MuseumObjectsModule (references/module-and-routes.md)
|
|
22
22
|
├── objects.routes.ts
|
|
23
|
-
├── components/ # description,
|
|
24
|
-
│ # menu, objects-searchbar (
|
|
23
|
+
├── components/ # description, filters-top (extends ObjectsFiltersBarBaseComponent), high-def-request-modal,
|
|
24
|
+
│ # menu, objects-searchbar (only where the app renders the panel search), tabs/* (thin classes on the framework bases);
|
|
25
|
+
│ # no filter-input (it comes from the shared lib); Podlasie-style apps also keep
|
|
26
|
+
│ # objects-search-panel and objects-advanced-filters (extend the framework bases)
|
|
25
27
|
├── pages/ # list (ListComponent), item (ItemComponent) + index
|
|
26
28
|
└── services/ # no local objects.service / object-media.service (framework ObjectsDataService, ObjectMediaService)
|
|
27
29
|
```
|
|
@@ -29,7 +31,10 @@ libs/museum-objects/
|
|
|
29
31
|
`pages/list` extends `ObjectsCatalogBaseComponent` (logic only: first read, persistence, SSR read, read guard,
|
|
30
32
|
load-more, masonry lifecycle, list SEO) and keeps the app's own markup and `sortOptions` over `isNewType`;
|
|
31
33
|
`pages/item` extends `ObjectDetailsBaseComponent` (SSR SEO, scroll, visit counter, retry, opt-in related / related
|
|
32
|
-
collections; the app keeps its markup).
|
|
34
|
+
collections; the app keeps its markup). `components/filters-top` extends `ObjectsFiltersBarBaseComponent` (cycle tags
|
|
35
|
+
opt-in); `objects-search-panel` / `objects-advanced-filters` / `objects-share-results`, where an app has them, extend
|
|
36
|
+
`ObjectsSearchPanelBaseComponent` / `ObjectsAdvancedFiltersBaseComponent` / `ObjectsShareResultsBaseComponent`
|
|
37
|
+
(S/T-style museums use the filters bar only). There is no local `objects.service` or `object-media.service`.
|
|
33
38
|
|
|
34
39
|
## Domain entity (verbatim, thread nothing — pure types)
|
|
35
40
|
|
|
@@ -68,6 +73,8 @@ export class MuseumObject extends MsObject {
|
|
|
68
73
|
- **jest `__mocks__`** — keep the `src/__mocks__/@smartsoft001-mobilems/*` stubs from the reference;
|
|
69
74
|
they let the shell unit-test without the real feature packages.
|
|
70
75
|
|
|
71
|
-
The tabs (`tabs/*`)
|
|
76
|
+
The tabs (`tabs/*`) are thin classes: each extends its `Object…TabBaseComponent` from
|
|
77
|
+
`@smartsoft001-mobilems/objects-shell-angular` (logic) and keeps only selector, template, styles and imports. The 3D
|
|
78
|
+
model tab implements `loadModel3dLibrary()` with `import('online-3d-viewer')` — that's why this skill
|
|
72
79
|
adds that dep. The map tab uses `leaflet`, images use `openseadragon`/`swiper` — already foundation
|
|
73
80
|
deps.
|
|
@@ -138,6 +138,68 @@ else
|
|
|
138
138
|
pass "no hand-rolled visit counter, SSR apply or media service"
|
|
139
139
|
fi
|
|
140
140
|
|
|
141
|
+
# 7. filter bar logic lives in the framework filters bar base; no local copies of its helpers
|
|
142
|
+
FILTERSF="$LIBDIR/components/filters-top/filters-top.component.ts"
|
|
143
|
+
if [ -f "$FILTERSF" ]; then
|
|
144
|
+
if grep -qE "extends[[:space:]]+ObjectsFiltersBarBaseComponent" "$FILTERSF"; then
|
|
145
|
+
pass "filters-top extends ObjectsFiltersBarBaseComponent"
|
|
146
|
+
else
|
|
147
|
+
bad "components/filters-top/filters-top.component.ts does not extend ObjectsFiltersBarBaseComponent — filter bar logic belongs to the framework base ($FILTERSF)"
|
|
148
|
+
fi
|
|
149
|
+
fi
|
|
150
|
+
if grep -rqE "class RedirectFiltersContext|function buildObjectsResultsUrl" "$LIBDIR" 2>/dev/null; then
|
|
151
|
+
bad "lib contains class RedirectFiltersContext or function buildObjectsResultsUrl — use ObjectsFiltersBarBaseComponent / buildObjectsResultsUrl from the framework:"
|
|
152
|
+
grep -rnE "class RedirectFiltersContext|function buildObjectsResultsUrl" "$LIBDIR" >&2 || true
|
|
153
|
+
else
|
|
154
|
+
pass "no hand-rolled redirect filters context or results URL builder"
|
|
155
|
+
fi
|
|
156
|
+
|
|
157
|
+
# 8. search panel and advanced filters (Podlasie-style apps only) extend the framework bases
|
|
158
|
+
for pair in "objects-search-panel.component.ts:ObjectsSearchPanelBaseComponent" "objects-advanced-filters.component.ts:ObjectsAdvancedFiltersBaseComponent"; do
|
|
159
|
+
fname="${pair%%:*}"
|
|
160
|
+
base="${pair##*:}"
|
|
161
|
+
for found in $(find "$LIBDIR" -name "$fname" 2>/dev/null); do
|
|
162
|
+
if grep -qE "extends[[:space:]]+$base" "$found"; then
|
|
163
|
+
pass "$fname extends $base"
|
|
164
|
+
else
|
|
165
|
+
bad "$fname does not extend $base — its logic belongs to the framework base ($found)"
|
|
166
|
+
fi
|
|
167
|
+
done
|
|
168
|
+
done
|
|
169
|
+
|
|
170
|
+
# 9. media tabs are thin classes on the framework bases; no hand-rolled fullscreen / download plumbing
|
|
171
|
+
TABDIR="$LIBDIR/components/tabs"
|
|
172
|
+
if [ -d "$TABDIR" ]; then
|
|
173
|
+
for f in "$TABDIR"/*.component.ts; do
|
|
174
|
+
[ -f "$f" ] || continue
|
|
175
|
+
case "$(basename "$f")" in
|
|
176
|
+
*.spec.ts) continue ;;
|
|
177
|
+
audio-description-tab.component.ts) base=ObjectAudioDescriptionTabBaseComponent ;;
|
|
178
|
+
audio-tab.component.ts) base=ObjectAudioTabBaseComponent ;;
|
|
179
|
+
animation-tab.component.ts) base=ObjectAnimationTabBaseComponent ;;
|
|
180
|
+
documents-tab.component.ts) base=ObjectDocumentsTabBaseComponent ;;
|
|
181
|
+
map-tab.component.ts) base=ObjectMapTabBaseComponent ;;
|
|
182
|
+
model-3d-tab.component.ts) base=ObjectModel3dTabBaseComponent ;;
|
|
183
|
+
video-tab.component.ts) base=ObjectVideoTabBaseComponent ;;
|
|
184
|
+
virtual-walk-tab.component.ts) base=ObjectVirtualWalkTabBaseComponent ;;
|
|
185
|
+
download-button.component.ts) base=ObjectDownloadButtonBaseComponent ;;
|
|
186
|
+
media-sections.component.ts) base=ObjectMediaSectionsBaseComponent ;;
|
|
187
|
+
*) continue ;;
|
|
188
|
+
esac
|
|
189
|
+
if grep -qE "extends[[:space:]]+$base" "$f"; then
|
|
190
|
+
pass "$(basename "$f") extends $base"
|
|
191
|
+
else
|
|
192
|
+
bad "$(basename "$f") does not extend $base - tab logic belongs to the framework base ($f)"
|
|
193
|
+
fi
|
|
194
|
+
done
|
|
195
|
+
fi
|
|
196
|
+
if [ -d "$TABDIR" ] && grep -rqE --include='*.ts' --exclude='*.spec.ts' "fullscreenchange|createObjectURL" "$TABDIR" 2>/dev/null; then
|
|
197
|
+
bad "tabs contain fullscreenchange or createObjectURL - fullscreen / download logic belongs to the framework bases:"
|
|
198
|
+
grep -rnE --include='*.ts' --exclude='*.spec.ts' "fullscreenchange|createObjectURL" "$TABDIR" >&2 || true
|
|
199
|
+
else
|
|
200
|
+
pass "no hand-rolled fullscreen or blob-download plumbing in the tabs"
|
|
201
|
+
fi
|
|
202
|
+
|
|
141
203
|
echo "== Translations (REF_ROOT=${REF_ROOT:-unset}) =="
|
|
142
204
|
# Groups: present in pl+en, no dead references, complete vs the reference repo
|
|
143
205
|
# (export REF_ROOT=<reference-repo-root> to enable the completeness diff).
|
|
@@ -57,6 +57,8 @@ not as copies of the reference logic. The museum keeps its own template and styl
|
|
|
57
57
|
stores (favorites, selection, downloads, recommended items) stay in the museum and reach the base
|
|
58
58
|
through its hooks (`IFavoriteHooks`, `ISelectionActionsFavorites`, `IRecommendedSources`, the abstract `downloadCardsExport`).
|
|
59
59
|
|
|
60
|
+
A share-link button (e.g. `share-results`, collection share) extends `ShareLinkBaseComponent` (`SHARE_LINK_COPIED_TIMEOUT`): implement `shareLinkUrl()`, bind `share()` and `copied()`, write the markup yourself.
|
|
61
|
+
|
|
60
62
|
The same applies to the foundation shared components `favorite-button`, `favorites-notice`,
|
|
61
63
|
`selection-actions`, `download-cards` and `recommended`: extend `FavoriteButtonBaseComponent`,
|
|
62
64
|
`FavoritesNoticeBaseComponent`, `SelectionActionsBaseComponent`, `DownloadCardsBaseComponent` and
|