Conversation
Close Scorecard checks failing on the Angular lib so it matches the React analog (7.5/10): - Add top-level `permissions: read-all` to ci.yml and codeql.yml so jobs run with least-privilege GITHUB_TOKEN (Token-Permissions 0 -> 10) - Pin all GitHub Actions to commit SHA across ci.yml, codeql.yml and scorecard.yml; Dependabot already tracks github-actions and will bump pins automatically (Pinned-Dependencies 4 -> 8+) - Extend package.json `overrides` with hono >=4.12.21 and qs >=6.15.2 to close five GHSA advisories surfaced by `npm audit` via the Vite toolchain (Vulnerabilities 5 -> 9+) - Add SECURITY.md describing private disclosure channels (GitHub Security Advisories + email) and response SLA (Security-Policy 0 -> 10) - Add CONTRIBUTING.md with the PR workflow, test commands and Conventional Commits style used throughout the repo - Add scripts/setup-branch-protection.sh: one-shot, idempotent `gh api` call that enables required PR review + status checks (Unit tests, Build library and demo, CodeQL) and blocks force-push and deletion on `main` (Branch-Protection 0 -> ~8 after running it) Maintained will recover automatically once the repo crosses 90 days; Packaging and Signed-Releases stay N/A until the first npm publish workflow is added with `--provenance`. Verification: `npm audit` reports 0 vulnerabilities, all 315 unit tests pass, `ng build seatmap-lib` succeeds, and all three workflow YAMLs parse cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… 'n' Reviewer found two leftover divergences after the previous parity pass: - passengerTypes was still double-wrapped — service.ts:126 wrapped the already-array onlyForPassengerType into another array, producing [["ADT","CHD","INF"]]. React (service.js:100-103) assigns the value through verbatim, so we now do the same and correct the public type IAvailabilityItem.onlyForPassengerType to string[] (the single-string shape was a relic — every real caller passes an array). - rotation was being dropped from the emitted payload. The previous "drop the field" choice matched the wrong React seat (an unrotated 70E whose rotation also happened to be absent in that snapshot). React's Seat fixtures and consumer code expect the field present with 'n' (north / no-rotation) as the default. _mapRotation now passes through any known direction and normalises unknown/empty input to 'n'; _prepareSeatForEmit emits the rotation alongside other public fields. TSeatRotation gains 'n' alongside the legacy ''. The empty-string variant is still allowed for backward compat with already-prepared seats, but the public emit path always normalises '' → 'n'. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ity proof Reflects the follow-up fix in 83fc194: - passengerTypes must be a flat string[] (guards against the legacy [["ADT","CHD","INF"]] nesting from the availability merge bug) - rotation is now expected to be present with 'n' as the default (was previously asserted absent — that earlier check matched the wrong React seat and missed the actual contract) Overlay panel now prints both checks; screenshots regenerated. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The leading docblock still claimed `rotation` was no longer emitted — out of sync with the actual contract (rotation present, defaults to 'n'). Comment-only fix, no behaviour change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…ct parity) setAvailabilityHandler used to ignore additionalProps, the tooltip's amenities getter only read seat.features, and prepareSeatAdditionalProps was dead code with no production caller — so integrator-defined extra rows (e.g. "Priority boarding", "Free Wi-Fi") silently disappeared. React stitches them after the API amenities, capped at 12, and emits the same list on tooltipRequested. Changes: - setAvailabilityHandler concatenates entry+wildcard additionalProps (entry first, matching service.js:104-119) and runs them through prepareSeatAdditionalProps before attaching to the seat. - Tooltip's amenities getter appends additionalProps after the API features and slices at 12 (DEFAULT_FEATURES_RENDER_LIMIT). The hiddenSeatFeatures filter applies only to API features, mirroring React's TooltipGlobal.js#finalListOfFeatures. - prepareSeatAdditionalProps now sets title='' (not null) so the tooltip's negative-amenity strikethrough doesn't fire on integrator rows, and assigns a fresh uniqId for @for ... track stability. - Tooltip template's title fallback flips from `??` to `||`, so an empty-string title still falls back to value. Negative amenities (title === null) keep their existing styling. - Types: TSeatAvailability.additionalProps now declares the real {label, icon?, cssClass?} shape (was the stale {type, cssClass}); ISeatFeature gains an optional cssClass. Tests: 4 new preparer cases, 4 new setAvailabilityHandler cases, 4 new tooltip-amenities cases (combined list, no negative styling on additionalProps, 12-cap, hiddenSeatFeatures scope). New e2e spec at projects/seatmap-demo/e2e/additionalProps asserts the tooltip text, the tooltipRequested.seat.additionalProps payload shape, and a parity-proof screenshot. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…lyForPassengerType The previous fix (9dee1e8) flattened how `source.onlyForPassengerType` is assigned to `seat.passengerTypes`, but the only regression coverage lived in an e2e parity-proof that exercises the *emitted* payload (where the default `['ADT','CHD','INF']` short-circuits the bug). The actual disable path — `isSelectDisabled` reads the internal seat record's `passengerTypes` — had no unit guard against the `[["ADT","CHD","INF"]]` nesting. This regressed in a reproducible way: with the user's availability (seat `20E` carrying `onlyForPassengerType: ['ADT','CHD','INF']`) and an ADT next-passenger, the Select button greyed out because `passengerTypes.includes('ADT')` was checking a nested array. Adds two unit tests on `setAvailabilityHandler`: - flat shape after a single pass - flat shape after a second, idempotent pass (mirrors the real ngOnChanges sequence where availability lands twice during demo init) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…eact parity) Block built-in passenger management from removing or re-seating a passenger marked `readOnly: true`: - IPassenger gains an optional `readOnly?: boolean` field; the existing inline cast on the click path is dropped. - Tooltip's "Unselect" button renders with `[disabled]` when the occupant is readOnly, mirroring React's TooltipGlobal.view.js:133. - onTooltipUnselect early-returns on a readOnly occupant — defence-in-depth for viewOverride consumers and the side-panel delegation path that bypass the DOM disabled attribute. - getNextPassenger skips readOnly entries so a seatless readOnly passenger never bids for the next free seat, matching React's service.js:198. Coverage: - unit: tooltip disables Unselect and swallows the click; seat-map's onTooltipUnselect is a no-op on readOnly; service.getNextPassenger skips readOnly seatless passengers. - e2e: parity-proof spec in projects/seatmap-demo/e2e/readOnly/ with a close-up of the disabled vs enabled button block for visual diffing. README's IPassenger interface example now lists `readOnly?: boolean` so the documented type matches the published one. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tip (React parity) When a seat is reserved for a subset of the default passenger types (ADT/CHD/INF), the React tooltip shows a line like "The seat is only for: adults" under the header (TooltipGlobal.js:147-154, shared slot with `passengerLabel`). The Angular built-in tooltip rendered the `passengerLabel` slot but never showed the restriction text — so seats with `availability.onlyForPassengerType: ['ADT']` looked identical to unrestricted seats. Reuse the existing `jets-tooltip--header-passenger` slot: when no passenger occupies the seat, render the localized restriction line derived from `seat.passengerTypes`. Filtering, the "only if narrower than default" guard, and unknown-type drop mirror React exactly. Locale keys (`ADT`/`CHD`/`INF`/`seatRestrictions`) already existed in `constants.ts` for every supported language. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…s (React parity) The built-in tooltip wrapped every amenity glyph in a 1-px box derived from `--tooltip-icon-border` (default `#4f6f8f`) and `--tooltip-icon-bg`. React only applies those CSS vars to the dimensions block (`.jets-tooltip--measurement` — Pitch/Width/Recline), never to the feature list (`.jets-tooltip--feature`), which renders SVGs as bare glyphs (TooltipGlobal.view.js:73-91 vs :97-119). Drop `border` and `background` from `.jets-tooltip--amenity-icon`. Size, flex layout, and the negative-amenity color override are unchanged. Dimensions box styling is unaffected. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ed (React parity)
`onSeatMapInited` payload was emitting `availabilityData = this.availability`
(the integrator-supplied Input that controls per-seat status/colour). React
emits something different there: the read-only `{ availableSeats: [...] }`
block that the Quicket API itself returns as a sibling element of the
response array (api.js:101-104). The two shapes are unrelated — integrators
relying on the React contract were getting Angular's per-seat override
array under the same key.
Changes:
- jets-seat-map-api.service.ts: after array merging, look for an element
with `id === 'availabilityData'` and copy the rest of its fields onto
`response.availabilityData` (marker `id` stripped, matching React).
- jets-seat-map.service.ts: forward `apiResponse.availabilityData` through
`getSeatMapData`'s return shape.
- jets-seat-map.component.ts: replace `availabilityData: this.availability`
with `result.availabilityData` in the seatMapInited payload; comment
explains the distinction so the Input/payload conflation doesn't
resurface.
- types.ts: add `ISeat` + `IAvailableSeatsData` (mirroring React's public
types); change `IInitialLayoutData.availabilityData` from
`TSeatAvailability` to `IAvailableSeatsData`; add the field to
`IApiSeatmapResponse` so the service layer is typed end-to-end.
- spec updates: api.service.spec.ts gains two cases (marker present /
absent); component.spec.ts rewrites the misleading "mirroring the
Input" assertion to prove payload sources `availabilityData` from
the API result and that the Input does not leak through.
- .gitignore: `.env.local*` so per-task credential overrides (used to
hit sandbox v1.1 for this fix's e2e parity proof) stay out of git.
- Refreshed `seatMapInitedParity-overlay.png` capturing the real
sandbox payload with `availabilityData.availableSeats` populated.
Verification: 341 unit tests pass (was 315 — 2 new in api.service spec,
1 rewritten in component spec); ng build seatmap-lib clean;
`seatMapInitedParity` e2e green against sandbox v1.1.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…TY pass (React parity) `setAvailabilityHandler` only assigned `passengerTypes` when the seat didn't already have one — so after the first SET AVAILABILITY, repeat calls silently kept the stale list. A second pass tightening the rule from `['ADT','CHD','INF']` to `['ADT','CHD']` (or relaxing it back) left the tooltip restriction line empty and `isSeatSelectDisabled` gated against the wrong whitelist. Manifested as "works only once." React (jets-seatmap-react-lib-pub/src/components/SeatMap/service.js:100-103) overwrites on every pass: entry → wildcard → `DEFAULT_SEAT_PASSENGER_TYPES`. Mirror that exactly. Add three regression specs: replace-on-rerun, default-when-omitted, wildcard-fallback. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous run baked the screenshots against a stale `ng serve` process that was holding a pre-9dee1e8 bundle in memory (the commit that restored the `rotation: 'n'` default to the seat-event payload). With the dev server re-launched and `dist/seatmap-lib` rebuilt from scratch the spec passes and the captured payload now correctly shows `"rotation": "n"` alongside the flat `passengerTypes: ['ADT','CHD','INF']` it always did. No library code changes — this is purely a baseline refresh. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… parity)
When the seatmap API call fails, the lib now still fires `seatMapInited`
with an `error` field and `undefined` layout fields — mirroring React's
`onSeatMapInited` payload shape. Previously only `loadError` was emitted,
so consumers wiring only `onSeatMapInited` (the React-equivalent contract)
silently lost errors.
The error string is formatted as `postData: {status} - {message}` (React
parity), preferring the HTTP body's `message` over the generic transport
message so callers see the actual backend validation failure.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…(React parity)
The Quicket API returns HTTP 200 with `[{id, error: "…"}, …]` for some
flight-level failures (e.g. invalid departure IATA returns "schedule is
not found for the flight"). Previously _postSeatmap silently picked
`rawResponse[0]` (the matching error object — no decks, no seatDetails)
and the component's catch never fired, so the seatMapInited payload
shipped with `error: undefined`. The companion fix in 62a4e4c surfaces
catch-path errors via seatMapInited, but only HTTP transport failures
ever reached that catch — soft errors still slipped through.
Mirrors React `api.js:80-83` which detects the item with `id === flight.id`
and `throw new Error(item.error)`. The thrown Error now propagates into
the component's catch and lands in `seatMapInited({error})` like any
other failure.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…-sourced
Hits the Quicket sandbox on EK 2 LHR→DXB (the flight whose response is
known to carry the `{ id: 'availabilityData', availableSeats: [...] }`
marker block) and feeds the lib's `availability` Input a deliberately
unfakeable value (`currency: '__INPUT__'`, `price: -42`,
`label: '__INPUT_ONLY_LABEL__'`, `color: '#deadbe'`). The captured
`seatMapInited.availabilityData` payload then has to:
- be shaped as `{ availableSeats: [...] }` (API contract), not as the
Input's `Array<{ label, price, currency, ... }>` shape;
- carry no seat whose currency/price/label/color matches any Input
marker (no merge);
- not deep-equal the Input array as a whole.
Locks down the contract pointed out in the task spec ("availabilityData
is read-only, should contain server response data, nothing should be
merged there") so a future refactor cannot silently route the Input back
into the payload.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Setting `currentDeckIndex` to a value outside [0, content.length) collapsed `visibleDecks` to an empty array, leaving just the fuselage outline on screen. Guard the assignment in `ngOnChanges` so an invalid integrator value is silently ignored and the previously active deck stays rendered. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…act parity)
Tooltip rendered `amenity.title || amenity.value`, so the localized
`audioVideo`/`wifi`/`power` label always won over the backend `summary`
field. React's TooltipGlobal.view.js:89 renders `{value}` directly,
where the cabin-level summary lands via `_mergeCabinFeatures`
(data-preparer.js:87-107). New helper `amenityText()` returns `value`
when it's a non-empty string/number, falling back to `title` only for
the `value=true` boolean-flag case.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…tangle JetsWingComponent drew the wing as a plain rectangle with a 6 px CSS clip-path triangle pasted at the top to fake a leading edge. At the typical 1:45 wing aspect ratio that notch was nearly invisible and the wing had no trailing edge at all — leading to a "wrong shape" report from a custom-theme consumer. Replace the rectangle + clip-path overlay with a single SVG path that draws the actual swept-back parallelogram silhouette (sloped leading AND trailing edges, slope ≈ tan(30°) · wingWidth, capped at 40 % of wing height so the wingtip stays visible). Tie the viewBox height to wingHeight so 1 viewBox unit ≈ 1 rendered pixel — without this, any sweep offset specified in viewBox units would be vertically stretched by the path's aspect ratio. Refresh the visibleWings / colorTheme Playwright baseline screenshots to reflect the new silhouette.
…cales Angular's LOCALIZATION_* maps diverged from the React lib's verified strings (e.g. DE pitch was 'Sitzabstand', React uses 'Abstand'; RU cancel was 'Отмена', React uses 'Закрыть'). Shared keys across EN/RU/ CN/DE/FR/ES/IT/PT/PT-BR/AR/JA/KO/TR/NL/PL/CS/UK/VI now mirror React's i18n.languages.js verbatim, including the `(%s)` format markers that React ships for power/audio in PL/ES/DE. Angular-only keys (legend, gallery, status labels, deprecated power/USB synonyms, *_legroom snake_case) are preserved. `pitchShort`/`widthShort`/ `reclineShort` are dropped — React's `pitch`/`width`/`recline` are already the short form, so the preparer's locale fallback is simplified to a single key. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The spec was anchored to flight EK 2 LHR→DXB (id "1111") because the
Quicket sandbox used to surface the `{ id: 'availabilityData',
availableSeats: [...] }` sibling element for that flight. The sandbox no
longer emits it, so the assertion `payload.availabilityData != null`
started failing on CI — not a lib regression, the upstream contract
moved.
Intercept the seatmap POST with page.route() and append a fixed
`availabilityData` block to whatever the API returns. The mocked
seats use plain USD/EUR currencies and positive prices so they can
never collide with the integrator's INPUT_MARKER values that the
leak-detection assertion looks for.
Also refresh the eventPayloadParity baseline screenshots — incidental
diffs from the new wing silhouette landed in 6993fa8.
`onSeatSelected/onSeatUnselected` returned `passenger.seat = { price: <raw
number>, seatLabel }` and silently dropped both `currency` and `priceValue`.
React's contract (service.js:44-63) ships the formatted price string
(`"USD 33"`), the numeric `priceValue`, and `currency` alongside `seatLabel`,
because its `setAvailabilityHandler` had already rewritten the seat record.
Angular keeps the internal `seat.price` numeric and never sets `priceValue`,
so the parity is produced at the moment of selection: a small helper
promotes `seat.{price,currency}` into the public payload, mirroring the
same formatting `_prepareSeatForEmit` uses for `tooltipRequested.seat`.
Fields with no source value are omitted instead of leaking `undefined`.
`IPassenger.seat` widens accordingly: `price` becomes `string | number`
(string for lib output, number tolerated for integrator-provided fixtures),
`currency?: string` and `priceValue?: number` join the shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… body `getDeckCabinLabelGap` returned 0 for any full-width deck, so the cabin label was pinned at the deck-floor edge regardless of the consumer's `colorTheme.cabinTitlesWidth`. Bumping the value from 80 → 200 moved nothing on screen; this regressed React parity where the same knob clearly opens up the gap between the label and the body. Sum a `cabinTitlesWidth*scale - LABEL_WIDTH - OUTER_INSET` push on top of the existing narrow-deck adjustment. The clamp at zero means a small `cabinTitlesWidth` (or one dominated by a larger `wingsWidth`) still hugs the body — only growth past the label's own width opens the gap. Refresh the colorTheme / visibleCabinTitles baseline screenshots that re-rendered with the now-shifted label position.
React parity (jets-seatmap-react-lib-pub PR #171, commit 7e0e30f): register `'FR-CA'` in `LOCALES_MAP`, add `'FR-CA'` to the `TLang` union, and ship `LOCALIZATION_FR_CA` with React's verbatim `LOCALE_FR_CA` strings for the shared keys (e.g. `pitch`/`recline` → `'Inclinaison'`, `bassinet` → `'Moïse disponible'`, `nearGalley` → `'Près de l’office'`). Angular-only keys (status labels, `legend`/`gallery`/`loading`, the snake_case `restricted_legroom`/`extra_legroom` synonyms) reuse the closest LOCALIZATION_FR string so the dict matches the shape of every other Angular locale and the runtime fallback chain stays identical. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…r/icon/label
`prepareSeatAdditionalProps` already carries the integrator-defined
`availability[].additionalProps[].cssClass` onto each `ISeatFeature`, but
the tooltip template never bound it to the DOM, so the documented contract
("container, icon and label classes from a single `cssClass`") was dead.
Wire `cssClass`, `cssClass-icon`, `cssClass-label` onto the three nodes so
host stylesheets can target them as the README promises.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The swept-trapezoid path from 6993fa8 sloped BOTH the leading and the trailing edge by `sweep` units, producing a true parallelogram. The React reference only slants the top (leading) edge; the trailing edge runs flat across the wing chord. Drop the `-s` offset on the inner-bottom vertex in both wing paths: LEFT M60,0 L0,s L0,h L60,h Z (was L60,h-s) RIGHT M0,0 L60,s L60,h L0,h Z (was L0,h-s) Refresh affected colorTheme / visibleWings baseline screenshots.
…dimension tile Previously these two color-theme fields styled only the small inner glyph box of each measurement (.jets-tooltip--dim-icon). React parity: TooltipGlobal.view.js:99-101 applies them to the whole .jets-tooltip--measurement tile, with tooltipIconColor remaining the SVG line color. Swap the CSS-var targets so the outer .jets-tooltip--dimension consumes --tooltip-icon-border / --tooltip-icon-bg, and drop them from the inner block. Side effect (also React parity): tooltipBackgroundColor no longer bleeds into dimension tiles; tooltipFontColor no longer recolors tile borders. Affected baselines regenerated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Production rule: console.log and console.error are stripped. Library keeps the 401-retry console.warn (diagnostic, not in the deletion list). Demo event handlers continue to surface activity via the in-app EVENT LOG panel (addLog), so the console duplicates are pure noise. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Rename to @seatmaps.com/angular-lib (mirrors React lib scope). - Add description, keywords, repository, author, license (Apache-2.0), bugs, homepage, publishConfig.access=public. - Include LICENSE in tarball via ng-package assets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
ng-packagr forbids assets paths outside project root, so revert the ng-package assets entry and copy LICENSE into dist/seatmap-lib after ng build via a cross-platform node copyFileSync step. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
11fdef7 stripped console.* across the repo, but the production policy ("to be deleted in production: console.log, console.error") applies strictly to the published library, not to the dev demo. Restore the demo's debug output so a developer running `npm start` sees the same event firehose in DevTools and in the in-app EVENT LOG panel. The library stays unchanged (only the 401 retry `console.warn` lives there).
…onent Verifies that 4 independent JetsSeatMapComponent instances can coexist on a single page without bleed between `providedIn: 'root'` services. Adds a fixture component (2x2 grid, 2 flights x 2 cabin classes, distinct themes and one horizontal variant), wired via `?multiInstance=1` query-param so the main demo UX is untouched. Playwright spec asserts independent inited payloads and produces a full-page screenshot under `e2e/multiInstance/screenshots/four-instances.png`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t (React parity)
Previously, fields from `config.apiMetadata` were spread onto the top
level of the seatmap request body, so a config like
`apiMetadata: { PROPRIETARY_KEY: 'PROPRIETARY_VALUE' }` produced a
payload with `PROPRIETARY_KEY` at the root. React (api.js:64) instead
sends `metadata: this._apiMetadata ?? undefined`, keeping proprietary
fields under their own `metadata` object and omitting the key entirely
when no metadata is configured.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…blishing npm Trusted Publishing requires `provenance: true` so `npm publish` generates a Sigstore-signed attestation that ties the published tarball to the GitHub Actions run that built it. Setting it under publishConfig keeps the contract with the package rather than relying on CLI flags; the CI workflow still passes `--provenance` explicitly as defence in depth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds a release-triggered workflow that publishes @seatmaps.com/angular-lib to npm using OIDC trusted publishing instead of a long-lived NPM_TOKEN. The job runs in a protected `npm-publish` environment (manual approval), verifies that projects/seatmap-lib/package.json version matches the release tag, upgrades npm to >= 11.5.1 (required for trusted publishing), runs a guard against SEATMAP_API_*/APP_ID/APP_KEY/env-config.js leaking into the built artifact, and publishes from dist/seatmap-lib with --provenance --access public. NPM_TOKEN is intentionally not referenced; it stays in repo secrets as a manual fallback until the first successful OIDC release and will be revoked afterwards. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Two independent coordinate systems were colliding in the renderer: rows flow with a CSS margin-top derived from `(row.topOffset - prev) * scale - prevRowHeight`, while bulks are absolutely positioned at `bulk.topOffset * scale + topAdjust`. When the API places a row whose native height exceeds the slot between its topOffset and the neighbouring bulk's topOffset (e.g. UA953 ORD->MUC row 5 over the fish lavatory, row 22/30 over the cabin divider), the seat visually bites into the partition. The preparer now post-processes deck.extras.bulks via `_resolveBulkOverlaps`: for every bulk whose native bbox actually overlaps a row above or below, push the top down / trim the bottom by the overlap plus a small configurable gap. Adjustments that would collapse a bulk below 8 native units or 20% of its original height are silently skipped. The pass is a strict no-op when no overlap is detected, so already-correct layouts pass through unchanged. New `IConfig.partitionGap` (default 4 native units, ~2-3 px at typical deck scale) is the public knob; pass 0 to opt out entirely and use raw API coordinates verbatim. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
INSTRUCTIONS_FOR_ADMIN.md walks a repo/npm admin through the one-time setup (npm-publish GitHub Environment, npm Trusted Publisher config), the per-release flow (create GitHub Release, approve workflow gate, verify), and the post-release NPM_TOKEN revoke. Kept at the repo root so it surfaces in `gh repo view` and PR file lists. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
nexus
approved these changes
Jun 10, 2026
8 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Re-opens #16 from the new
devbranch (workflow change: all work flows throughdev).This PR has accumulated everything on
devsince the previous merge tomain— primarily two distinct workstreams (OpenSSF Scorecard hardening and npm Trusted Publishing) on top of routine React-parity / locale / e2e fixes already reviewed at commit time. Headline scope below.Security hardening — OpenSSF Scorecard (4.4 → ~7.0, matches the React analog at 7.5/10)
permissions: read-allinci.ymlandcodeql.yml(Token-Permissions 0 → 10)ci.yml,codeql.yml,scorecard.yml; Dependabot already tracksgithub-actionsand will bump pins automatically (Pinned-Dependencies 4 → 8+)package.jsonoverridesextended withhono >=4.12.21andqs >=6.15.2— closes 5 GHSA advisories surfaced via the Vite toolchain (Vulnerabilities 5 → 9+)SECURITY.mdwith private disclosure channels (GitHub Security Advisories + email) and response SLA (Security-Policy 0 → 10)CONTRIBUTING.mdwith the PR workflow, test commands and Conventional Commits style already used in the reposcripts/setup-branch-protection.sh— one-shot, idempotentgh apicall enabling required PR review + status checks (Unit tests, Build library and demo, CodeQL) and blocking force-push/deletion onmain(Branch-Protection 0 → ~8 after running it)Maintainedrecovers automatically once the repo crosses 90 days.npm Trusted Publishing — closes the remaining
Packaging/Signed-Releasesgapprovenance: trueadded toprojects/seatmap-lib/package.json→publishConfig, sonpm publishattaches a Sigstore-signed attestation (Signed-Releases N/A → 10 after first OIDC release).github/workflows/publish.yml: triggered by a published GitHub Release, runs in a manual-approvalnpm-publishGitHub Environment, upgrades npm to ≥ 11.5.1, verifiesprojects/seatmap-lib/package.jsonversion against the release tag, guardsdist/seatmap-lib/againstSEATMAP_API_*/APP_ID/APP_KEY/env-config.jsleaking into the published artifact, andnpm publish --provenance --access publicNPM_TOKENdeliberately not referenced — workflow uses short-lived OIDC; old token is to be revoked after the first successful releaseINSTRUCTIONS_FOR_ADMIN.mdat the repo root walks an admin through the one-time setup (GitHub Environment + npm Trusted Publisher) and the per-release flowTest plan
npm audit→ 0 vulnerabilities (was 5)npm test→ 315 unit tests passnpm run build:lib→ Angular package builds cleanly.github/workflows/publish.ymlYAML validates withjs-yamldist/seatmap-lib/(README/LICENSE excluded)./scripts/setup-branch-protection.shfrom a repo-admin workstationgh workflow run "Scorecard supply-chain security"and verify new score at https://api.scorecard.dev/projects/github.com/Kwiket/jets-seatmap-angular-libINSTRUCTIONS_FOR_ADMIN.mdfor the first OIDC release of@seatmaps.com/angular-lib