Skip to content

Feat(components): Add 5 new OpenBridge components (Speedometer, True-relative, Long-lat (two versions), GNSS Skyplot) - #944

Open
asijah2 wants to merge 28 commits into
Ocean-Industries-Concept-Lab:developfrom
asijah2:Jotron-Skipper
Open

Feat(components): Add 5 new OpenBridge components (Speedometer, True-relative, Long-lat (two versions), GNSS Skyplot)#944
asijah2 wants to merge 28 commits into
Ocean-Industries-Concept-Lab:developfrom
asijah2:Jotron-Skipper

Conversation

@asijah2

@asijah2 asijah2 commented Jun 5, 2026

Copy link
Copy Markdown

This proposal introduces several new components that could be included in the official OpenBridge component library.

Components

  • Speedometer
  • True-Relative
  • Long-lat (two versions)
  • GNSS Skyplot

Motivation

These components are commonly used in maritime applications and follow the OpenBridge design language. Including them in the official library would improve consistency across projects and reduce the need for custom implementations.

Benefits

  • Reusable maritime-specific components
  • Consistent OpenBridge styling and behaviour
  • Reduced development effort for future projects
  • Improved coverage of navigation and positioning use cases

I would be happy to contribute the implementation, documentation, stories and tests for these components.

Summary by CodeRabbit

Release Notes

  • New Features
    • Added GNSS skyplot with clickable satellite markers.
    • Added speedometer, long/lat, environmental, true-relative, vessel view, and logging graph components.
    • Enhanced angle-based guidance, current-speed indicators, and wind icon selection.
  • Bug Fixes
    • Improved read-only input behavior, formatting, styling, and focus handling.
    • Fixed keyboard capitalization and value consistency.
  • Style
    • Accordion cards now use full width with tighter expanded spacing.
  • Documentation
    • Added Storybook stories and controls for new components.
  • Chores
    • Pinned development container feature versions.

@coderabbitai

coderabbitai Bot commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds shared skipper instrument types and SVG helpers, new LitElement components for speed, long-lat, GNSS skyplot, true-relative, vessel views, and logging graphs. Adds Storybook coverage and package exports. Updates keyboard casing, readonly inputs, accordion styling, and devcontainer lock data.

Changes

Skipper instruments and supporting infrastructure

Layer / File(s) Summary
Shared types and SVG utilities
packages/openbridge-webcomponents/src/skipper/interfaces.ts, models.ts, tickmark.ts, advice.ts, current-icons.ts, wind-icons.ts, utils/*
Adds shared contracts, colors, advice masks, tickmarks, indicator icons, UUID generation, and Storybook argument mapping.
Speedometer gauge component
packages/openbridge-webcomponents/src/skipper/speedometer/*
Adds the speedometer with dual speed needles, alert masks, tickmarks, value display, styles, and Storybook variants.
Long-lat speed instruments
packages/openbridge-webcomponents/src/skipper/long-lat/*
Adds standard and environmental instruments with speed arrows, sensor positioning, rotation, vessel trails, styles, and stories.
GNSS skyplot
packages/openbridge-webcomponents/src/skipper/gnss-skyplot/*
Adds satellite plotting, constellation filtering, strength tiers, spoofing and jamming overlays, click events, styles, and a Storybook story.
True-relative instrument
packages/openbridge-webcomponents/src/skipper/true-relative/*
Adds direction-based rotation, navigation overlays, animated rotation bars, styles, and Storybook variants.
Vessel view rendering
packages/openbridge-webcomponents/src/skipper/vessel-view/*
Adds side-view, top-down, and combined vessel SVG rendering with sensor, CCRP, dimensions, vessel types, styles, and stories.
Logging graph
packages/openbridge-webcomponents/src/skipper/logginggraph/*
Adds range calculation, SVG data series, legends, point selection, edit events, styles, and stories.
Barrel exports
packages/openbridge-webcomponents/src/skipper/index.ts
Re-exports the new skipper modules.

Input, keyboard, and layout updates

Layer / File(s) Summary
Keyboard and readonly input behavior
packages/openbridge-webcomponents/src/components/keyboard-full/*, packages/openbridge-webcomponents/src/components/number-input-field/*, packages/openbridge-webcomponents/src/components/text-input-field/*, packages/openbridge-webcomponents/src/mixins/base-input-field.css
Resolves keyboard casing before insertion. Preserves readonly number formatting, hides readonly text clear buttons, applies readonly classes and styles, and adds tests and stories.
Accordion and devcontainer updates
.devcontainer/devcontainer-lock.json, packages/openbridge-webcomponents/src/components/accordion-card/accordion-card.css
Updates the Git feature lock metadata. Changes accordion width, spacing, description flex sizing, and alert stacking.

Estimated code review effort: 5 (Critical) | ~120 minutes

Possibly related PRs

Suggested labels: Instrument Component, enhancement

Suggested reviewers: jon-daeh

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 5.88% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: adding five maritime OpenBridge components, including Speedometer, True-relative, Long-lat variants, and GNSS Skyplot.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch Jotron-Skipper
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 14

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🟡 Minor comments (9)
packages/openbridge-webcomponents/src/skipper/tickmark.ts-70-70 (1)

70-70: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove inline comment in TS source.

Line 70 adds an inline comment in a .ts module where comments are disallowed by repo rules.

As per coding guidelines, "**/*.{js,ts,tsx,jsx}: Do not include comments in code whatsoever unless the code is extremely unusual and impossible to understand without explanation`."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/tickmark.ts` at line 70, Remove
the inline comment "// check if scale is not infinite" from the TypeScript
module in packages/openbridge-webcomponents/src/skipper/tickmark.ts; locate that
exact comment text in the tickmark.ts source and delete the comment line so the
file contains no inline comments (do not replace with other comments or disable
rules).
packages/openbridge-webcomponents/src/skipper/tickmark.ts-71-73 (1)

71-73: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Handle NaN in scale validation.

Line 71 rejects Infinity and non-positive values, but NaN still passes and propagates invalid coordinates into SVG output.

Suggested fix
-  if (scale === Infinity || scale <= 0) {
+  if (!Number.isFinite(scale) || scale <= 0) {
     throw new Error('Scale is not valid');
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/tickmark.ts` around lines 71 -
73, The scale validation in tickmark.ts currently only rejects Infinity and
non-positive numbers but allows NaN; update the check around the variable scale
(in the function where scale is validated) to use Number.isFinite(scale) &&
scale > 0 (or equivalently !Number.isNaN(scale) and scale !== Infinity and scale
> 0) and throw the same Error('Scale is not valid') when it fails so NaN no
longer propagates into SVG coordinates.
packages/openbridge-webcomponents/src/skipper/advice.ts-66-68 (1)

66-68: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove commented-out code blocks from this TS module.

These lines leave dead/commented implementation fragments in production source.

As per coding guidelines, "**/*.{js,ts,tsx,jsx}: Do not include comments in code whatsoever unless the code is extremely unusual and impossible to understand without explanation`."

Also applies to: 85-87, 150-160, 171-172

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/advice.ts` around lines 66 -
68, Remove the dead commented-out implementation fragments in the
skipper/advice.ts module (specifically the commented ternary assignments for
mainColor and fillColor that reference AdviceType.caution/warning/alarm and the
other commented blocks at the other noted locations); delete those commented
lines entirely so the file contains no leftover commented code, leaving only
active code and, if necessary, refactor any remaining references to the
variables mainColor/fillColor or AdviceType to use the existing active
implementation paths to keep builds/tests passing.
packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.ts-7-7 (1)

7-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Title Case for the story title path.

INSTRUMENT/LongLatEnvironmental is not Title Case and is likely to fail the story title lint rule. Use a Title Case path (for example, Instrument/Long Lat Environmental).

As per coding guidelines, “Storybook story title and name fields must use Title Case — enforced by ESLint rule openbridge/storybook-title-case (auto-fixable)”.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.ts`
at line 7, The story title string currently uses
"INSTRUMENT/LongLatEnvironmental" which violates the Title Case rule; update the
exported story's title value to a Title Case path such as "Instrument/Long Lat
Environmental" (or similar Title Case segmentation) so it satisfies the
openbridge/storybook-title-case lint rule and Storybook conventions.
packages/openbridge-webcomponents/src/skipper/true-relative/true-relative-styles.ts-20-23 (1)

20-23: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add unit to font-size value.

Line 22 has font-size: 64 without a unit, which is invalid CSS (except for line-height). It should be font-size: 64px or another appropriate unit.

🐛 Proposed fix
   .speed-gauge-value {
     position: relative;
-    font-size: 64;
+    font-size: 64px;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative-styles.ts`
around lines 20 - 23, The CSS rule for the .speed-gauge-value selector in
true-relative-styles.ts uses an invalid unitless font-size ("font-size: 64");
update this to a valid CSS size such as "64px" (or another appropriate unit like
rem/em) so the rule reads font-size: 64px; and ensure any other occurrences in
true-relative-styles.ts follow the same change.
packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.stories.ts-8-8 (1)

8-8: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Title Case for Storybook title.

The title 'INSTRUMENT/TrueRelative' should use Title Case with proper spacing: 'INSTRUMENT/True Relative'.

As per coding guidelines: "Storybook story title and name fields must use Title Case — enforced by ESLint rule openbridge/storybook-title-case (auto-fixable)".

📝 Proposed fix
-  title: 'INSTRUMENT/TrueRelative',
+  title: 'INSTRUMENT/True Relative',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.stories.ts`
at line 8, The Storybook story title currently uses 'INSTRUMENT/TrueRelative' —
update the title property to use Title Case with proper spacing, e.g.
'INSTRUMENT/True Relative'; also check and update any corresponding story "name"
fields in this story file (true-relative.stories.ts) to follow the same Title
Case convention to satisfy the openbridge/storybook-title-case rule.
packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts-46-63 (1)

46-63: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove hardcoded test data from production component.

The satellites property contains hardcoded test data with the comment "this is just for test". Production components should not ship with placeholder data. Either move test data to the stories file or initialize to an empty array.

🧹 Proposed fix
-  `@property`({type: Array}) satellites: SatelliteData[] = [ // this is just for test
-    {id: 13, type: SatelliteType.GPS, strength: 0.8, azimuth: 90, elevation: 45, spoofing: true, jamming: false, spoJamConfirmed: false},
-    ...
-  ];
+  `@property`({type: Array}) satellites: SatelliteData[] = [];

Move the test data to gnss-skyplot.stories.ts default args instead.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`
around lines 46 - 63, The satellites property in gnss-skyplot.ts currently
contains hardcoded test data; remove the inline array and comment and initialize
`@property`({type: Array}) satellites: SatelliteData[] = [] (empty array) so the
production component ships without placeholder data, then move the provided
sample objects into the story file (gnss-skyplot.stories.ts) as the default
args.satellites for storybook tests and examples.
packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.stories.ts-7-7 (1)

7-7: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use Title Case for Storybook title.

The title 'INSTRUMENT/Gnss-Skyplot' violates the Title Case requirement. It should be 'INSTRUMENT/GNSS Skyplot' (GNSS is an acronym and Skyplot should be title-cased as one word or two).

As per coding guidelines: "Storybook story title and name fields must use Title Case — enforced by ESLint rule openbridge/storybook-title-case (auto-fixable)".

📝 Proposed fix
-  title: 'INSTRUMENT/Gnss-Skyplot',
+  title: 'INSTRUMENT/GNSS Skyplot',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.stories.ts`
at line 7, Update the Storybook title string used in the stories file: replace
the current title value `'INSTRUMENT/Gnss-Skyplot'` with the Title
Case-compliant `'INSTRUMENT/GNSS Skyplot'` so the acronym GNSS is uppercase and
"Skyplot" is title-cased; this will satisfy the openbridge/storybook-title-case
ESLint rule for the exported title constant in the gnss-skyplot story module.
packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts-22-22 (1)

22-22: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Correct the typo in satellite type name.

Galileio should be Galileo (the European GNSS constellation).

📝 Proposed fix
-  Galileio = 'Galileio',
+  Galileo = 'Galileo',

Update all references throughout the file (lines 49, 53, 56, 59, 61, 414).

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`
at line 22, Replace the misspelled satellite constellation identifier "Galileio"
with the correct "Galileo" everywhere it appears in this file (update the
enum/member name and all uses, string labels, switch/case checks, mappings and
any references in methods like legend rendering or color/mapping functions);
ensure the identifier casing matches existing enum/style and update all
occurrences of "Galileio" to "Galileo" so type checks and UI labels remain
consistent.
🧹 Nitpick comments (6)
packages/openbridge-webcomponents/src/skipper/wind-icons.ts (1)

89-89: ⚡ Quick win

Remove TODO/dead commented code from TS source.

Lines 89/97/105 and Lines 116–132 keep TODOs and a legacy commented implementation inside runtime code. Please move TODOs to tracking issues and delete the commented-out block.

As per coding guidelines, "**/*.{js,ts,tsx,jsx}: Do not include comments in code whatsoever unless the code is extremely unusual and impossible to understand without explanation."

Also applies to: 97-97, 105-105, 116-132

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/wind-icons.ts` at line 89,
Remove the inline TODOs and the legacy commented implementation in
packages/openbridge-webcomponents/src/skipper/wind-icons.ts (the "// TODO add
the missing speeds..." comment at line ~89 and the commented block around lines
~116–132); file should not contain commented-out code or TODOs per
guidelines—create a tracking issue for the missing speeds and delete the
commented block and any other TODO comments (also remove the comments at lines
~97 and ~105) so the module exports/definitions in wind-icons.ts remain clean
and build/test behavior unchanged.
packages/openbridge-webcomponents/src/skipper/current-icons.ts (1)

5-5: ⚡ Quick win

Remove inline TODO comment from runtime code.

Line 5 violates the repository rule that TS/JS code should not contain comments unless the code is unusually hard to understand. Move this note to a tracking issue/PR task and keep the function body comment-free.

As per coding guidelines, "**/*.{js,ts,tsx,jsx}: Do not include comments in code whatsoever unless the code is extremely unusual and impossible to understand without explanation."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/current-icons.ts` at line 5,
Remove the inline runtime comment "// TODO - check this icon" at the top of
current-icons.ts; delete that TODO from the module (top-level comment near the
exported icon definitions) and instead record the note in your issue tracker or
PR checklist so the runtime code (exports like the current icon
constants/exports) contains no comments per repo rules.
packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.ts (1)

6-7: ⚡ Quick win

Fix the Title Case claim and downgrade the Storybook typing concern

  • meta.title: 'INSTRUMENT/LongLatEnvironmental' already matches the openbridge/storybook-title-case rule (no update needed).
  • Meta<typeof LongLatEnvironmental> / satisfies Meta<LongLatEnvironmental> / StoryObj<LongLatEnvironmental> are self-referential to the exported story constant (there’s only a side-effect import), but the repo uses the same pattern in packages/openbridge-webcomponents/src/skipper/long-lat/long-lat.stories.ts, so treat this as an optional cleanup: import the component type and use that in the generics if you want the typing to target the component class specifically.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.ts`
around lines 6 - 7, The title is already compliant so leave meta.title as
'INSTRUMENT/LongLatEnvironmental'; to address the typing comment optionally
import the actual component class/type and replace the self-referential generics
(Meta<typeof LongLatEnvironmental> and any StoryObj<typeof
LongLatEnvironmental>) with Meta<LongLatEnvironmentalComponent> /
StoryObj<LongLatEnvironmentalComponent> (or whatever the exported component type
is) to target the component class explicitly; otherwise no change is required.
packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts (1)

25-25: 💤 Low value

Remove or resolve the TODO comment.

Line 25 has // TODO: remove next to speedType. If this property is deprecated and should be removed, do so before merging; otherwise, remove the TODO comment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts`
at line 25, The field speedType (declared as `@property`({type: String}) speedType
= SpeedType.SOG) still has a TODO comment; either remove the property if it’s
deprecated or keep it and delete the TODO. Locate the true-relative component
class (TrueRelative / true-relative.ts) and either delete the speedType property
and any usages of SpeedType.SOG, or simply remove the "// TODO: remove" comment
and ensure any external consumers are updated if you keep it; make sure imports
and tests compile after the change.
packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts (2)

114-120: 💤 Low value

Clarify the TODO or remove if designer confirmation is not needed.

The comment "TODO check the right satellite strength" suggests the threshold mapping (0/30/70) may be placeholder. If design confirmation is pending, document the requirement or open a tracking issue; otherwise remove the TODO.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`
around lines 114 - 120, The TODO in mapStrength suggests the thresholds
(0/30/70) are provisional; update the comment to either document the source of
these thresholds or remove the TODO if they are final: inside the private method
mapStrength(value: number): Strength (and alongside the Strength enum), add a
short doc comment explaining why 0, 30, and 70 were chosen or reference a
design/issue ID that defines the thresholds, or simply delete the TODO if no
further confirmation is required; ensure the mapping logic remains unchanged
unless design provides new threshold values.

33-42: ⚡ Quick win

Consider consolidating duplicate interfaces.

Satellite (lines 33-42) duplicates all fields from SatelliteData (lines 8-17). If the two interfaces serve identical purposes, use a single type; if Satellite is a transformed version with computed x/y, consider making Satellite extend or compose SatelliteData.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`
around lines 33 - 42, The Satellite interface duplicates fields from
SatelliteData; either remove the duplicate by reusing SatelliteData where
possible or make Satellite extend SatelliteData (e.g., declare Satellite extends
SatelliteData) and only add the transformed fields (like x, y) or additional
flags; update usages of Satellite and SatelliteData accordingly
(functions/methods referencing Satellite, SatelliteData, and any transformations
inside gnss-skyplot.ts) so there is a single source of truth for the shared
fields.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/openbridge-webcomponents/src/skipper/advice.ts`:
- Around line 175-193: The AdviceType.advice branch always renders both
tickmarks, ignoring the AngleAdviceRaw flags hideMinTickmark/hideMaxTickmark;
update the SVG return in advice.ts (the branch handling AdviceType.advice / the
function that returns svg`...`) to conditionally include the tickmark(...) calls
based on advice.hideMinTickmark and advice.hideMaxTickmark so the min tickmark
is omitted when hideMinTickmark is true and the max tickmark is omitted when
hideMaxTickmark is true (keep the same TickmarkType.primary config and props for
the included tickmarks).

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`:
- Around line 426-453: Remove the commented-out generateRandomSatellites block
from gnss-skyplot.ts to eliminate dead code; if the random-satellite generator
is useful for testing or stories, move the implementation into the component's
Storybook story or a dedicated test utility (e.g., export a helper in a
test-utils file) and update references to SatelliteData and SatelliteType there
instead of keeping the commented code inside the GnssSkyplot component file.
- Around line 44-45: Add a JSDoc block above the GnssSkyplot class declaration
describing the component tag <ob-gnss-skyplot> with a one-line summary, a short
bullet list of features/variants, and minimal usage guidelines; include an
`@fires` satellite tag to document the click event, do not add any `@property` tags
(properties are documented inline above fields), and avoid domain-specific
qualifiers — keep the description domain-agnostic. Ensure the JSDoc sits
immediately above the "export class GnssSkyplot extends LitElement" line and
references the tag name and the "satellite" event.
- Around line 70-78: The render() method mutates this.satellites which is not
allowed; move the mapping logic out of render and into a lifecycle or state: add
a private `@state`() property (e.g., mappedSatellites) and compute
mappedSatellites from this.satellites inside willUpdate(changedProps) or
update(), then have render() and getGnssSkylot()/getSatellites() read
mappedSatellites instead of reassigning this.satellites; ensure you update
getSatellites() to iterate this.mappedSatellites and remove any assignment to
this.satellites from render().
- Around line 127-134: Replace the global window event dispatch with a
component-host dispatch and remove the debug log: in the GnssSkyplot component
where the 'satellite' event is emitted (the code that currently calls
window.dispatchEvent(new CustomEvent<Satellite>('satellite', { detail:
clickedSatellite }))), call this.dispatchEvent(...) instead so consumers can
listen on the element, and delete the console.log(clickedSatellite) debug
statement; keep the CustomEvent name and detail payload unchanged.

In
`@packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.ts`:
- Around line 49-63: The animation loop is being started twice (in firstUpdated
and connectedCallback) and disconnectedCallback only cancels one frame id; fix
by ensuring the RAF loop is started exactly once and fully cancelled: in
firstUpdated and connectedCallback (functions: firstUpdated, connectedCallback)
add a guard like “if (!this._animId)” before calling
requestAnimationFrame/_animate so you never start a second loop, unify how you
start the loop (prefer requestAnimationFrame and set this._animId =
requestAnimationFrame(this._animate) inside _animate’s recursion), and in
disconnectedCallback cancelAnimationFrame only when this._animId is set and then
clear it (this._animId = undefined/null) so no updates continue after detach;
update references to _lastTime, _animId, and _animate accordingly.

In `@packages/openbridge-webcomponents/src/skipper/long-lat/long-lat.stories.ts`:
- Line 7: The storybook title string used in long-lat.stories.ts (`title:
'INSTRUMENT/LongLat'`) is not Title Case; update the `title` field to a Title
Case value (for example `Instrument/LongLat` or `Instrument/Long Lat`) so it
complies with the openbridge/storybook-title-case ESLint rule; ensure any `name`
fields in the same file are also converted to Title Case.

In
`@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.stories.ts`:
- Line 7: The Storybook story title string in speedometer.stories.ts
("INSTRUMENT/Speedometer") is not Title Case; update the title field in the
export default (the Storybook config object) to use Title Case (e.g.,
"Instrument/Speedometer" or another Title Case variant) so it complies with the
openbridge/storybook-title-case ESLint rule; change any corresponding story
"name" fields in this file if present to Title Case as well.
- Around line 1-7: The Storybook generics are incorrectly using the story export
name "Speedometer" instead of the actual component class type, so update the
file to either import the real component class and reference it in the generics
(e.g., import { Speedometer as SpeedometerElement } from './speedometer.js' and
use Meta<typeof SpeedometerElement>, StoryObj<typeof SpeedometerElement>) or
rename the story export to avoid the conflict (e.g., export const
SpeedometerStory) and update all generics to use the correct imported component
type; ensure you change occurrences of Meta<typeof Speedometer>, satisfies
Meta<Speedometer>, and StoryObj<Speedometer> accordingly and keep the story
export symbol unique (SpeedometerStory) if you choose renaming.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`:
- Around line 302-360: getAlertLowMask and getAlertHighMask both declare
identical SVG mask IDs (mask9_6621_15748 and mask10_6621_15748) causing
collisions when both alerts render; fix by giving each mask a unique ID (e.g.,
append "_low" and "_high" or generate unique suffixes) and update every
reference inside getAlertLowMask (mask="url(#...)") and getAlertHighMask to the
corresponding unique IDs so the low and high alert groups reference their own
masks.

In
`@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts`:
- Around line 15-16: Add a JSDoc block immediately above the TrueRelative class
declaration (the `@customElement`('ob-true-relative') export class TrueRelative)
that begins with a one-line summary including the tag name <ob-true-relative>,
then a short bullet-style features/variants list covering: multi-layer
navigation watch, SOG/COG, STW/heading, current, wind, north arrow, rotating
rate bar, and configurable direction reference; follow that with brief usage
guidelines showing how to include the component and any key attributes/slots
(but do not add `@property` tags — document properties inline in code instead),
and ensure the copy avoids domain qualifiers like "maritime", "navigation", or
"bridge".
- Around line 80-90: The render() method is mutating component state by calling
setMaxSpeed(), setSpeedRatio(), and setRotationsPerMinute(); move those calls
out of render and into a lifecycle hook such as willUpdate(changedProps) (or
updated(changedProps) if you need post-render effects) so state updates occur
outside rendering; implement willUpdate to call setMaxSpeed(), setSpeedRatio(),
and setRotationsPerMinute() (guarding with changedProps checks if needed) and
remove those calls from render() to prevent infinite re-renders and stale state.
- Line 34: The `@property` decorator for sensorPosition is incorrectly declared as
Boolean while its default is SensorPosition.bow (an enum); open SensorPosition
in interfaces.ts to confirm whether the enum is numeric or string-valued, then
change the decorator type for sensorPosition from Boolean to the correct runtime
type (Number if the enum uses numeric values or String if it uses strings) so
the property metadata matches SensorPosition and update any related consumers
that assume the corrected type; locate the declaration named sensorPosition and
the SensorPosition enum to make this change.

In `@packages/openbridge-webcomponents/src/skipper/wind-icons.ts`:
- Around line 82-109: The wind icon mapping omits handlers for 55–60 and 65–70
knots and the final branch (else if (windSpeed >= 74.0)) returns an empty SVG,
producing no glyph; update the wind-to-SVG logic (the branches checking
windSpeed and returning svg`...`) to add explicit branches for >=55.0 && <60.0
and >=65.0 && <70.0 using the same SVG pattern as nearby cases (incremental
bar/flag additions using Colors.elementNeutralColor), and replace the empty
>=74.0 branch with a full SVG (the strongest/highest-speed icon) consistent with
the style of the other cases so high speeds render a visible icon.

---

Minor comments:
In `@packages/openbridge-webcomponents/src/skipper/advice.ts`:
- Around line 66-68: Remove the dead commented-out implementation fragments in
the skipper/advice.ts module (specifically the commented ternary assignments for
mainColor and fillColor that reference AdviceType.caution/warning/alarm and the
other commented blocks at the other noted locations); delete those commented
lines entirely so the file contains no leftover commented code, leaving only
active code and, if necessary, refactor any remaining references to the
variables mainColor/fillColor or AdviceType to use the existing active
implementation paths to keep builds/tests passing.

In
`@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.stories.ts`:
- Line 7: Update the Storybook title string used in the stories file: replace
the current title value `'INSTRUMENT/Gnss-Skyplot'` with the Title
Case-compliant `'INSTRUMENT/GNSS Skyplot'` so the acronym GNSS is uppercase and
"Skyplot" is title-cased; this will satisfy the openbridge/storybook-title-case
ESLint rule for the exported title constant in the gnss-skyplot story module.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`:
- Around line 46-63: The satellites property in gnss-skyplot.ts currently
contains hardcoded test data; remove the inline array and comment and initialize
`@property`({type: Array}) satellites: SatelliteData[] = [] (empty array) so the
production component ships without placeholder data, then move the provided
sample objects into the story file (gnss-skyplot.stories.ts) as the default
args.satellites for storybook tests and examples.
- Line 22: Replace the misspelled satellite constellation identifier "Galileio"
with the correct "Galileo" everywhere it appears in this file (update the
enum/member name and all uses, string labels, switch/case checks, mappings and
any references in methods like legend rendering or color/mapping functions);
ensure the identifier casing matches existing enum/style and update all
occurrences of "Galileio" to "Galileo" so type checks and UI labels remain
consistent.

In
`@packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.ts`:
- Line 7: The story title string currently uses
"INSTRUMENT/LongLatEnvironmental" which violates the Title Case rule; update the
exported story's title value to a Title Case path such as "Instrument/Long Lat
Environmental" (or similar Title Case segmentation) so it satisfies the
openbridge/storybook-title-case lint rule and Storybook conventions.

In `@packages/openbridge-webcomponents/src/skipper/tickmark.ts`:
- Line 70: Remove the inline comment "// check if scale is not infinite" from
the TypeScript module in
packages/openbridge-webcomponents/src/skipper/tickmark.ts; locate that exact
comment text in the tickmark.ts source and delete the comment line so the file
contains no inline comments (do not replace with other comments or disable
rules).
- Around line 71-73: The scale validation in tickmark.ts currently only rejects
Infinity and non-positive numbers but allows NaN; update the check around the
variable scale (in the function where scale is validated) to use
Number.isFinite(scale) && scale > 0 (or equivalently !Number.isNaN(scale) and
scale !== Infinity and scale > 0) and throw the same Error('Scale is not valid')
when it fails so NaN no longer propagates into SVG coordinates.

In
`@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative-styles.ts`:
- Around line 20-23: The CSS rule for the .speed-gauge-value selector in
true-relative-styles.ts uses an invalid unitless font-size ("font-size: 64");
update this to a valid CSS size such as "64px" (or another appropriate unit like
rem/em) so the rule reads font-size: 64px; and ensure any other occurrences in
true-relative-styles.ts follow the same change.

In
`@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.stories.ts`:
- Line 8: The Storybook story title currently uses 'INSTRUMENT/TrueRelative' —
update the title property to use Title Case with proper spacing, e.g.
'INSTRUMENT/True Relative'; also check and update any corresponding story "name"
fields in this story file (true-relative.stories.ts) to follow the same Title
Case convention to satisfy the openbridge/storybook-title-case rule.

---

Nitpick comments:
In `@packages/openbridge-webcomponents/src/skipper/current-icons.ts`:
- Line 5: Remove the inline runtime comment "// TODO - check this icon" at the
top of current-icons.ts; delete that TODO from the module (top-level comment
near the exported icon definitions) and instead record the note in your issue
tracker or PR checklist so the runtime code (exports like the current icon
constants/exports) contains no comments per repo rules.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`:
- Around line 114-120: The TODO in mapStrength suggests the thresholds (0/30/70)
are provisional; update the comment to either document the source of these
thresholds or remove the TODO if they are final: inside the private method
mapStrength(value: number): Strength (and alongside the Strength enum), add a
short doc comment explaining why 0, 30, and 70 were chosen or reference a
design/issue ID that defines the thresholds, or simply delete the TODO if no
further confirmation is required; ensure the mapping logic remains unchanged
unless design provides new threshold values.
- Around line 33-42: The Satellite interface duplicates fields from
SatelliteData; either remove the duplicate by reusing SatelliteData where
possible or make Satellite extend SatelliteData (e.g., declare Satellite extends
SatelliteData) and only add the transformed fields (like x, y) or additional
flags; update usages of Satellite and SatelliteData accordingly
(functions/methods referencing Satellite, SatelliteData, and any transformations
inside gnss-skyplot.ts) so there is a single source of truth for the shared
fields.

In
`@packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.ts`:
- Around line 6-7: The title is already compliant so leave meta.title as
'INSTRUMENT/LongLatEnvironmental'; to address the typing comment optionally
import the actual component class/type and replace the self-referential generics
(Meta<typeof LongLatEnvironmental> and any StoryObj<typeof
LongLatEnvironmental>) with Meta<LongLatEnvironmentalComponent> /
StoryObj<LongLatEnvironmentalComponent> (or whatever the exported component type
is) to target the component class explicitly; otherwise no change is required.

In
`@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts`:
- Line 25: The field speedType (declared as `@property`({type: String}) speedType
= SpeedType.SOG) still has a TODO comment; either remove the property if it’s
deprecated or keep it and delete the TODO. Locate the true-relative component
class (TrueRelative / true-relative.ts) and either delete the speedType property
and any usages of SpeedType.SOG, or simply remove the "// TODO: remove" comment
and ensure any external consumers are updated if you keep it; make sure imports
and tests compile after the change.

In `@packages/openbridge-webcomponents/src/skipper/wind-icons.ts`:
- Line 89: Remove the inline TODOs and the legacy commented implementation in
packages/openbridge-webcomponents/src/skipper/wind-icons.ts (the "// TODO add
the missing speeds..." comment at line ~89 and the commented block around lines
~116–132); file should not contain commented-out code or TODOs per
guidelines—create a tracking issue for the missing speeds and delete the
commented block and any other TODO comments (also remove the comments at lines
~97 and ~105) so the module exports/definitions in wind-icons.ts remain clean
and build/test behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 06472481-804f-459b-b28c-7a29a32e09ea

📥 Commits

Reviewing files that changed from the base of the PR and between 6d5b327 and 0559719.

⛔ Files ignored due to path filters (41)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/bars-graphs/area-graph/area-graph.stories.ts/no-labels-ticks-area-graph--but-yes-32px-padding-for-optional-points--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/bars-graphs/line-graph/line-graph.stories.ts/no-labels-ticks-line-graph--but-yes-32px-padding-for-optional-points--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/chart-integration--as-external-bottom-axis--scalebackground--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/component-size-comparison--regular-medium-large-xl--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-vertical/bar-vertical.stories.ts/chart-integration--as-external-right-axis--scalebackground--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/chart-line/chart-line-base.stories.ts/no-labels-ticks-line-graph--but-yes-32px-padding-for-optional-points--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/chart-line/chart-line-base.stories.ts/threshold-height-line-graph--192px--where-labels-appear--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/horizontal--bottom-side--hasbar--advices--setpoint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/horizontal--top-side--hasbar--advices--setpoint--fillmode-tint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/vertical--left-side--hasbar--advices--setpoint--fillmode-tint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-button-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-state-off-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-state-on-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/state-off-without-icon-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-multi-application-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-multi-regular-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-single-application-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-single-regular-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-multi-application-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-multi-regular-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-single-application-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-single-regular-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/toggle-button-vertical-group/toggle-button-vertical-group.stories.ts/initially-disabled-selection-falls-back-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/toggle-button-vertical-group/toggle-button-vertical-group.stories.ts/label-under-regular-disabled-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/topbar-message-item/topbar-message-item.stories.ts/large-with-long-text-and-description-and-both-timestamps-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/azimuth-thruster/azimuth-thruster.stories.ts/in-command-at-setpoint-disable-auto-setpoint-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/component-size-comparison--regular-medium-large-xl--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/fill-mode-comparison--enhanced--fill-vs-tint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/state-comparison--incommand-active-loading-off--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/with-setpoint--value-away-from-setpoint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/component-size-comparison--regular-medium-large-xl--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/enhanced-mode-comparison--regular-vs-enhanced--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/fill-mode-comparison--enhanced--fill-vs-tint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/state-comparison--incommand-active-loading-off--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/main-engine-indicator/propulsion-main-engine-indicator.stories.ts/in-command---silhouette-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/main-engine-indicator/propulsion-main-engine-indicator.stories.ts/not-in-command---silhouette-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/propulsion-tunnel-thruster/propulsion-tunnel-thruster.stories.ts/not-in-command---silhouette-auto.png is excluded by !**/*.png
📒 Files selected for processing (25)
  • .devcontainer/devcontainer-lock.json
  • packages/openbridge-webcomponents/src/components/accordion-card/accordion-card.css
  • packages/openbridge-webcomponents/src/skipper/advice.ts
  • packages/openbridge-webcomponents/src/skipper/current-icons.ts
  • packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot-styles.ts
  • packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.stories.ts
  • packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts
  • packages/openbridge-webcomponents/src/skipper/index.ts
  • packages/openbridge-webcomponents/src/skipper/interfaces.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-styles.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat.stories.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat.ts
  • packages/openbridge-webcomponents/src/skipper/models.ts
  • packages/openbridge-webcomponents/src/skipper/speedometer/speedometer-styles.ts
  • packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.stories.ts
  • packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts
  • packages/openbridge-webcomponents/src/skipper/tickmark.ts
  • packages/openbridge-webcomponents/src/skipper/true-relative/true-relative-styles.ts
  • packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.stories.ts
  • packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts
  • packages/openbridge-webcomponents/src/skipper/utils/storybook-helpers.ts
  • packages/openbridge-webcomponents/src/skipper/utils/uuid.ts
  • packages/openbridge-webcomponents/src/skipper/wind-icons.ts

Comment on lines +175 to +193
return svg`
${adviceMask(advice.minAngle, advice.maxAngle, advice.state === AdviceState.regular ? mainColor : 'none', mainColor)}
${tickmark(advice.minAngle, {
size: TickmarkType.primary,
style: tickmarkStyle,
scale: 1,
inside: false,
textRadius: 0,
maxDigits: 0,
})}
${tickmark(advice.maxAngle, {
size: TickmarkType.primary,
style: tickmarkStyle,
scale: 1,
inside: false,
textRadius: 0,
maxDigits: 0,
})}
`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Apply hide-tickmark flags in the AdviceType.advice branch too.

Lines 177-192 always render both tickmarks, so hideMinTickmark/hideMaxTickmark are ignored for AdviceType.advice even though they are part of the shared AngleAdviceRaw contract.

Suggested fix
     return svg`
             ${adviceMask(advice.minAngle, advice.maxAngle, advice.state === AdviceState.regular ? mainColor : 'none', mainColor)}
-            ${tickmark(advice.minAngle, {
-              size: TickmarkType.primary,
-              style: tickmarkStyle,
-              scale: 1,
-              inside: false,
-              textRadius: 0,
-              maxDigits: 0,
-            })}
-            ${tickmark(advice.maxAngle, {
-              size: TickmarkType.primary,
-              style: tickmarkStyle,
-              scale: 1,
-              inside: false,
-              textRadius: 0,
-              maxDigits: 0,
-            })}
+            ${
+              advice.hideMinTickmark
+                ? nothing
+                : tickmark(advice.minAngle, {
+                    size: TickmarkType.primary,
+                    style: tickmarkStyle,
+                    scale: 1,
+                    inside: false,
+                    textRadius: 0,
+                    maxDigits: 0,
+                  })
+            }
+            ${
+              advice.hideMaxTickmark
+                ? nothing
+                : tickmark(advice.maxAngle, {
+                    size: TickmarkType.primary,
+                    style: tickmarkStyle,
+                    scale: 1,
+                    inside: false,
+                    textRadius: 0,
+                    maxDigits: 0,
+                  })
+            }
         `;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/advice.ts` around lines 175 -
193, The AdviceType.advice branch always renders both tickmarks, ignoring the
AngleAdviceRaw flags hideMinTickmark/hideMaxTickmark; update the SVG return in
advice.ts (the branch handling AdviceType.advice / the function that returns
svg`...`) to conditionally include the tickmark(...) calls based on
advice.hideMinTickmark and advice.hideMaxTickmark so the min tickmark is omitted
when hideMinTickmark is true and the max tickmark is omitted when
hideMaxTickmark is true (keep the same TickmarkType.primary config and props for
the included tickmarks).

Comment on lines +44 to +45
@customElement('ob-gnss-skyplot')
export class GnssSkyplot extends LitElement {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add JSDoc documentation to the component class.

The GnssSkyplot class is missing JSDoc. As per coding guidelines, components should include:

  • One-line summary with tag name (<ob-gnss-skyplot>)
  • Features/variants bullet list
  • Usage guidelines
  • @fires satellite - to document the click event
  • No @property tags (properties are documented inline above field declarations)

Also avoid domain qualifiers like "maritime" or "GNSS navigation" in the description; keep it domain-agnostic.

As per coding guidelines for component JSDoc.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`
around lines 44 - 45, Add a JSDoc block above the GnssSkyplot class declaration
describing the component tag <ob-gnss-skyplot> with a one-line summary, a short
bullet list of features/variants, and minimal usage guidelines; include an
`@fires` satellite tag to document the click event, do not add any `@property` tags
(properties are documented inline above fields), and avoid domain-specific
qualifiers — keep the description domain-agnostic. Ensure the JSDoc sits
immediately above the "export class GnssSkyplot extends LitElement" line and
references the tag name and the "satellite" event.

Comment on lines +70 to +78
override render() {
// this.satellites = this.mapSatellitesToCircle(this.generateRandomSatellites());
this.satellites = this.mapSatellitesToCircle(this.satellites);
return html`
<div class="container">
${this.getGnssSkylot()}
</div>
`;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not mutate properties during render.

Line 72 reassigns this.satellites during render(), which violates LitElement's reactive update contract. Property mutations should occur in lifecycle methods (willUpdate, update) or event handlers, not in render(). Move the mapping logic to willUpdate or store the mapped result in a private @state() property.

♻️ Recommended refactor
+  `@state`()
+  private mappedSatellites: (SatelliteData & { x: number; y: number })[] = [];
+
+  override willUpdate(changed: PropertyValues) {
+    super.willUpdate(changed);
+    if (changed.has('satellites')) {
+      this.mappedSatellites = this.mapSatellitesToCircle(this.satellites);
+    }
+  }
+
   override render() {
-    this.satellites = this.mapSatellitesToCircle(this.satellites);
     return html`
       <div class="container">
         ${this.getGnssSkylot()}
       </div>
     `;
   }

Then update getSatellites() (line 407) to iterate this.mappedSatellites.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`
around lines 70 - 78, The render() method mutates this.satellites which is not
allowed; move the mapping logic out of render and into a lifecycle or state: add
a private `@state`() property (e.g., mappedSatellites) and compute
mappedSatellites from this.satellites inside willUpdate(changedProps) or
update(), then have render() and getGnssSkylot()/getSatellites() read
mappedSatellites instead of reassigning this.satellites; ensure you update
getSatellites() to iterate this.mappedSatellites and remove any assignment to
this.satellites from render().

Comment on lines +127 to +134
window.dispatchEvent(
new CustomEvent<Satellite>('satellite', {
detail: clickedSatellite
})
);

// TODO: remove
console.log(clickedSatellite);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Consider dispatching event on component host instead of window.

Dispatching the satellite event on window (line 127) breaks component encapsulation. Standard practice is to dispatch custom events on the component itself (this.dispatchEvent(...)) so consumers can listen via <ob-gnss-skyplot @satellite="..."> or element.addEventListener('satellite', ...).

Also remove the debug console.log (line 134) before merging.

♻️ Proposed refactor
   private handleClickOnSatellite(id: number) {
     const clickedSatellite = this.satellites.find(s => s.id === id);
     if (!clickedSatellite) return;
-    window.dispatchEvent(
+    this.dispatchEvent(
       new CustomEvent<Satellite>('satellite', {
+        bubbles: true,
+        composed: true,
         detail: clickedSatellite
       })
     );
-    // TODO: remove
-    console.log(clickedSatellite);
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`
around lines 127 - 134, Replace the global window event dispatch with a
component-host dispatch and remove the debug log: in the GnssSkyplot component
where the 'satellite' event is emitted (the code that currently calls
window.dispatchEvent(new CustomEvent<Satellite>('satellite', { detail:
clickedSatellite }))), call this.dispatchEvent(...) instead so consumers can
listen on the element, and delete the console.log(clickedSatellite) debug
statement; keep the CustomEvent name and detail payload unchanged.

Comment on lines +426 to +453
/*
private generateRandomSatellites(count = 30): SatelliteData[] {
const types = [
SatelliteType.GPS,
SatelliteType.GLONASS,
SatelliteType.Galileio,
SatelliteType.BeiDou
];

return Array.from({ length: count }, (_, i) => {
const type = types[Math.floor(Math.random() * types.length)];

return {
id: i + 1,
type,
strength: this.mapStrength(Math.round(Math.random() * 100)), // 0–100

azimuth: Math.floor(Math.random() * 360), // 0–360°
elevation: Math.floor(Math.random() * 91), // 0–90°

spoofing: Math.random() < 0.1, // 10%
jamming: Math.random() < 0.1, // 10%
spoJamConfirmed: false
};
});
}
*/

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Remove commented-out code.

The generateRandomSatellites function (lines 426-452) is commented out. Dead code should be removed; if needed for testing, move it to the stories file or a separate test utility.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts`
around lines 426 - 453, Remove the commented-out generateRandomSatellites block
from gnss-skyplot.ts to eliminate dead code; if the random-satellite generator
is useful for testing or stories, move the implementation into the component's
Storybook story or a dedicated test utility (e.g., export a helper in a
test-utils file) and update references to SatelliteData and SatelliteType there
instead of keeping the commented code inside the GnssSkyplot component file.

Comment on lines +302 to +360
private getAlertLowMask() {
if (this.alertLowType != AlertTypes.none && this.useAlerts && this.alertLow > -this.maxSpeed/5) {
return svg`
<mask id="mask9_6621_15748" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="65" y="17" width="825" height="824">
<path d="M477.284 126.438C644.385 126.438 779.847 261.899 779.847 429C779.847 507.92 749.632 579.783 700.138 633.651C695.327 638.887 687.153 638.869 682.125 633.841L618.397 570.112C613.369 565.084 613.406 556.96 618.078 551.599C646.661 518.801 663.972 475.922 663.972 429C663.972 325.895 580.389 242.312 477.284 242.312C374.18 242.312 290.597 325.895 290.597 429C290.597 475.922 307.908 518.801 336.491 551.599C341.163 556.96 341.2 565.084 336.172 570.112L272.444 633.841C267.416 638.869 259.242 638.887 254.431 633.651C204.937 579.783 174.722 507.92 174.722 429C174.722 261.899 310.184 126.438 477.284 126.438Z" fill="${Colors.instrumentFramePrimary}" stroke="black" stroke-width="2.08333"/>
</mask>
<g mask="url(#mask9_6621_15748)">
<mask id="mask10_6621_15748" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="-106" y="-154" width="1166" height="1166">
<circle cx="477.285" cy="429" r="263.937" transform="rotate(135 477.285 429)" fill="${Colors.instrumentFramePrimary}" stroke="black" stroke-width="2.08333"/>
</mask>
${this.alertLowType != AlertTypes.caution ? svg`
<g transform="rotate(${-180 + this.alertLow * this.rotationAngle} 477.355 429)">
<path d="M477.284 429 L742.1 429" stroke="${Colors.instrumentTickMarkSecondary}" stroke-width="2.08333"/>
</g>
<g transform="rotate(${-180 + this.minSpeed * this.rotationAngle} 477.355 429)">
<path d="M477.284 429 L742.1 429" stroke="${Colors.instrumentTickMarkSecondary}" stroke-width="2.08333"/>
</g>
`
: svg`
<g transform="rotate(${-180 + this.alertLow * this.rotationAngle} 477.355 429)">
<path d="M477.284 429 L742.1 429" stroke="${Colors.instrumentFramePrimary}" stroke-width="2.08333"/>
<path d="M477.284 429 L742.1 429" stroke="${Colors.instrumentTickMarkSecondary}" stroke-width="2.08333" stroke-dasharray="8 4"/>
</g>
<g transform="rotate(${-180 + this.minSpeed * this.rotationAngle} 477.355 429)">
<path d="M477.284 429 L742.1 429" stroke="${Colors.instrumentFramePrimary}" stroke-width="2.08333"/>
<path d="M477.284 429 L742.1 429" stroke="${Colors.instrumentTickMarkSecondary}" stroke-width="2.08333" stroke-dasharray="8 4"/>
</g>
`
}
<!--
${
this.alertLowType == AlertTypes.caution ? svg`
<g mask="url(#mask10_6621_15748)">
<path d="M65.2845 429C65.2845 483.105 75.9412 536.679 96.6461 586.666C117.351 636.652 147.699 682.07 185.956 720.328L477.284 429L65.2845 429Z" fill="black" fill-opacity="0.05"/>
<path d="M65.2845 429C65.2845 483.105 75.9412 536.679 96.6461 586.666C117.351 636.652 147.699 682.07 185.956 720.328L477.284 429L65.2845 429Z" stroke="${Colors.instrumentFramePrimary}" stroke-width="2.08333"/>
<path d="M65.2845 429C65.2845 483.105 75.9412 536.679 96.6461 586.666C117.351 636.652 147.699 682.07 185.956 720.328L477.284 429L65.2845 429Z" stroke="${Colors.instrumentTickMarkSecondary}" stroke-width="2.08333" stroke-dasharray="8.33 8.33"/>
</g>
` : nothing
}
-->

<path d="${this.cutTickmarks(this.getAngle(this.minSpeed + 0.25), this.getAngle(this.alertLow - 0.15))}" fill="${Colors.instrumentFramePrimary}"/>
</g>
`;
}
else {
return nothing;
}
}

private getAlertHighMask() {
if (this.alertHighType != AlertTypes.none && this.useAlerts && this.alertHigh < this.maxSpeed) {
return svg`
<mask id="mask9_6621_15748" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="65" y="17" width="825" height="824">
<path d="M477.284 126.438C644.385 126.438 779.847 261.899 779.847 429C779.847 507.92 749.632 579.783 700.138 633.651C695.327 638.887 687.153 638.869 682.125 633.841L618.397 570.112C613.369 565.084 613.406 556.96 618.078 551.599C646.661 518.801 663.972 475.922 663.972 429C663.972 325.895 580.389 242.312 477.284 242.312C374.18 242.312 290.597 325.895 290.597 429C290.597 475.922 307.908 518.801 336.491 551.599C341.163 556.96 341.2 565.084 336.172 570.112L272.444 633.841C267.416 638.869 259.242 638.887 254.431 633.651C204.937 579.783 174.722 507.92 174.722 429C174.722 261.899 310.184 126.438 477.284 126.438Z" fill="${Colors.instrumentFramePrimary}" stroke="black" stroke-width="2.08333"/>
</mask>
<g mask="url(#mask9_6621_15748)">
<mask id="mask10_6621_15748" style="mask-type:alpha" maskUnits="userSpaceOnUse" x="-106" y="-154" width="1166" height="1166">
<circle cx="477.285" cy="429" r="263.937" transform="rotate(135 477.285 429)" fill="${Colors.instrumentFramePrimary}" stroke="black" stroke-width="2.08333"/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use unique SVG mask IDs for low/high alert paths.

Line 305 and Line 355 both define mask9_6621_15748 (and similarly mask10_6621_15748), so low/high alert rendering can reference the wrong mask when both are enabled.

🔧 Suggested fix
-<mask id="mask9_6621_15748" ...>
+<mask id="mask9_low_6621_15748" ...>
 ...
-<g mask="url(`#mask9_6621_15748`)">
+<g mask="url(`#mask9_low_6621_15748`)">
 ...
-<mask id="mask10_6621_15748" ...>
+<mask id="mask10_low_6621_15748" ...>
-<mask id="mask9_6621_15748" ...>
+<mask id="mask9_high_6621_15748" ...>
 ...
-<g mask="url(`#mask9_6621_15748`)">
+<g mask="url(`#mask9_high_6621_15748`)">
 ...
-<mask id="mask10_6621_15748" ...>
+<mask id="mask10_high_6621_15748" ...>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 302 - 360, getAlertLowMask and getAlertHighMask both declare
identical SVG mask IDs (mask9_6621_15748 and mask10_6621_15748) causing
collisions when both alerts render; fix by giving each mask a unique ID (e.g.,
append "_low" and "_high" or generate unique suffixes) and update every
reference inside getAlertLowMask (mask="url(#...)") and getAlertHighMask to the
corresponding unique IDs so the low and high alert groups reference their own
masks.

Comment on lines +15 to +16
@customElement('ob-true-relative')
export class TrueRelative extends LitElement {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Add JSDoc documentation to the component class.

The TrueRelative class is missing JSDoc. As per coding guidelines, components should include:

  • One-line summary with tag name (<ob-true-relative>)
  • Features/variants bullet list (multi-layer navigation watch, SOG/COG, STW/heading, current, wind, north arrow, rotating rate bar, configurable direction reference)
  • Usage guidelines
  • No @property tags (properties are documented inline)
  • Avoid domain qualifiers (maritime, navigation, bridge) in documentation

As per coding guidelines for component JSDoc.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts`
around lines 15 - 16, Add a JSDoc block immediately above the TrueRelative class
declaration (the `@customElement`('ob-true-relative') export class TrueRelative)
that begins with a one-line summary including the tag name <ob-true-relative>,
then a short bullet-style features/variants list covering: multi-layer
navigation watch, SOG/COG, STW/heading, current, wind, north arrow, rotating
rate bar, and configurable direction reference; follow that with brief usage
guidelines showing how to include the component and any key attributes/slots
(but do not add `@property` tags — document properties inline in code instead),
and ensure the copy avoids domain qualifiers like "maritime", "navigation", or
"bridge".

@property({type: Number}) rotationsPerMinute = 1;

@property({type: String}) direction: TrueRelativeDirection = TrueRelativeDirection.NorthUp;
@property({type: Boolean}) sensorPosition = SensorPosition.bow;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Fix type annotation for sensorPosition property.

sensorPosition is declared as @property({type: Boolean}) but defaults to SensorPosition.bow (an enum, not a boolean). The correct type should be Number or String depending on the enum's runtime value type.

🐛 Proposed fix
-  `@property`({type: Boolean}) sensorPosition = SensorPosition.bow;
+  `@property`({type: Number}) sensorPosition = SensorPosition.bow;

Verify SensorPosition enum uses numeric values (check interfaces.ts).

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@property({type: Boolean}) sensorPosition = SensorPosition.bow;
`@property`({type: Number}) sensorPosition = SensorPosition.bow;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts`
at line 34, The `@property` decorator for sensorPosition is incorrectly declared
as Boolean while its default is SensorPosition.bow (an enum); open
SensorPosition in interfaces.ts to confirm whether the enum is numeric or
string-valued, then change the decorator type for sensorPosition from Boolean to
the correct runtime type (Number if the enum uses numeric values or String if it
uses strings) so the property metadata matches SensorPosition and update any
related consumers that assume the corrected type; locate the declaration named
sensorPosition and the SensorPosition enum to make this change.

Comment on lines +80 to +90
override render() {
this.setMaxSpeed();
this.setSpeedRatio();
this.setRotationsPerMinute();

return html`
<div class="container">
${this.getTrueRelative()}
</div>
`;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not call state-mutating methods during render.

Lines 81-83 call setMaxSpeed(), setSpeedRatio(), and setRotationsPerMinute() during render(). These methods mutate component state, which violates LitElement's reactive update contract and can cause infinite re-render loops or stale data. Move these calls to willUpdate() or updated().

♻️ Recommended refactor
+  override willUpdate(changed: PropertyValues) {
+    super.willUpdate(changed);
+    if (changed.has('maxSpeed')) {
+      this.setMaxSpeed();
+    }
+    if (changed.has('hdgSpeed') || changed.has('maxSpeed')) {
+      this.setSpeedRatio();
+    }
+    if (changed.has('rotationsPerMinute')) {
+      this.setRotationsPerMinute();
+    }
+  }
+
   override render() {
-    this.setMaxSpeed();
-    this.setSpeedRatio();
-    this.setRotationsPerMinute();
     return html`
       <div class="container">
         ${this.getTrueRelative()}
       </div>
     `;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
override render() {
this.setMaxSpeed();
this.setSpeedRatio();
this.setRotationsPerMinute();
return html`
<div class="container">
${this.getTrueRelative()}
</div>
`;
}
override willUpdate(changed: PropertyValues) {
super.willUpdate(changed);
if (changed.has('maxSpeed')) {
this.setMaxSpeed();
}
if (changed.has('hdgSpeed') || changed.has('maxSpeed')) {
this.setSpeedRatio();
}
if (changed.has('rotationsPerMinute')) {
this.setRotationsPerMinute();
}
}
override render() {
return html`
<div class="container">
${this.getTrueRelative()}
</div>
`;
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts`
around lines 80 - 90, The render() method is mutating component state by calling
setMaxSpeed(), setSpeedRatio(), and setRotationsPerMinute(); move those calls
out of render and into a lifecycle hook such as willUpdate(changedProps) (or
updated(changedProps) if you need post-render effects) so state updates occur
outside rendering; implement willUpdate to call setMaxSpeed(), setSpeedRatio(),
and setRotationsPerMinute() (guarding with changedProps checks if needed) and
remove those calls from render() to prevent infinite re-renders and stale state.

Comment on lines +82 to +109
else if (windSpeed >= 50.0 && windSpeed < 55.0) {
return svg`
<svg width="13" height="23" viewBox="0 0 13 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.08237 0.00343268L4.19761 0.0200342L11.6917 1.51906C12.7644 1.73378 12.7644 3.26722 11.6917 3.48195L5.00034 4.81984V15.0005H6.99839C7.74195 15.0005 8.22661 15.7828 7.8939 16.4487L4.89585 22.4458C4.52677 23.1831 3.47373 23.1833 3.10483 22.4458L0.106787 16.4487C-0.22571 15.7833 0.258565 15.0008 1.00229 15.0005H3.00034V1.00148C3.00048 0.4093 3.50876 -0.0441781 4.08237 0.00343268Z" fill="${Colors.elementNeutralColor}"/>
</svg>
`;
}
// TODO add the missing speeds (>= 50.0 && <55.0)
else if (windSpeed >= 60.0 && windSpeed < 65.0) {
return svg`
<svg width="13" height="23" viewBox="0 0 13 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.08237 0.00343268L4.19761 0.0200342L11.6917 1.51906C12.7644 1.73378 12.7644 3.26722 11.6917 3.48195L5.00034 4.81984V6.0005H11.0003C11.5526 6.0005 12.0003 6.44822 12.0003 7.0005C12.0001 7.5526 11.5525 8.0005 11.0003 8.0005H5.00034V15.0005H6.99839C7.74195 15.0005 8.22661 15.7828 7.8939 16.4487L4.89585 22.4458C4.52677 23.1831 3.47373 23.1833 3.10483 22.4458L0.106787 16.4487C-0.22571 15.7833 0.258565 15.0008 1.00229 15.0005H3.00034V1.00148C3.00048 0.4093 3.50876 -0.0441781 4.08237 0.00343268Z" fill="${Colors.elementNeutralColor}"/>
</svg>
`;
}
// TODO add the missing speeds (>= 60.0 && <65.0)
else if (windSpeed >= 70.0 && windSpeed < 75.0) {
return svg`
<svg width="13" height="23" viewBox="0 0 13 23" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M4.08237 0.00343268L4.19761 0.0200342L11.6917 1.51906C12.7644 1.73378 12.7644 3.26722 11.6917 3.48195L5.00034 4.81984V6.0005H11.0003C11.5526 6.0005 12.0003 6.44822 12.0003 7.0005C12.0001 7.5526 11.5525 8.0005 11.0003 8.0005H5.00034V9.5005H11.0003C11.5526 9.5005 12.0003 9.94822 12.0003 10.5005C12.0001 11.0526 11.5525 11.5005 11.0003 11.5005H5.00034V15.0005H6.99839C7.74195 15.0005 8.22661 15.7828 7.8939 16.4487L4.89585 22.4458C4.52677 23.1831 3.47373 23.1833 3.10483 22.4458L0.106787 16.4487C-0.22571 15.7833 0.258565 15.0008 1.00229 15.0005H3.00034V1.00148C3.00048 0.4093 3.50876 -0.0441781 4.08237 0.00343268Z" fill="${Colors.elementNeutralColor}"/>
</svg>
`;
}
// TODO add the missing speeds (>= 70.0 && <74.0)
else if (windSpeed >= 74.0) {
return svg`

`;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Wind-speed mapping is incomplete and returns blank icon for high speeds.

The current branches skip explicit handling for 55–60 and 65–70, and the >= 74.0 branch returns an empty <svg> (Line 106–109), which produces no visible glyph for high wind speeds.

Suggested patch
-    else if (windSpeed >= 50.0 && windSpeed < 55.0) {
+    else if (windSpeed >= 50.0 && windSpeed < 55.0) {
         return svg`
             <svg width="13" height="23" viewBox="0 0 13 23" fill="none" xmlns="http://www.w3.org/2000/svg">
                 <path fill-rule="evenodd" clip-rule="evenodd" d="M4.08237 0.00343268L4.19761 0.0200342L11.6917 1.51906C12.7644 1.73378 12.7644 3.26722 11.6917 3.48195L5.00034 4.81984V15.0005H6.99839C7.74195 15.0005 8.22661 15.7828 7.8939 16.4487L4.89585 22.4458C4.52677 23.1831 3.47373 23.1833 3.10483 22.4458L0.106787 16.4487C-0.22571 15.7833 0.258565 15.0008 1.00229 15.0005H3.00034V1.00148C3.00048 0.4093 3.50876 -0.0441781 4.08237 0.00343268Z" fill="${Colors.elementNeutralColor}"/>
             </svg>
         `;
     }
-    // TODO add the missing speeds (>= 50.0 && <55.0)
+    else if (windSpeed >= 55.0 && windSpeed < 60.0) {
+        // temporary fallback until dedicated icon is added
+        return getWindIcon(60.0);
+    }
     else if (windSpeed >= 60.0 && windSpeed < 65.0) {
         return svg`
             <svg width="13" height="23" viewBox="0 0 13 23" fill="none" xmlns="http://www.w3.org/2000/svg">
                 <path fill-rule="evenodd" clip-rule="evenodd" d="M4.08237 0.00343268L4.19761 0.0200342L11.6917 1.51906C12.7644 1.73378 12.7644 3.26722 11.6917 3.48195L5.00034 4.81984V6.0005H11.0003C11.5526 6.0005 12.0003 6.44822 12.0003 7.0005C12.0001 7.5526 11.5525 8.0005 11.0003 8.0005H5.00034V15.0005H6.99839C7.74195 15.0005 8.22661 15.7828 7.8939 16.4487L4.89585 22.4458C4.52677 23.1831 3.47373 23.1833 3.10483 22.4458L0.106787 16.4487C-0.22571 15.7833 0.258565 15.0008 1.00229 15.0005H3.00034V1.00148C3.00048 0.4093 3.50876 -0.0441781 4.08237 0.00343268Z" fill="${Colors.elementNeutralColor}"/>
             </svg>
         `;
     }
-    // TODO add the missing speeds (>= 60.0 && <65.0)
+    else if (windSpeed >= 65.0 && windSpeed < 70.0) {
+        // temporary fallback until dedicated icon is added
+        return getWindIcon(70.0);
+    }
     else if (windSpeed >= 70.0 && windSpeed < 75.0) {
         return svg`
             <svg width="13" height="23" viewBox="0 0 13 23" fill="none" xmlns="http://www.w3.org/2000/svg">
                 <path fill-rule="evenodd" clip-rule="evenodd" d="M4.08237 0.00343268L4.19761 0.0200342L11.6917 1.51906C12.7644 1.73378 12.7644 3.26722 11.6917 3.48195L5.00034 4.81984V6.0005H11.0003C11.5526 6.0005 12.0003 6.44822 12.0003 7.0005C12.0001 7.5526 11.5525 8.0005 11.0003 8.0005H5.00034V9.5005H11.0003C11.5526 9.5005 12.0003 9.94822 12.0003 10.5005C12.0001 11.0526 11.5525 11.5005 11.0003 11.5005H5.00034V15.0005H6.99839C7.74195 15.0005 8.22661 15.7828 7.8939 16.4487L4.89585 22.4458C4.52677 23.1831 3.47373 23.1833 3.10483 22.4458L0.106787 16.4487C-0.22571 15.7833 0.258565 15.0008 1.00229 15.0005H3.00034V1.00148C3.00048 0.4093 3.50876 -0.0441781 4.08237 0.00343268Z" fill="${Colors.elementNeutralColor}"/>
             </svg>
         `;
     }
-    // TODO add the missing speeds (>= 70.0 && <74.0)
     else if (windSpeed >= 74.0) {
-        return svg`
-                
-        `;
+        // temporary fallback until >=74 icon path is added
+        return getWindIcon(70.0);
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/wind-icons.ts` around lines 82
- 109, The wind icon mapping omits handlers for 55–60 and 65–70 knots and the
final branch (else if (windSpeed >= 74.0)) returns an empty SVG, producing no
glyph; update the wind-to-SVG logic (the branches checking windSpeed and
returning svg`...`) to add explicit branches for >=55.0 && <60.0 and >=65.0 &&
<70.0 using the same SVG pattern as nearby cases (incremental bar/flag additions
using Colors.elementNeutralColor), and replace the empty >=74.0 branch with a
full SVG (the strongest/highest-speed icon) consistent with the style of the
other cases so high speeds render a visible icon.

@talpitoo talpitoo left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

note to self: requesting 'blank' changes to avoid accidental merge.

@talpitoo

talpitoo commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Hey @asijah2, thank you for your contribution! Attaching these screenshots so our team can more easily evaluate it.

skyplot longlatenvironmental longlat speedometer truerelative

@asijah2

asijah2 commented Jun 8, 2026

Copy link
Copy Markdown
Author
image image image image image

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 10

🧹 Nitpick comments (9)
packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts (2)

405-409: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Stray comment block.

/// // /* TOP DOWN VIEW FUNCTIONS */ is a comment that doesn't meet the "extremely unusual" bar for inclusion.

As per coding guidelines, "Do not include comments in code whatsoever unless the code is extremely unusual and impossible to understand without explanation."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts`
around lines 405 - 409, Remove the stray comment block in vessel-types.ts under
the top-down view section; it is not needed and does not meet the commenting
guideline. Locate the TOP DOWN VIEW FUNCTIONS marker near the related vessel
type/view helpers and delete that comment-only block without changing any
surrounding logic or symbols.

Source: Coding guidelines


810-827: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove large commented-out dead SVG block.

A ~17-line commented-out SVG fragment is left at the end of the file.

As per coding guidelines, "Do not include comments in code whatsoever unless the code is extremely unusual and impossible to understand without explanation."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts`
around lines 810 - 827, Remove the large commented-out SVG dead block from
vessel-types.ts; the file should not keep commented code per the guideline.
Locate the trailing commented SVG fragment near the Vessel-related constants and
delete the entire block, leaving only the active vessel type definitions and
render logic intact.

Source: Coding guidelines

packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.ts (2)

44-44: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove commented-out dead code.

// width: 512, is a leftover commented-out line.

As per coding guidelines, "Do not include comments in code whatsoever unless the code is extremely unusual and impossible to understand without explanation."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.ts`
at line 44, Remove the commented-out dead code from the vessel story by deleting
the leftover width assignment comment in the relevant story configuration.
Update the vessel.stories.ts content around the vessel story so only active
settings remain, and keep the Storybook story definitions clean without
unnecessary comments.

Source: Coding guidelines


7-7: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Title Case violation in story title.

'INSTRUMENT/Vessel' uses an all-caps segment instead of Title Case.

As per coding guidelines, "Use Title Case for Storybook title and name fields."

✏️ Proposed fix
-  title: 'INSTRUMENT/Vessel',
+  title: 'Instrument/Vessel',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.ts`
at line 7, The Storybook metadata in the vessel story uses an all-caps segment
in the title, so update the `title` field in `vessel.stories.ts` to use Title
Case consistently with the project guideline. Adjust the `INSTRUMENT/Vessel`
entry in the story metadata so it follows the same casing convention as other
Storybook `title` and `name` values.

Source: Coding guidelines

packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts (4)

36-36: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Boolean property naming unclear.

toggleSLAndDLSensor doesn't clearly convey its true/false semantics (what does true vs. false mean?).

As per coding guidelines, "Name boolean properties and parameters using positive phrasing." Consider a descriptive name reflecting the actual state, e.g. useDoubleLineSensorMode.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts` at line
36, The boolean property name toggleSLAndDLSensor is unclear and uses
toggle-style wording instead of positive phrasing. Rename the property in the
vessel class to something that describes the enabled state, like
useDoubleLineSensorMode, and update any references in the vessel-view component
to match the new name so true/false meaning is obvious.

Source: Coding guidelines


37-37: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Excessive inline comments violate the no-comments guideline.

Numerous // comments (e.g. "values for SIDE_VIEW", "COMMON VIEW LOGIC", checkmark-annotated comments, commented-out dimTopY/dimBottomY lines) are present where the code is not "extremely unusual and impossible to understand without explanation."

As per coding guidelines, "Do not include comments in code whatsoever unless the code is extremely unusual and impossible to understand without explanation."

Also applies to: 45-45, 57-57, 160-162, 219-221, 260-261, 270-270, 273-273, 293-293, 297-297, 309-310

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts` at line
37, Remove the unnecessary inline comments in vessel.ts, including section
labels like “values for SIDE_VIEW” and “COMMON VIEW LOGIC,” the checkmark-style
annotations, and any commented-out code such as the dimTopY/dimBottomY lines.
Keep the implementation in the affected blocks by relying on the existing
structure and symbol names like the SIDE_VIEW logic and related vessel-view
helpers, but delete comments unless they are truly required for unusually
complex behavior.

Source: Coding guidelines


226-228: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Dead no-op method referencing a nonexistent property.

toggleVesselViewer() does nothing; its only content is a commented-out reference to this.sideTopDownViewToggle, a property that doesn't exist on this class.

Should this method be removed, or is a real toggle implementation still pending? Happy to help wire it up if there's a missing requirement here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts` around
lines 226 - 228, The toggleVesselViewer() method in the vessel class is
currently a dead no-op and only references a nonexistent sideTopDownViewToggle
property. Remove toggleVesselViewer() if it is not needed, or replace it with a
real implementation that toggles an actual class property/state used by the
vessel view; make sure the method and any referenced field names are consistent
with the rest of the class.

230-440: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

getVessel() is a very large, complex method.

This single method computes dozens of local variables and assembles both view modes; it would benefit from being broken into smaller, named helper methods for readability and testability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts` around
lines 230 - 440, The getVessel() method is doing too much by calculating both
side and top-down view state in one large block, which hurts readability and
testability. Refactor it by extracting the distinct calculation/rendering pieces
into smaller named helpers (for example for side-view geometry, top-down
geometry, and view assembly) and keep getVessel() focused on selecting between
SIDE_VIEW, TOP_DOWN_VIEW, or the combined layout.
packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-styles.ts (1)

64-84: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Hardcoded layout magic numbers.

400px width, scale(0.69), and margin-left: 60px are magic values with no documented derivation. Given vessel.stories.ts exposes a width control in its args, these fixed values won't respond to it, which may cause confusing/clipped output at other widths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-styles.ts`
around lines 64 - 84, The vessel layout in vessel-styles should not rely on
hardcoded magic numbers for .both-views, .both-views-inner, and .top-down-view.
Update the styles to derive width, scale, and horizontal offset from the
configurable width used by vessel.stories.ts so the vessel view responds
correctly to different sizes instead of clipping or misaligning. Use the
existing vessel-view styling selectors to replace fixed values with dynamic
sizing logic tied to the story args or shared sizing constants.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts`:
- Around line 268-274: The side-dimension SVG lines in `dimSternToCCRPSide` and
`dimBowToCCRPSide` are hardcoded to y=303 instead of using the computed line
position from `buildDimensionLine`. Update these functions to use
`sideViewParams.sternLine.y` and `sideViewParams.bowLine.y` for the `y1`/`y2`
attributes so they stay in sync with the shared `LINE_Y` value used by
`vessel.ts`.
- Around line 1-4: The module in vessel-types.ts is missing the required
top-of-module JSDoc for a pure function module. Add a full JSDoc block above the
first export in this file, describing the module’s functionality and intended
usage, and place it before the symbols imported and used by the exported helpers
such as vesselTypes and any related template/render functions.
- Around line 261-293: The stern/bow CCRP rendering helpers are near-duplicates
of each other, and the same pattern also applies to the port/starboard line
renderers. Refactor `dimSternToCCRPSide` and `dimBowToCCRPSide` into a single
parameterized helper that accepts the varying text, line, and label fields from
`getSideViewParams`, and do the same for `dimPortToCCRPLine` and
`dimStarboardToCCRPLine`. Keep the existing SVG output behavior unchanged while
centralizing the shared `textLabel`, `path`, and `line` generation logic.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.ts`:
- Around line 60-64: The Vessel_cruiser story name is misleading because
VesselTypes has no CRUISER value and the story currently falls back to the
default CARGO vessel. Update the story in vessel.stories.ts so the exported
story name and any related labels/args match an actual VesselTypes entry, using
the existing VesselTypes and Vessel component story definitions as the reference
points.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts`:
- Line 24: Add a class-level JSDoc block directly above the Vessel class
declaration to document its purpose and intended usage. Use domain-agnostic
wording only, and describe the concrete component at a high level without any
maritime-specific terms; keep the documentation on the Vessel class itself so it
matches the project’s component JSDoc guideline.
- Around line 147-159: The negative-value guard in updated() only covers
sternToCCRP and bowToCCRP, but the same clamping should be applied consistently
to the other dimension properties that drive the ratio/division logic. Update
vessel.ts in the VesselView component’s updated method to also normalize
portToCCRP, starboardToCCRP, sensorHeightOverKeel, and vesselHeight to a minimum
of 0 when they change, using the same pattern already used for sternToCCRP and
bowToCCRP.
- Line 393: The `getArrowHalfHeight` callback is being passed around as an
unbound method in the dim/starboard params blocks, which makes its `this` depend
on the caller’s object shape. Update the params assembly in `vessel.ts` so
`getArrowHalfHeight` is consistently bound to the class instance, matching the
existing `getArrowHalfHeight.bind(this)` usage, and apply the same fix in the
other affected params objects that currently pass `this.getArrowHalfHeight`
directly.
- Around line 23-24: The custom element registration in Vessel still uses the
legacy ob- prefix, which is inconsistent with the repo convention. Update the
`@customElement` decorator on Vessel to use the obc- prefix so it matches other
published components and the project’s LitElement naming standard. Keep the
class name the same and only change the registered tag name to the new obc-
form.
- Around line 443-447: The HTMLElementTagNameMap entry is using the wrong custom
element name for Vessel. Update the declaration in the global
HTMLElementTagNameMap so it matches the registered tag used by `@customElement` on
Vessel, and ensure the key is the same tag name that document.createElement
should use for correct typing.
- Line 25: Remove the unused vesselWidth property from the vessel component; it
is declared in the vessel class but never read or used in rendering, so it
should be deleted from the vessel-view/vessel.ts component definition to keep
the API clean.

---

Nitpick comments:
In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-styles.ts`:
- Around line 64-84: The vessel layout in vessel-styles should not rely on
hardcoded magic numbers for .both-views, .both-views-inner, and .top-down-view.
Update the styles to derive width, scale, and horizontal offset from the
configurable width used by vessel.stories.ts so the vessel view responds
correctly to different sizes instead of clipping or misaligning. Use the
existing vessel-view styling selectors to replace fixed values with dynamic
sizing logic tied to the story args or shared sizing constants.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts`:
- Around line 405-409: Remove the stray comment block in vessel-types.ts under
the top-down view section; it is not needed and does not meet the commenting
guideline. Locate the TOP DOWN VIEW FUNCTIONS marker near the related vessel
type/view helpers and delete that comment-only block without changing any
surrounding logic or symbols.
- Around line 810-827: Remove the large commented-out SVG dead block from
vessel-types.ts; the file should not keep commented code per the guideline.
Locate the trailing commented SVG fragment near the Vessel-related constants and
delete the entire block, leaving only the active vessel type definitions and
render logic intact.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.ts`:
- Line 44: Remove the commented-out dead code from the vessel story by deleting
the leftover width assignment comment in the relevant story configuration.
Update the vessel.stories.ts content around the vessel story so only active
settings remain, and keep the Storybook story definitions clean without
unnecessary comments.
- Line 7: The Storybook metadata in the vessel story uses an all-caps segment in
the title, so update the `title` field in `vessel.stories.ts` to use Title Case
consistently with the project guideline. Adjust the `INSTRUMENT/Vessel` entry in
the story metadata so it follows the same casing convention as other Storybook
`title` and `name` values.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts`:
- Line 36: The boolean property name toggleSLAndDLSensor is unclear and uses
toggle-style wording instead of positive phrasing. Rename the property in the
vessel class to something that describes the enabled state, like
useDoubleLineSensorMode, and update any references in the vessel-view component
to match the new name so true/false meaning is obvious.
- Line 37: Remove the unnecessary inline comments in vessel.ts, including
section labels like “values for SIDE_VIEW” and “COMMON VIEW LOGIC,” the
checkmark-style annotations, and any commented-out code such as the
dimTopY/dimBottomY lines. Keep the implementation in the affected blocks by
relying on the existing structure and symbol names like the SIDE_VIEW logic and
related vessel-view helpers, but delete comments unless they are truly required
for unusually complex behavior.
- Around line 226-228: The toggleVesselViewer() method in the vessel class is
currently a dead no-op and only references a nonexistent sideTopDownViewToggle
property. Remove toggleVesselViewer() if it is not needed, or replace it with a
real implementation that toggles an actual class property/state used by the
vessel view; make sure the method and any referenced field names are consistent
with the rest of the class.
- Around line 230-440: The getVessel() method is doing too much by calculating
both side and top-down view state in one large block, which hurts readability
and testability. Refactor it by extracting the distinct calculation/rendering
pieces into smaller named helpers (for example for side-view geometry, top-down
geometry, and view assembly) and keep getVessel() focused on selecting between
SIDE_VIEW, TOP_DOWN_VIEW, or the combined layout.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 149e1a1f-bee2-486a-9edd-350f54a0ca0e

📥 Commits

Reviewing files that changed from the base of the PR and between affb300 and 9b11357.

📒 Files selected for processing (6)
  • packages/openbridge-webcomponents/oicl-openbridge-webcomponents-2.0.0-next.29.tgz
  • packages/openbridge-webcomponents/src/skipper/interfaces.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-styles.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/openbridge-webcomponents/src/skipper/interfaces.ts

Comment on lines +1 to +4
import {nothing, svg, type TemplateResult} from 'lit';
import {Colors} from '../interfaces.js';
import {VesselTypes, ViewType} from './vessel.js';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Missing top-of-module JSDoc for this pure function module.

No JSDoc block describing functionality/usage precedes the first export.

As per coding guidelines, "For pure function modules, place the full JSDoc block at the top of the module above the first export."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts`
around lines 1 - 4, The module in vessel-types.ts is missing the required
top-of-module JSDoc for a pure function module. Add a full JSDoc block above the
first export in this file, describing the module’s functionality and intended
usage, and place it before the symbols imported and used by the exported helpers
such as vesselTypes and any related template/render functions.

Source: Coding guidelines

Comment on lines +261 to +293
function dimSternToCCRPSide(sideViewParams: getSideViewParams): TemplateResult {
return svg`${textLabel({
posX: sideViewParams.sternTextX - 7,
posY: 297.656,
label: sideViewParams.sternToCCRP,
})}
<path id="line_3" d="${sideViewParams.sternLine.arrowPath}" fill="${Colors.instrumentRegularSecondaryDif}" fill-opacity="0.97"/>
<line
x1="${sideViewParams.sternLine.startX + 4.5}"
y1="303"
x2="${sideViewParams.sternLine.endX - 4.5}"
y2="303"
stroke="${Colors.instrumentRegularSecondaryDif}"
stroke-width="1"
/>`;
}

function dimBowToCCRPSide(sideViewParams: getSideViewParams): TemplateResult {
return svg`${textLabel({
posX: sideViewParams.bowTextX - 7,
posY: 297.656,
label: sideViewParams.bowToCCRP,
})}
<path id="line_4" d="${sideViewParams.bowLine.arrowPath}" fill="${Colors.instrumentRegularSecondaryDif}" fill-opacity="0.97"/>
<line
x1="${sideViewParams.bowLine.startX + 4.5}"
y1="303"
x2="${sideViewParams.bowLine.endX - 4.5}"
y2="303"
stroke="${Colors.instrumentRegularSecondaryDif}"
stroke-width="1"
/>`;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Near-duplicate stern/bow and port/starboard rendering functions.

dimSternToCCRPSide/dimBowToCCRPSide and dimPortToCCRPLine/dimStarboardToCCRPLine are almost identical, differing only by field names. Consider a single parameterized helper for each pair to reduce duplication.

Also applies to: 424-501

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts`
around lines 261 - 293, The stern/bow CCRP rendering helpers are near-duplicates
of each other, and the same pattern also applies to the port/starboard line
renderers. Refactor `dimSternToCCRPSide` and `dimBowToCCRPSide` into a single
parameterized helper that accepts the varying text, line, and label fields from
`getSideViewParams`, and do the same for `dimPortToCCRPLine` and
`dimStarboardToCCRPLine`. Keep the existing SVG output behavior unchanged while
centralizing the shared `textLabel`, `path`, and `line` generation logic.

Comment on lines +268 to +274
<line
x1="${sideViewParams.sternLine.startX + 4.5}"
y1="303"
x2="${sideViewParams.sternLine.endX - 4.5}"
y2="303"
stroke="${Colors.instrumentRegularSecondaryDif}"
stroke-width="1"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Hardcoded y="303" duplicates vessel.ts's LINE_Y constant.

dimSternToCCRPSide/dimBowToCCRPSide hardcode y1="303"/y2="303" instead of using sideViewParams.sternLine.y/bowLine.y (already computed from LINE_Y in buildDimensionLine). If LINE_Y in vessel.ts ever changes, these lines will silently desync from the rest of the dimension rendering.

🔧 Proposed fix
     <line
       x1="${sideViewParams.sternLine.startX + 4.5}"
-      y1="303"
+      y1="${sideViewParams.sternLine.y}"
       x2="${sideViewParams.sternLine.endX - 4.5}"
-      y2="303"
+      y2="${sideViewParams.sternLine.y}"

(apply the equivalent fix using bowLine.y in dimBowToCCRPSide)

Also applies to: 286-292

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts`
around lines 268 - 274, The side-dimension SVG lines in `dimSternToCCRPSide` and
`dimBowToCCRPSide` are hardcoded to y=303 instead of using the computed line
position from `buildDimensionLine`. Update these functions to use
`sideViewParams.sternLine.y` and `sideViewParams.bowLine.y` for the `y1`/`y2`
attributes so they stay in sync with the shared `LINE_Y` value used by
`vessel.ts`.

Comment on lines +60 to +64
export const Vessel_cruiser: Story = {
args: {
width: 512,
},
};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Vessel_cruiser story name is misleading.

There is no CRUISER entry in VesselTypes (only CARGO, TANKER, FISHING, CAR_FERRY); this story will silently render the default CARGO vessel, not a "cruiser."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.ts`
around lines 60 - 64, The Vessel_cruiser story name is misleading because
VesselTypes has no CRUISER value and the story currently falls back to the
default CARGO vessel. Update the story in vessel.stories.ts so the exported
story name and any related labels/args match an actual VesselTypes entry, using
the existing VesselTypes and Vessel component story definitions as the reference
points.

Comment on lines +23 to +24
@customElement('ob-vessel-view')
export class Vessel extends LitElement {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Custom element tag doesn't follow the repo's obc- prefix convention.

ob-vessel-view uses the legacy ob- prefix. This repo's history shows an explicit migration from ob- to obc-, and other published components use tags like obc-top-bar.

As per coding guidelines, "Components must extend LitElement, register with @customElement('obc-…')."

🏷️ Proposed fix
-@customElement('ob-vessel-view')
+@customElement('obc-vessel-view')
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@customElement('ob-vessel-view')
export class Vessel extends LitElement {
`@customElement`('obc-vessel-view')
export class Vessel extends LitElement {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts` around
lines 23 - 24, The custom element registration in Vessel still uses the legacy
ob- prefix, which is inconsistent with the repo convention. Update the
`@customElement` decorator on Vessel to use the obc- prefix so it matches other
published components and the project’s LitElement naming standard. Keep the
class name the same and only change the registered tag name to the new obc-
form.

Source: Coding guidelines

}

@customElement('ob-vessel-view')
export class Vessel extends LitElement {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Missing class-level JSDoc.

Vessel has no JSDoc block describing its purpose/usage.

As per coding guidelines, "Write JSDoc for concrete components on the class, and keep the documentation domain-agnostic without mentioning maritime or related qualifiers."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts` at line
24, Add a class-level JSDoc block directly above the Vessel class declaration to
document its purpose and intended usage. Use domain-agnostic wording only, and
describe the concrete component at a high level without any maritime-specific
terms; keep the documentation on the Vessel class itself so it matches the
project’s component JSDoc guideline.

Source: Coding guidelines


@customElement('ob-vessel-view')
export class Vessel extends LitElement {
@property({type: Number}) vesselWidth: number = 30;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
rg -n 'vesselWidth' packages/openbridge-webcomponents/src/skipper/vessel-view/

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 1189


🏁 Script executed:

#!/bin/bash
sed -n '1,220p' packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 5574


🏁 Script executed:

#!/bin/bash
sed -n '220,520p' packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 6740


🏁 Script executed:

#!/bin/bash
rg -n '\bvesselWidth\b' .

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 318


Remove the unused vesselWidth property vesselWidth is never read in packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts, so setting it has no visual effect.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts` at line
25, Remove the unused vesselWidth property from the vessel component; it is
declared in the vessel class but never read or used in rendering, so it should
be deleted from the vessel-view/vessel.ts component definition to keep the API
clean.

Comment on lines +147 to +159
override updated(changedProps: Map<string, string>) {
if (changedProps.has('sternToCCRP')) {
if (this.sternToCCRP < 0) {
this.sternToCCRP = 0;
}
}

if (changedProps.has('bowToCCRP')) {
if (this.bowToCCRP < 0) {
this.bowToCCRP = 0;
}
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Inconsistent negative-value guarding across dimension properties.

Only sternToCCRP/bowToCCRP are clamped to >= 0; portToCCRP, starboardToCCRP, sensorHeightOverKeel, and vesselHeight feed similar ratio/division logic elsewhere but aren't guarded against negative input.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts` around
lines 147 - 159, The negative-value guard in updated() only covers sternToCCRP
and bowToCCRP, but the same clamping should be applied consistently to the other
dimension properties that drive the ratio/division logic. Update vessel.ts in
the VesselView component’s updated method to also normalize portToCCRP,
starboardToCCRP, sensorHeightOverKeel, and vesselHeight to a minimum of 0 when
they change, using the same pattern already used for sternToCCRP and bowToCCRP.

showSensorDim,
yAxisCCRPPos: this.yAxisCCRPPos,
starboardDimensionLine: this.starboardDimensionLine,
getArrowHalfHeight: this.getArrowHalfHeight.bind(this),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Fragile unbound method reference for getArrowHalfHeight.

getArrowHalfHeight: this.getArrowHalfHeight (lines 406, 413) passes the raw unbound method, unlike line 393 which uses .bind(this). It only works today because the receiving params object happens to also carry an ARROW_SIZE field, so this.ARROW_SIZE resolves correctly when later invoked as dimPortParams.getArrowHalfHeight() — relying on the caller's object shape mimicking this. This is fragile: if the shape changes, this.ARROW_SIZE would be undefined.

🔧 Proposed fix
-        getMidY: this.getMidY,
         portToCCRP: this.portToCCRP,
         ARROW_SIZE: this.ARROW_SIZE,
-        getArrowHalfHeight: this.getArrowHalfHeight,
+        getArrowHalfHeight: this.getArrowHalfHeight.bind(this),

(apply similarly to the starboard params block)

Also applies to: 406-406, 413-413

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts` at line
393, The `getArrowHalfHeight` callback is being passed around as an unbound
method in the dim/starboard params blocks, which makes its `this` depend on the
caller’s object shape. Update the params assembly in `vessel.ts` so
`getArrowHalfHeight` is consistently bound to the class instance, matching the
existing `getArrowHalfHeight.bind(this)` usage, and apply the same fix in the
other affected params objects that currently pass `this.getArrowHalfHeight`
directly.

Comment thread packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 11

🧹 Nitpick comments (5)
packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts (3)

604-625: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unused helpers.

createPolyline has no active call site; both calls are commented out at Lines 408 and 467. getSVGCoordinates has no call site at all. getSVGCoordinates also passes a possibly undefined matrix into matrixTransform, because getScreenCTM() returns null for a detached element.

Both helpers also use any, which conflicts with the strict TypeScript requirement. Delete them.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`
around lines 604 - 625, Delete the unused createPolyline and getSVGCoordinates
methods from the LoggingGraph class, along with their any-typed implementations;
leave the surrounding active logic and commented-out call sites unchanged.

Source: Coding guidelines


243-262: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Align the gap check with the actual gap value.

The guard tests this.listPointsFunctionOne[i] !== null, but the data encodes gaps with NaN, not null. NaN passes the guard and enters the min/max comparisons. The comparisons happen to evaluate to false for NaN, so the result is correct by accident. Test with Number.isFinite to make the intent explicit and to also reject undefined and Infinity.

The same applies to the functionTwo loop at Lines 277-296.

♻️ Proposed refactor
-      if (this.listPointsFunctionOne[i] !== null) {
+      if (Number.isFinite(this.listPointsFunctionOne[i])) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`
around lines 243 - 262, Update the value guards in the function-one loop and the
corresponding function-two loop to use Number.isFinite on each data point
instead of checking against null. Preserve the existing behavior of pushing
valid points and representing gaps with NaN, while ensuring NaN, undefined, and
Infinity are treated as gaps and excluded from min/max updates.

184-185: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the commented-out code and debug artifacts, and fix the field typo.

The file contains many commented-out console.log calls, a commented-out getFunction helper, and a large commented-out tooltip block at Lines 562-580. The repository guidelines forbid inline comments unless the code is extremely unusual. Delete these blocks. If the tooltip is planned work, track it in an issue instead.

The fields hiddeFunctionOne and hiddeFunctionTwo are misspelled. Rename them to hiddenFunctionOne and hiddenFunctionTwo.

Do you want me to open an issue for the tooltip feature?

Also applies to: 190-215, 447-459, 562-580, 654-654

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`
around lines 184 - 185, In logging-graph.ts, remove the commented-out
console.log calls, getFunction helper, tooltip block, and other debug artifacts
identified in the review; do not replace the tooltip block with inline comments.
Rename the fields hiddeFunctionOne and hiddeFunctionTwo to hiddenFunctionOne and
hiddenFunctionTwo, updating every reference consistently.

Source: Coding guidelines

packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.ts (1)

14-19: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Add height to args and argTypes.

widthDecorator in packages/openbridge-webcomponents/src/storybook-util.ts reads context.args.height and falls back to the width. The second story sets height: 512 at Line 33, but the meta declares neither a default nor a control for it. Users cannot change the height from the Storybook controls panel.

♻️ Proposed change
   args: {
-    width: 512
+    width: 512,
+    height: 512,
   },
   argTypes: {
     width: {control: {type: 'range', min: 32, max: 1028, step: 1}},
+    height: {control: {type: 'range', min: 32, max: 1028, step: 1}},
   },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.ts`
around lines 14 - 19, Update the story meta configuration around the existing
width entries to add a default height of 512 in args and expose height in
argTypes with the same range control as width, so widthDecorator can use and
control context.args.height.
packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.ts (1)

5-29: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove the unused legend rules, or use them.

logging-graph.ts renders the legend as an SVG <rect> in getLegendItem at Lines 585-602. No element carries the legend-item, legend-color, or legend-text class. These rules are dead code. .font-scales is also unused. Delete the unused rules, or apply them to the legend markup.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.ts`
around lines 5 - 29, Remove the unused .legend-item, .legend-color,
.legend-text, and .font-scales rules from the logging graph styles unless the
corresponding classes are intentionally added to the SVG legend markup produced
by getLegendItem. Keep the change limited to eliminating dead CSS or applying
these existing rules to the rendered legend elements.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.ts`:
- Around line 31-43: Update the `.ob-font-ui-body` and `.font-scales` rules in
the logging graph styles to use valid CSS: convert camelCase properties to
kebab-case, terminate declarations with semicolons, and replace `fontColor` with
`fill` for SVG text. Preserve the existing font family, sizes, weights, and
color variable values.

In
`@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.ts`:
- Line 6: Update the Storybook metadata title from “GRAPHS/LoggingGraph” to
“Graphs/Logging Graph”, and rename the second story’s name to use Title Case
while preserving its existing meaning.
- Around line 1-30: Import the LoggingGraph component class as a type alongside
the existing module import, then rename the exported LoggingGraph story to a
distinct story name. Update Meta<LoggingGraph> and StoryObj<LoggingGraph> to
resolve to the imported class while preserving the story’s existing
configuration.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`:
- Around line 15-145: Move the literal sample data from listPointsFunctionOne,
listPointsFunctionTwo, and listTimeStampOfPoints into logging-graph.stories.ts,
and change these component properties to default to empty arrays so library
consumers do not load demo data by default. Preserve the sample values for the
story configuration, correcting the malformed timestamp value '22.06.2023
12:4:055' while relocating it.
- Around line 391-465: Replace the imperative SVG construction around
splitPolyline with Lit svg templates that render both polyline segment
collections and hit-test lines declaratively. Bind each line’s click behavior
through Lit’s `@click` mechanism while preserving the existing coordinate,
selection, timestamp, nameOfFunction, and handleEdit behavior. Avoid direct
document.createElementNS calls and per-render listener creation so Lit can reuse
nodes and support server-side rendering.
- Around line 515-537: Fix the SVG grouping in the template around
getHorizontalBorderLine and getLegendItem: remove the premature closing </g>
after the first group, ensure the second horizontal-border/legend/text block is
enclosed by its own matching <g>...</g> pair, and eliminate the stray closing
tag while preserving the existing rendered elements.
- Around line 323-343: Update the zone calculation logic for functionOne and
functionTwo to clamp each computed range to a positive minimum span before any
division, preserving finite coordinates for constant-value series. Remove the
unreachable negative-range else branches and retain only the nonnegative zone
calculations in the relevant graph setup method.
- Around line 150-163: Update the showVerticalSelectedLine property declaration
to use Lit’s Boolean type while preserving its boolean initialization and
assignments. Remove the unused handleEvent property declaration entirely,
including its invalid Event type.
- Around line 442-462: Update the click handler’s timeOfPoint assignment near
handleEdit so the listTimeStampOfPoints[i] lookup treats both null and undefined
as missing and always assigns a string fallback. Preserve existing timestamp
values while preventing out-of-range point clicks from setting timeOfPoint to
undefined.
- Around line 7-8: Add the required class-level JSDoc above LoggingGraph,
including a one-line summary, features or variants, usage guidance, slots, and a
final `@fires` edit tag documenting the dispatched CustomEvent. Rename the
`@customElement` registration from ob-logging-graph-skipper to the required obc-…
element name, updating any corresponding references if necessary.
- Around line 189-208: Remove the setVerticalLine() call from render() and move
its derived-state update into willUpdate(), alongside the other render-derived
assignments currently mutating timeLeft, timeRight, functionOne, functionTwo,
and zone values. Ensure render() only reads state and produces output, while
showVerticalSelectedLine is updated before rendering.

---

Nitpick comments:
In
`@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.ts`:
- Around line 5-29: Remove the unused .legend-item, .legend-color, .legend-text,
and .font-scales rules from the logging graph styles unless the corresponding
classes are intentionally added to the SVG legend markup produced by
getLegendItem. Keep the change limited to eliminating dead CSS or applying these
existing rules to the rendered legend elements.

In
`@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.ts`:
- Around line 14-19: Update the story meta configuration around the existing
width entries to add a default height of 512 in args and expose height in
argTypes with the same range control as width, so widthDecorator can use and
control context.args.height.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`:
- Around line 604-625: Delete the unused createPolyline and getSVGCoordinates
methods from the LoggingGraph class, along with their any-typed implementations;
leave the surrounding active logic and commented-out call sites unchanged.
- Around line 243-262: Update the value guards in the function-one loop and the
corresponding function-two loop to use Number.isFinite on each data point
instead of checking against null. Preserve the existing behavior of pushing
valid points and representing gaps with NaN, while ensuring NaN, undefined, and
Infinity are treated as gaps and excluded from min/max updates.
- Around line 184-185: In logging-graph.ts, remove the commented-out console.log
calls, getFunction helper, tooltip block, and other debug artifacts identified
in the review; do not replace the tooltip block with inline comments. Rename the
fields hiddeFunctionOne and hiddeFunctionTwo to hiddenFunctionOne and
hiddenFunctionTwo, updating every reference consistently.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 5861f7a1-57fa-4016-88db-b4d5c54c4a97

📥 Commits

Reviewing files that changed from the base of the PR and between 9b11357 and 74d0aef.

📒 Files selected for processing (5)
  • packages/openbridge-webcomponents/src/skipper/interfaces.ts
  • packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.ts
  • packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.ts
  • packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/openbridge-webcomponents/src/skipper/interfaces.ts

Comment on lines +31 to +43
.ob-font-ui-body {
fontFamily: var(--font-family-main),
fontSize: 48px,
fontWeight: var(--global-typography-ui-label-font-weight),
fontColor: var(--instrument-tick-mark-label-secondary-color),
}

.font-scales {
fontFamily: var(--font-family-main),
fontSize: 64px,
fontWeight: var(--global-typography-ui-label-font-weight),
fontColor: var(--instrument-tick-mark-label-secondary-color),
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

These declarations are not valid CSS, so the typography never applies.

.ob-font-ui-body and .font-scales use JavaScript-style camelCase property names and comma terminators. CSS requires kebab-case names and semicolon terminators. fontColor is not a CSS property; SVG text needs fill. The browser therefore drops every declaration in both rules.

The effect is visible: logging-graph.ts applies ob-font-ui-body to the scale and time labels at Lines 488-538, and those labels render with the default font size instead of the intended size.

🐛 Proposed fix
     .ob-font-ui-body {
-        fontFamily: var(--font-family-main),
-        fontSize: 48px,
-        fontWeight: var(--global-typography-ui-label-font-weight),
-        fontColor: var(--instrument-tick-mark-label-secondary-color),
+        font-family: var(--font-family-main);
+        font-size: 48px;
+        font-weight: var(--global-typography-ui-label-font-weight);
+        fill: var(--instrument-tick-mark-label-secondary-color);
     }
 
     .font-scales {
-        fontFamily: var(--font-family-main),
-        fontSize: 64px,
-        fontWeight: var(--global-typography-ui-label-font-weight),
-        fontColor: var(--instrument-tick-mark-label-secondary-color),
+        font-family: var(--font-family-main);
+        font-size: 64px;
+        font-weight: var(--global-typography-ui-label-font-weight);
+        fill: var(--instrument-tick-mark-label-secondary-color);
     }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.ob-font-ui-body {
fontFamily: var(--font-family-main),
fontSize: 48px,
fontWeight: var(--global-typography-ui-label-font-weight),
fontColor: var(--instrument-tick-mark-label-secondary-color),
}
.font-scales {
fontFamily: var(--font-family-main),
fontSize: 64px,
fontWeight: var(--global-typography-ui-label-font-weight),
fontColor: var(--instrument-tick-mark-label-secondary-color),
}
.ob-font-ui-body {
font-family: var(--font-family-main);
font-size: 48px;
font-weight: var(--global-typography-ui-label-font-weight);
fill: var(--instrument-tick-mark-label-secondary-color);
}
.font-scales {
font-family: var(--font-family-main);
font-size: 64px;
font-weight: var(--global-typography-ui-label-font-weight);
fill: var(--instrument-tick-mark-label-secondary-color);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.ts`
around lines 31 - 43, Update the `.ob-font-ui-body` and `.font-scales` rules in
the logging graph styles to use valid CSS: convert camelCase properties to
kebab-case, terminate declarations with semicolons, and replace `fontColor` with
`fill` for SVG text. Preserve the existing font family, sizes, weights, and
color variable values.

Comment on lines +1 to +30
import type {Meta, StoryObj} from '@storybook/web-components-vite';
import {widthDecorator} from '../../storybook-util.js';
import "./logging-graph.js";

const meta: Meta<typeof LoggingGraph> = {
title: 'GRAPHS/LoggingGraph',
tags: ['autodocs', '6.0'],
component: 'ob-logging-graph-skipper',
parameters: {
actions: {
handles: ['click'],
},
},
args: {
width: 512
},
argTypes: {
width: {control: {type: 'range', min: 32, max: 1028, step: 1}},
},
decorators: [widthDecorator],
} satisfies Meta<LoggingGraph>;

export default meta;
type Story = StoryObj<LoggingGraph>;

export const Default: Story = {
args: {},
};

export const LoggingGraph: Story = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

The file does not compile: LoggingGraph is used as a type but only exists as a story value.

Line 3 imports ./logging-graph.js for its side effect only, so the class LoggingGraph is not in scope. Lines 21 and 24 then use LoggingGraph in type positions (Meta<LoggingGraph>, StoryObj<LoggingGraph>). The only LoggingGraph binding in this file is the story constant declared at Line 30, which is a value, not a type. TypeScript reports "refers to a value, but is being used as a type".

Import the class as a type and rename the story so the two names do not collide.

🐛 Proposed fix
 import type {Meta, StoryObj} from '`@storybook/web-components-vite`';
 import {widthDecorator} from '../../storybook-util.js';
-import "./logging-graph.js";
+import type {LoggingGraph} from './logging-graph.js';
+import './logging-graph.js';
 
-const meta: Meta<typeof LoggingGraph> = {
+const meta: Meta<LoggingGraph> = {
@@
-export const LoggingGraph: Story = {
+export const WithTwoSeries: Story = {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import type {Meta, StoryObj} from '@storybook/web-components-vite';
import {widthDecorator} from '../../storybook-util.js';
import "./logging-graph.js";
const meta: Meta<typeof LoggingGraph> = {
title: 'GRAPHS/LoggingGraph',
tags: ['autodocs', '6.0'],
component: 'ob-logging-graph-skipper',
parameters: {
actions: {
handles: ['click'],
},
},
args: {
width: 512
},
argTypes: {
width: {control: {type: 'range', min: 32, max: 1028, step: 1}},
},
decorators: [widthDecorator],
} satisfies Meta<LoggingGraph>;
export default meta;
type Story = StoryObj<LoggingGraph>;
export const Default: Story = {
args: {},
};
export const LoggingGraph: Story = {
import type {Meta, StoryObj} from '`@storybook/web-components-vite`';
import {widthDecorator} from '../../storybook-util.js';
import type {LoggingGraph} from './logging-graph.js';
import './logging-graph.js';
const meta: Meta<LoggingGraph> = {
title: 'GRAPHS/LoggingGraph',
tags: ['autodocs', '6.0'],
component: 'ob-logging-graph-skipper',
parameters: {
actions: {
handles: ['click'],
},
},
args: {
width: 512
},
argTypes: {
width: {control: {type: 'range', min: 32, max: 1028, step: 1}},
},
decorators: [widthDecorator],
} satisfies Meta<LoggingGraph>;
export default meta;
type Story = StoryObj<LoggingGraph>;
export const Default: Story = {
args: {},
};
export const WithTwoSeries: Story = {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.ts`
around lines 1 - 30, Import the LoggingGraph component class as a type alongside
the existing module import, then rename the exported LoggingGraph story to a
distinct story name. Update Meta<LoggingGraph> and StoryObj<LoggingGraph> to
resolve to the imported class while preserving the story’s existing
configuration.

import "./logging-graph.js";

const meta: Meta<typeof LoggingGraph> = {
title: 'GRAPHS/LoggingGraph',

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Use Title Case for the story title and story names.

The guidelines require Title Case for Storybook title and name fields. Change 'GRAPHS/LoggingGraph' to 'Graphs/Logging Graph', and give the second story a Title Case name.

♻️ Proposed change
-  title: 'GRAPHS/LoggingGraph',
+  title: 'Graphs/Logging Graph',
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.ts`
at line 6, Update the Storybook metadata title from “GRAPHS/LoggingGraph” to
“Graphs/Logging Graph”, and rename the second story’s name to use Title Case
while preserving its existing meaning.

Source: Coding guidelines

Comment on lines +7 to +8
@customElement('ob-logging-graph-skipper')
export class LoggingGraph extends LitElement {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add the required class JSDoc and the @fires tag for edit.

The class has no documentation block. The repository guidelines require a class JSDoc with a one-line summary, features or variants, usage guidelines, slots, and events, plus a final tag block that documents dispatched events. The component dispatches an edit CustomEvent at Line 650, so the manifest needs a @fires edit tag.

Also confirm the element name. The guidelines require registration with an obc-… tag; this component uses ob-logging-graph-skipper.

📝 Proposed documentation block
+/**
+ * Logging graph that plots two time series over a shared time axis.
+ *
+ * ## Features
+ * - Two independent series with separate left and right value scales.
+ * - Optional vertical selection line with a time label.
+ * - Gap handling: non-finite values split a series into segments.
+ *
+ * ## Usage guidelines
+ * Use this component to show logged sensor history over a time window.
+ * Do not use it for single instantaneous values; use a readout instead.
+ *
+ * ## Events
+ * Dispatches `edit` when the user selects a point.
+ *
+ * `@fires` edit - Dispatched when the user clicks a point. Detail contains `dataType`, `x`, `y` and `time`.
+ */
 `@customElement`('ob-logging-graph-skipper')
 export class LoggingGraph extends LitElement {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`
around lines 7 - 8, Add the required class-level JSDoc above LoggingGraph,
including a one-line summary, features or variants, usage guidance, slots, and a
final `@fires` edit tag documenting the dispatched CustomEvent. Rename the
`@customElement` registration from ob-logging-graph-skipper to the required obc-…
element name, updating any corresponding references if necessary.

Source: Coding guidelines

Comment on lines +15 to +145
@property({type: Array}) listPointsFunctionOne = [
-5, 0, 5, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 0, 15, 15, 15, 15,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN,
NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN,
NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 12, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 30, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN,
NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN,
NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN,
NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN, NaN,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 30, 28, 29, 28, 27, 26, 25, -15, 23, 22, 21,
20, 21, 22, 8, 8, 8, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 40, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 30, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
26, 25, 24, 23, 22, 21
];

@property({type: Array}) listPointsFunctionTwo = [
-3, 10, -5, 15, 0, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 12, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 56, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 8, 8, 8, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 75, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 65, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 65, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 21,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 28, 27, 26, 25, 24, 23, 22, 22
];

@property({type: Array}) listTimeStampOfPoints = [
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:4:055', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05',
'22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05', '22.06.2023 12:23:05', '22.06.2023 12:45:05', '22.06.2023 13:15:05', '22.06.2023 13:20:05'
];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy lift

Move the large hardcoded sample data out of the component defaults.

listPointsFunctionOne, listPointsFunctionTwo, and listTimeStampOfPoints contain about 1,800 literal values as property defaults. Every consumer that does not set these properties still pays the memory cost, and the demo data ships in the library bundle. Move the sample data into logging-graph.stories.ts and default these properties to empty arrays.

The timestamp at Line 95 also contains a typo: '22.06.2023 12:4:055'.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`
around lines 15 - 145, Move the literal sample data from listPointsFunctionOne,
listPointsFunctionTwo, and listTimeStampOfPoints into logging-graph.stories.ts,
and change these component properties to default to empty arrays so library
consumers do not load demo data by default. Preserve the sample values for the
story configuration, correcting the malformed timestamp value '22.06.2023
12:4:055' while relocating it.

Comment on lines +189 to +208
setVerticalLine(): void {
// console.log('clicked: ' + this.clicked);
if (!this.showFunctionOne && !this.showFunctionTwo) {
this.showVerticalSelectedLine = false;
}
else if (this.clicked) {
this.showVerticalSelectedLine = true;
}
else {
this.showVerticalSelectedLine = false;
}
}

static override styles = [
loggingGraphStyles
];

override render(): unknown {

this.setVerticalLine();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Lit 3 scheduled an update after an update completed warning setting reactive property in render

💡 Result:

The warning "Element X scheduled an update (generally because a property was set) after an update completed, causing a new update to be scheduled" is a developer-mode diagnostic in Lit [1][2]. It indicates that an action taken after the component's update cycle (the update and updated lifecycle stages) has triggered a new, unnecessary update cycle [3][4]. This pattern is considered inefficient because it forces the browser to perform multiple rendering passes when one could suffice [1][5]. Lit components are designed to batch property changes; however, if a property is changed after the update has already finalized, Lit must schedule a subsequent asynchronous update [5][6]. Common causes and recommended resolutions include: 1. Setting reactive properties in updated() or firstUpdated(): These lifecycle methods run after the DOM has been updated [3][4]. If you update a reactive property here, it will trigger another update cycle [4]. * Resolution: If you need to compute derived state before the render, use the update(changedProperties) lifecycle method instead [3][7]. You can perform calculations and set internal state before calling super.update(changedProperties), which avoids triggering an extra update [3][7][8]. 2. Side effects from Controllers or Observers: Sometimes controllers or observers may fire callbacks during hostUpdated [1][2]. * Resolution: If a callback must update a reactive property, see if the logic can be moved to hostUpdate (which runs before the update) [1]. Alternatively, you can use a microtask or await this.updateComplete to defer the update if the side effect is truly necessary [1][9]. 3. Initialization logic: Setting properties in response to initial rendering (e.g., in firstUpdated) often triggers this warning [2]. * Resolution: While sometimes unavoidable, check if these properties can be initialized or computed differently. If the behavior is intentional and unavoidable (e.g., a component must set state based on its own rendered DOM), it is often safe to ignore, though it remains a performance concern [2]. In summary, prioritize performing state calculations in update() before super.update() [3][7]. Only use updated() for side effects that do not involve modifying reactive properties, such as measuring DOM elements or interacting with third-party libraries [10].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate and inspect the target file and relevant declarations/lifecycle usage.
FILE="packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts"
if [ -f "$FILE" ]; then
  echo "== file exists =="
  wc -l "$FILE"
  echo
  echo "== outline =="
  ast-grep outline "$FILE" --view expanded || true
  echo
  echo "== relevant sections =="
  sed -n '1,260p' "$FILE" | cat -n
else
  echo "Target file not found: $FILE"
  echo "Candidate files:"
  fd -i 'logging-graph\.ts$' .
fi

echo
echo "== property declarations and render-side writes/search =="
rg -n "static .*createProperty|`@property`|showVerticalSelectedLine|timeLeft|timeRight|functionOne|functionTwo|setVerticalLine|willUpdate|render\\(" "$FILE" || true

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 34076


🌐 Web query:

Lit Element requestUpdate during render warning reactive properties

💡 Result:

In Lit, triggering an update or changing reactive properties while the component is already in the process of rendering (i.e., during the render() method) is generally discouraged and can lead to warnings or inefficient cycles [1]. Lit components are designed to have their template expressed as a pure function of their state, meaning the render() method should not have side effects like triggering new updates [1][2]. Here is how to handle state updates effectively within the Lit lifecycle: 1. Updates during render are discouraged: Lit is designed to batch property changes to ensure efficient rendering [3][1][2]. If you modify a reactive property or call requestUpdate() while render() is executing, you risk triggering an unnecessary, second update cycle or violating the reactive architecture [1]. 2. Compute state during update(): If you need to derive new state from existing properties, do so inside the update(changedProperties) method before calling super.update(changedProperties) [4][1]. Because this happens before the component renders the DOM, Lit will include these changes in the current update cycle, avoiding the need for a secondary update [1]. 3. Avoid updated() for state triggers: The updated(changedProperties) lifecycle method runs after the component has finished rendering and updating the DOM [1][5]. If you change a reactive property or call requestUpdate() here, Lit will recognize it as a state change occurring after the current cycle and will force an additional, asynchronous update cycle [4][1][5]. 4. Using requestUpdate correctly: requestUpdate() is intended for scheduling an update when state changes occur that are not automatically tracked by reactive properties (e.g., external state or timers) [3][5][6]. It should not be used to force an update loop inside the rendering process [1]. If you find yourself needing to update properties during render(), consider refactoring your component to move that logic into update() or by utilizing computed properties that derive their value directly within the template expression [4][1][2]. top_results: [4][3][1][5][2]

Citations:


🌐 Web query:

Lit 3 "scheduled an update" "requestUpdate" render property setter

💡 Result:

In Lit 3, Lit automatically manages the update request process for properties decorated with @property or @state, including those with custom accessors [1][2]. When you manually implement a property setter, you are responsible for calling the requestUpdate method to trigger an update cycle [3][4]. To do this correctly, you should call this.requestUpdate(propertyName, oldValue) within your custom setter [3][4]. Passing the property name and the previous value is important because it ensures that Lit can correctly track the change in the changedProperties map, which is subsequently used by other lifecycle methods such as willUpdate or updated [5][3][6]. Key takeaways for custom setters in Lit 3: 1. Decorator Placement: If you are using @property or @state with a custom setter, apply the decorator to the setter itself [1]. 2. Manual Update Trigger: Unlike standard properties where Lit handles the call automatically, a manual setter must explicitly call requestUpdate [3][4]. 3. Method Signature: Use this.requestUpdate(name, oldValue) to ensure property options (like hasChanged) are honored and the change is accurately tracked [3][4]. 4. Async Batching: As with all update requests in Lit, the update will be performed asynchronously, batching multiple changes into a single update cycle if they occur before the next frame [5][1][6].

Citations:


🌐 Web query:

Lit Element source requestUpdate render warning reactive properties

💡 Result:

In Lit, scheduling an update (such as by setting a reactive property or calling requestUpdate) while the component is already in the middle of an update cycle will trigger a warning because it forces the component to perform an additional, inefficient update cycle [1]. Specifically, Lit issues a warning when an update is scheduled after the component has already completed its update or is currently rendering, because this often results in a "double-render" or unnecessary overhead [1][2]. To avoid this: 1. Use the update lifecycle appropriately: If you need to derive state or update properties based on changes, do so inside the update method before calling super.update(changedProperties) [3][4]. Properties set in this specific window (before super.update) are captured in the current cycle and do not trigger an additional update [5][3][4]. 2. Avoid setting properties or calling requestUpdate inside the render method or the updated lifecycle hook, as these run after the initial update logic has already been processed or finalized [5][3][4]. 3. If you must perform an action that triggers a new update based on a state change that occurs late in the lifecycle, consider if it can be moved to an earlier stage, such as willUpdate or update [3][4]. If you encounter this warning, it is an indication that your component architecture is triggering multiple render passes for a single logical state change [1]. Moving logic that modifies state into the update hook (before super.update) ensures that Lit can batch these changes into a single, efficient update [6][3].

Citations:


🌐 Web query:

search "Lit" "scheduled an update" ReactiveElement requestUpdate render

💡 Result:

In Lit, the ReactiveElement base class manages a reactive update cycle that is triggered either by changes to reactive properties or by an explicit call to the requestUpdate method [1][2]. Key components of this process include: 1. Requesting an update: When a reactive property changes, its generated setter automatically calls requestUpdate [3][4]. You can also call requestUpdate manually to trigger an update when state changes that are not tied to reactive properties (e.g., a timer or manual DOM manipulation) [1][5]. 2. Asynchronous Batching: Lit performs updates asynchronously to optimize performance [1][5]. If multiple properties change or requestUpdate is called multiple times before the next update cycle begins, these changes are batched into a single update [1][5]. This ensures the component does not perform unnecessary render work [3][6]. 3. Scheduling and Rendering: - The update cycle is scheduled to run before the next frame is painted [1][5]. - Internally, this is handled by the scheduleUpdate method [2][7]. By default, this schedules the update (often as a microtask), but it can be overridden if you need to customize the timing of the update (e.g., to defer it) [5][8]. - When the update executes, the update method is called [1][2]. This method reflects properties to attributes and, in LitElement, invokes the render method to update the component's internal DOM via lit-html [9][5]. 4. Completion: Once the update is complete, the updateComplete promise resolves, allowing external code to react to the finalized rendering [1][5]. For customization, while you can override scheduleUpdate, you should generally avoid overriding performUpdate to change timing, as that method is responsible for synchronously processing the pending update [2][9]. [1][2][5][6]

Citations:


🌐 Web query:

Lit ReactiveElement requestUpdate render warning source

💡 Result:

In Lit, if you receive a warning related to calling requestUpdate() with an Event object as the property name, it is typically because the method was incorrectly bound as an event listener [1]. The official source code for ReactiveElement in Lit includes a development-mode warning designed to catch this common mistake [1]: if (DEV_MODE && (name as unknown) instanceof Event) { issueWarning( '', 'The requestUpdate method was called with an Event as the property name. This is probably a mistake caused by binding this.requestUpdate as an event listener. Instead bind a function that will call it with no arguments: => this.requestUpdate'); } When you use requestUpdate directly as an event listener (e.g., @click="${this.requestUpdate}"), the browser passes the Event object as the first argument to the function [1]. Because requestUpdate expects the first argument to be the name (a property key) of the property being updated, receiving an Event object triggers this warning [1][2]. To fix this, you should wrap the call in an arrow function to ensure requestUpdate is called without arguments, which tells Lit to schedule a standard, non-property-specific update [3][1]: // Incorrect <button @click="${this.requestUpdate}">Update // Correct <button @click="${ => this.requestUpdate}">Update While other warnings related to updates exist—such as those involving improper lifecycle usage or double-rendering patterns—the specific warning mentioning an Event and requestUpdate is explicitly defined in the ReactiveElement source code to address this binding issue [1][4].

Citations:


Do not mutate reactive properties inside render().

render() calls setVerticalLine(), which writes this.showVerticalSelectedLine; render() also writes this.timeLeft, this.timeRight, this.functionOne, this.functionTwo, and the zone values. Lit schedules a new update when reactive properties are changed during rendering, which can produce an additional render pass.

Move this derived state into willUpdate(), or compute the values into locals/properties that are not decorated with @property.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`
around lines 189 - 208, Remove the setVerticalLine() call from render() and move
its derived-state update into willUpdate(), alongside the other render-derived
assignments currently mutating timeLeft, timeRight, functionOne, functionTwo,
and zone values. Ensure render() only reads state and produces output, while
showVerticalSelectedLine is updated before rendering.

Comment on lines +323 to +343
this.functionOne = maxPointFunctionOne - minPointFunctionOne;
this.functionTwo = maxPointFunctionTwo - minPointFunctionTwo;
// console.log('max i min point function one: ' + maxPointFunctionOne + ', ' + minPointFunctionOne);
// console.log('max i min point function two: ' + maxPointFunctionTwo + ', ' + minPointFunctionTwo);

if (this.functionOne >= 0) {
this.zoneZero = Math.round(minPointFunctionOne * 10) / 10; // minPointFunctionOne;
this.zoneOne = Math.round((this.zoneZero + this.functionOne / 5) * 10) / 10;
this.zoneTwo = Math.round((this.zoneOne + this.functionOne / 5) * 10) / 10;
this.zoneThree = Math.round((this.zoneTwo + this.functionOne / 5) * 10) / 10;
this.zoneFour = Math.round((this.zoneThree + this.functionOne / 5) * 10) / 10;
this.zoneFive = Math.round((this.zoneFour + this.functionOne / 5) * 10) / 10;
}
else {
this.zoneFive = Math.round(maxPointFunctionOne * 10) / 10; // maxPointFunctionOne;
this.zoneFour = Math.round((this.functionOne / 5) * 10) / 10;
this.zoneThree = Math.round((this.zoneFour + this.functionOne / 5) * 10) / 10;
this.zoneTwo = Math.round((this.zoneThree + this.functionOne / 5) * 10) / 10;
this.zoneOne = Math.round((this.zoneTwo + this.functionOne / 5) * 10) / 10;
this.zoneZero = Math.round((this.zoneOne + this.functionOne / 5) * 10) / 10;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win

Guard against a zero value range, and remove the unreachable branch.

this.functionOne is maxPointFunctionOne - minPointFunctionOne, so it is never negative. The else branch at Lines 336-343 is unreachable dead code. The same applies to functionTwo at Lines 352-367.

The reachable risk is the zero case. If all plotted values are equal, for example a constant series of 15, then minPointFunctionOne === maxPointFunctionOne and functionOne === 0. Lines 372 and 379 then divide by zero and produce Infinity or NaN coordinates, so the polyline disappears. Clamp the range to a minimum span before you use it as a divisor.

🐛 Proposed fix
-    this.functionOne = maxPointFunctionOne - minPointFunctionOne;
-    this.functionTwo = maxPointFunctionTwo - minPointFunctionTwo;
+    this.functionOne = Math.max(maxPointFunctionOne - minPointFunctionOne, 5);
+    this.functionTwo = Math.max(maxPointFunctionTwo - minPointFunctionTwo, 5);
 
-    if (this.functionOne >= 0) {
-      this.zoneZero = Math.round(minPointFunctionOne * 10) / 10;
-      this.zoneOne = Math.round((this.zoneZero + this.functionOne / 5) * 10) / 10;
-      this.zoneTwo = Math.round((this.zoneOne + this.functionOne / 5) * 10) / 10;
-      this.zoneThree = Math.round((this.zoneTwo + this.functionOne / 5) * 10) / 10;
-      this.zoneFour = Math.round((this.zoneThree + this.functionOne / 5) * 10) / 10;
-      this.zoneFive = Math.round((this.zoneFour + this.functionOne / 5) * 10) / 10;
-    }
-    else {
-      // unreachable
-    }
+    this.zoneZero = Math.round(minPointFunctionOne * 10) / 10;
+    this.zoneOne = Math.round((this.zoneZero + this.functionOne / 5) * 10) / 10;
+    this.zoneTwo = Math.round((this.zoneOne + this.functionOne / 5) * 10) / 10;
+    this.zoneThree = Math.round((this.zoneTwo + this.functionOne / 5) * 10) / 10;
+    this.zoneFour = Math.round((this.zoneThree + this.functionOne / 5) * 10) / 10;
+    this.zoneFive = Math.round((this.zoneFour + this.functionOne / 5) * 10) / 10;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`
around lines 323 - 343, Update the zone calculation logic for functionOne and
functionTwo to clamp each computed range to a positive minimum span before any
division, preserving finite coordinates for constant-value series. Remove the
unreachable negative-range else branches and retain only the nonnegative zone
calculations in the relevant graph setup method.

Comment on lines +391 to +465
const svgElementFunctionOne = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
const segmentsOfPolylineOne = this.splitPolyline(functionOneArray);
segmentsOfPolylineOne.forEach(segment => {
let pointsAttr = '';
for(let i = 0; i < segment.length; i++) {
pointsAttr += segment[i] + ' ';
}
const polyline = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
polyline.setAttribute("points", pointsAttr);
polyline.setAttribute("stroke", "var(--instrument-enhanced-primary-color)");
polyline.setAttribute("fill", "none");
polyline.setAttribute("stroke-width", "2");

svgElementFunctionOne.appendChild(polyline);

});

// this.polylineOne = this.createPolyline(functionOneArray);

const svgElementFunctionTwo = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
const segmentsOfPolylineTwo = this.splitPolyline(functionTwoArray);
segmentsOfPolylineTwo.forEach(segment => {
let pointsAttr = '';
for(let i = 0; i < segment.length; i++) {
pointsAttr += segment[i] + ' ';
}
const polyline = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
polyline.setAttribute("points", pointsAttr);
polyline.setAttribute("stroke", "var(--alert-running-color)");
polyline.setAttribute("fill", "none");
polyline.setAttribute("stroke-width", "2");

svgElementFunctionTwo.appendChild(polyline);
});

const svgContainer = document.createElementNS("http://www.w3.org/2000/svg", 'svg');
const fragment = document.createDocumentFragment();
for (let i = 0; i < functionOneArray.length; i++) {
const coordinatex: number = functionOneArray[i][0];

const line = document.createElementNS("http://www.w3.org/2000/svg", 'line');
line.setAttribute("x1", coordinatex + '');
line.setAttribute("x2", coordinatex + '');
line.setAttribute("y1", 495 + '');
line.setAttribute("y2", 70 + '');
line.setAttribute("stroke", "transparent");
line.setAttribute("stroke-width", lineWidth + '');
line.setAttribute("cursor", "inherit");

fragment.appendChild(line);

line.addEventListener('click', () => { //'click'

this.xPositionSelectedLine = coordinatex - 256.85;
this.showVerticalSelectedLine = true;
this.clicked = true;
// this.yPositionSelectedLine = coordinateyMax - 351;
this.xValue = i.toString();
// console.log('valueOfFunctionOne: ' + i);
// if (this.listPointsFunctionOneMax.length >= i && this.listPointsFunctionOneMax[i] !== null) {
// const temp: number = this.listPointsFunctionOneMax[i];
// this.yValue = (temp.toFixed(2)).toString(); //.toFixed(1);
// }
// this.yValue = (this.listPointsFunctionOneMax[i].toFixed(2)).toString(); //.toFixed(2);
this.timeOfPoint = this.listTimeStampOfPoints[i] !== null ? this.listTimeStampOfPoints[i] : '';
this.nameOfFunction = this.nameOfFunctionOne;

// this.opacity = 1;
// this.colorPoint = "var(--instrument-enhanced-primary-color)";

this.handleEdit('Blue&Green_Data', this.xValue, this.yValue, this.timeOfPoint);
});
}

svgContainer.appendChild(fragment);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀 Performance & Scalability | 🟠 Major | 🏗️ Heavy lift

Build the series and hit targets with Lit templates instead of imperative DOM.

This block creates detached SVG nodes with document.createElementNS on every render. It also attaches a fresh click listener to each of the 601 hit-test lines on every render. Every property change therefore allocates about 600 elements and 600 closures, and Lit cannot reuse any of them. The direct document reference also blocks server-side rendering.

Render the polyline segments and the hit-test lines with the svg template and bind the handler with @click, so Lit reuses the nodes and manages the listeners.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`
around lines 391 - 465, Replace the imperative SVG construction around
splitPolyline with Lit svg templates that render both polyline segment
collections and hit-test lines declaratively. Bind each line’s click behavior
through Lit’s `@click` mechanism while preserving the existing coordinate,
selection, timestamp, nameOfFunction, and handleEdit behavior. Avoid direct
document.createElementNS calls and per-render listener creation so Lit can reuse
nodes and support server-side rendering.

Comment on lines +442 to +462
line.addEventListener('click', () => { //'click'

this.xPositionSelectedLine = coordinatex - 256.85;
this.showVerticalSelectedLine = true;
this.clicked = true;
// this.yPositionSelectedLine = coordinateyMax - 351;
this.xValue = i.toString();
// console.log('valueOfFunctionOne: ' + i);
// if (this.listPointsFunctionOneMax.length >= i && this.listPointsFunctionOneMax[i] !== null) {
// const temp: number = this.listPointsFunctionOneMax[i];
// this.yValue = (temp.toFixed(2)).toString(); //.toFixed(1);
// }
// this.yValue = (this.listPointsFunctionOneMax[i].toFixed(2)).toString(); //.toFixed(2);
this.timeOfPoint = this.listTimeStampOfPoints[i] !== null ? this.listTimeStampOfPoints[i] : '';
this.nameOfFunction = this.nameOfFunctionOne;

// this.opacity = 1;
// this.colorPoint = "var(--instrument-enhanced-primary-color)";

this.handleEdit('Blue&Green_Data', this.xValue, this.yValue, this.timeOfPoint);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

timeOfPoint can become undefined and then crash the next render.

The click handler reads this.listTimeStampOfPoints[i]. The !== null check does not catch a missing index, because an out-of-range read returns undefined. The default data makes this reachable: the point loop pushes indices 0 through 600, so it produces 601 hit lines, while listTimeStampOfPoints holds 600 entries. A click on the last line sets this.timeOfPoint = undefined. The next render then calls this.timeOfPoint.split(' ') at Line 559 and throws TypeError: Cannot read properties of undefined.

Fix the read at the source so timeOfPoint always holds a string.

🐛 Proposed fix
-        this.timeOfPoint = this.listTimeStampOfPoints[i] !== null ? this.listTimeStampOfPoints[i] : '';
+        this.timeOfPoint = this.listTimeStampOfPoints[i] ?? '';
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
line.addEventListener('click', () => { //'click'
this.xPositionSelectedLine = coordinatex - 256.85;
this.showVerticalSelectedLine = true;
this.clicked = true;
// this.yPositionSelectedLine = coordinateyMax - 351;
this.xValue = i.toString();
// console.log('valueOfFunctionOne: ' + i);
// if (this.listPointsFunctionOneMax.length >= i && this.listPointsFunctionOneMax[i] !== null) {
// const temp: number = this.listPointsFunctionOneMax[i];
// this.yValue = (temp.toFixed(2)).toString(); //.toFixed(1);
// }
// this.yValue = (this.listPointsFunctionOneMax[i].toFixed(2)).toString(); //.toFixed(2);
this.timeOfPoint = this.listTimeStampOfPoints[i] !== null ? this.listTimeStampOfPoints[i] : '';
this.nameOfFunction = this.nameOfFunctionOne;
// this.opacity = 1;
// this.colorPoint = "var(--instrument-enhanced-primary-color)";
this.handleEdit('Blue&Green_Data', this.xValue, this.yValue, this.timeOfPoint);
});
line.addEventListener('click', () => { //'click'
this.xPositionSelectedLine = coordinatex - 256.85;
this.showVerticalSelectedLine = true;
this.clicked = true;
// this.yPositionSelectedLine = coordinateyMax - 351;
this.xValue = i.toString();
// console.log('valueOfFunctionOne: ' + i);
// if (this.listPointsFunctionOneMax.length >= i && this.listPointsFunctionOneMax[i] !== null) {
// const temp: number = this.listPointsFunctionOneMax[i];
// this.yValue = (temp.toFixed(2)).toString(); //.toFixed(1);
// }
// this.yValue = (this.listPointsFunctionOneMax[i].toFixed(2)).toString(); //.toFixed(2);
this.timeOfPoint = this.listTimeStampOfPoints[i] ?? '';
this.nameOfFunction = this.nameOfFunctionOne;
// this.opacity = 1;
// this.colorPoint = "var(--instrument-enhanced-primary-color)";
this.handleEdit('Blue&Green_Data', this.xValue, this.yValue, this.timeOfPoint);
});
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts`
around lines 442 - 462, Update the click handler’s timeOfPoint assignment near
handleEdit so the listTimeStampOfPoints[i] lookup treats both null and undefined
as missing and always assigns a string fallback. Preserve existing timestamp
values while preventing out-of-range point clicks from setting timeOfPoint to
undefined.

talpitoo and others added 2 commits August 11, 2026 13:43
Closes Ocean-Industries-Concept-Lab#1113. Closes Ocean-Industries-Concept-Lab#972.

obc-keyboard-full rendered its letter keys uppercase whenever CAPS was on,
but onKeyPress only uppercased the inserted character when the number row
was hidden — so with showNumberRow the keys read "Q" and typed "q". The
inserted character is now the exact label the key renders.

The readonly property on obc-text-input-field and obc-number-input-field
reached the native input but nothing else: the wrapper still ran the
@mixin style hover and pressed states, the clear button still cleared, and
the number field still reformatted its display on focus.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Note

GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 13

🧹 Nitpick comments (9)
packages/openbridge-webcomponents/src/components/accordion-card/accordion-card.css (1)

8-11: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the ineffective duplicate width declaration.

Line 8 sets width: auto, but Line 11 immediately overrides it with width: 100%. Keep only the value that matches the intended sizing.

Remove the no-op declaration
 .wrapper {
   display: flex;
   user-select: none;
-  width: auto;
   flex-direction: column;
   align-items: center;
   width: 100%;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/components/accordion-card/accordion-card.css`
around lines 8 - 11, Remove the earlier width: auto declaration from the
accordion-card styles, retaining the later width: 100% declaration as the
intended sizing.
packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts (8)

309-311: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the commented-out advice block and the unused mask10_6621_15748 mask.

Lines 331-341 contain a commented-out SVG group. Lines 309-311 define mask10_6621_15748. The only reference to that mask is inside the commented-out block, so the mask is dead markup that still reaches the DOM. getAlertHighMask() repeats the same unused definition at Lines 359-361.

Delete both the comment block and the unused mask definitions. If the caution shading is planned work, track it in an issue instead.

Do you want me to open an issue for the caution shading?

Also applies to: 331-341

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 309 - 311, Remove the unused SVG mask definitions named
mask10_6621_15748 from the speedometer markup and getAlertHighMask(), along with
the commented-out SVG group that references them. Do not alter active rendering
logic or add replacement caution shading.

936-1013: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Share the polar-coordinate helpers between the two sector builders.

describeSector() and describeDonutSector() each declare their own toRad and polar closures with identical bodies. The closures are recreated on every call, and every call happens during render.

Extract one module-level polarPoint(cx, cy, r, angleDeg) function and use it in both methods.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 936 - 1013, Extract the duplicated toRad/polar logic from
describeSector and describeDonutSector into a module-level polarPoint(cx, cy, r,
angleDeg) helper. Replace both methods’ local closures with calls to polarPoint
while preserving the existing coordinate calculations and sector paths.

878-901: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

getHighAlertColor() is unused and is part of the public API.

getHighAlertColor() has no call site in this file. It is also declared without private, so it is exposed on the element instance. Its logic ignores showSog, showStw, and stwSpeed, unlike getHighAdviceColor() at Line 858, so the two colour helpers would disagree if a consumer called it.

Several other internal methods are also public by omission: setViewBox, getSogSpeed, getStwSpeed, setRotate, setStwRotate, setLowIntegrity, getLowIntegrity, getTickmarks25, getTickmarks50, getRadiusMask, and cutTickmarks. Once released, these become part of the component contract.

Delete getHighAlertColor() and getAlertColor() if they are not needed. Mark the remaining internal methods private.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 878 - 901, Remove the unused getHighAlertColor() and
getAlertColor() helpers. In the speedometer class, mark the internal methods
setViewBox, getSogSpeed, getStwSpeed, setRotate, setStwRotate, setLowIntegrity,
getLowIntegrity, getTickmarks25, getTickmarks50, getRadiusMask, and cutTickmarks
as private without changing their behavior.

701-718: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Both getSecondaryNeedle() branches emit identical markup.

The STW branch and the else branch differ only in the rotation source. The circle and path elements are identical.

♻️ Proposed refactor
   private getSecondaryNeedle(speedType: SpeedType) {
-    if (speedType == SpeedType.STW) {
-      return svg`
-        <g transform="rotate(${-125 + this.rotateStw} 478.355 429)">
-          <circle cx="478.355" cy="430.774" r="19.8088" fill="${Colors.instrumentRegularSecondary}"/>
-          <path d="M578.738 283.422L450.751 451.759C447.116 456.541 448.185 463.386 453.105 466.831C458.026 470.277 464.824 468.94 468.074 463.889L582.485 286.046C583.15 285.012 582.88 283.637 581.873 282.932C580.866 282.227 579.482 282.443 578.738 283.422Z" fill="${this.getSecondaryNeedleFillColor(speedType)}" stroke="${Colors.instrumentTickmarkTertiary}" stroke-width="2.08333"/>
-        </g>
-      `;
-    }
-    else {
-      return svg`
-        <g transform="rotate(${-125 + this.rotate} 478.355 429)">
-          <circle cx="478.355" cy="430.774" r="19.8088" fill="${Colors.instrumentRegularSecondary}"/>
-          <path d="M578.738 283.422L450.751 451.759C447.116 456.541 448.185 463.386 453.105 466.831C458.026 470.277 464.824 468.94 468.074 463.889L582.485 286.046C583.15 285.012 582.88 283.637 581.873 282.932C580.866 282.227 579.482 282.443 578.738 283.422Z" fill="${this.getSecondaryNeedleFillColor(speedType)}" stroke="${Colors.instrumentTickmarkTertiary}" stroke-width="2.08333"/>
-        </g>
-      `;
-    }
+    const rotation = speedType == SpeedType.STW ? this.rotateStw : this.rotate;
+    return svg`
+      <g transform="rotate(${-125 + rotation} 478.355 429)">
+        <circle cx="478.355" cy="430.774" r="19.8088" fill="${Colors.instrumentRegularSecondary}"/>
+        <path d="M578.738 283.422L450.751 451.759C447.116 456.541 448.185 463.386 453.105 466.831C458.026 470.277 464.824 468.94 468.074 463.889L582.485 286.046C583.15 285.012 582.88 283.637 581.873 282.932C580.866 282.227 579.482 282.443 578.738 283.422Z" fill="${this.getSecondaryNeedleFillColor(speedType)}" stroke="${Colors.instrumentTickmarkTertiary}" stroke-width="2.08333"/>
+      </g>
+    `;
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 701 - 718, Refactor getSecondaryNeedle so the shared circle and
path SVG markup is emitted once, while preserving the branch-specific rotation
source: use rotateStw for SpeedType.STW and rotate otherwise. Keep
getSecondaryNeedleFillColor(speedType) and all existing visual attributes
unchanged.

673-674: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the leftover implementation note, or complete the behavior.

Line 673 is an inline note describing unfinished work. The repository guidelines allow inline comments only when the code cannot be understood without them. This note also depends on lowIntegrity, which the component sets but never reads for rendering. getMainNeedleFillColor() already applies Colors.alertCautionColor for out-of-range values, so the note may already be obsolete.

Delete the note if the behavior is complete. If it is not complete, track it in an issue and remove the flag or use it.

Do you want me to open an issue for the low-integrity needle colour?

As per coding guidelines: "Avoid inline comments unless code is extremely unusual and cannot be understood without explanation".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 673 - 674, Remove the leftover inline note immediately before
getMainNeedle, since getMainNeedleFillColor already handles the applicable
caution color and lowIntegrity is not used for rendering. Leave the existing
needle behavior unchanged.

Source: Coding guidelines


66-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Collapse the duplicated clamp helpers.

getSogSpeed() and getStwSpeed() are identical except for the source field. Each uses four branches where one expression is enough. The same duplication pattern repeats in setRotate()/setStwRotate() (Lines 113-141), getMainNeedleFillColor()/getSecondaryNeedleFillColor() (Lines 654-699), and getHighState()/getLowState() (Lines 819-841).

Extract one parameterised helper and call it from both speed types.

♻️ Proposed refactor
-  getSogSpeed(): number {
-    if (this.sogSpeed <= this.maxSpeed && this.sogSpeed >= this.minSpeed) {
-      return this.sogSpeed;
-    }
-    else if (this.sogSpeed > this.maxSpeed) {
-      return this.maxSpeed;
-    }
-    else if (this.sogSpeed < this.minSpeed) {
-      return this.minSpeed;
-    }
-    else {
-      return this.minSpeed;
-    }
-  }
-
-  getStwSpeed(): number {
-    if (this.stwSpeed <= this.maxSpeed && this.stwSpeed >= this.minSpeed) {
-      return this.stwSpeed;
-    }
-    else if (this.stwSpeed > this.maxSpeed) {
-      return this.maxSpeed;
-    }
-    else if (this.stwSpeed < this.minSpeed) {
-      return this.minSpeed;
-    }
-    else {
-      return this.minSpeed;
-    }
-  }
+  private clampSpeed(speed: number): number {
+    if (Number.isNaN(speed)) {
+      return this.minSpeed;
+    }
+    return Math.min(this.maxSpeed, Math.max(this.minSpeed, speed));
+  }
+
+  getSogSpeed(): number {
+    return this.clampSpeed(this.sogSpeed);
+  }
+
+  getStwSpeed(): number {
+    return this.clampSpeed(this.stwSpeed);
+  }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 66 - 94, Extract a parameterized clamp helper for speed values and
update getSogSpeed() and getStwSpeed() to delegate to it, preserving
minSpeed/maxSpeed bounds and existing behavior. Apply the same deduplication
approach to setRotate()/setStwRotate(),
getMainNeedleFillColor()/getSecondaryNeedleFillColor(), and
getHighState()/getLowState() by introducing shared helpers with the differing
values passed as parameters.

804-817: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the unused getRadiusMask() method.

No repository call site exists. Its path-10-outside-1_22_3634 mask definition is inside an HTML comment, so the rendered mask reference cannot resolve.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 804 - 817, Remove the unused getRadiusMask() method and its
unreachable SVG mask markup, including the commented path-10-outside-1_22_3634
definition.

422-485: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consolidate the duplicated dual-value branches and correct the single-value viewport.

  • In getSpeedValue(), only the two dual-value branches duplicate the template. Select main and secondary once, while preserving their values, tags, and neutralColor settings.
  • When one speed is shown, setViewBox() ends at y=692, but the single-value <foreignObject> spans y=580..780. Adjust the viewport or field position so the scaled enhanced field remains visible. Cover both single-value stories with showSpeedAsNumber: true.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 422 - 485, In getSpeedValue(), consolidate the showSog && showStw
branches by selecting main and secondary speed values, tags, and neutralColor
settings once, then rendering one shared dual-value template. Correct the
single-value layout versus setViewBox() so the enhanced foreignObject remains
visible within the viewport, and add coverage for both single-speed cases with
showSpeedAsNumber enabled.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@packages/openbridge-webcomponents/src/components/accordion-card/accordion-card.css`:
- Around line 5-11: Restore the :host display rule for obc-accordion-card with
display: block, while leaving the existing .wrapper layout unchanged so its
width: 100% reliably fills the parent and preserves the WithAlert fixed-width
layout.

In
`@packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.ts`:
- Around line 218-220: Update number-input-field’s onBlur() readonly path to
synchronize displayText from the current value before returning, while
preserving any non-empty displayOverride. Add a regression test covering an
external value update while the readonly input is focused, then blur and verify
the displayed text refreshes correctly.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`:
- Around line 769-775: Update getTickmarks25 so its mask uses a unique SVG id
instead of mask0_6621_15748, and change the corresponding mask reference in that
method to match. Keep getWatchFace’s existing mask id and preserve the current
rendering behavior.
- Around line 212-224: Update the masks in renderPrimaryBarArea() and the
secondary bar rendering around barMaskSTW-${index} so their revealing path uses
white, preserving the default luminance mask behavior. Replace the arbitrary
bar.fillColor and Colors.instrumentFramePrimary fills used for mask sectors
without changing the visible bar colors elsewhere.
- Around line 113-149: Update setRotate() and setStwRotate() so neither method
independently overwrites lowIntegrity; retain their rotation calculations while
deriving the flag once from both SOG and STW range checks, ensuring
getLowIntegrity() is true when either speed is outside the valid range.
- Around line 10-15: Add a class-level JSDoc immediately above the registered
Speedometer class with a domain-agnostic summary, features or variants, and
usage guidelines, plus explicit documentation that it has no slots or custom
events. Do not add `@property`, `@slot`, or `@fires` tags; keep the component
implementation unchanged.
- Around line 14-15: Rename the Speedometer custom element registration from
`ob-speedometer` to `obc-speedometer`, and update the corresponding
`HTMLElementTagNameMap` entry, stories, and skipper barrel export to use the new
public tag name. Run `npm run analyze` after the rename.
- Around line 487-525: Remove the main needle `<g>` groups from both the SOG and
STW branches of getPrimaryContainer(), leaving only their bar-area rendering;
getPrimaryNeedle() already supplies the correctly layered needle. Do not change
the existing speed checks or rotation behavior elsewhere.
- Around line 365-375: Update the high-advice mask condition in the speedometer
render logic to gate each speed threshold by its corresponding visibility flag:
only consider SOG when showSog is enabled and STW when showStw is enabled. Keep
the existing renderPathMaskOfHighAdvice behavior unchanged once either displayed
speed exceeds alertHigh, matching getHighAdviceColor() and getHighState().
- Around line 729-733: Update getLabels25() to return its label paths inside a
plain <g> rather than a nested <svg>, matching getLabels50() and preserving the
outer SVG coordinate system; replace the corresponding closing element as well,
then verify the 25-knot labels align with their tickmarks in Storybook.
- Around line 40-51: Update render and the related speedometer calculations so
setMaxSpeed() no longer mutates the reactive maxSpeed input. Derive separate
effectiveMaxSpeed (25 or 50), minSpeed, and rotationAngle values, then use
effectiveMaxSpeed consistently for geometry, alert thresholds, and labels while
preserving maxSpeed unchanged.

In `@packages/openbridge-webcomponents/src/skipper/tickmark.ts`:
- Around line 70-73: Update the scale validation in the tickmark logic to reject
every non-finite value, including NaN, while retaining rejection of non-positive
finite values. Remove the redundant inline comment above the validation.
- Line 1: Update the scale validation in the tickmark rendering logic to reject
all non-finite values and non-positive scales using the finite check before
calculating SVG coordinates or attributes. Remove the redundant inline comment
and preserve the existing handling for invalid scales.

---

Nitpick comments:
In
`@packages/openbridge-webcomponents/src/components/accordion-card/accordion-card.css`:
- Around line 8-11: Remove the earlier width: auto declaration from the
accordion-card styles, retaining the later width: 100% declaration as the
intended sizing.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`:
- Around line 309-311: Remove the unused SVG mask definitions named
mask10_6621_15748 from the speedometer markup and getAlertHighMask(), along with
the commented-out SVG group that references them. Do not alter active rendering
logic or add replacement caution shading.
- Around line 936-1013: Extract the duplicated toRad/polar logic from
describeSector and describeDonutSector into a module-level polarPoint(cx, cy, r,
angleDeg) helper. Replace both methods’ local closures with calls to polarPoint
while preserving the existing coordinate calculations and sector paths.
- Around line 878-901: Remove the unused getHighAlertColor() and getAlertColor()
helpers. In the speedometer class, mark the internal methods setViewBox,
getSogSpeed, getStwSpeed, setRotate, setStwRotate, setLowIntegrity,
getLowIntegrity, getTickmarks25, getTickmarks50, getRadiusMask, and cutTickmarks
as private without changing their behavior.
- Around line 701-718: Refactor getSecondaryNeedle so the shared circle and path
SVG markup is emitted once, while preserving the branch-specific rotation
source: use rotateStw for SpeedType.STW and rotate otherwise. Keep
getSecondaryNeedleFillColor(speedType) and all existing visual attributes
unchanged.
- Around line 673-674: Remove the leftover inline note immediately before
getMainNeedle, since getMainNeedleFillColor already handles the applicable
caution color and lowIntegrity is not used for rendering. Leave the existing
needle behavior unchanged.
- Around line 66-94: Extract a parameterized clamp helper for speed values and
update getSogSpeed() and getStwSpeed() to delegate to it, preserving
minSpeed/maxSpeed bounds and existing behavior. Apply the same deduplication
approach to setRotate()/setStwRotate(),
getMainNeedleFillColor()/getSecondaryNeedleFillColor(), and
getHighState()/getLowState() by introducing shared helpers with the differing
values passed as parameters.
- Around line 804-817: Remove the unused getRadiusMask() method and its
unreachable SVG mask markup, including the commented path-10-outside-1_22_3634
definition.
- Around line 422-485: In getSpeedValue(), consolidate the showSog && showStw
branches by selecting main and secondary speed values, tags, and neutralColor
settings once, then rendering one shared dual-value template. Correct the
single-value layout versus setViewBox() so the enhanced foreignObject remains
visible within the viewport, and add coverage for both single-speed cases with
showSpeedAsNumber enabled.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 469457d9-741b-49d0-b377-662a83d6432c

📥 Commits

Reviewing files that changed from the base of the PR and between dd8c261 and 3209cd2.

⛔ Files ignored due to path filters (42)
  • package-lock.json is excluded by !**/package-lock.json
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/bars-graphs/area-graph/area-graph.stories.ts/no-labels-ticks-area-graph--but-yes-32px-padding-for-optional-points--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/bars-graphs/line-graph/line-graph.stories.ts/no-labels-ticks-line-graph--but-yes-32px-padding-for-optional-points--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/chart-integration--as-external-bottom-axis--scalebackground--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/component-size-comparison--regular-medium-large-xl--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-vertical/bar-vertical.stories.ts/chart-integration--as-external-right-axis--scalebackground--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/chart-line/chart-line-base.stories.ts/no-labels-ticks-line-graph--but-yes-32px-padding-for-optional-points--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/chart-line/chart-line-base.stories.ts/threshold-height-line-graph--192px--where-labels-appear--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/horizontal--bottom-side--hasbar--advices--setpoint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/horizontal--top-side--hasbar--advices--setpoint--fillmode-tint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/vertical--left-side--hasbar--advices--setpoint--fillmode-tint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-button-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-state-off-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-state-on-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/state-off-without-icon-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-multi-application-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-multi-regular-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-single-application-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-single-regular-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-multi-application-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-multi-regular-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-single-application-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-single-regular-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/number-input-field/number-input-field.stories.ts/readonly-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/toggle-button-vertical-group/toggle-button-vertical-group.stories.ts/initially-disabled-selection-falls-back-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/toggle-button-vertical-group/toggle-button-vertical-group.stories.ts/label-under-regular-disabled-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/components/topbar-message-item/topbar-message-item.stories.ts/large-with-long-text-and-description-and-both-timestamps-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/azimuth-thruster/azimuth-thruster.stories.ts/in-command-at-setpoint-disable-auto-setpoint-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/component-size-comparison--regular-medium-large-xl--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/fill-mode-comparison--enhanced--fill-vs-tint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/state-comparison--incommand-active-loading-off--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/with-setpoint--value-away-from-setpoint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/component-size-comparison--regular-medium-large-xl--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/enhanced-mode-comparison--regular-vs-enhanced--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/fill-mode-comparison--enhanced--fill-vs-tint--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/state-comparison--incommand-active-loading-off--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/main-engine-indicator/propulsion-main-engine-indicator.stories.ts/in-command---silhouette-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/main-engine-indicator/propulsion-main-engine-indicator.stories.ts/not-in-command---silhouette-auto.png is excluded by !**/*.png
  • packages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/propulsion-tunnel-thruster/propulsion-tunnel-thruster.stories.ts/not-in-command---silhouette-auto.png is excluded by !**/*.png
📒 Files selected for processing (41)
  • .devcontainer/devcontainer-lock.json
  • packages/openbridge-webcomponents/oicl-openbridge-webcomponents-2.0.0-next.29.tgz
  • packages/openbridge-webcomponents/src/components/accordion-card/accordion-card.css
  • packages/openbridge-webcomponents/src/components/keyboard-full/keyboard-full.spec.ts
  • packages/openbridge-webcomponents/src/components/keyboard-full/keyboard-full.ts
  • packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.spec.ts
  • packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.stories.ts
  • packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.ts
  • packages/openbridge-webcomponents/src/components/text-input-field/text-input-field.spec.ts
  • packages/openbridge-webcomponents/src/components/text-input-field/text-input-field.ts
  • packages/openbridge-webcomponents/src/mixins/base-input-field.css
  • packages/openbridge-webcomponents/src/skipper/advice.ts
  • packages/openbridge-webcomponents/src/skipper/current-icons.ts
  • packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot-styles.ts
  • packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.stories.ts
  • packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts
  • packages/openbridge-webcomponents/src/skipper/index.ts
  • packages/openbridge-webcomponents/src/skipper/interfaces.ts
  • packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.ts
  • packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.ts
  • packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-styles.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat.stories.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat.ts
  • packages/openbridge-webcomponents/src/skipper/models.ts
  • packages/openbridge-webcomponents/src/skipper/speedometer/speedometer-styles.ts
  • packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.stories.ts
  • packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts
  • packages/openbridge-webcomponents/src/skipper/tickmark.ts
  • packages/openbridge-webcomponents/src/skipper/true-relative/true-relative-styles.ts
  • packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.stories.ts
  • packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts
  • packages/openbridge-webcomponents/src/skipper/utils/storybook-helpers.ts
  • packages/openbridge-webcomponents/src/skipper/utils/uuid.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-styles.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts
  • packages/openbridge-webcomponents/src/skipper/wind-icons.ts
🚧 Files skipped from review as they are similar to previous changes (29)
  • packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.ts
  • packages/openbridge-webcomponents/src/skipper/utils/uuid.ts
  • .devcontainer/devcontainer-lock.json
  • packages/openbridge-webcomponents/src/skipper/utils/storybook-helpers.ts
  • packages/openbridge-webcomponents/src/skipper/true-relative/true-relative-styles.ts
  • packages/openbridge-webcomponents/src/skipper/models.ts
  • packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot-styles.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.ts
  • packages/openbridge-webcomponents/src/skipper/speedometer/speedometer-styles.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-styles.ts
  • packages/openbridge-webcomponents/src/skipper/interfaces.ts
  • packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.stories.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-styles.ts
  • packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.ts
  • packages/openbridge-webcomponents/src/skipper/current-icons.ts
  • packages/openbridge-webcomponents/src/skipper/wind-icons.ts
  • packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.stories.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat.stories.ts
  • packages/openbridge-webcomponents/src/skipper/index.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel.ts
  • packages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.ts
  • packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.stories.ts
  • packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts
  • packages/openbridge-webcomponents/src/skipper/true-relative/true-relative.ts
  • packages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.ts
  • packages/openbridge-webcomponents/src/skipper/advice.ts
  • packages/openbridge-webcomponents/src/skipper/long-lat/long-lat.ts

Comment on lines 5 to 11
.wrapper {
display: flex;
user-select: none;
width: auto;
flex-direction: column;
align-items: center;
width: 100%;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Restore the host display rule.

Removing :host { display: block; } leaves obc-accordion-card with the CSS initial display: inline. The width property does not apply to non-replaced inline elements, so .wrapper { width: 100%; } does not guarantee that the component fills its parent. This can break the fixed-width WithAlert layout. (w3.org)

Restore the block-level host
+:host {
+  display: block;
+}
+
 .wrapper {
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
.wrapper {
display: flex;
user-select: none;
width: auto;
flex-direction: column;
align-items: center;
width: 100%;
:host {
display: block;
}
.wrapper {
display: flex;
user-select: none;
width: auto;
flex-direction: column;
align-items: center;
width: 100%;
🧰 Tools
🪛 Stylelint (17.14.0)

[error] 8-8: Duplicate property "width" (declaration-block-no-duplicate-properties)

(declaration-block-no-duplicate-properties)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/components/accordion-card/accordion-card.css`
around lines 5 - 11, Restore the :host display rule for obc-accordion-card with
display: block, while leaving the existing .wrapper layout unchanged so its
width: 100% reliably fills the parent and preserves the WithAlert fixed-width
layout.

Comment on lines 218 to +220
private onBlur() {
this.hasFocus = false;
if (this.readonly) return;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the focus, blur, and reactive-update paths.
sed -n '205,226p;331,356p' \
  packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.ts

# Inspect existing readonly coverage before adding the focused external-update case.
sed -n '355,391p' \
  packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.spec.ts

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 2798


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the complete state-update and rendering paths.
sed -n '1,180p;180,245p;300,370p' \
  packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.ts

# Inspect the component properties and readonly tests for display behavior.
rg -n -C 5 "displayText|displayOverride|shouldUpdateValue|readonly|value" \
  packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.ts \
  packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.spec.ts

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 50409


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the render binding and every source of displayOverride.
rg -n -C 8 "getEffectiveDisplay|displayOverride|`@property`.*value|`@property`.*readonly|`@focus`|`@blur`" \
  packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.ts \
  packages/openbridge-webcomponents/src/components \
  packages/openbridge-webcomponents/src/navigation-instruments

# Read the focused render section and all existing readonly tests without broad output.
sed -n '370,470p' \
  packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.ts
sed -n '330,410p' \
  packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.spec.ts

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 50410


Refresh the readonly display after blur.

When a consumer updates value while a readonly input has focus, willUpdate() preserves the old displayText. onBlur() then returns before formatting the new value, so the input can show stale text.

Synchronize displayText from value on the readonly blur path. Preserve a non-empty displayOverride. Add a regression test for an external value update while focused.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@packages/openbridge-webcomponents/src/components/number-input-field/number-input-field.ts`
around lines 218 - 220, Update number-input-field’s onBlur() readonly path to
synchronize displayText from the current value before returning, while
preserving any non-empty displayOverride. Add a regression test covering an
external value update while the readonly input is focused, then blur and verify
the displayed text refreshes correctly.

Comment on lines +10 to +15
export const OUTER_RING_RADIUS = 368 / 2;
const RING2_RADIUS = 528.25 / 2;
const RING3_RADIUS = 373.25 / 2;

@customElement('ob-speedometer')
export class Speedometer extends LitElement {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add class-level JSDoc for the component.

The class has no JSDoc. Concrete registered components require a class-level JSDoc block with a summary, features or variants, usage guidelines, slots, and events. The manifest generator reads this block for autodocs. Keep the text domain-agnostic. Do not add @property tags. The component renders no <slot> and dispatches no custom event, so do not add placeholder @slot or @fires tags.

As per coding guidelines: "Concrete registered components require class-level JSDoc with a summary, features or variants, usage guidelines, slots, and events; do not use class-level @property tags."

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 10 - 15, Add a class-level JSDoc immediately above the registered
Speedometer class with a domain-agnostic summary, features or variants, and
usage guidelines, plus explicit documentation that it has no slots or custom
events. Do not add `@property`, `@slot`, or `@fires` tags; keep the component
implementation unchanged.

Source: Coding guidelines

Comment on lines +14 to +15
@customElement('ob-speedometer')
export class Speedometer extends LitElement {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Register the element with the obc- prefix.

The repository convention requires Lit components to register with @customElement('obc-…'). This component registers ob-speedometer. The tag name is public API. Rename it now, before release, and update the HTMLElementTagNameMap entry at Line 1019, the stories file, and the skipper barrel export.

As per coding guidelines: "Lit components must extend LitElement and register with @customElement('obc-…')".

♻️ Proposed change
-@customElement('ob-speedometer')
+@customElement('obc-speedometer')
 export class Speedometer extends LitElement {
 declare global {
   interface HTMLElementTagNameMap {
-    'ob-speedometer': Speedometer;
+    'obc-speedometer': Speedometer;
   }
 }

After the rename, run npm run analyze.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 14 - 15, Rename the Speedometer custom element registration from
`ob-speedometer` to `obc-speedometer`, and update the corresponding
`HTMLElementTagNameMap` entry, stories, and skipper barrel export to use the new
public tag name. Run `npm run analyze` after the rename.

Source: Coding guidelines

Comment on lines +40 to +51
override render() {
this.setViewBox();
this.setMaxSpeed();
this.setRotate();
this.setStwRotate();

return html`
<div class="container">
${this.getSpeedometer()}
</div>
`;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🌐 Web query:

Lit 3 warning reactive property changed during render willUpdate

💡 Result:

The "reactive property changed during render" warning in Lit is a development-only diagnostic tool designed to help you avoid inefficient update cycles [1][2]. It triggers when a reactive property is modified after an update cycle has already begun, which forces the component to schedule an additional, subsequent update [2]. Key aspects of this warning: 1. Timing and Cause: The warning occurs if you change a reactive property during the update process—specifically after the update() or render() methods have executed [3][2]. If you change a property during the update (e.g., inside willUpdate() or render()), Lit includes that change in the current changedProperties map without triggering a new, separate update cycle [3]. However, if you change a property after the update has completed (for example, inside the updated() method), it initiates an entirely new update cycle, which is less efficient [3][2]. 2. Development vs. Production: This warning is part of the change-in-update category and is enabled by default in Lit's development builds [1][4]. It is intended to catch patterns where state updates cause "cascading" re-renders that could be batched or handled more efficiently [2]. Because it is a development-only check, it does not appear in production builds [1][4]. 3. Recommended Patterns: - Use willUpdate(): Lit specifically recommends using willUpdate() to compute values that depend on other properties [5][6][7]. Because willUpdate() runs before render() within the current update cycle, setting properties here is safe, efficient, and does not trigger the warning or an extra update [3][8]. - Avoid side effects in render(): Your render() method should be deterministic and avoid changing component state [9]. - Use updated() for side effects: If you need to perform actions after the DOM has been updated (such as third-party library integration or manual DOM manipulation), use the updated() lifecycle method [3][7]. If you must change state in updated(), be aware that it will trigger a new update cycle [3]. 4. Managing Warnings: If you have a legitimate use case that requires this pattern and wish to suppress the warning, you can disable it for your element using LitElement.disableWarning?.('change-in-update') in your element class or on the base class [1][4]. Top results: [1][3][2]

Citations:


🏁 Script executed:

#!/bin/bash
set -e
file="packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts"
printf '%s\n' '--- outline ---'
ast-grep outline "$file" || true
printf '%s\n' '--- property and method references ---'
rg -n -C 3 'maxSpeed|minSpeed|rotationAngle|setMaxSpeed|setViewBox|setRotate|setStwRotate|render\(' "$file"
printf '%s\n' '--- relevant source sections ---'
sed -n '1,180p' "$file"
sed -n '180,430p' "$file"
sed -n '430,760p' "$file"

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 50409


🏁 Script executed:

#!/bin/bash
set -e
file="packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts"
printf '%s\n' '--- package and Lit version references ---'
rg -n '"lit"|from .lit|`@property`|change-in-update|willUpdate|updated' package.json package-lock.json npm-shrinkwrap.json pnpm-lock.yaml yarn.lock "$file" 2>/dev/null || true
printf '%s\n' '--- all maxSpeed writes and reads ---'
rg -n 'maxSpeed\s*=|this\.maxSpeed|\.maxSpeed' packages/openbridge-webcomponents/src packages/openbridge-webcomponents/stories packages/openbridge-webcomponents/test packages/openbridge-webcomponents/tests 2>/dev/null || true
printf '%s\n' '--- focused source context ---'
sed -n '1,125p' "$file"
sed -n '700,775p' "$file"
printf '%s\n' '--- consumer references ---'
rg -n -C 4 '<ob-speedometer|ob-speedometer|maxSpeed' packages/openbridge-webcomponents 2>/dev/null | head -n 240

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 50409


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
import re

path = Path("packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts")
text = path.read_text()

property_decl = re.search(r'`@property`\(\{type: Number\}\)\s+maxSpeed\s*=\s*([^;]+);', text)
writes = re.findall(r'(?:this\.)?maxSpeed\s*=', text)
reads = re.findall(r'this\.maxSpeed', text)

print("maxSpeed declaration:", property_decl.group(0) if property_decl else "not found")
print("assignment-like maxSpeed writes:", len(writes))
print("all this.maxSpeed references:", len(reads))
print("setMaxSpeed assignments:", re.findall(r'this\.maxSpeed\s*=\s*[^;]+;', text[text.find("private setMaxSpeed"):text.find("private setMaxSpeed") + 500]))

# Model the exact current snapping function for representative consumer inputs.
def snap(value):
    if value <= 25:
        return 25, -5, 9
    if value > 25:
        return 50, -10, 4.5
    raise AssertionError

for value in (10, 25, 30, 50):
    print(f"input={value} -> maxSpeed,minSpeed,rotationAngle={snap(value)}")
PY

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 624


🌐 Web query:

Lit reactive property changed inside render render lifecycle warning additional update exact behavior

💡 Result:

In Lit, the warning regarding reactive property changes during the render lifecycle occurs when you modify a reactive property after the component's update cycle has passed the point where it can safely capture those changes without triggering an unintended, additional update cycle [1][2]. The specific behavior and the warning can be broken down as follows: When is it safe? Changing reactive properties within the update(changedProperties) method or the render method does not trigger an additional, separate update cycle [1][2][3]. During these stages, Lit can still capture these property changes and include them in the current update cycle, effectively batching them [1][2]. When does it trigger a warning/additional update? If you modify a reactive property after the render method has finished—such as in the updated method—it triggers a new, separate update cycle [1][2][3]. Because the component has already finished its current render, this subsequent change forces Lit to schedule another asynchronous update to reflect the new state, which is inefficient and often indicative of a logic error [1][2][3]. The Warning In development builds, Lit provides a warning (specifically the 'change-in-update' category) when this pattern is detected to help developers identify potentially costly or infinite update loops [4]. This warning is enabled by default [4]. How to avoid it If you need to derive state or modify properties based on other property changes: 1. Use the update(changedProperties) method: Perform calculations or property updates before calling super.update(changedProperties) [5]. This ensures the changes are reflected in the current render pass without triggering an extra cycle [5]. 2. Use properties responsibly: Avoid setting reactive properties in lifecycle methods that run after the DOM has been updated (e.g., updated), unless absolutely necessary, to prevent triggering unnecessary subsequent updates [1][2][3]. You can control these warnings using ReactiveElement.disableWarning?.('change-in-update') if you have a specific, validated use case that requires this behavior, though this is generally discouraged [4].

Citations:


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
import re

path = Path("packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts")
text = path.read_text()

property_decl = re.search(r'`@property`\(\{type: Number\}\)\s+maxSpeed\s*=\s*([^;]+);', text)
writes = re.findall(r'(?:this\.)?maxSpeed\s*=', text)
reads = re.findall(r'this\.maxSpeed', text)

print("maxSpeed declaration:", property_decl.group(0) if property_decl else "not found")
print("assignment-like maxSpeed writes:", len(writes))
print("all this.maxSpeed references:", len(reads))
print("setMaxSpeed assignments:", re.findall(r'this\.maxSpeed\s*=\s*[^;]+;', text[text.find("private setMaxSpeed"):text.find("private setMaxSpeed") + 500]))

def snap(value):
    if value <= 25:
        return 25, -5, 9
    if value > 25:
        return 50, -10, 4.5
    raise AssertionError

for value in (10, 25, 30, 50):
    print(f"input={value} -> maxSpeed,minSpeed,rotationAngle={snap(value)}")
PY

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 624


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- speedometer files ---'
fd -i 'speedometer' packages/openbridge-webcomponents
printf '%s\n' '--- speedometer-specific maxSpeed documentation and usage ---'
rg -n -C 5 'maxSpeed|Speedometer|ob-speedometer' packages/openbridge-webcomponents/src/skipper packages/openbridge-webcomponents/README.md README.md 2>/dev/null || true
printf '%s\n' '--- package Lit resolution ---'
node - <<'JS'
const fs = require('fs');
const lock = JSON.parse(fs.readFileSync('package-lock.json', 'utf8'));
const entries = Object.entries(lock.packages || {}).filter(([name]) => name.endsWith('/node_modules/lit') || name === 'node_modules/lit');
for (const [name, value] of entries) console.log(name, value.version);
JS

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 50409


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
import re

root = Path("packages/openbridge-webcomponents/src/skipper")
for path in sorted(root.rglob("*speedometer*")):
    print(f"--- {path} ---")
    text = path.read_text(errors="replace")
    for i, line in enumerate(text.splitlines(), 1):
        if re.search(r"maxSpeed|ob-speedometer|Speedometer", line):
            lo, hi = max(1, i - 3), min(len(text.splitlines()), i + 4)
            lines = text.splitlines()
            print("\n".join(f"{n}: {lines[n-1]}" for n in range(lo, hi + 1)))
            print()
PY

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 701


🏁 Script executed:

#!/bin/bash
set -e
python3 - <<'PY'
from pathlib import Path
import re

root = Path("packages/openbridge-webcomponents/src/skipper")
for path in sorted(root.rglob("*speedometer*")):
    print(f"--- {path} ---")
    lines = path.read_text(errors="replace").splitlines()
    for i, line in enumerate(lines, 1):
        if re.search(r"maxSpeed|ob-speedometer|Speedometer", line):
            lo, hi = max(1, i - 3), min(len(lines), i + 4)
            print("\n".join(f"{n}: {lines[n-1]}" for n in range(lo, hi + 1)))
            print()
PY

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 701


Keep maxSpeed unchanged during rendering. setMaxSpeed() currently overwrites the reactive input (27 becomes 50). Derive effectiveMaxSpeed (25 or 50), minSpeed, and rotationAngle separately, then use the effective value in geometry, alert, and label calculations.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 40 - 51, Update render and the related speedometer calculations so
setMaxSpeed() no longer mutates the reactive maxSpeed input. Derive separate
effectiveMaxSpeed (25 or 50), minSpeed, and rotationAngle values, then use
effectiveMaxSpeed consistently for geometry, alert thresholds, and labels while
preserving maxSpeed unchanged.

Comment on lines +487 to +525
private getPrimaryContainer() {
if (this.showSog && this.mainSpeedType == SpeedType.SOG && this.sogSpeed != undefined) {
return svg`
<g transform="translate(477.355 429)">
${this.renderPrimaryBarArea([
{
startAngle: this.getAngle(0),
endAngle: this.getAngle(this.getSogSpeed()),
fillColor: Colors.instrumentEnhancedTertiary,
}])
}
</g>

<g transform="rotate(${-100 + this.rotate} 477.355 429)">
${this.getMainNeedle(SpeedType.SOG)}
</g>
`
}
else if (this.showStw && this.mainSpeedType == SpeedType.STW && this.stwSpeed != undefined) {
return svg`
<g transform="translate(477.355 429)">
${this.renderPrimaryBarArea([
{
startAngle: this.getAngle(0),
endAngle: this.getAngle(this.getStwSpeed()),
fillColor: Colors.instrumentEnhancedTertiary,
}])
}
</g>

<g transform="rotate(${-100 + this.rotateStw} 477.355 429)">
${this.getMainNeedle(SpeedType.STW)}
</g>
`
}
else {
return nothing;
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

The main needle renders twice.

getPrimaryContainer() returns the bar area and the needle group at Lines 500-502 and Lines 517-519. getPrimaryNeedle() at Lines 527-545 returns the same needle group with the same rotation transform. getSpeedometer() calls both, at Line 252 and Line 256.

Every render therefore emits two identical needle groups at the same position. The strokes overlay each other, and the DOM node count doubles.

getPrimaryNeedle() is called after getAlerts(), so it is the one that correctly draws the needle above the alert overlays. Remove the needle group from getPrimaryContainer().

🐛 Proposed fix
       return svg`
         <g transform="translate(477.355 429)">
           ${this.renderPrimaryBarArea([
             {
               startAngle: this.getAngle(0),
               endAngle: this.getAngle(this.getSogSpeed()),
               fillColor: Colors.instrumentEnhancedTertiary,
             }])
           }
         </g>
-
-        <g transform="rotate(${-100 + this.rotate} 477.355 429)">
-          ${this.getMainNeedle(SpeedType.SOG)}
-        </g>
       `

Apply the same removal to the STW branch at Lines 517-519.

Note also that this.sogSpeed != undefined at Line 488 is always true. sogSpeed is typed number and defaults to 5. The same applies to stwSpeed at Line 505.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private getPrimaryContainer() {
if (this.showSog && this.mainSpeedType == SpeedType.SOG && this.sogSpeed != undefined) {
return svg`
<g transform="translate(477.355 429)">
${this.renderPrimaryBarArea([
{
startAngle: this.getAngle(0),
endAngle: this.getAngle(this.getSogSpeed()),
fillColor: Colors.instrumentEnhancedTertiary,
}])
}
</g>
<g transform="rotate(${-100 + this.rotate} 477.355 429)">
${this.getMainNeedle(SpeedType.SOG)}
</g>
`
}
else if (this.showStw && this.mainSpeedType == SpeedType.STW && this.stwSpeed != undefined) {
return svg`
<g transform="translate(477.355 429)">
${this.renderPrimaryBarArea([
{
startAngle: this.getAngle(0),
endAngle: this.getAngle(this.getStwSpeed()),
fillColor: Colors.instrumentEnhancedTertiary,
}])
}
</g>
<g transform="rotate(${-100 + this.rotateStw} 477.355 429)">
${this.getMainNeedle(SpeedType.STW)}
</g>
`
}
else {
return nothing;
}
}
private getPrimaryContainer() {
if (this.showSog && this.mainSpeedType == SpeedType.SOG && this.sogSpeed != undefined) {
return svg`
<g transform="translate(477.355 429)">
${this.renderPrimaryBarArea([
{
startAngle: this.getAngle(0),
endAngle: this.getAngle(this.getSogSpeed()),
fillColor: Colors.instrumentEnhancedTertiary,
}])
}
</g>
`
}
else if (this.showStw && this.mainSpeedType == SpeedType.STW && this.stwSpeed != undefined) {
return svg`
<g transform="translate(477.355 429)">
${this.renderPrimaryBarArea([
{
startAngle: this.getAngle(0),
endAngle: this.getAngle(this.getStwSpeed()),
fillColor: Colors.instrumentEnhancedTertiary,
}])
}
</g>
`
}
else {
return nothing;
}
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 487 - 525, Remove the main needle `<g>` groups from both the SOG
and STW branches of getPrimaryContainer(), leaving only their bar-area
rendering; getPrimaryNeedle() already supplies the correctly layered needle. Do
not change the existing speed checks or rotation behavior elsewhere.

Comment on lines +729 to +733
private getLabels25() {
return svg`
<svg width="955" height="800" viewBox="0 0 875 800" fill="none" xmlns="http://www.w3.org/2000/svg">
<g>
<path d="M431.436 108.167H429.511V95.4417C429.511 94.9584 429.511 94.5501 429.511 94.2167C429.527 93.8834 429.536 93.5751 429.536 93.2917C429.552 92.9917 429.569 92.6917 429.586 92.3917C429.336 92.6417 429.102 92.8584 428.886 93.0417C428.669 93.2084 428.402 93.4334 428.086 93.7167L425.986 95.3667L424.961 94.0417L429.786 90.3167H431.436V108.167ZM450.017 99.2167C450.017 100.667 449.901 101.967 449.667 103.117C449.451 104.25 449.109 105.208 448.642 105.992C448.176 106.775 447.567 107.375 446.817 107.792C446.067 108.208 445.167 108.417 444.117 108.417C442.817 108.417 441.726 108.058 440.842 107.342C439.976 106.608 439.326 105.558 438.892 104.192C438.476 102.808 438.267 101.15 438.267 99.2167C438.267 97.3167 438.459 95.6834 438.842 94.3167C439.226 92.9334 439.851 91.8751 440.717 91.1417C441.584 90.4084 442.717 90.0417 444.117 90.0417C445.467 90.0417 446.576 90.4084 447.442 91.1417C448.309 91.8751 448.951 92.9251 449.367 94.2917C449.801 95.6584 450.017 97.3001 450.017 99.2167ZM440.242 99.2167C440.242 100.9 440.367 102.3 440.617 103.417C440.884 104.517 441.301 105.35 441.867 105.917C442.434 106.467 443.184 106.742 444.117 106.742C445.051 106.742 445.801 106.467 446.367 105.917C446.934 105.367 447.351 104.533 447.617 103.417C447.884 102.3 448.017 100.9 448.017 99.2167C448.017 97.5667 447.884 96.1917 447.617 95.0917C447.367 93.9751 446.959 93.1334 446.392 92.5667C445.842 92.0001 445.084 91.7167 444.117 91.7167C443.167 91.7167 442.409 92.0084 441.842 92.5917C441.276 93.1584 440.867 94.0001 440.617 95.1167C440.367 96.2167 440.242 97.5834 440.242 99.2167Z" fill="${Colors.instrumentTickMarkLabelSecondary}"/>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

getLabels25() wraps the labels in a nested <svg>; getLabels50() does not.

Line 731 opens <svg width="955" height="800" viewBox="0 0 875 800"> inside the outer SVG. A nested <svg> establishes a new viewport. The label coordinates are then resolved against 0 0 875 800 and scaled by 955/875, not against the outer 130 80 712 712 viewBox.

getLabels50() at Line 745 returns a plain <g> with coordinates in the outer space. The two label sets therefore use different coordinate systems, and the 25-knot labels shift and scale relative to the tickmarks they annotate.

Use a plain <g> in getLabels25() for consistency with getLabels50(), then confirm the label positions in Storybook.

🐛 Proposed fix
   private getLabels25() {
     return svg`
-      <svg width="955" height="800" viewBox="0 0 875 800" fill="none" xmlns="http://www.w3.org/2000/svg">
-        <g>
+      <g>

Close the element accordingly at Lines 741-742.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 729 - 733, Update getLabels25() to return its label paths inside a
plain <g> rather than a nested <svg>, matching getLabels50() and preserving the
outer SVG coordinate system; replace the corresponding closing element as well,
then verify the 25-knot labels align with their tickmarks in Storybook.

Comment on lines +769 to +775
getTickmarks25() {
return svg`
<mask id="mask0_6621_15748" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="65" y="17" width="825" height="824">
<path d="M477.284 126.438C644.385 126.438 779.847 261.899 779.847 429C779.847 507.92 749.633 579.784 700.139 633.652C695.328 638.888 687.153 638.868 682.125 633.84L663.917 615.632C711.68 567.869 741.222 501.884 741.222 429C741.222 283.231 623.053 165.062 477.284 165.062C331.516 165.062 213.347 283.231 213.347 429C213.347 501.884 242.889 567.869 290.652 615.632L272.444 633.84C267.416 638.868 259.241 638.888 254.43 633.652C204.936 579.784 174.722 507.92 174.722 429C174.722 261.899 310.184 126.438 477.284 126.438Z" fill="white" stroke="black" stroke-width="2.08333"/>
</mask>

<g mask="url(#mask0_6621_15748)">

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

mask0_6621_15748 is defined twice in the same SVG.

getWatchFace() defines mask0_6621_15748 at Line 268. getTickmarks25() defines the same id again at Line 771. getSpeedometer() renders both into one SVG root, so the document contains two elements with the same id. url(#mask0_6621_15748) then resolves to the first match in document order.

The two definitions are currently identical, so the rendering matches by chance. Any later edit to one of them breaks the other silently. getTickmarks50() already uses a distinct id (mask9_6832_10114).

Give the tickmark mask its own id, or hoist the shared mask into a single <defs> block that both methods reference.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/speedometer/speedometer.ts`
around lines 769 - 775, Update getTickmarks25 so its mask uses a unique SVG id
instead of mask0_6621_15748, and change the corresponding mask reference in that
method to match. Keep getWatchFace’s existing mask id and preserve the current
rendering behavior.

@@ -0,0 +1,154 @@
import {SVGTemplateResult, svg} from 'lit';

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n --hidden --glob 'tsconfig*.json' \
  '"verbatimModuleSyntax"\s*:\s*true|"importsNotUsedAsValues"' . || true

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 191


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- TypeScript configuration files ---'
git ls-files '*tsconfig*.json' | sort

printf '%s\n' '--- Relevant compiler options and package scripts ---'
rg -n --hidden \
  'verbatimModuleSyntax|importsNotUsedAsValues|preserveValueImports|moduleResolution|typescript|typecheck|build' \
  -g 'tsconfig*.json' -g 'package.json' -g '!node_modules' -g '!dist' -g '!build' . || true

printf '%s\n' '--- tickmark.ts ---'
sed -n '1,180p' packages/openbridge-webcomponents/src/skipper/tickmark.ts

printf '%s\n' '--- Import usage and nearby compiler settings ---'
rg -n --hidden 'SVGTemplateResult|from ['"'"'"]lit['"'"'"]' \
  packages/openbridge-webcomponents/src packages/openbridge-webcomponents \
  -g '*.ts' -g 'tsconfig*.json' -g 'package.json' -g '!node_modules' -g '!dist' -g '!build' || true

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 50410


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Tracked TypeScript configuration files ---'
git ls-files '*tsconfig*.json' | sort

printf '%s\n' '--- Compiler options in tracked configuration files ---'
while IFS= read -r file; do
  matches=$(rg -n 'verbatimModuleSyntax|importsNotUsedAsValues|preserveValueImports|moduleResolution|strict' "$file" || true)
  if [ -n "$matches" ]; then
    printf '\n[%s]\n%s\n' "$file" "$matches"
  fi
done < <(git ls-files '*tsconfig*.json' | sort)

printf '%s\n' '--- Package TypeScript scripts ---'
python3 - <<'PY'
import json
from pathlib import Path

for path in [Path("package.json"), Path("packages/openbridge-webcomponents/package.json")]:
    if path.is_file():
        data = json.loads(path.read_text())
        print(f"[{path}]")
        for key, value in data.get("scripts", {}).items():
            if any(term in key.lower() or term in str(value).lower()
                   for term in ("type", "build", "lint", "tsc")):
                print(f"{key}: {value}")
PY

printf '%s\n' '--- tickmark.ts ---'
cat -n packages/openbridge-webcomponents/src/skipper/tickmark.ts

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 648


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Relevant compiler options ---'
for file in $(git ls-files '*tsconfig*.json' | sort); do
  matches=$(rg -n 'verbatimModuleSyntax|importsNotUsedAsValues|preserveValueImports|moduleResolution|strict|extends' "$file" || true)
  if [ -n "$matches" ]; then
    printf '\n[%s]\n%s\n' "$file" "$matches"
  fi
done

printf '%s\n' '--- openbridge-webcomponents package scripts ---'
if [ -f packages/openbridge-webcomponents/package.json ]; then
  sed -n '1,220p' packages/openbridge-webcomponents/package.json
fi

printf '%s\n' '--- root package scripts ---'
sed -n '1,180p' package.json

printf '%s\n' '--- tickmark.ts ---'
cat -n packages/openbridge-webcomponents/src/skipper/tickmark.ts

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 15113


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- openbridge-webcomponents TypeScript configuration ---'
cat -n packages/openbridge-webcomponents/tsconfig.json

printf '%s\n' '--- Import-type lint rules ---'
for file in packages/openbridge-webcomponents/eslint.config.mjs .eslintrc .eslintrc.json .eslintrc.js; do
  if [ -f "$file" ]; then
    printf '\n[%s]\n' "$file"
    rg -n 'consistent-type-imports|verbatimModuleSyntax|importsNotUsedAsValues|no-unused-vars|import' "$file" || true
  fi
done

printf '%s\n' '--- Behavioral verifier for tickmark scale validation ---'
python3 - <<'PY'
import math

def accepted(scale):
    return not (scale == math.inf or scale <= 0)

def derived_values(scale):
    return {
        "text_radius": 10 + (3 / scale + 3),
        "x1": math.sin(0) * 164,
        "y1": -math.cos(0) * 164,
    }

for scale in [1, 0, -1, math.inf, -math.inf, math.nan]:
    try:
        print(scale, "accepted=", accepted(scale), "derived=", derived_values(scale))
    except Exception as exc:
        print(scale, "accepted=", accepted(scale), "derived_error=", type(exc).__name__, str(exc))
PY

Repository: Ocean-Industries-Concept-Lab/openbridge-webcomponents

Length of output: 3441


Reject non-finite scales before calculating SVG values.

NaN bypasses the current check and produces NaN text coordinates and SVG attributes. Use !Number.isFinite(scale) || scale <= 0. Remove the redundant inline comment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/tickmark.ts` at line 1, Update
the scale validation in the tickmark rendering logic to reject all non-finite
values and non-positive scales using the finite check before calculating SVG
coordinates or attributes. Remove the redundant inline comment and preserve the
existing handling for invalid scales.

Comment on lines +70 to +73
// check if scale is not infinite
if (scale === Infinity || scale <= 0) {
throw new Error('Scale is not valid');
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject all non-finite scale values.

Line 71 accepts NaN because both comparisons are false. The later SVG geometry then contains NaN coordinates and does not render. Remove the redundant inline comment.

Proposed fix
-  // check if scale is not infinite
-  if (scale === Infinity || scale <= 0) {
+  if (!Number.isFinite(scale) || scale <= 0) {
     throw new Error('Scale is not valid');
   }

As per coding guidelines, “Avoid inline comments unless code is extremely unusual and cannot be understood without explanation.”

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// check if scale is not infinite
if (scale === Infinity || scale <= 0) {
throw new Error('Scale is not valid');
}
if (!Number.isFinite(scale) || scale <= 0) {
throw new Error('Scale is not valid');
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/openbridge-webcomponents/src/skipper/tickmark.ts` around lines 70 -
73, Update the scale validation in the tickmark logic to reject every non-finite
value, including NaN, while retaining rejection of non-positive finite values.
Remove the redundant inline comment above the validation.

Source: Coding guidelines

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants