4.3.29: Black layers, idle refresh-rate setting, copy layers from unavailable devices#206
Open
logicallysynced wants to merge 9 commits into
Open
4.3.29: Black layers, idle refresh-rate setting, copy layers from unavailable devices#206logicallysynced wants to merge 9 commits into
logicallysynced wants to merge 9 commits into
Conversation
…3.24 -> v4.3.25)
Idle refresh: the disconnect throttle (surface drops to the idle tick
rate while FFXIV is not attached, resumes the configured rate on
attach) is now gated by a new General setting, on by default. The
Settings toggle re-applies the rate immediately through
RGBController.ReapplyIdleUpdateRate instead of waiting for the next
connect or disconnect.
Copy layers: the copy dialog now lists devices that survive only in
layers.chromatics4 - provider off or hardware gone - as copy sources,
alongside Mapping-tab-disabled devices. Both carry a state label in
the picker ("not connected" / "disabled") and a notice under the
source row so the origin is obvious, and a checkbox hides them for a
shorter list. Absent devices have no IRGBDevice, so a new
LayerCopier.ComputeDefaultMappingForLayer overload derives the
default LedId mapping from the layer's own LedId set (identity for
keyboard pairs, ordinal fallback otherwise); they stay source-only
since a destination needs live LEDs. Device names aren't persisted
with layers, so absent entries are labelled by device type plus an
id slice.
New locale keys in all seven languages. 160/160 tests pass.
…ng rules under test (v4.3.25 -> v4.3.26) The hide checkbox only rebuilt the source list: when the selected source survived the filter, the selection setter no-oped, the source-changed handler never ran, and the destination dropdown kept (or kept missing) disabled devices until the source changed. The filter toggle now rebuilds destinations, revalidates the selected destination, and rebuilds the layer rows whenever the source selection carries over. The absent-source default-mapping rules moved onto a device-free core (source LedIds + destination LedId list) with the IRGBDevice overload delegating to it, since constructing an IRGBDevice needs a full device stack that kept the rules out of unit-test reach. Five tests pin the behaviour: keyboard identity with no positional fallback, exact-id match, ordinal fallback, tail dropping, and empty-input handling. 165/165 tests pass.
Machine translation rendered the single-word key as the noun (a pair of things) instead of the verb the Nanoleaf pairing button needs. Corrected to match each language's own pairing strings: Emparejar, Appairer, and the katakana pairing form. German, Korean, and Chinese were already verbs. Locale-only change, no version bump.
…> v4.3.27)
The update dialog prepends its own "## {version}" heading per release,
but the notes publish.py embedded started with the version's changelog
heading themselves and ran to the end of CHANGELOG.md - so the prompt
showed the version line twice and stacked older sections into every
entry when listing target plus deltas.
Fixed at both ends. publish.py's extract_release_notes now captures
only the version's own bullets (heading excluded, stopping at the next
section), since the dialog assembles the cumulative changelog itself
from per-version asset notes. The dialog trims each release's notes
through a new UpdateService.TrimNotesToOwnSection before rendering,
which drops a leading heading naming that version (all changelog
heading shapes, 4-part tolerated) and cuts at the next section - a
no-op for new-format notes, and the compatibility path for releases
already published with the old format. Entries whose notes trim to
nothing are skipped instead of rendering an empty section.
Eight tests pin the trimming: old format reduces to own bullets, new
format passes through, every heading variant strips, a mismatched
heading is kept while trailing sections still cut, and empty input
returns empty. Also corrected a small grammar slip in the changelog
bullet wording. 173/173 tests pass.
Avalonia 12 deprecated Watermark (AVLN5001); the Nanoleaf adoption dialog's manual-IP field was the only use. Same rendered behaviour and the same locale key, so no version bump.
Both hold their keys at #000000: the base variant covers the whole device (a dark foundation only higher layers light up), the dynamic variant covers the selected keys (a mask over anything a lower layer would paint). Neither reads the palette - black is the point, so the colour never varies and there is nothing to configure beyond the key assignment. Wiring follows the layer-type invariants: DynamicLayerType.Black = 18 matches its appended position in _dynamicLayerOrder, BaseLayerType .Black = 6 appends to the enum, both register in LayerProcessorFactory (whose switches throw on missing entries), and layers.chromatics4 schemaVersion bumps 7 -> 8 in all three write sites with the version history noting the new value space. SanitizeLayerTypeIndexes already guards by Enum.IsDefined, so older builds reset Black layers to None on load instead of dispatching a cast they cannot handle. The ComboBox tooltip comes from the LayerDisplay description; both descriptions and the shared "Black" name ship in all seven languages. 173/173 tests pass.
…lack tooltip (v4.3.28 -> v4.3.29) The surface tick rate was set from a flag each caller passed: RunStartupEffects passed idle:true, StopEffects(gameFirstConnected) passed false. But RunStartupEffects also runs on device hot-plug and re-enable while the game is attached, so the effective rate depended on which path fired last rather than on whether FFXIV was connected - which could leave the rate lowered at the wrong time. ApplyUpdateRate now derives the idle state from GameController.IsGameConnected and the setting, recomputed on every call and on the Settings toggle, so the configured rate always runs while connected and whenever the setting is off. Removes the caller-passed flag and the last-requested-state field. Also removed the "(#000000)" from the two Black layer tooltips per request; the descriptions still say the keys are set to black. Locale keys regenerated for all seven languages. 173/173 tests pass.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Covers v4.3.25 through v4.3.29 on top of the 4.3.24 release.
Black layer type
"Black" is now available as both a base layer and a dynamic layer. The base variant holds the whole device at black, a dark foundation where only your dynamic layers light up. The dynamic variant holds the selected keys at black, a mask over anything a lower layer would paint. Neither reads the palette; black is the point, so there is nothing to configure beyond the key assignment.
Plumbing:
DynamicLayerType.Black = 18matches its position in_dynamicLayerOrder,BaseLayerType.Black = 6appends to the enum, both processors register inLayerProcessorFactory, and layers.chromatics4schemaVersionbumps 7 -> 8 in all three write sites. Older builds reset Black layers to None on load through the existingSanitizeLayerTypeIndexesguard.Lower the RGB refresh rate while FFXIV is not running
New checkbox under Settings → General, on by default. The disconnect throttle already existed; this PR puts it behind a setting so users can turn it off and keep their configured rate at all times. Flipping the toggle applies the rate immediately.
A follow-up commit changed how the rate is decided: it now derives from the live game-connection state instead of a flag each caller passed. The old flag approach meant device hot-plug or re-enable while in game could re-apply the idle rate, so the effective rate depended on which code path ran last. With the setting off, the configured rate now runs in every state.
Copy layers from disabled and disconnected devices
The copy dialog lists devices that only exist in layers.chromatics4 (provider off, or hardware gone) as copy sources, alongside devices disabled on the Mappings tab. The picker labels their state - "(disabled)" or "(not connected)" - and a note under the source dropdown tells the user the copy runs from saved layers. A checkbox hides unavailable devices, and the destination list follows that filter too.
Disconnected devices are source-only, since a destination needs live LEDs to map onto. Their default LED mapping comes from a device-free
LayerCopiercore: identity match for keyboard pairs, ordinal fallback otherwise, with the per-layer mapping expander available to correct any row. Device names are not persisted with layers, so disconnected entries are labelled by device type plus an id slice.Smaller fixes
TextBox.WatermarkwithPlaceholderText(AVLN5001).Notes
🤖 Generated with Claude Code