Skip to content

feat(colorbar): stack-order fix, stacking direction, and resizable panel (#884) - #901

Merged
giswqs merged 5 commits into
mainfrom
fix/issue-884-colorbar-stacking
Jun 26, 2026
Merged

feat(colorbar): stack-order fix, stacking direction, and resizable panel (#884)#901
giswqs merged 5 commits into
mainfrom
fix/issue-884-colorbar-stacking

Conversation

@giswqs

@giswqs giswqs commented Jun 25, 2026

Copy link
Copy Markdown
Member

Summary

Colorbar plugin improvements from #884, plus a resizable config panel:

  • Issue 2 (bug): colorbars jumped position on update. When several colorbars shared a corner, Update Selected Colorbar moved the edited one to the top of the stack (the control removed and re-added it, and MapLibre inserts bottom-corner controls at the front). Fixed: colorbars sharing a corner are grouped into one control and re-render in place in stable order.
  • Issue 3 (feature): stacking direction. Choose to stack multiple colorbars vertically (default) or horizontally, via a new Stack multiple colorbars dropdown. Independent of a single bar's orientation.
  • Resizable panel. The colorbar config panel now resizes from two bottom-corner grips (matching the HTML and data panels); drag either grip to grow it toward the map interior.

Issue 1 (separate Name vs Label field) was scoped by the reporter to a later release and is not included.

How

All three live in the upstream maplibre-gl-components package:

This PR bumps maplibre-gl-components to ^0.25.3 in apps/geolibre-desktop and packages/plugins, and mirrors the new stackOrientation field in the saved colorbar project state so a horizontal choice round-trips on reopen.

Testing

  • npm run build and scoped pre-commit run --files ... (incl. the npm-build hook) pass; added tests/colorbar-state-normalization.test.ts for the stackOrientation round-trip.
  • Upstream suites: 215 tests pass, with regression tests for grouped stacking, the stacking toggle, and the resize grips.
  • Verified in the running app with Playwright (light + dark): update keeps stack order; horizontal lays colorbars side by side; dragging a bottom grip grows the panel in both width and height. No console errors.

Fixes #884

Summary by CodeRabbit

  • Bug Fixes
    • Improved color bar orientation handling: an explicit “horizontal” setting is preserved; missing or invalid values now reliably fall back to “vertical” and remain stable across repeated normalization.
  • Tests
    • Added Node test coverage for stackOrientation normalization, including backward-compatible defaults, invalid-value coercion, and idempotency.
  • Chores
    • Updated the shared map rendering component dependency in both the desktop and plugin packages to ^0.25.3.

…884)

Bump maplibre-gl-components to 0.25.2, which groups colorbars sharing a
corner into one control so updating a colorbar no longer jumps it to the
top of the stack, and adds a stacking-direction option (vertical or
horizontal) for multiple colorbars. Mirror the new stackOrientation field
in the saved project state so the choice round-trips on reopen.

Fixes #884
@netlify

netlify Bot commented Jun 25, 2026

Copy link
Copy Markdown

Deploy Preview for geolibre-app ready!

Name Link
🔨 Latest commit 4ada432
🔍 Latest deploy log https://app.netlify.com/projects/geolibre-app/deploys/6a3dd42995891b0007c5f49d
😎 Deploy Preview https://deploy-preview-901--geolibre-app.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: ba7905d8-2d59-4b7d-baf1-fb4ff48c2ff4

📥 Commits

Reviewing files that changed from the base of the PR and between 5e2205e and 4ada432.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • apps/geolibre-desktop/package.json
  • packages/plugins/package.json

📝 Walkthrough

Walkthrough

The colorbar state now includes stackOrientation, normalization exports a helper that defaults missing or unknown values to vertical, and tests cover the new orientation behavior. Both package manifests bump maplibre-gl-components.

Changes

Colorbar stack orientation

Layer / File(s) Summary
State shape and normalization
packages/plugins/src/plugins/maplibre-components.ts
ComponentColorbarGuiEntryState adds stackOrientation, normalizeColorbarState is exported, and the normalization logic maps input orientation to "horizontal" or "vertical".
Regression tests and package updates
tests/colorbar-state-normalization.test.ts, apps/geolibre-desktop/package.json, packages/plugins/package.json
Tests cover preserved, missing, unknown, and repeat stackOrientation values, and both package manifests bump maplibre-gl-components to ^0.25.3.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related issues

Poem

🐰 I hopped where stacky colors spin,
Horizontal or vertical tucked in.
Tests gave a nod, the bump marched by,
A tidy little change beneath the sky.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the PR’s main Colorbar changes: stack ordering, stacking direction, and panel resizing.
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
  • Commit unit tests in branch fix/issue-884-colorbar-stacking

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.

@github-actions

github-actions Bot commented Jun 25, 2026

Copy link
Copy Markdown
Contributor

⚡ Cloudflare Pages preview

Item Value
Preview https://7d536b34.geolibre-preview.pages.dev
Demo app https://7d536b34.geolibre-preview.pages.dev/demo/
Commit f958330

Comment thread packages/plugins/src/plugins/maplibre-components.ts
@github-actions

Copy link
Copy Markdown
Contributor

Code review

This is a focused, clean change — a version bump to pick up an upstream bug fix plus the minimal GeoLibre-side plumbing to expose the new stackOrientation field in saved project state.

Bugs

Nothing found. The normalization candidate.stackOrientation === "horizontal" ? "horizontal" : "vertical" correctly handles all inputs including undefined (old project files default to "vertical", matching the described default). The pattern is identical to the existing orientation guard in normalizeColorbarEntry (line 1526), so the logic is consistent.

Security

Nothing found. This is pure data serialization; no user-controlled input flows to any execution path that could cause injection.

Performance

Nothing found.

Quality

  • Round-trip external dependency (medium confidence): Persisting the user's horizontal choice across project save/load silently depends on the upstream ColorbarGuiControl.getState() including stackOrientation in its return value. If the library omits it, the normalization always sees undefined and snapshots "vertical", so the choice is lost on reload. The PR's manual Playwright testing makes this almost certainly fine, but the dependency is non-obvious. See inline comment at line 1457.
  • No automated test for the new field (low confidence): normalizeColorbarState has no unit-test coverage in the test suite. A small test asserting that stackOrientation: "horizontal" round-trips (and that unknown values default to "vertical") would be a cheap regression guard.

CLAUDE.md

  • Both workspace package.json files are updated consistently (apps/geolibre-desktop and packages/plugins). Lock file is in sync with two separate hoisted copies, both at 0.25.2.
  • No new user-facing strings added in plugin TypeScript code (the dropdown UI lives entirely in the upstream library), so no t() obligation.
  • No MapLibre direct mutation; state flows through the store pattern as expected.

Export normalizeColorbarState and add unit tests that a horizontal stack
choice persists, missing/unknown values fall back to vertical, and the
state round-trips cleanly. Addresses review feedback on PR #901.
Comment thread tests/colorbar-state-normalization.test.ts Outdated
Comment thread tests/colorbar-state-normalization.test.ts
Comment thread tests/colorbar-state-normalization.test.ts
@github-actions

Copy link
Copy Markdown
Contributor

Code review

Bugs

  • None found. The stackOrientation normalization logic is correct: === "horizontal" ? "horizontal" : "vertical" is a safe, strict guard. The normalizeColorbarState export is used only by the test (not re-exported from packages/plugins/src/index.ts), so it doesn't widen the package's public API. The restoreColorbarPanelrestoreGuiControlStatesetState chain passes the full state including stackOrientation to the upstream control, which is the intended path given the upstream fix in maplibre-gl-components 0.25.2.

Security

  • None found. No new input boundaries, no string interpolation into commands, no secrets exposure.

Performance

  • None found. The normalization is a single-pass object transform; the dependency bump is a patch-range increment with no API-breaking changes.

Quality

  • Missing test for explicit "vertical" pass-through (medium confidence) — the ternary has two distinct valid inputs ("horizontal" and "vertical") but the test suite only verifies one positive case. A typo like === "vertical" instead of === "horizontal" would still pass all existing tests. See inline comment at line 34.
  • Round-trip test uses un-normalized input (low confidence) — customColors: "" is silently replaced by the default palette on the first normalization call, so deepEqual(twice, once) checks idempotency of already-normalized state, not a true end-to-end round-trip from the raw input. Not a correctness bug, but potentially misleading. See inline comment at lines 39–53.

CLAUDE.md

  • Multi-line block comment in test file (high confidence) — the 7-line /** … */ block at lines 5–11 violates CLAUDE.md's rule "Don't write multi-paragraph docstrings or multi-line comment blocks — one short line max." See inline comment.

Overall the implementation is sound. The core fix (package bump + stackOrientation field in state + normalization) is clean and the normalization logic is idempotent and backwards-compatible. The three findings above are all in the new test file and are about test hygiene rather than correctness.

- Add an explicit "vertical" pass-through test so the two valid
  stackOrientation values are isolated (a `=== "vertical"` typo would
  otherwise pass every existing case).
- Use a non-empty customColors in the round-trip input so the
  normalizer's empty-string substitution no longer obscures the intent.
- Trim the file header comment to a concise three lines.
Comment thread packages/plugins/src/plugins/maplibre-components.ts
Comment thread tests/colorbar-state-normalization.test.ts Outdated
Comment thread tests/colorbar-state-normalization.test.ts
Comment thread packages/plugins/src/plugins/maplibre-components.ts
@github-actions

Copy link
Copy Markdown
Contributor

Code review

Overall this is a clean, well-scoped fix. The normalization logic is correct, backward-compat defaulting is solid, and the round-trip idempotency test is a nice touch. Four findings follow, ranging from a medium-confidence correctness note to minor nits.


Bugs / Correctness

# Finding Confidence
1 stackOrientation persistence hinges on upstream getState() shapenormalizeColorbarState reads candidate.stackOrientation from colorbarControl.getState(). If ColorbarGuiControl.getState() in v0.25.2 doesn't include that key, the field always normalises to "vertical" and the user's horizontal choice is silently dropped on save/reopen. The upstream PR (#116) explicitly adds this, so it is very likely correct — but worth a manual smoke-test (save a horizontal stack, reopen, confirm). Inline comment at lines 1456–1457. Medium

Quality

# Finding Confidence
2 Asymmetric export on normalizeColorbarState — This is the only normalize* helper exported from the file; normalizeLegendState, normalizeHtmlState, etc. remain private. The function is not re-exported through index.ts so it won't leak into the public package API, but the asymmetry could mislead future readers. A @internal JSDoc tag would make the intent explicit. Inline comment at line 1435. Low
3 Test comment block duplicates the describe/test names — Lines 5–7 of the new test file describe what the test names already express; CLAUDE.md asks to omit comments whose removal wouldn't confuse a future reader. Minor nit. Inline comment at line 5. Low
4 No test for null/undefined/non-object inputnormalizeColorbarState explicitly returns undefined for falsy values (line 1438), but none of the new tests exercises this branch. Purely informational — the guard is trivially simple — but one extra case would complete the contract. Inline comment at line 19. Low

Security / Performance / CLAUDE.md

Nothing to raise. The stackOrientation field is sanitised with a strict equality check before use (no injection surface), the dependency bump is a patch release with a clean lockfile integrity hash, and no new i18n strings are needed (the new dropdown lives entirely in the upstream control).

- Add an @internal JSDoc tag to normalizeColorbarState clarifying it is
  exported only for unit testing.
- Add a test covering null/undefined/non-object input (the early-return
  branch).
- Drop the file header comment; the describe label and test names cover it.
}

function normalizeColorbarState(
/** @internal Exported only so the project-state normalizer can be unit-tested. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Minor wording nit: normalizeColorbarState is one of the project-state normalizers, so the phrase "so the project-state normalizer can be unit-tested" is slightly circular. Consider:

Suggested change
/** @internal Exported only so the project-state normalizer can be unit-tested. */
/** @internal Exported only for unit testing. */

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Leaving this open for a maintainer call. These three round-3 comments are all subjective style nits (wording of an @internal note, splitting one assertion group into separate it() blocks, and an informational observation about idempotent deepEqual) with no functional or correctness impact. The prior round's comment-block guidance also reversed itself between rounds, so I'm stopping here rather than churning the branch on marginal stylistic preferences. Happy to apply any of these if you'd like.

Comment on lines +15 to +19
it("returns undefined for null/undefined/non-object input", () => {
assert.equal(normalizeColorbarState(null), undefined);
assert.equal(normalizeColorbarState(undefined), undefined);
assert.equal(normalizeColorbarState("nope"), undefined);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Nit (test quality): three independent assertions in one it() means a failure message identifies the test but not the failing case. Splitting each into its own it() gives a more precise failure signal:

Suggested change
it("returns undefined for null/undefined/non-object input", () => {
assert.equal(normalizeColorbarState(null), undefined);
assert.equal(normalizeColorbarState(undefined), undefined);
assert.equal(normalizeColorbarState("nope"), undefined);
});
it("returns undefined for null input", () => {
assert.equal(normalizeColorbarState(null), undefined);
});
it("returns undefined for undefined input", () => {
assert.equal(normalizeColorbarState(undefined), undefined);
});
it("returns undefined for non-object input", () => {
assert.equal(normalizeColorbarState("nope"), undefined);
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Leaving this open for a maintainer call. These three round-3 comments are all subjective style nits (wording of an @internal note, splitting one assertion group into separate it() blocks, and an informational observation about idempotent deepEqual) with no functional or correctness impact. The prior round's comment-block guidance also reversed itself between rounds, so I'm stopping here rather than churning the branch on marginal stylistic preferences. Happy to apply any of these if you'd like.

Comment on lines +44 to +65
it("round-trips a horizontal choice through a second normalization", () => {
const once = normalizeColorbarState({
visible: true,
colorbars: [
{
mode: "named",
colormap: "viridis",
customColors: "#440154, #31688e, #21918c, #90d743, #fde725",
vmin: 0,
vmax: 100,
label: "Depth",
units: "",
orientation: "vertical",
colorbarPosition: "bottom-right",
},
],
stackOrientation: "horizontal",
});
const twice = normalizeColorbarState(once);
assert.equal(twice?.stackOrientation, "horizontal");
assert.deepEqual(twice, once);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

assert.deepEqual(twice, once) passes correctly (idempotent), but is slightly surprising on first read: the top-level form fields (mode, colormap, label, vmin, vmax, etc.) in once are set to their defaults — not derived from colorbars[0] — because the original input has no top-level form fields. So once.label === "" even though colorbars[0].label === "Depth".

The test is correct, but a short inline comment would help future readers understand why deepEqual holds without surprise:

Suggested change
it("round-trips a horizontal choice through a second normalization", () => {
const once = normalizeColorbarState({
visible: true,
colorbars: [
{
mode: "named",
colormap: "viridis",
customColors: "#440154, #31688e, #21918c, #90d743, #fde725",
vmin: 0,
vmax: 100,
label: "Depth",
units: "",
orientation: "vertical",
colorbarPosition: "bottom-right",
},
],
stackOrientation: "horizontal",
});
const twice = normalizeColorbarState(once);
assert.equal(twice?.stackOrientation, "horizontal");
assert.deepEqual(twice, once);
});
it("round-trips a horizontal choice through a second normalization", () => {
// Top-level form fields in `once` come from defaults (the input has no top-level
// mode/label/etc.), so `once.label` will be "" regardless of colorbars[0].label.
// The deepEqual check verifies that normalizeColorbarState is idempotent once the
// state is already normalized.
const once = normalizeColorbarState({
visible: true,
colorbars: [
{
mode: "named",
colormap: "viridis",
customColors: "#440154, #31688e, #21918c, #90d743, #fde725",
vmin: 0,
vmax: 100,
label: "Depth",
units: "",
orientation: "vertical",
colorbarPosition: "bottom-right",
},
],
stackOrientation: "horizontal",
});
const twice = normalizeColorbarState(once);
assert.equal(twice?.stackOrientation, "horizontal");
assert.deepEqual(twice, once);
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Leaving this open for a maintainer call. These three round-3 comments are all subjective style nits (wording of an @internal note, splitting one assertion group into separate it() blocks, and an informational observation about idempotent deepEqual) with no functional or correctness impact. The prior round's comment-block guidance also reversed itself between rounds, so I'm stopping here rather than churning the branch on marginal stylistic preferences. Happy to apply any of these if you'd like.

@github-actions

Copy link
Copy Markdown
Contributor

Code review

Reviewed the dependency bump to maplibre-gl-components@0.25.2, the stackOrientation field added to ComponentColorbarGuiState, the normalizeColorbarState export, and the new test file. No bugs or security issues found. All findings are quality/test nits.

Bugs

None found. The normalization logic is correct: "horizontal" is preserved, anything else (including undefined) falls back to "vertical". The save/restore path correctly flows stackOrientation through getState() → normalizeColorbarState → setState(). The lock file integrity hash update is consistent with the version bump.

Security

None. No user input reaches normalizeColorbarState without sanitization; the union-type guard pattern (=== "horizontal" ? "horizontal" : "vertical") is the same safe approach already used for orientation at line 1527.

Performance

None. The change is additive (one extra string field in a state object); no new hot paths.

Quality

# Finding Confidence
1 @internal JSDoc wording"so the project-state normalizer can be unit-tested" is circular since normalizeColorbarState is the normalizer; "Exported only for unit testing." is clearer. (inline comment on line 1435) High
2 Three assertions in one it() — the null/undefined/string guard test bundles three independent assertions so a failure message identifies the test but not the failing case. Splitting each into its own it() costs two extra lines and gives precise output. (inline comment on lines 15–19) High
3 Round-trip deepEqual is subtly non-obviousonce.label is "" (default), not "Depth" from colorbars[0], because the test input carries no top-level form fields. The test is correct and idempotency holds, but a short comment would help future readers. (inline comment on lines 44–65) Medium

CLAUDE.md

The new test is picked up by npm run test:frontend (tests/*.test.ts glob) without manual registration — consistent with CLAUDE.md conventions. The export is appropriately scoped with @internal. No concerns.

Bump maplibre-gl-components to 0.25.3, which adds two bottom-corner resize
grips to the colorbar GUI panel (matching the HTML and data panels). Drag
either grip to grow the panel toward the map interior.
@giswqs giswqs changed the title fix(colorbar): keep stack order on update and add stacking direction (#884) feat(colorbar): stack-order fix, stacking direction, and resizable panel (#884) Jun 26, 2026
Comment on lines +26 to +33
it("coerces an unknown stack orientation to vertical", () => {
const normalized = normalizeColorbarState({
visible: true,
colorbars: [],
stackOrientation: "diagonal",
});
assert.equal(normalized?.stackOrientation, "vertical");
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The "coerces unknown" test only covers a string value ("diagonal"). Project files are arbitrary JSON, so stackOrientation could also arrive as a number, boolean, or array. Since the normalization uses strict equality (=== "horizontal"), these all correctly fall through to "vertical" — but a quick additional case would make that intent explicit and guard against a future refactor that adds || typeof candidate.stackOrientation === "number" etc.

Suggested change
it("coerces an unknown stack orientation to vertical", () => {
const normalized = normalizeColorbarState({
visible: true,
colorbars: [],
stackOrientation: "diagonal",
});
assert.equal(normalized?.stackOrientation, "vertical");
});
it("coerces an unknown stack orientation to vertical", () => {
for (const bad of ["diagonal", 42, true, null, [], {}]) {
const normalized = normalizeColorbarState({
visible: true,
colorbars: [],
stackOrientation: bad,
});
assert.equal(normalized?.stackOrientation, "vertical", `expected vertical for ${JSON.stringify(bad)}`);
}
});

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Leaving open for a maintainer call. The code already handles non-string values correctly via strict === "horizontal" (number/boolean/array all fall through to vertical), so this is a marginal coverage nit rather than a defect. I'm holding off on more test-only churn here since the review has converged to stylistic nits on this file across several rounds.

Comment on lines +44 to +65
it("round-trips a horizontal choice through a second normalization", () => {
const once = normalizeColorbarState({
visible: true,
colorbars: [
{
mode: "named",
colormap: "viridis",
customColors: "#440154, #31688e, #21918c, #90d743, #fde725",
vmin: 0,
vmax: 100,
label: "Depth",
units: "",
orientation: "vertical",
colorbarPosition: "bottom-right",
},
],
stackOrientation: "horizontal",
});
const twice = normalizeColorbarState(once);
assert.equal(twice?.stackOrientation, "horizontal");
assert.deepEqual(twice, once);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The assert.deepEqual(twice, once) at the end is a useful idempotency check. One thing to be aware of: the input to the first normalization has no top-level entry-level fields (mode, colormap, etc.), so once gets these from defaults via normalizeColorbarEntry. The deep-equal passes because normalizeColorbarEntry(once) reads those same default values back from once and produces an identical object.

If ComponentColorbarGuiState ever gains a computed/ephemeral field (e.g. a _dirty flag set only by setState), this assertion could start failing for unrelated reasons. It's a minor fragility. If idempotency is the goal, targeted field assertions (assert.equal(twice?.colorbars[0]?.colormap, once?.colorbars[0]?.colormap) etc.) are more resilient — though the current broad check is a net positive for catching regressions.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Acknowledged, and it's intentional: the test is an idempotency check on already-normalized state. The top-level form fields come from defaults because the input has no top-level fields, and deepEqual(twice, once) verifies normalization is stable. Leaving open for your call rather than reworking the test further.

@github-actions

Copy link
Copy Markdown
Contributor

Code review

Reviewed the diff (81 additions, 11 deletions across 5 files): dependency bump, stackOrientation type + normalization, normalizeColorbarState export, and new test suite. Checked the surrounding normalizer, restore/snapshot flow, package index exports, and project-format docs.

Bugs

None found. The normalization logic candidate.stackOrientation === 'horizontal' ? 'horizontal' : 'vertical' is correct, follows the pre-existing pattern used for orientation in normalizeColorbarEntry, and the vertical default is the right backward-compat choice.

Security

None found. stackOrientation is constrained to a two-value union by the normalizer before being stored or passed to the upstream control — no injection surface.

Performance

None found. The version bump is a minor feature/bug-fix release with no reported performance changes.

Quality

  • [medium confidence] normalizeColorbarState is now exported with @internal, but is not re-exported from packages/plugins/src/index.ts, so it does not widen the package's public API. The approach is pragmatic and correctly documented. The test suite does not cover non-string values for stackOrientation (e.g. a number or boolean from a corrupted project file) — since the normalization uses strict equality, those all fall through to "vertical" correctly, but the intent is not demonstrated. See inline comment on the coercion test.
  • [low confidence] assert.deepEqual(twice, once) in the round-trip test is a useful idempotency assertion. It passes because the first normalization populates entry-level fields from defaults and the second normalization reads those same values back unchanged. If ComponentColorbarGuiState ever gains a computed or ephemeral field, this assertion could fail incidentally. See inline comment.
  • [low confidence] Both workspace copies of maplibre-gl-components remain as separate lock file entries (apps/geolibre-desktop/node_modules/ and packages/plugins/node_modules/). This is a pre-existing monorepo hoisting situation, not introduced by this PR; the bump correctly updates both entries.

CLAUDE.md

  • No new user-visible strings require t(). The "Stack multiple colorbars" dropdown label lives in the upstream maplibre-gl-components library outside GeoLibre's i18n scope.
  • All three dependency sites (apps/geolibre-desktop/package.json, packages/plugins/package.json, lock file) are bumped consistently from ^0.25.1 to ^0.25.3.

Overall the PR is clean and well-scoped. The normalization is correct, backward-compat, and well-tested. The two inline notes are minor nits.

@giswqs
giswqs merged commit 3f81729 into main Jun 26, 2026
23 checks passed
@giswqs
giswqs deleted the fix/issue-884-colorbar-stacking branch June 26, 2026 01:32
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.

UX Evaluation & Feature Request: Colorbar Plugin

1 participant