Skip to content

improve(pi): add recent color history and preset swatches to ird-color-picker (#262)#328

Merged
niklam merged 2 commits into
masterfrom
improve/262-color-picker-history
Apr 12, 2026
Merged

improve(pi): add recent color history and preset swatches to ird-color-picker (#262)#328
niklam merged 2 commits into
masterfrom
improve/262-color-picker-history

Conversation

@niklam
Copy link
Copy Markdown
Owner

@niklam niklam commented Apr 12, 2026

Related Issue

Fixes #262

What changed?

Redesigned ird-color-picker to replace the single × clear button with an inline swatch group to the right of the hex input:

  • Not-Set — white with red diagonal indicator, clears the picker on click.
  • Icon default — rendered only when the picker has a default or data-default-color attribute (and it's not Black or White).
  • Black / White — always-shown fixed presets.
  • Recent colors — up to 4 visible, 6 stored.

Recents are persisted per slot type (backgroundColor, textColor, graphic1Color, graphic2Color, borderColor) under flat _colorHistory* global setting keys, so per-action overrides and global defaults of the same slot share one history. Each picker also subscribes to its slot's history so multiple open pickers (e.g. different actions, or per-action vs global pages) stay in sync without a refresh.

The hex input is now fixed at 54px to fit all swatches cleanly in the PI panel width.

Commit semantics:

  • Dedupe + bump-to-front on every committed color, truncate to 6 stored.
  • Skip history writes for Not-Set, Black, White, and the icon default.
  • Redundant clicks (same color already current) short-circuit before saving.
  • lastSavedHistoryJson guard prevents feedback loops between the commit path and the subscription echo.

How to test

  1. Open any action's PI (e.g. Splits Delta Cycle). Confirm the row next to each color picker shows: Not-Set ▦, icon default (if any), Black, White.
  2. Set Background to #aa3300. Confirm it appears as the first recent (slot 4) and persists across PI reloads.
  3. Set Text to #ffaa00. Confirm Background recents are unchanged; Text recents are independent.
  4. Open another action's PI and confirm its pickers show the same recents for each slot type (cross-picker sync).
  5. Open Global Settings → Color Defaults. Confirm the same recents appear on the global pickers and that picking a recent from one side updates the other.
  6. Commit #000000 and #ffffff — neither should appear in recents.
  7. Commit 7 distinct non-preset colors; confirm only 4 are visible and the oldest was dropped.
  8. Click the Not-Set swatch on a picker with a value — it clears the color and does not write to history.
  9. Click the same swatch twice — the second click is a no-op.
  10. Restart Stream Deck and confirm recents persist.

Checklist

  • Linked to an approved issue
  • New code has unit tests
  • Changes registered in all applicable plugins (Stream Deck, Mirabox)
  • No unrelated changes included

Summary by CodeRabbit

  • New Features
    • Color history: recent colors are saved and persist across sessions for quick access.
    • Enhanced swatch interface: preset swatches (including icon-default when applicable), fixed Black/White presets, and up to four recent swatches.
    • Improved workflow: a “Not set” swatch clears the value; recent-history dedupes, trims, and skips reserved values.

…r-picker (#262)

Replaces the × clear button with an inline swatch group: Not-Set,
optional icon default, Black, White, and up to 4 recent colors. Recents
are persisted per slot type in flat `_colorHistory*` global settings,
shared across per-action overrides and global defaults, and kept in
sync across pickers via the global-settings subscription.

Commits dedupe and bump colors to the front, truncate to 6 stored, and
skip history for preset/not-set/icon-default values. Redundant clicks
short-circuit before saving.
@github-actions github-actions Bot added the type: improvement Improvements to existing functionality label Apr 12, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 12, 2026

📝 Walkthrough

Walkthrough

The ird-color-picker now infers a slot type from setting, shows a compact swatch group (Not set, optional icon-default, fixed presets, recent-history swatches), maintains per-slot recent color history persisted to global settings, and exports helpers for slot inference and history parsing.

Changes

Cohort / File(s) Summary
Color Picker Core
packages/stream-deck-plugin/src/pi-components/color-picker.ts
Added inferSlotType, historySettingKey, parseColorHistory. Replaced clear button with swatch group, introduced slotType, iconDefault, recentColors state, history parsing/truncation/dedupe rules, and persistence via settings writes; tightened observed attributes to value.
Tests
packages/stream-deck-plugin/src/pi-components/color-picker.test.ts
Added SDPI test stub utilities and extensive tests for slot inference, history parsing, swatch rendering, interactions, and persistence edge-cases (dedupe, exclusion rules, truncation, redundant-save suppression).
Compiled Bundle
packages/stream-deck-plugin/com.iracedeck.sd.core.sdPlugin/.../pi-components.js
Replaced minified build to reflect TypeScript changes; runtime behavior for color picker updated. Other exported components remain functionally equivalent.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ColorPicker
    participant SettingsAPI
    participant Renderer

    User->>ColorPicker: Click swatch / input color / blur hex
    ColorPicker->>ColorPicker: normalize color, infer slotType, update currentValue
    ColorPicker->>SettingsAPI: write history key (deduped, truncated) [if applicable]
    SettingsAPI-->>ColorPicker: settings subscription echo (may mirror write)
    ColorPicker->>Renderer: re-render swatches (recentColors, icon-default, Not set)
    Renderer-->>User: updated UI
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰
I nibble colors, one by one,
I store the hues of setting sun,
Small swatches hop, recent and bright,
Per-slot memories saved at night,
Click a swatch — and voilà, delight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.32% 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
Title check ✅ Passed The title clearly and specifically summarizes the main change: adding recent color history and preset swatches to the ird-color-picker component, referencing the related issue #262.
Description check ✅ Passed The PR description comprehensively covers all required sections: Related Issue, What Changed (with detailed swatch behavior), How to Test (with 10 verification steps), and a completed Checklist confirming linked issues, unit tests, plugin registration, and no unrelated changes.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from #262: recent color history per slot type with persistence, hex input shortening, swatch-based UI (Not-Set, icon default, Black/White, recent colors), deduplication with 6-color storage cap, and cross-picker sync via subscriptions.
Out of Scope Changes check ✅ Passed All changes are directly scoped to #262: color-picker component redesign, test coverage expansion, and plugin registration updates. No unrelated refactoring or modifications detected outside the specified objectives.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch improve/262-color-picker-history

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 and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/stream-deck-plugin/src/pi-components/color-picker.test.ts`:
- Around line 9-33: The SDPI stub's makeHook currently overwrites callbacks[key]
(in installSDPIStub), losing previous listeners and preventing multiple
subscribers from syncing; change callbacks to map keys to arrays of listeners
(e.g., callbacks: Record<string, Array<(v:string)=>void>>) and in makeHook push
the new callback into callbacks[key], invoke it with initialByKey[key] on
registration, and ensure when simulating updates you iterate and call every
listener for that key; update any save/callback invocation sites to use the
array shape while preserving saves and
SDPIComponents.useSettings/useGlobalSettings behavior.

In `@packages/stream-deck-plugin/src/pi-components/color-picker.ts`:
- Around line 211-216: attributeChangedCallback currently only reacts to "value"
but observedAttributes lists "setting","default","global" (and
resolveIconDefault reads data-default-color); update attributeChangedCallback to
also handle changes to "setting", "default", "global", and "data-default-color"
by recomputing this.slotType = inferSlotType(this.getAttribute("setting")),
this.iconDefault = this.resolveIconDefault(), calling
this.updateSwatchDisplay(), and re-running this.hookSettings() (ensure
hookSettings unsubscribes the previous history subscription/key before
re-subscribing so the subscription follows the new key); alternatively remove
immutable attributes from observedAttributes if they truly never change.
🪄 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: 24c3cb2f-3949-40f8-a1a0-f560e083f8c6

📥 Commits

Reviewing files that changed from the base of the PR and between 2fe2704 and 4c17e3e.

📒 Files selected for processing (3)
  • packages/stream-deck-plugin/com.iracedeck.sd.core.sdPlugin/ui/pi-components.js
  • packages/stream-deck-plugin/src/pi-components/color-picker.test.ts
  • packages/stream-deck-plugin/src/pi-components/color-picker.ts

Comment thread packages/stream-deck-plugin/src/pi-components/color-picker.test.ts Outdated
Comment thread packages/stream-deck-plugin/src/pi-components/color-picker.ts
- Trim observedAttributes to just `value`: the `setting`, `default`,
  and `global` attributes are set once via EJS templates before the
  element upgrades and are never mutated at runtime. `data-default-color`
  wasn't observed anyway. Listing them but only handling `value` in
  attributeChangedCallback was misleading.
- Rework the SDPI test stub to track multiple listeners per settings
  key via an `emit(key, value)` helper, so a test can create two
  pickers for the same slot and verify they genuinely stay in sync
  when history changes — the previous shape silently dropped all but
  the last subscriber.
- Add a 2-picker cross-sync test exercising the new behavior.
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/stream-deck-plugin/src/pi-components/color-picker.ts (1)

5-7: ⚠️ Potential issue | 🟡 Minor

Top-level doc comment is stale about the clear button.

The header still describes a built-in clear button, but behavior is now swatch-based.

📝 Suggested doc fix
- * A custom color picker that supports a "not set" state, inline hex display,
- * and a built-in clear button. Replaces `<sdpi-color>` which always requires
+ * A custom color picker that supports a "not set" state, inline hex display,
+ * and preset/recent swatches (including a "Not set" swatch). Replaces `<sdpi-color>` which always requires
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stream-deck-plugin/src/pi-components/color-picker.ts` around lines 5
- 7, Update the top-level doc comment in color-picker.ts to remove the outdated
mention of a "built-in clear button" and instead describe the current
swatch-based clearing behavior; keep that the component is a custom color picker
supporting a "not set" state and inline hex display, and explicitly note that
clearing is done via swatches rather than a dedicated clear button so the docs
match the implementation of the color picker component.
🧹 Nitpick comments (1)
packages/stream-deck-plugin/src/pi-components/color-picker.ts (1)

466-471: Canonicalize incoming history before storing lastSavedHistoryJson.

Using raw subscription JSON weakens the echo-loop guard when payload formatting differs but data is equivalent.

♻️ Suggested hardening
-        (value: string) => {
-          const incoming = typeof value === "string" ? value : "";
-
-          this.recentColors = parseColorHistory(incoming);
-          this.lastSavedHistoryJson = incoming;
-          this.updateSwatchDisplay();
-        },
+        (value: string) => {
+          const incoming = typeof value === "string" ? value : "";
+          const parsed = parseColorHistory(incoming);
+          this.recentColors = parsed;
+          this.lastSavedHistoryJson = JSON.stringify(parsed);
+          this.updateSwatchDisplay();
+        },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/stream-deck-plugin/src/pi-components/color-picker.ts` around lines
466 - 471, The code stores the raw incoming subscription JSON in
lastSavedHistoryJson which can differ in formatting even when logically
identical; instead canonicalize by parsing via parseColorHistory and then
serialize the canonical representation before storing. Update the subscription
handler (the arrow function that sets this.recentColors and calls
updateSwatchDisplay) to set this.lastSavedHistoryJson to a stable JSON produced
from the parsed/normalized recentColors (e.g., JSON.stringify of the canonical
structure returned by parseColorHistory) so the echo-loop guard compares
normalized payloads.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@packages/stream-deck-plugin/src/pi-components/color-picker.ts`:
- Around line 5-7: Update the top-level doc comment in color-picker.ts to remove
the outdated mention of a "built-in clear button" and instead describe the
current swatch-based clearing behavior; keep that the component is a custom
color picker supporting a "not set" state and inline hex display, and explicitly
note that clearing is done via swatches rather than a dedicated clear button so
the docs match the implementation of the color picker component.

---

Nitpick comments:
In `@packages/stream-deck-plugin/src/pi-components/color-picker.ts`:
- Around line 466-471: The code stores the raw incoming subscription JSON in
lastSavedHistoryJson which can differ in formatting even when logically
identical; instead canonicalize by parsing via parseColorHistory and then
serialize the canonical representation before storing. Update the subscription
handler (the arrow function that sets this.recentColors and calls
updateSwatchDisplay) to set this.lastSavedHistoryJson to a stable JSON produced
from the parsed/normalized recentColors (e.g., JSON.stringify of the canonical
structure returned by parseColorHistory) so the echo-loop guard compares
normalized payloads.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1e29f2f9-6329-4ce1-a1c6-0548aa63a2bf

📥 Commits

Reviewing files that changed from the base of the PR and between 4c17e3e and f9c2549.

📒 Files selected for processing (3)
  • packages/stream-deck-plugin/com.iracedeck.sd.core.sdPlugin/ui/pi-components.js
  • packages/stream-deck-plugin/src/pi-components/color-picker.test.ts
  • packages/stream-deck-plugin/src/pi-components/color-picker.ts

@niklam niklam merged commit 93f10ef into master Apr 12, 2026
5 checks passed
@niklam niklam deleted the improve/262-color-picker-history branch April 12, 2026 14:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type: improvement Improvements to existing functionality

Projects

None yet

Development

Successfully merging this pull request may close these issues.

improve(pi): add recent color history to ird-color-picker

1 participant