docs(skills): tighten validated guidance

This commit is contained in:
4gray committed 2026-07-30 12:50:48 +02:00
1 parent 543d3fd8c7
commit 0d6192f20e
9 files changed
+61 -25

No files matched your search

+3 -1
View File
@@ -40,7 +40,9 @@ consumers currently use relative `@use` paths to the needed partial.
of copied SCSS.
- In an Electron drag region, every interactive descendant—buttons, links,
inputs, overlays, and resize handles—must explicitly use
`app-region: no-drag`.
`app-region: no-drag`. The shared directive-generated `.resize-handle` does
not set this centrally yet; consumers in drag regions must cover it
themselves and must not assume the generated handle opts out.
- A shared change must be checked across M3U, Xtream, Stalker, workspace,
portal catalog/shared UI, and unified collections where relevant.
+11 -9
View File
@@ -24,16 +24,18 @@ must remain provider-neutral.
## Series Contract
Inside Stalker portal code, `isStalkerSeriesFlag()` is the sole flag
interpretation and accepts exactly `true`, `1`, and `'1'`. The activity
normalizer in shared interfaces remains dependency-neutral and preserves the
same closed set for favorites/recent and dashboard classification. Preserve
all three modes: regular `/series`, VOD with embedded `series[]`, and lazy
Ministra VOD `is_series`.
Inside Stalker portal code, `isStalkerSeriesFlag()` is the canonical predicate
and accepts exactly `true`, `1`, and `'1'`. `normalizeStalkerSeriesFlag()`
delegates to it and produces the normalized positive marker `true` or
`undefined`. The activity normalizer in shared interfaces keeps its
dependency-neutral equivalent for favorites/recent and dashboard
classification. Preserve all three modes: regular `/series`, VOD with embedded
`series[]`, and lazy Ministra VOD `is_series`.
Favorites/recent preserve the raw flag and VOD origin so reopening still uses
the correct lazy or embedded mode. Keep quick-start translation parameters and
the naturally ordered season fallback when `season_number` is absent.
Favorites/recent preserve the normalized positive marker and VOD origin so
reopening still uses the correct lazy or embedded mode. Keep quick-start
translation parameters and the naturally ordered season fallback when
`season_number` is absent.
Lazy episodes use a deterministic tracking ID scoped by parent series,
provider episode, season key, and episode number. `legacyTrackingId` is only a
@@ -116,6 +116,14 @@ Do not copy the full detail-view stylesheet into feature libraries. Add shared
layout changes to the mixin, and keep provider-specific differences explicit in
the wrapper file that includes it.
## Electron Drag Regions
Every interactive descendant of a drag region—including buttons, links,
inputs, overlays, and resize handles—requires `app-region: no-drag`. The shared
directive-generated `.resize-handle` does not set this centrally yet. Until
that debt is fixed, consumers in drag regions must cover the handle themselves
and must not assume it already opts out.
## Channel List Item
The shared row should be reused instead of rebuilding channel markup per view.
+6 -5
View File
@@ -268,11 +268,12 @@ list:
Stalker has multiple real-world data shapes. The current implementation supports all three:
Within Stalker portal data access and feature code,
`isStalkerSeriesFlag()` is the sole interpretation of `is_series`. The
dependency-neutral activity normalizer in `libs/shared/interfaces` preserves
the same closed set for dashboard records: boolean `true`, numeric `1`, or
string `'1'`. Unsupported values do not by themselves classify a VOD item as a
series.
`isStalkerSeriesFlag()` is the canonical predicate for `is_series`.
`normalizeStalkerSeriesFlag()` delegates to it and produces the normalized
positive marker `true` or `undefined`. The activity normalizer in
`libs/shared/interfaces` keeps its dependency-neutral equivalent for dashboard
records. Both accept the same closed set: boolean `true`, numeric `1`, or string
`'1'`. Unsupported values do not by themselves classify a VOD item as a series.
1. Regular Series (`/series`):
@@ -5,11 +5,31 @@ import {
createStalkerInfo,
createStalkerInlineDetailState,
createStalkerDetailViewState,
isStalkerSeriesFlag,
normalizeStalkerFavoriteItem,
normalizeStalkerSeriesFlag,
toggleStalkerVodFavorite,
} from './stalker-vod.utils';
describe('stalker-vod.utils regressions', () => {
describe('Stalker series flag contract', () => {
it.each([true, 1, '1'])(
'accepts %p and normalizes it to the positive marker',
(value) => {
expect(isStalkerSeriesFlag(value)).toBe(true);
expect(normalizeStalkerSeriesFlag(value)).toBe(true);
}
);
it.each([false, 0, '0', 'true', null, undefined, {}, []])(
'rejects unsupported value %p',
(value) => {
expect(isStalkerSeriesFlag(value)).toBe(false);
expect(normalizeStalkerSeriesFlag(value)).toBeUndefined();
}
);
});
it('routes embedded series[] items to series view state', () => {
const state = createStalkerDetailViewState(
{
@@ -54,10 +54,7 @@ export function isStalkerSeriesFlag(value: unknown): boolean {
export function normalizeStalkerSeriesFlag(
value: unknown
): StalkerSeriesFlag | undefined {
if (value === true || value === 1 || value === '1') {
return true;
}
return undefined;
return isStalkerSeriesFlag(value) ? true : undefined;
}
export function isStalkerSeriesItem(item: {
+4 -2
View File
@@ -1,7 +1,9 @@
// Shared content grid and card styles
//
// Usage (after adding libs/ui/styles to stylePreprocessorOptions.includePaths):
// @use 'content-grid' as grid;
// Choose a relative @use path from each consuming stylesheet. A current
// portal-shared component uses its local forwarding module as shown below;
// direct consumers of this partial may need a different depth.
// @use '../../styles/content-grid' as grid;
// @include grid.content-grid;
// @include grid.content-card;
+4 -2
View File
@@ -4,8 +4,10 @@
// Include this mixin in a component's SCSS to get the shared flex host+sidebar
// skeleton, then add only the component-specific rules underneath.
//
// Usage (after adding libs/ui/styles to stylePreprocessorOptions.includePaths):
// @use 'portal-layout' as portal;
// Choose the relative @use path from each consuming stylesheet. Current portal
// live layouts use the example below; other consumers may need a different
// depth.
// @use '../../../../../../ui/styles/portal-layout' as portal;
// @include portal.live-layout;
@mixin live-layout {
+4 -2
View File
@@ -1,8 +1,10 @@
// ─── Shared sidebar + sidebar-header styles ───────────────────────────────────
// Plain CSS rules (not a mixin) used for M3U/legacy sidebar layouts.
//
// Usage (after adding libs/ui/styles to stylePreprocessorOptions.includePaths):
// @use 'portal-sidebar'; (no alias needed — no exported members to call)
// Choose the relative @use path from each consuming stylesheet. Current portal
// live layouts use the example below; other consumers may need a different
// depth. No alias is needed because this partial exports no callable members.
// @use '../../../../../../ui/styles/portal-sidebar';
.sidebar {
// Width is controlled by the resizable directive