Skip to content

Add per-area mowing overrides (outline count/overlap/offset, angle) - #5

Merged
rovo89 merged 4 commits into
xtech:mainfrom
rpeltola:per-area-mow-overrides
Jul 3, 2026
Merged

Add per-area mowing overrides (outline count/overlap/offset, angle)#5
rovo89 merged 4 commits into
xtech:mainfrom
rpeltola:per-area-mow-overrides

Conversation

@rpeltola

@rpeltola rpeltola commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

Summary

Adds optional per-area overrides for the global mowing settings, editable in the area settings dialog:

  • outline_count
  • outline_overlap_count
  • outline_offset (m)
  • angle (fixed mow direction)

They live in a collapsible Advanced section (matching the settings fieldset style). It's collapsed by default and auto-expands when the area already has any override set, with a badge showing how many are set. Empty fields fall back to the global config default — the key is simply omitted from map.json.

Context

The ROS v1.2.0 release notes introduced these per-area overrides and noted the app side was still to come:

For now, these attributes must be added manually by editing map.json in a text editor. Support in the OpenMower app is planned.

This PR implements that planned support for all four attributes.

Relationship to #3

This extends #3 (thanks @jrv), which surfaces outline_count. This PR covers all four per-area overrides and implements the advanced collapsible section and "use global default" placeholders suggested in the review of #3.

Details

  • Schema: four optional fields added to the area properties (src/stores/schemas.ts).
  • Save path is unchanged — properties round-trip through featuresToMapmap.replace; the fields are shown only for mowing areas.
  • Angle is entered in degrees and stored as radians in map.json to match ROS (open_mower_ros mower_map/msg/MapArea.msg: angle is radians). The "use global default" sentinels match ROS's overrideOrGlobal (omit the key; ROS uses -1 for the counts and NaN for offset/angle).

Screenshots

  • Default collapsed when no values have been added
image - Opening when value has been added opens the accordion and shows number of overridden values image - All 4 fields and their descriptions visible image

Testing

  • tsc --noEmit, prettier --check, and next build all pass.
  • Verified end-to-end against a live mower: writing the four fields via map.replace persists and reads back correctly, with other areas untouched.

Summary by CodeRabbit

  • New Features
    • Added advanced per-area mowing overrides (outline count, overlap, offset, and angle) for mowing areas, including an override-count indicator.
    • Introduced a reusable text input with an info tooltip for clearer field guidance.
  • Bug Fixes
    • Override values now load/sync correctly, including angle conversion between radians (stored) and degrees (shown).
    • Invalid or cleared override inputs are not saved, restoring global/default behavior.
  • Documentation
    • Updated the area schema to include and validate the new optional override fields.

@CLAassistant

CLAassistant commented Jun 28, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@rpeltola
rpeltola marked this pull request as ready for review June 28, 2026 20:02
@ClemensElflein
ClemensElflein requested a review from rovo89 June 28, 2026 20:25
@ClemensElflein
ClemensElflein force-pushed the per-area-mow-overrides branch from 8fc70d0 to 8351bb7 Compare June 28, 2026 20:48
@ClemensElflein

ClemensElflein commented Jun 28, 2026

Copy link
Copy Markdown
Member

@coderabbitai full review

Edit: sorry coderabbit for the ping, I added a space too many

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

@ClemensElflein The PR description images show an Area Settings modal in a dark-themed app UI with green accents:

  • The first PR description image shows the modal with fields for Name (Alapihaa part 1), Type (Mowing Area), an Active toggle enabled, and an Advanced accordion that is collapsed. The footer has Cancel and Save buttons.
  • The second PR description image shows the same modal with the Advanced section expanded and a green badge labeled 1, indicating one override is set. It includes explanatory text: “Per-area overrides for the global mowing settings. Leave empty to use the global default.” Visible fields include Outline count set to 6 and Outline overlap count showing Global default.
  • The third PR description image shows the Advanced section fully expanded with all four override inputs visible: Outline count, Outline overlap count, Outline offset (m), and Mow angle (°). Placeholder/default text includes Global default and Auto-detect, along with helper descriptions for each field.

I’ll run a full review of the PR now.

✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@rovo89, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8f179d0f-1883-4f7e-a889-28dba18fa031

📥 Commits

Reviewing files that changed from the base of the PR and between cd6700b and a36a533.

📒 Files selected for processing (1)
  • src/components/ui/TooltipTextField.tsx
📝 Walkthrough

Walkthrough

Adds optional mowing override fields to areaSchema and wires them into AreaSettingsDialog through state, hydration, validation, radian/degree conversion, and a conditional mow-only advanced accordion.

Changes

Per-area mowing overrides

Layer / File(s) Summary
Area schema override fields
src/stores/schemas.ts
Adds optional angle, outline_count, outline_overlap_count, and outline_offset fields to areaSchema with validation for the count fields.
Tooltip text field
src/components/ui/TooltipTextField.tsx
Adds a text field wrapper that renders an info tooltip adornment and blocks arrow-key stepping on number inputs.
Dialog state, initialization, and save logic
src/components/map/edit/AreaSettingsDialog.tsx
Adds override string state, hydrates it from stored properties, expands the overrides section when overrides exist, and saves by parsing or deleting override keys with angle converted between degrees and radians.
Advanced accordion UI
src/components/map/edit/AreaSettingsDialog.tsx
Adds the mow-only advanced accordion, numeric override inputs, an override-count chip, tooltip help, and validation-aware save enablement.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Poem

🐇 I hopped through fields both neat and wide,
With angle, outlines, tucked inside.
Degrees to radians, back again,
The mower now can choose its lane.
A chip, a tooltip, grassy grin—
New overrides bloom where plots begin 🌿

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: adding per-area mowing override settings for outline count, overlap, offset, and angle.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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

❤️ Share

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

@coderabbitai

coderabbitai Bot commented Jun 28, 2026

Copy link
Copy Markdown

The PR description images show an Area Settings modal in a dark-themed UI with green accents and white form cards. The first image shows Name set to Alapihaa part 1, Type set to Mowing Area, an enabled Active toggle, and an Advanced section that is collapsed, with Cancel and Save buttons at the bottom. The second image shows the same modal with Advanced expanded, a green badge labeled 1, helper text saying “Per-area overrides for the global mowing settings. Leave empty to use the global default.”, and fields including Outline count set to 6 plus Outline overlap count showing Global default. The third image shows the full expanded advanced form with all four override fields visible: Outline count, Outline overlap count, Outline offset (m), and Mow angle (°), with placeholders such as Global default and Auto-detect, plus helper text explaining each field.

@ClemensElflein Running a full review now.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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 `@src/components/map/edit/AreaSettingsDialog.tsx`:
- Around line 93-103: The override parsing in AreaSettingsDialog’s applyOverride
helper is too permissive for outline_count and outline_overlap_count, which are
defined as non-negative integers in the schema. Update the parsing/validation
path used by applyOverride so these count fields only accept whole, non-negative
integers and reject fractional or negative input instead of silently coercing it
via parseInt. Keep outline_offset parsing separate as a float, and make the
change in the AreaSettingsDialog logic that sets properties[key] so persisted
area properties match the schema contract.
🪄 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: 33bd30b0-4017-4b47-950b-ce83f19bccc8

📥 Commits

Reviewing files that changed from the base of the PR and between 1bf5bab and 8351bb7.

📒 Files selected for processing (2)
  • src/components/map/edit/AreaSettingsDialog.tsx
  • src/stores/schemas.ts

Comment thread src/components/map/edit/AreaSettingsDialog.tsx
@github-actions

Copy link
Copy Markdown

🧪 Test this PR

A multi-arch image (amd64 + arm64) for this PR has been pushed to:

ghcr.io/xtech/openmower-app:pr-5

OpenMowerOS v2

  1. Go to http://<mower-ip>:5001/compose/openmower in your browser.

  2. Switch to edit mode.

  3. In the compose.yaml editor, add the following above the # Dockge-specific extras shown in the UI line (mind the 2-space indentation, like the other containers):

      app:
        image: ghcr.io/xtech/openmower-app:pr-5
        container_name: app
        ports:
          - 3000:3000
        restart: unless-stopped
  4. Add the URL under x-dockge:

    x-dockge:
      urls:
        - http://${HOSTNAME}:8080
        - http://${HOSTNAME}:3000
  5. Click Deploy, then open http://<mower-ip>:3000.

Plain Docker

docker run --rm -p 3000:3000 ghcr.io/xtech/openmower-app:pr-5

This comment updates automatically on every new push to the PR.

Add optional per-area overrides for outline_count, outline_overlap_count, outline_offset and angle, shown in a collapsible "Advanced" section in the area settings dialog (auto-expanded when the area already has overrides set, with a badge showing how many are set). Empty fields fall back to the global config defaults by omitting the key from map.json, matching ROS's overrideOrGlobal sentinels. Angle is entered in degrees and stored as radians to match ROS (mower_map MapArea.msg).
@ClemensElflein
ClemensElflein force-pushed the per-area-mow-overrides branch from 39572a7 to 5546f03 Compare July 2, 2026 07:35
@ClemensElflein

Copy link
Copy Markdown
Member

Tested this today, functionality LGTM!

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
src/components/ui/TooltipTextField.tsx (2)

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

Icon button lacks an accessible name and is unreachable via keyboard.

tabIndex={-1} removes the info button from tab order, and there is no aria-label, so keyboard/screen-reader users cannot discover the tooltip content conveyed only through this icon.

🛠️ Proposed fix
-        <IconButton size="small" tabIndex={-1} edge="end">
+        <IconButton size="small" tabIndex={-1} edge="end" aria-label={tooltip}>
🤖 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 `@src/components/ui/TooltipTextField.tsx` around lines 32 - 34, The IconButton
in TooltipTextField is missing an accessible name and is removed from the
keyboard tab order. Update the IconButton used with InfoOutlinedIcon to be
reachable by keyboard (remove the negative tabIndex) and add an appropriate
aria-label so screen readers can identify the tooltip trigger. Keep the fix
localized to TooltipTextField and preserve the existing icon/tooltip behavior.

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

Unsafe Record<string, unknown> cast for slotProps.input.

Casting away the typed slotProps shape loses type safety on the merge target. Consider narrowing via TextFieldProps['slotProps'] instead of an opaque cast.

🤖 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 `@src/components/ui/TooltipTextField.tsx` around lines 50 - 56, The merge for
TooltipTextField’s slotProps.input is using an unsafe Record<string, unknown>
cast that bypasses the intended type shape. Update the merge in TooltipTextField
to use the typed TextFieldProps['slotProps'] shape (or a properly narrowed
equivalent) when spreading slotProps.input so the merge stays type-safe and
doesn’t rely on an opaque cast.
🤖 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 `@src/components/ui/TooltipTextField.tsx`:
- Around line 46-58: The TooltipTextField wrapper is overriding any
caller-provided onKeyDown handler by always setting blockArrowKeys after
spreading props, so update the TextField composition to chain both handlers
instead of replacing one. Locate the TextField render in TooltipTextField and
merge props.onKeyDown with blockArrowKeys so both run when present, preserving
the existing arrow-key blocking behavior while honoring consumer callbacks.

---

Nitpick comments:
In `@src/components/ui/TooltipTextField.tsx`:
- Around line 32-34: The IconButton in TooltipTextField is missing an accessible
name and is removed from the keyboard tab order. Update the IconButton used with
InfoOutlinedIcon to be reachable by keyboard (remove the negative tabIndex) and
add an appropriate aria-label so screen readers can identify the tooltip
trigger. Keep the fix localized to TooltipTextField and preserve the existing
icon/tooltip behavior.
- Around line 50-56: The merge for TooltipTextField’s slotProps.input is using
an unsafe Record<string, unknown> cast that bypasses the intended type shape.
Update the merge in TooltipTextField to use the typed
TextFieldProps['slotProps'] shape (or a properly narrowed equivalent) when
spreading slotProps.input so the merge stays type-safe and doesn’t rely on an
opaque cast.
🪄 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: b1f2ae2d-86e2-4784-8e5b-f4725adf7b4e

📥 Commits

Reviewing files that changed from the base of the PR and between 5546f03 and cd6700b.

📒 Files selected for processing (3)
  • src/components/map/edit/AreaSettingsDialog.tsx
  • src/components/ui/TooltipTextField.tsx
  • src/stores/schemas.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/stores/schemas.ts
  • src/components/map/edit/AreaSettingsDialog.tsx

Comment on lines +46 to +58
return (
<TextField
{...props}
onKeyDown={blockArrowKeys}
slotProps={{
...slotProps,
input: {
endAdornment: infoAdornment,
...(slotProps as Record<string, unknown>)?.input,
},
}}
/>
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

onKeyDown silently overrides any caller-supplied handler.

onKeyDown={blockArrowKeys} is set after {...props}, so if a consumer ever passes its own onKeyDown, it is discarded instead of composed. No current caller does this, but it's a latent trap for this reusable wrapper.

♻️ Proposed fix to chain handlers
-    <TextField
-      {...props}
-      onKeyDown={blockArrowKeys}
+    <TextField
+      {...props}
+      onKeyDown={(e) => {
+        blockArrowKeys?.(e);
+        props.onKeyDown?.(e);
+      }}
📝 Committable suggestion

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

Suggested change
return (
<TextField
{...props}
onKeyDown={blockArrowKeys}
slotProps={{
...slotProps,
input: {
endAdornment: infoAdornment,
...(slotProps as Record<string, unknown>)?.input,
},
}}
/>
);
return (
<TextField
{...props}
onKeyDown={(e) => {
blockArrowKeys?.(e);
props.onKeyDown?.(e);
}}
slotProps={{
...slotProps,
input: {
endAdornment: infoAdornment,
...(slotProps as Record<string, unknown>)?.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 `@src/components/ui/TooltipTextField.tsx` around lines 46 - 58, The
TooltipTextField wrapper is overriding any caller-provided onKeyDown handler by
always setting blockArrowKeys after spreading props, so update the TextField
composition to chain both handlers instead of replacing one. Locate the
TextField render in TooltipTextField and merge props.onKeyDown with
blockArrowKeys so both run when present, preserving the existing arrow-key
blocking behavior while honoring consumer callbacks.

@rovo89
rovo89 merged commit 0b14146 into xtech:main Jul 3, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants