Feat(components): Add 5 new OpenBridge components (Speedometer, True-relative, Long-lat (two versions), GNSS Skyplot) - #944
Conversation
|
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds 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. ChangesSkipper instruments and supporting infrastructure
Input, keyboard, and layout updates
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
Suggested labels: Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1⚔️ Resolve merge conflicts 💡
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
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 winRemove inline comment in TS source.
Line 70 adds an inline comment in a
.tsmodule 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 winHandle
NaNinscalevalidation.Line 71 rejects
Infinityand non-positive values, butNaNstill 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 winRemove 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 winUse Title Case for the story title path.
INSTRUMENT/LongLatEnvironmentalis 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
titleandnamefields must use Title Case — enforced by ESLint ruleopenbridge/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 winAdd unit to
font-sizevalue.Line 22 has
font-size: 64without a unit, which is invalid CSS (except forline-height). It should befont-size: 64pxor 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 winUse 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
titleandnamefields must use Title Case — enforced by ESLint ruleopenbridge/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 winRemove hardcoded test data from production component.
The
satellitesproperty 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.tsdefault 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 winUse 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
titleandnamefields must use Title Case — enforced by ESLint ruleopenbridge/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 winCorrect the typo in satellite type name.
Galileioshould beGalileo(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 winRemove 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 winRemove 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 winFix the Title Case claim and downgrade the Storybook typing concern
meta.title: 'INSTRUMENT/LongLatEnvironmental'already matches theopenbridge/storybook-title-caserule (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 inpackages/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 valueRemove or resolve the TODO comment.
Line 25 has
// TODO: removenext tospeedType. 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 valueClarify 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 winConsider consolidating duplicate interfaces.
Satellite(lines 33-42) duplicates all fields fromSatelliteData(lines 8-17). If the two interfaces serve identical purposes, use a single type; ifSatelliteis a transformed version with computedx/y, consider makingSatelliteextend or composeSatelliteData.🤖 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
⛔ Files ignored due to path filters (41)
package-lock.jsonis excluded by!**/package-lock.jsonpackages/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.pngis excluded by!**/*.pngpackages/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.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/chart-integration--as-external-bottom-axis--scalebackground--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/component-size-comparison--regular-medium-large-xl--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-vertical/bar-vertical.stories.ts/chart-integration--as-external-right-axis--scalebackground--auto.pngis excluded by!**/*.pngpackages/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.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/chart-line/chart-line-base.stories.ts/threshold-height-line-graph--192px--where-labels-appear--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/horizontal--bottom-side--hasbar--advices--setpoint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/horizontal--top-side--hasbar--advices--setpoint--fillmode-tint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/vertical--left-side--hasbar--advices--setpoint--fillmode-tint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-button-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-state-off-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-state-on-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/state-off-without-icon-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-multi-application-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-multi-regular-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-single-application-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-single-regular-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-multi-application-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-multi-regular-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-single-application-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-single-regular-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/toggle-button-vertical-group/toggle-button-vertical-group.stories.ts/initially-disabled-selection-falls-back-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/toggle-button-vertical-group/toggle-button-vertical-group.stories.ts/label-under-regular-disabled-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/topbar-message-item/topbar-message-item.stories.ts/large-with-long-text-and-description-and-both-timestamps-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/azimuth-thruster/azimuth-thruster.stories.ts/in-command-at-setpoint-disable-auto-setpoint-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/component-size-comparison--regular-medium-large-xl--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/fill-mode-comparison--enhanced--fill-vs-tint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/state-comparison--incommand-active-loading-off--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/with-setpoint--value-away-from-setpoint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/component-size-comparison--regular-medium-large-xl--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/enhanced-mode-comparison--regular-vs-enhanced--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/fill-mode-comparison--enhanced--fill-vs-tint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/state-comparison--incommand-active-loading-off--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/main-engine-indicator/propulsion-main-engine-indicator.stories.ts/in-command---silhouette-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/main-engine-indicator/propulsion-main-engine-indicator.stories.ts/not-in-command---silhouette-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/propulsion-tunnel-thruster/propulsion-tunnel-thruster.stories.ts/not-in-command---silhouette-auto.pngis excluded by!**/*.png
📒 Files selected for processing (25)
.devcontainer/devcontainer-lock.jsonpackages/openbridge-webcomponents/src/components/accordion-card/accordion-card.csspackages/openbridge-webcomponents/src/skipper/advice.tspackages/openbridge-webcomponents/src/skipper/current-icons.tspackages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot-styles.tspackages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.stories.tspackages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.tspackages/openbridge-webcomponents/src/skipper/index.tspackages/openbridge-webcomponents/src/skipper/interfaces.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat-styles.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat.stories.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat.tspackages/openbridge-webcomponents/src/skipper/models.tspackages/openbridge-webcomponents/src/skipper/speedometer/speedometer-styles.tspackages/openbridge-webcomponents/src/skipper/speedometer/speedometer.stories.tspackages/openbridge-webcomponents/src/skipper/speedometer/speedometer.tspackages/openbridge-webcomponents/src/skipper/tickmark.tspackages/openbridge-webcomponents/src/skipper/true-relative/true-relative-styles.tspackages/openbridge-webcomponents/src/skipper/true-relative/true-relative.stories.tspackages/openbridge-webcomponents/src/skipper/true-relative/true-relative.tspackages/openbridge-webcomponents/src/skipper/utils/storybook-helpers.tspackages/openbridge-webcomponents/src/skipper/utils/uuid.tspackages/openbridge-webcomponents/src/skipper/wind-icons.ts
| 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, | ||
| })} | ||
| `; |
There was a problem hiding this comment.
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).
| @customElement('ob-gnss-skyplot') | ||
| export class GnssSkyplot extends LitElement { |
There was a problem hiding this comment.
🛠️ 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
@propertytags (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.
| override render() { | ||
| // this.satellites = this.mapSatellitesToCircle(this.generateRandomSatellites()); | ||
| this.satellites = this.mapSatellitesToCircle(this.satellites); | ||
| return html` | ||
| <div class="container"> | ||
| ${this.getGnssSkylot()} | ||
| </div> | ||
| `; | ||
| } |
There was a problem hiding this comment.
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().
| window.dispatchEvent( | ||
| new CustomEvent<Satellite>('satellite', { | ||
| detail: clickedSatellite | ||
| }) | ||
| ); | ||
|
|
||
| // TODO: remove | ||
| console.log(clickedSatellite); |
There was a problem hiding this comment.
🛠️ 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.
| /* | ||
| 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 | ||
| }; | ||
| }); | ||
| } | ||
| */ | ||
|
|
There was a problem hiding this comment.
🛠️ 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.
| 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"/> |
There was a problem hiding this comment.
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.
| @customElement('ob-true-relative') | ||
| export class TrueRelative extends LitElement { |
There was a problem hiding this comment.
🛠️ 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
@propertytags (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; |
There was a problem hiding this comment.
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.
| @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.
| override render() { | ||
| this.setMaxSpeed(); | ||
| this.setSpeedRatio(); | ||
| this.setRotationsPerMinute(); | ||
|
|
||
| return html` | ||
| <div class="container"> | ||
| ${this.getTrueRelative()} | ||
| </div> | ||
| `; | ||
| } |
There was a problem hiding this comment.
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.
| 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.
| 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` | ||
|
|
||
| `; |
There was a problem hiding this comment.
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
left a comment
There was a problem hiding this comment.
note to self: requesting 'blank' changes to avoid accidental merge.
|
Hey @asijah2, thank you for your contribution! Attaching these screenshots so our team can more easily evaluate it.
|
There was a problem hiding this comment.
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 valueStray 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 winRemove 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 winRemove 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 winTitle 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
titleandnamefields."✏️ 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 valueBoolean property naming unclear.
toggleSLAndDLSensordoesn'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 winExcessive inline comments violate the no-comments guideline.
Numerous
//comments (e.g. "values for SIDE_VIEW", "COMMON VIEW LOGIC", checkmark-annotated comments, commented-outdimTopY/dimBottomYlines) 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 winDead no-op method referencing a nonexistent property.
toggleVesselViewer()does nothing; its only content is a commented-out reference tothis.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 valueHardcoded layout magic numbers.
400pxwidth,scale(0.69), andmargin-left: 60pxare magic values with no documented derivation. Givenvessel.stories.tsexposes awidthcontrol 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
📒 Files selected for processing (6)
packages/openbridge-webcomponents/oicl-openbridge-webcomponents-2.0.0-next.29.tgzpackages/openbridge-webcomponents/src/skipper/interfaces.tspackages/openbridge-webcomponents/src/skipper/vessel-view/vessel-styles.tspackages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.tspackages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.tspackages/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
| import {nothing, svg, type TemplateResult} from 'lit'; | ||
| import {Colors} from '../interfaces.js'; | ||
| import {VesselTypes, ViewType} from './vessel.js'; | ||
|
|
There was a problem hiding this comment.
📐 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
| 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" | ||
| />`; | ||
| } |
There was a problem hiding this comment.
📐 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.
| <line | ||
| x1="${sideViewParams.sternLine.startX + 4.5}" | ||
| y1="303" | ||
| x2="${sideViewParams.sternLine.endX - 4.5}" | ||
| y2="303" | ||
| stroke="${Colors.instrumentRegularSecondaryDif}" | ||
| stroke-width="1" |
There was a problem hiding this comment.
🎯 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`.
| export const Vessel_cruiser: Story = { | ||
| args: { | ||
| width: 512, | ||
| }, | ||
| }; |
There was a problem hiding this comment.
🎯 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.
| @customElement('ob-vessel-view') | ||
| export class Vessel extends LitElement { |
There was a problem hiding this comment.
📐 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.
| @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 { |
There was a problem hiding this comment.
📐 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; |
There was a problem hiding this comment.
🎯 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.tsRepository: 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.tsRepository: 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.
| 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; | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 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), |
There was a problem hiding this comment.
🩺 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.
There was a problem hiding this comment.
Actionable comments posted: 11
🧹 Nitpick comments (5)
packages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.ts (3)
604-625: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRemove the unused helpers.
createPolylinehas no active call site; both calls are commented out at Lines 408 and 467.getSVGCoordinateshas no call site at all.getSVGCoordinatesalso passes a possiblyundefinedmatrix intomatrixTransform, becausegetScreenCTM()returnsnullfor 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 winAlign the gap check with the actual gap value.
The guard tests
this.listPointsFunctionOne[i] !== null, but the data encodes gaps withNaN, notnull.NaNpasses the guard and enters the min/max comparisons. The comparisons happen to evaluate tofalseforNaN, so the result is correct by accident. Test withNumber.isFiniteto make the intent explicit and to also rejectundefinedandInfinity.The same applies to the
functionTwoloop 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 winRemove the commented-out code and debug artifacts, and fix the field typo.
The file contains many commented-out
console.logcalls, a commented-outgetFunctionhelper, 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
hiddeFunctionOneandhiddeFunctionTwoare misspelled. Rename them tohiddenFunctionOneandhiddenFunctionTwo.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 valueAdd
heighttoargsandargTypes.
widthDecoratorinpackages/openbridge-webcomponents/src/storybook-util.tsreadscontext.args.heightand falls back to the width. The second story setsheight: 512at 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 valueRemove the unused legend rules, or use them.
logging-graph.tsrenders the legend as an SVG<rect>ingetLegendItemat Lines 585-602. No element carries thelegend-item,legend-color, orlegend-textclass. These rules are dead code..font-scalesis 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
📒 Files selected for processing (5)
packages/openbridge-webcomponents/src/skipper/interfaces.tspackages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.tspackages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.tspackages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.tspackages/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
| .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), | ||
| } |
There was a problem hiding this comment.
🎯 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.
| .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.
| 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 = { |
There was a problem hiding this comment.
🎯 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.
| 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', |
There was a problem hiding this comment.
📐 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
| @customElement('ob-logging-graph-skipper') | ||
| export class LoggingGraph extends LitElement { |
There was a problem hiding this comment.
📐 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
| @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' | ||
| ]; |
There was a problem hiding this comment.
📐 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.
| 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(); |
There was a problem hiding this comment.
🩺 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:
- 1: [labs/observers] Callback is called during
hostUpdated, causingchange-in-updatewarnings lit/lit#3597 - 2: Lit-based components show a warning on init [1 day] vaadin/web-components#7496
- 3: Synchronous update lit/lit-element#1103
- 4: https://mintlify.wiki/lit/lit/concepts/lifecycle
- 5: https://lit.dev/docs/components/lifecycle/
- 6: https://lit.dev/docs/components/rendering/
- 7: Synchronous update lit/lit-element#1103
- 8: https://github.com/lit/lit/blob/c134604f178e36444261d83eabe9e578c1ed90c4/packages/lit-element/src/lit-element.ts
- 9: Fix change-in-update warning from Tasks lit/lit#3660
- 10: https://lit.dev/docs/components/properties/
🏁 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" || trueRepository: 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:
- 1: https://lit.dev/docs/components/lifecycle/
- 2: https://lit.dev/docs/components/rendering/
- 3: https://lit.dev/docs/v2/components/lifecycle/
- 4: Synchronous update lit/lit-element#1103
- 5: https://lit.dev/docs/api/LitElement/
- 6: https://lit.dev/docs/v3/components/lifecycle/
🌐 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:
- 1: https://lit.dev/docs/v3/components/properties/
- 2: https://lit.dev/blog/2023-10-10-lit-3.0/
- 3: https://lit.dev/docs/v3/api/LitElement/
- 4: https://lit.dev/docs/v3/api/ReactiveElement/
- 5: https://lit.dev/docs/v3/components/lifecycle/
- 6: https://lit.dev/docs/components/lifecycle/
🌐 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:
- 1: [labs/observers] Callback is called during
hostUpdated, causingchange-in-updatewarnings lit/lit#3597 - 2: [labs/task] Task improperly warns user about rendering lit/lit#3566
- 3: Synchronous update lit/lit-element#1103
- 4: Synchronous update lit/lit-element#1103
- 5: https://github.com/Polymer/lit-element/blob/master/src/lit-element.ts
- 6: https://lit.dev/docs/components/lifecycle/
🌐 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:
- 1: https://lit.dev/docs/components/lifecycle/
- 2: https://lit.dev/docs/v3/api/ReactiveElement/
- 3: https://lit.dev/docs/v3/components/properties/
- 4: https://lit.dev/docs/components/properties/
- 5: https://lit.dev/docs/v3/components/lifecycle/
- 6: https://open-wc.org/blog/lit-element-deepdive-in-batching-updates/
- 7: https://lit.dev/docs/api/ReactiveElement/
- 8: https://lit.dev/docs/v1/components/lifecycle/
- 9: https://lit.dev/docs/api/LitElement/
🌐 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:
- 1: https://github.com/lit/lit/blob/main/packages/reactive-element/src/reactive-element.ts
- 2: https://github.com/lit/lit/blob/12109c25997ef03180d7eefe05c64e0fb20dd2b0/packages/reactive-element/src/reactive-element.ts
- 3: https://lit.dev/docs/components/lifecycle/
- 4: [labs/task] Task improperly warns user about rendering lit/lit#3566
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.
| 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; | ||
| } |
There was a problem hiding this comment.
🎯 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.
| 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); |
There was a problem hiding this comment.
🚀 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.
| 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); | ||
| }); |
There was a problem hiding this comment.
🩺 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.
| 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.
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>
…-input-fields' into Jotron-Skipper
|
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. |
There was a problem hiding this comment.
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 winRemove the ineffective duplicate width declaration.
Line 8 sets
width: auto, but Line 11 immediately overrides it withwidth: 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 winRemove the commented-out advice block and the unused
mask10_6621_15748mask.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 valueShare the polar-coordinate helpers between the two sector builders.
describeSector()anddescribeDonutSector()each declare their owntoRadandpolarclosures 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 withoutprivate, so it is exposed on the element instance. Its logic ignoresshowSog,showStw, andstwSpeed, unlikegetHighAdviceColor()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, andcutTickmarks. Once released, these become part of the component contract.Delete
getHighAlertColor()andgetAlertColor()if they are not needed. Mark the remaining internal methodsprivate.🤖 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 winBoth
getSecondaryNeedle()branches emit identical markup.The
STWbranch and theelsebranch differ only in the rotation source. Thecircleandpathelements 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 winRemove 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 appliesColors.alertCautionColorfor 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 winCollapse the duplicated clamp helpers.
getSogSpeed()andgetStwSpeed()are identical except for the source field. Each uses four branches where one expression is enough. The same duplication pattern repeats insetRotate()/setStwRotate()(Lines 113-141),getMainNeedleFillColor()/getSecondaryNeedleFillColor()(Lines 654-699), andgetHighState()/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 winRemove the unused
getRadiusMask()method.No repository call site exists. Its
path-10-outside-1_22_3634mask definition is inside an HTML comment, so the renderedmaskreference 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 winConsolidate the duplicated dual-value branches and correct the single-value viewport.
- In
getSpeedValue(), only the two dual-value branches duplicate the template. Selectmainandsecondaryonce, while preserving their values, tags, andneutralColorsettings.- When one speed is shown,
setViewBox()ends aty=692, but the single-value<foreignObject>spansy=580..780. Adjust the viewport or field position so the scaled enhanced field remains visible. Cover both single-value stories withshowSpeedAsNumber: 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
⛔ Files ignored due to path filters (42)
package-lock.jsonis excluded by!**/package-lock.jsonpackages/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.pngis excluded by!**/*.pngpackages/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.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/chart-integration--as-external-bottom-axis--scalebackground--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/component-size-comparison--regular-medium-large-xl--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-horizontal/bar-horizontal.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/bar-vertical/bar-vertical.stories.ts/chart-integration--as-external-right-axis--scalebackground--auto.pngis excluded by!**/*.pngpackages/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.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/chart-line/chart-line-base.stories.ts/threshold-height-line-graph--192px--where-labels-appear--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/horizontal--bottom-side--hasbar--advices--setpoint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/horizontal--top-side--hasbar--advices--setpoint--fillmode-tint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/building-blocks/external-scale/external-scale.stories.ts/vertical--left-side--hasbar--advices--setpoint--fillmode-tint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-button-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-state-off-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/mixed-value-and-state-on-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/automation-button-readout-stack/automation-button-readout-stack.stories.ts/state-off-without-icon-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-multi-application-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-multi-regular-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-single-application-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-horizontal-single-regular-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-multi-application-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-multi-regular-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-single-application-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/notification-floating-item/notification-floating-item.stories.ts/notification-vertical-single-regular-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/number-input-field/number-input-field.stories.ts/readonly-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/toggle-button-vertical-group/toggle-button-vertical-group.stories.ts/initially-disabled-selection-falls-back-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/toggle-button-vertical-group/toggle-button-vertical-group.stories.ts/label-under-regular-disabled-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/components/topbar-message-item/topbar-message-item.stories.ts/large-with-long-text-and-description-and-both-timestamps-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/azimuth-thruster/azimuth-thruster.stories.ts/in-command-at-setpoint-disable-auto-setpoint-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/component-size-comparison--regular-medium-large-xl--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/fill-mode-comparison--enhanced--fill-vs-tint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/state-comparison--incommand-active-loading-off--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-horizontal/gauge-horizontal.stories.ts/with-setpoint--value-away-from-setpoint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/component-size-comparison--regular-medium-large-xl--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/enhanced-mode-comparison--regular-vs-enhanced--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/fill-mode-comparison--enhanced--fill-vs-tint--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/state-comparison--incommand-active-loading-off--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/gauge-vertical/gauge-vertical.stories.ts/state-comparison--incommand-active-loading-off-focus--auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/main-engine-indicator/propulsion-main-engine-indicator.stories.ts/in-command---silhouette-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/main-engine-indicator/propulsion-main-engine-indicator.stories.ts/not-in-command---silhouette-auto.pngis excluded by!**/*.pngpackages/openbridge-webcomponents/__vis__/linux/__baselines__/navigation-instruments/propulsion-tunnel-thruster/propulsion-tunnel-thruster.stories.ts/not-in-command---silhouette-auto.pngis excluded by!**/*.png
📒 Files selected for processing (41)
.devcontainer/devcontainer-lock.jsonpackages/openbridge-webcomponents/oicl-openbridge-webcomponents-2.0.0-next.29.tgzpackages/openbridge-webcomponents/src/components/accordion-card/accordion-card.csspackages/openbridge-webcomponents/src/components/keyboard-full/keyboard-full.spec.tspackages/openbridge-webcomponents/src/components/keyboard-full/keyboard-full.tspackages/openbridge-webcomponents/src/components/number-input-field/number-input-field.spec.tspackages/openbridge-webcomponents/src/components/number-input-field/number-input-field.stories.tspackages/openbridge-webcomponents/src/components/number-input-field/number-input-field.tspackages/openbridge-webcomponents/src/components/text-input-field/text-input-field.spec.tspackages/openbridge-webcomponents/src/components/text-input-field/text-input-field.tspackages/openbridge-webcomponents/src/mixins/base-input-field.csspackages/openbridge-webcomponents/src/skipper/advice.tspackages/openbridge-webcomponents/src/skipper/current-icons.tspackages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot-styles.tspackages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.stories.tspackages/openbridge-webcomponents/src/skipper/gnss-skyplot/gnss-skyplot.tspackages/openbridge-webcomponents/src/skipper/index.tspackages/openbridge-webcomponents/src/skipper/interfaces.tspackages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph-styles.tspackages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.stories.tspackages/openbridge-webcomponents/src/skipper/logginggraph/logging-graph.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.stories.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat-environmental.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat-styles.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat.stories.tspackages/openbridge-webcomponents/src/skipper/long-lat/long-lat.tspackages/openbridge-webcomponents/src/skipper/models.tspackages/openbridge-webcomponents/src/skipper/speedometer/speedometer-styles.tspackages/openbridge-webcomponents/src/skipper/speedometer/speedometer.stories.tspackages/openbridge-webcomponents/src/skipper/speedometer/speedometer.tspackages/openbridge-webcomponents/src/skipper/tickmark.tspackages/openbridge-webcomponents/src/skipper/true-relative/true-relative-styles.tspackages/openbridge-webcomponents/src/skipper/true-relative/true-relative.stories.tspackages/openbridge-webcomponents/src/skipper/true-relative/true-relative.tspackages/openbridge-webcomponents/src/skipper/utils/storybook-helpers.tspackages/openbridge-webcomponents/src/skipper/utils/uuid.tspackages/openbridge-webcomponents/src/skipper/vessel-view/vessel-styles.tspackages/openbridge-webcomponents/src/skipper/vessel-view/vessel-types.tspackages/openbridge-webcomponents/src/skipper/vessel-view/vessel.stories.tspackages/openbridge-webcomponents/src/skipper/vessel-view/vessel.tspackages/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
| .wrapper { | ||
| display: flex; | ||
| user-select: none; | ||
| width: auto; | ||
| flex-direction: column; | ||
| align-items: center; | ||
| width: 100%; |
There was a problem hiding this comment.
🎯 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.
| .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.
| private onBlur() { | ||
| this.hasFocus = false; | ||
| if (this.readonly) return; |
There was a problem hiding this comment.
🎯 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.tsRepository: 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.tsRepository: 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.tsRepository: 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.
| 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 { |
There was a problem hiding this comment.
📐 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
| @customElement('ob-speedometer') | ||
| export class Speedometer extends LitElement { |
There was a problem hiding this comment.
📐 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
| override render() { | ||
| this.setViewBox(); | ||
| this.setMaxSpeed(); | ||
| this.setRotate(); | ||
| this.setStwRotate(); | ||
|
|
||
| return html` | ||
| <div class="container"> | ||
| ${this.getSpeedometer()} | ||
| </div> | ||
| `; | ||
| } |
There was a problem hiding this comment.
🩺 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:
- 1: https://lit.dev/docs/v3/tools/development/
- 2: https://github.com/lit/lit/blob/c134604f178e36444261d83eabe9e578c1ed90c4/packages/reactive-element/src/reactive-element.ts
- 3: https://lit.dev/docs/v3/components/lifecycle/
- 4: https://lit.dev/docs/tools/development/
- 5: https://lit.dev/docs/components/lifecycle/
- 6: https://lit.dev/docs/api/LitElement/
- 7: https://lit.dev/docs/components/properties/
- 8: Custom Property Setter vs. `willUpdate` For Retrieving Data Dependent on a Property lit/lit#4716
- 9: https://lit.dev/docs/components/rendering/
🏁 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 240Repository: 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)}")
PYRepository: 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:
- 1: https://lit.dev/docs/components/lifecycle/
- 2: https://lit.dev/docs/v3/components/lifecycle/
- 3: https://lit.dev/docs/api/LitElement/
- 4: https://lit.dev/docs/tools/development/
- 5: Synchronous update lit/lit-element#1103
🏁 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)}")
PYRepository: 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);
JSRepository: 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()
PYRepository: 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()
PYRepository: 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.
| 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; | ||
| } | ||
| } |
There was a problem hiding this comment.
🎯 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.
| 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.
| 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}"/> |
There was a problem hiding this comment.
🎯 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.
| 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)"> |
There was a problem hiding this comment.
🎯 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'; | |||
There was a problem hiding this comment.
📐 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"' . || trueRepository: 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' || trueRepository: 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.tsRepository: 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.tsRepository: 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))
PYRepository: 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.
| // check if scale is not infinite | ||
| if (scale === Infinity || scale <= 0) { | ||
| throw new Error('Scale is not valid'); | ||
| } |
There was a problem hiding this comment.
🎯 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.
| // 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










This proposal introduces several new components that could be included in the official OpenBridge component library.
Components
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
I would be happy to contribute the implementation, documentation, stories and tests for these components.
Summary by CodeRabbit
Release Notes