Commit Graph
465 Commits
Author SHA1 Message Date
447997b9ec launcher: add vpn connections as a search provider (#3391)
* launcher: add vpn connections as a search provider

Adds a dms_vpn built-in launcher plugin that lists every configured VPN
profile and toggles it on enter. Connected rows show a lock icon and a
Connected badge, in-progress rows show Connecting, everything else shows
a crossed-out key.

Profiles come from the network state the shell already fetches at
startup, so there is no new timer, process or request. The provider
returns nothing when the backend is not NetworkManager.

It ships without a trigger. A trigger prefix replaces the whole result
set, so claiming "vpn" would have hidden every application whenever
someone typed it to find their VPN client. Matching on the vpn keyword
instead keeps apps and profiles in the same list.

Co-Authored-By: Claude Opus 5 <[email protected]>

* launcher: hide vpn profiles on the home view without a trigger

Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>

* launcher: list vpn profiles when browsing the plugin

The empty query guard also applied when opening VPN from the Browse
section, so the plugin view came up empty. The plugin filter path now
opts out of the guard, while the home view still hides the profiles.

Co-Authored-By: Claude Opus 5 <[email protected]>

* launcher: stop matching vpn profiles on connect and disconnect

Keywords match by substring, so "connect" also matched "disconnect" and
listed connected profiles too. Picking one of those disconnected it.

Co-Authored-By: Claude Opus 5 <[email protected]>

---------

Co-authored-by: Claude Opus 5 <[email protected]>
Co-authored-by: claude[bot] <209825114+claude[bot]@users.noreply.github.com>
2026-09-15 12:41:14 -04:00
Minghao YUandbbedward e809733e3a feat(display): per-output ICC color profiles and color temperature (#3388)
* feat(display): per-output ICC color profiles and color temperature

Rebuild of #3388 on current master: the previous branch was cut from an
older base and reverted unrelated fixes in the gamma stack and the display
settings UI.

ICC profiles:
- core/internal/icc: ICC v2/v4 parser (TRC, LUT8/16 and parametric curves,
  vcgt, description/version/color space) plus gamma ramp generation, with
  synthetic coverage and optional vendor-profile tests (ICC_TEST_DIR).
- wayland manager: load iccProfiles from the compositor's DMS config dir,
  build the ramp from the profile when one is set (falling back to the
  temperature ramp when it is missing or unparsable), and re-apply on
  output hotplug and after resume.
- ICC ramps apply while the night light schedule is disabled; outputs
  without a profile keep an identity ramp.
- desc/mluc offsets are validated before slicing, so a malformed profile
  returns an error instead of panicking the daemon.

Per-output temperature:
- outputTemps in the same config; SetOutputTemp accepts 1000-10000K and the
  Display Config slider exposes 3000-10000K, which matters for displays
  calibrated at a higher white point (e.g. a 7000K profile).

Gamma control lifecycle:
- needsControls() now keeps the controls alive for configured ICC profiles
  and per-output temperatures, so toggling the night light off no longer
  destroys the controls and drops the ramps.
- Re-apply paths clear the per-output dedup state instead of adding a
  manager-level guard, which would have suppressed contrast-only writes and
  the post-resume forced resend (#1235), and left a recreated control
  without a ramp.
- Monitor sleep/disconnect still re-establishes controls when an output
  comes back.

IPC / CLI / UI:
- wayland.icc.{getStatus,apply,remove,listOutputs,setTemp,getTemps}
- dms icc list | info <file> | apply <output> <file> | remove <output> | status
- Display Config output card gains a Color Profile row (browse/apply/remove
  with description, version, color space and active state) and a per-output
  color temperature slider, backed by Services/ICCService.qml.

Config path:
- DMSConfigDir() follows the compositor layout (niri/dms, hypr/dms,
  mango/dms) via the shared compositor detection and utils.XDGConfigHome(),
  replacing the hardcoded niri path in both the manager and `dms icc`.

Tests: TZ=UTC go test ./... (60 packages ok), including red/green coverage
for the icc parser bounds checks and needsControls().

* feat(icc): add `dms icc set-temp` and show per-output temps in `status`

The per-output temperature override was reachable from the Display Config
slider and from `wayland.icc.setTemp` over IPC, but not from the CLI.

- `dms icc set-temp <output> <kelvin>` (alias `setTemp`) sets a per-output
  override, 1000-10000K, and 0 clears it so the output follows the night
  light schedule again. Range checking matches the daemon so an invalid
  value fails in the CLI instead of over IPC.
- `dms icc status` gains a Temp column (the override in K, or `schedule`),
  so an override can be verified without opening the settings UI.

Verified against a running daemon: `dms icc status` reports 7000K for the
three overridden outputs and `schedule` for the remaining one.

* feat(icc): treat the per-output temperature as the profile's reference white point

The per-output temperature was documented as the white point a profile was
produced at, but an output with a profile ignored it entirely: the ramp was
generated from the profile and the temperature only applied to outputs
without one (or as the fallback when a profile failed to parse). A display
calibrated at 7000K therefore got the profile ramp, and enabling the night
light had no effect on it at all.

- `ProfileRampWithTemp` composes the profile ramp with the ratio between the
  target temperature ramp and the reference ramp, so the temperature is the
  white point the profile describes and the night light shifts relative to
  it.
- `applyGamma` passes the night light temperature as the target, and
  `noTempTarget` when the schedule is disabled, which leaves profiled
  outputs at their reference white point and drives plain outputs with the
  neutral ramp.
- The re-apply sentinel stays 0, so it cannot collide with an applied
  temperature.

Covered by TestProfileRampWithTemp (reference == target is a no-op, warmer
and cooler targets move the expected channels).

Also: `dms icc set-temp` help text describes the reference-white-point
semantics.

* fix(icc): load the configured profiles even when controls already exist

Restoring the upstream "output returned, re-establishing controls" branch
made the registry handler establish the gamma controls before the startup
post runs, and that post returned early on `m.controlsInitialized`, so the
configured ICC profiles and per-output temperatures were never attached:
every output reported "(none)" and the night light ran with defaults until
the user re-applied a profile by hand.

- `initializeControlsAndICC` (the startup post) loads the configuration
  first and only creates the controls when they are missing.
- The loading is split into `loadConfiguredICC` / `applyConfiguredICCForOutput`
  so it can also run per output.
- A hotplugged output now gets its configured profile and temperature as soon
  as its name is known (name handler) or when its control is created, which
  also makes the "re-applies on hotplug" claim in the description true.
- After attaching, the output's dedup state is cleared so the ramp is written.

Covered by TestManager_LoadConfiguredICCWhenControlsAlreadyExist and
TestManager_AttachConfiguredICCForNamedOutput.

* fix(icc): make the per-output temperature an actual target

The per-output value was treated as the white point a profile was produced
at, which meant a display with a profile never changed when the user set a
temperature: the profile ramp was applied as measured and the value only
mattered when a night light target existed. Per-display temperatures were
therefore unusable on profiled outputs while the night light was disabled,
which is the normal state for users who want each monitor at its own value.

- `effectiveTempTarget` decides per output: a per-output override wins over
  the night light schedule, the schedule applies when there is no override,
  and `noTempTarget` means neither. `0` is "no override", not 0K.
- `applyGamma` composes the override on top of the profile ramp, treating the
  profile as measured at 6500K (D65), and drives outputs without a profile
  with the same target so both paths agree.
- `dms icc set-temp` help text and the Display Config slider description go
  back to describing an independent per-output temperature.

Covered by TestEffectiveTempTarget (override wins, schedule fallback, none)
and the updated TestProfileRampWithTemp (7000K cools, 5000K warms relative to
the 6500K reference).

* feat(icc): show the profile details in Display Config

The profile row only showed the description, so there was no way to tell
which profile a display actually has applied (whitepoint, curve type, file
provenance) without leaving the settings page.

- `ICCStatus` carries the descriptive metadata: class, tone-curve kind (plus
  gamma or table size), vcgt channels/entries, white point chromaticity with
  a derived CCT and standard illuminant name, file size and mtime.
- `internal/icc` gains `WhitePointXY`, `WhitePointCCT` (McCamy),
  `WhitePointName` (D50/D65) and `TRCKind`, all covered by tests.
- Display Config's Color Profile row gets an info button that opens
  `ICCProfileInfoModal` with those fields and the profile path, alongside
  the existing browse/remove actions.
- `dms icc info` reports the white point as name/xy/CCT instead of raw XYZ.

* fix(icc): keep the daemon, the state snapshot and the temp slider honest

A malformed profile could still take the daemon down, the ICC getters ran off
the wayland actor goroutine, the pushed state hid a removal, and the
per-output temperature slider dropped wheel changes.

- `internal/icc`: the tag table was sized with `uint32` arithmetic, so
  `tagCount*12` wrapped (`0x15555556*12 == 8`) and a 140-byte profile passed
  the bounds check, allocated ~4.3 GB and then panicked. `ParseFile` runs on
  the wayland actor goroutine, which has no `recover()`, so a bad profile
  persisted in `wayland.json` took the daemon down at startup. The count is now
  compared against the bytes that are actually left before anything is sized
  with it.
- The manager publishes an ICC snapshot (`iccStatus`/`iccTemps`) whenever the
  actor changes an output's profile or temperature, and `GetICCStatus`/
  `GetOutputTemps` serve that snapshot instead of ranging `m.outputs` and
  reading fields the actor owns. That removes the data race reported by
  `go test -race` for `dms icc status` running against a hotplug, and keeps the
  per-profile `os.Stat` off the night light animation path, where it ran on
  every `updateStateFromSchedule` (including each 25 K step).
- `State.ICCProfiles`/`OutputTemps` drop `omitempty`, so removing the last
  profile pushes `{}` instead of omitting the key: the QML truthiness guard
  then clears `ICCService.status` instead of leaving the removed profile on
  screen with a green "active" dot.
- `removeOutputByRegistryName` (extracted from the registry remove handler)
  deletes the `outputNames`/`outputRegNames` entries along with the output, so
  a disconnected monitor stops being listed by `dms icc status`/`listOutputs`,
  and a rebound `wl_output` reusing the released object ID cannot attach the
  previous monitor's profile from the stale name.
- The per-output temperature slider sets `wheelEnabled: false` like the other
  drag-finished sliders: `DankSlider`'s wheel handler assigns `value` and emits
  `sliderValueChanged` without `sliderDragFinished`, so scrolling dropped the
  change, froze the label on the scrolled value and destroyed the value binding.

Covered by TestParseBytesTagTableBounds (allocates 4,299,161,600 bytes at
parser.go:138 and dies with an unrecovered fatal error on the previous
commit), TestManager_ICCStatusServesPublishedSnapshot,
TestManager_RemoveOutputByRegistryNamePrunesNames and
TestManager_ICCStatusConcurrentPublishAndRead; a temporary copy of the getter
test against the previous commit reports DATA RACE and passes here.

* fix(icc): keep the attached profile when a gamma control is re-created

`setupOutputControls` stored a fresh `outputState` for every output it touched,
and `createControls` calls it whenever the controls are rebuilt, so recreating a
control dropped the output's `iccPath`/`iccProfile`/`outputTemp` — configured
state that does not belong to the control. The path is dormant today because
`destroyControls` only runs when nothing is configured (`needsControls()` covers
`ICCProfiles`/`OutputTemps`), but the snapshot published after `createControls`
would then report whatever the rebuilt state holds instead of the profile the
display is configured with.

`controlStateFor` reuses the state of a known output and resets only what belongs
to the new control: the ramp bookkeeping and the failure budget (rampSize,
failed, retryCount, lastFailTime, lastTemp, lastGamma, lastContrast). A control
that has not reported gamma_size yet has to receive its ramp again, while the
profile and the per-output temperature survive. An output seen for the first
time still starts from a clean state.

Covered by TestManager_ControlStateReuseKeepsAttachedICC.

* fix(icc): keep the config a real snapshot and make the per-output temperature apply

Third review round.

- `Config` is copied by value all over the manager, but `ICCProfiles` and
  `OutputTemps` are maps, so every copy aliased the ones the manager writes under
  `configMutex`. `updateStateFromSchedule` stored such a copy in `m.state` and it
  was marshalled on the connection writer goroutine while `ApplyICC` stored a
  profile from the IPC goroutine (`fatal error: concurrent map iteration and map
  write`, which no `recover()` catches); `SaveConfig` marshalled its copy after
  the unlock, and `loadConfiguredICC`/`attachConfiguredICC` indexed the maps after
  releasing the lock. Every copy that outlives the lock is cloned now
  (`cloneConfig`/`configSnapshot`), so a value copy is a snapshot again.
- `SetOutputTemp` never created the gamma controls. On a default install (night
  light off, gamma 1.0, contrast 1.0, no profile, no override) they do not exist,
  so the `Range` over `m.outputs` matched nothing: the value was written to
  `wayland.json`, no ramp was applied, and the settings slider snapped back to
  "Default" until a daemon restart. It now persists first, creates the controls
  through `ensureOutputControls` (shared with `ApplyICC`), and pushes the state
  from the actor, and the published temperatures report the configured value even
  before an output is known.
- A TRC is not a video card gamma ramp: `rTRC`/`gTRC`/`bTRC` describe the
  display's own transfer function, so a gamma-2.2 profile was written as
  `t^(1/2.2)` — mid gray 0.5 became 47909/65535 — washing the display out instead
  of correcting it. `ApplyICC` refuses a profile without `vcgt`, the attach path
  skips one with a warning, and `GenerateGammaRamp` returns an error instead of
  falling back to the TRC or to an identity ramp. TRC stays profile metadata
  (`dms icc info`, the settings dialog).
- `icc: applied ICC ramp` was logged at Info on every 25 K animation step; it is
  Debug now, which is where a per-step message belongs.
- `I18n.tr("No information available")` in the profile info modal passes its
  translator context, like every other string this PR adds.

Covered by TestManager_ConfigSnapshotOwnsItsMaps,
TestManager_ConfigSnapshotConcurrentMarshal (the previous commit dies with
`fatal error: concurrent map iteration and map write`),
TestManager_SetOutputTempWithoutControlsPublishesValue (the previous commit
reports 0 for the value it just stored), TestGenerateGammaRampRequiresVCGT, and
the updated TestProfileRampWithTemp, whose fixture now carries an identity vcgt
table instead of relying on an empty profile.

* fix(icc): push the state when only the ICC part changed

Found while sweeping the same class of problem: `stateChanged` compared only the
schedule fields, so a change that touches nothing else — applying or removing a
profile, `dms icc set-temp` — was treated as "no change" and the notifier never
pushed it. The settings page then only caught up because the UI path calls
`requestStatus()` afterwards, and the CLI path did not catch up at all, which is
the symptom the omitempty fix was meant to remove.

`iccStateChanged` compares the ICC profile status and the per-output
temperatures, so those changes reach the subscribers on their own. Covered by
the new TestStateChanged cases (profile applied, profile removed, profile
description changed, output temp changed, unchanged ICC state); the four
change cases report `stateChanged() = false` on the previous commit.

* fix(icc): keep the night light state, prune names without a control, use a 0 sentinel

Fifth review round.

- `SaveICCConfig` replaces `SaveConfig` on the three ICC paths (apply, remove,
  set-temp). Writing the daemon's whole config persisted the night light fields
  it happens to hold in memory: with the night light on, applying a profile
  stored `"Enabled": true`, and since the shell only ever sends
  `wayland.gamma.setEnabled` when the mode is on, turning it off afterwards (an
  in-memory change on the daemon side, no write) left that `true` on disk — after
  log out/in the daemon came up driving the schedule while the UI showed night
  mode off, with no way to resync short of a toggle. The ICC save now reads the
  file and rewrites only `iccProfiles`/`outputTemps`, so the fields the shell
  owns stay as they are.
- `removeOutputByRegistryName` resolved the output from `m.outputs`, which only
  has entries once the gamma controls exist. On a default install (night light
  off, no profile, no override) no control is ever created, so a disconnected
  monitor kept its `outputNames`/`outputRegNames` entries for the life of the
  daemon: `dms icc status`/`listOutputs` listed monitors that were gone, and both
  maps grew with every dock/undock cycle. The object ID is resolved from
  `m.outputRegNames` (populated for every `wl_output`), the name entries are
  dropped before the output state is looked up, and a state that is known without
  a registry name still falls back to matching its own.
- The Display Config temperature row used `7000` as its fallback for an output
  with no override. `publishICCState` only copies entries with `temp != 0`, so
  the value is absent rather than `0` for an unconfigured output: the card claimed
  "7000K" while the daemon drove the night light or neutral, and a user who
  deliberately set 7000K got the "no override" colouring because the highlight
  tested `!== 7000`. The sentinel is `0` now, which is what the `Default` label
  branches and the "is overridden" tests were written against.

Covered by TestSaveICCConfigKeepsNightLightFields (the previous commit brings the
night light back on after a restart), TestManager_RemoveOutputByRegistryNameWithoutControl
(the previous commit keeps an unplugged monitor listed) and the existing
TestManager_RemoveOutputByRegistryNamePrunesNames.

* fix(icc): validate the CLI arguments and the IPC parameters of the ICC commands

Sweep of the two surfaces the review has not looked at yet, so the failures are
found here instead of in the next round.

- `dms icc apply` copied the profile into the config directory before asking the
  daemon, and only the *parse* was checked locally. A profile the daemon refuses
  (no `vcgt` table, non-RGB color space) was therefore left in the profile
  directory for `dms icc list` to keep offering, and an IPC failure left the copy
  behind as well. The command now checks the same rules the daemon enforces
  before copying anything, and removes the copy it just made when the request
  fails (a file that was already there is left alone).
- `dms icc list` marks a profile without a `vcgt` table, so the rule that makes
  `apply` refuse it is visible where the profiles are listed.
- `ApplyICC`/`RemoveICC`/`SetOutputTemp` reach the manager over IPC with whatever
  the caller sent: an empty (or whitespace-padded) output name used to be
  persisted as an entry in `wayland.json` that no `wl_output` can ever match, and
  a relative profile path was stored to be resolved again on the next start, from
  a different working directory. Both are rejected now, before anything is
  written.

Verified with the built CLI against the previous binary: with a profile that has
no vcgt table, `dms icc apply` now exits before touching the config directory
(`residual=[]`) where the previous one copied the file and went on to the IPC
(`residual=[nogamma.icc]`); with a valid profile and no daemon reachable, the
previous one leaves `vcgt.icc` behind and this one does not. Covered by
TestManager_RejectsInvalidICCArguments.

* fix(icc): apply the user gamma and contrast on profiled outputs

* fix(icc): recreate failed controls without the night light and allow clearing a per-output temp

* test(icc): cover the recreated control and the cleared temperature override

Follow-up on d55d3d1f, which fixed both points without tests:

- TestManager_RecreateOutputControlFollowsNeedsControls pins the predicate: with
  the night light off and nothing configured there is nothing to recreate, and
  with a configured profile or a per-output temperature the control has to be
  recreated. Both configured cases return nil on 9226251c.
- TestManager_SetOutputTempZeroClearsTheOverride pins what the reset button in
  Display Config does end to end: the stored value and the published entry both
  disappear, so the row reads "Default" and the output follows the schedule.

Also keeps the reset button's slot instead of revealing it on demand. The
temperature row shares its column with the slider below it, so the height change
between "Default" (no button) and an override moved the slider 14 px under the
pointer while it was being dragged; the button is now always laid out and only
its opacity and enabled state follow the override, which also lets the label
width drop the conditional.

* fix(icc): replicate a one-channel vcgt and keep the temp slider in sync

* fix(icc): release idle gamma controls and push the output list

* fix(icc): dedupe ramps on the effective target

* fix(icc): never overwrite a same-named profile in the config dir

---------

Co-authored-by: bbedward <[email protected]>
2026-09-15 11:45:35 -04:00
Huỳnh Thiện Lộc 4141089e63 fix(clipboard): preserve pinned state when editing entries (#3412)
* fix(clipboard): preserve pinned state when editing entries

* fix(clipboard): address PR review feedback on editEntry

* fix(clipboard): validate empty text and entry size in EditEntry

* fix(clipboard): gate saving entry on full text load completion

* fix(clipboard): add user feedback and error fallback for entry loading

* fix(clipboard): keep entry and gate save with error toast on load failure

* fix(clipboard): reject non-text edits and add translator contexts

* fix(clipboard): support alt text mime types in EditEntry and UI guards

* fix(clipboard): pre-fetch entry before opening editor and allow retry on load failure
2026-09-12 09:24:23 -04:00
Huỳnh Thiện Lộc 3adfa918c1 feat(launcher): add flatpak uninstall option in context menu (#3340)
* feat(launcher): add flatpak uninstall option to context menu

* fix(launcher): reuse existing catalog terms for uninstall toasts
2026-09-10 10:12:25 -04:00
BBandZoey b9365610c8 feat: support for Aqueous Compositor (#3357)
* updated dank to support aqueous

* further updates

* aqueous support work

* updated to support workspace options better in aqueous

* added blur to dms shell for aqueous

* added keybinding update

* remove unneeded blur

* clean up per claud review

* swapped to ipc

* aqueous: move IPC integration to QML, delete test fixtures (since we
dont have a QML test structure even setup), delete in-repo docs

* fix staticcheck

* aqueous: consolidate more aqueous specific stuff

* add aqueous logo

---------

Co-authored-by: Zoey <[email protected]>
2026-09-07 22:28:52 -04:00
bbedward 17d4ea6f88 frame: draw panel backgrounds when fullscreen hides the frame
port 1.6
2026-09-07 20:45:09 -04:00
bbedward 0f56536142 fix(launcher): stop injectnig plugins into app categories
port 1.6
2026-09-07 10:36:21 -04:00
bbedward de27853c46 frame: keep overlay backgrounds with their content
fixes #3350
port 1.6
2026-09-07 10:29:03 -04:00
purian23 e18931353b feat(island): vertical orientation layout management support
Port 1.6
2026-09-04 17:57:20 -04:00
bbedward 96a21fbe08 launcher: revalidate the cached launcher sections on load
fixes #3292
port 1.6
2026-09-04 12:00:19 -04:00
枫莹 433b13cb0e fix(plugins): use plugin ids for management actions (#3284) 2026-09-04 09:51:05 -04:00
Mario Moharandbbedward 5142d576f5 fix(bar): centre widget content on whole pixels (#3263)
* fix(bar): keep widget content on whole pixels

Bar widgets centre their content inside a box of
widgetThickness - horizontalPadding * 2, and Qt rounds the offset of a
centred anchor to whole pixels. When the content and that box have
different parity the exact offset falls on a half pixel, Qt rounds it
away from zero and the content renders half a pixel above the centre.
Widgets drawn from rectangles, such as the workspace switcher, compute
their own snapped height and stay exactly centred, which is why they
look right next to everything else in #3074.

With the stock configuration this is the normal case: the bar is 40 px,
the box is 14 px and barIconSize returns round((40 / 48) * 18) = 15.

Two sizes now round to whole pairs of pixels. The icon size, so that it
matches the parity of the box, and widgetThickness, because the box
inherits its parity from there (the padding is doubled, so it never
changes it). Going through every innerPadding from 0 to 16, nine of the
seventeen configurations currently put the icon on a half pixel and none
of them do afterwards. The 48 px default bar is untouched, the sizes
that change do so by one pixel.

Measured in a nested Hyprland at scale 1, ink centroid of the widget
content relative to the centre of its pill, before and after:

  launcher      -0.50   +0.00
  weather       -0.81   -0.52
  clipboard     -1.49   -0.93
  notifications +0.03   +0.46
  battery       -0.63   -0.49
  bluetooth     -0.53   -0.03

Every icon moves down by half a pixel. What remains matches the ink
asymmetry of the individual glyphs, measured separately at an exact
position, and is not a placement error.

Two things this does not cover. On a fractional or HiDPI scale
horizontalPadding can land on a half pixel, which puts the box back
between the grid lines, and a text only widget such as the clock has a
line box of 15 px at the default font size, which cannot sit exactly on
the centre of a 28 px pill no matter how it is rounded.

Closes #3074

* fix(bar): give bar text a line box that can be centred

A widget without an icon is centred as one line box, and the same parity
rule applies to it as to the icon in the commit before this one. At
fontSizeSmall the line box of the bar font is 15 px tall, so on a 28 px
pill it cannot sit on the centre no matter which way the offset is
rounded, and the clock reads about half a pixel high next to everything
else. One step up the box is 16 px and it lands on the grid.

Only the branch the standard bar uses changes. The 0.9 branch below a
scale of 0.75 already comes out at 14 px, and the fontSizeMedium branch
above 1.25 is left alone because there the next even box is two steps
away, which would be a bigger change than this is worth.

Same session and same measurement as the previous commit, ink centroid
of the widget content relative to the centre of its pill:

  clock and date  -0.58   +0.05
  battery         -0.63   -0.05
  weather         -0.82   -0.28

The bar font goes from 12 px to 13 px, which is the visible part of this
and the reason it is a separate commit. It can be dropped without
touching the icon fix.

* bar: snap widget thickness to even device pixels and pick text sizes by line box parity

* bar: snap bar thickness to even so the pill centres on whole pixels

* bar: share the bar thickness formula so dock, osd and popouts stay in step

* dock: read the bar clearance at the compositor screen scale

---------

Co-authored-by: bbedward <[email protected]>
2026-09-03 12:13:09 -04:00
bbedward f022aa6796 changelog: fix since async loading chnage 2026-09-02 17:05:40 -04:00
bbedward f65e5ef2f2 settings: quick simplification and cleanup prior to 1.6
the full overhaul/redesign will come later
2026-09-02 16:20:16 -04:00
purian23 5bb6058751 feat(clipboard): add a copy-as-text action for entries with a text alternates
- New Entries captured from a mixed image+text offer keep the image as the primary mime i.e. bitmap
- Copy Text context-menu item on atlernates

Closes #2849
2026-09-01 19:33:23 -04:00
bbedward 0540091a04 modals/welcome: tweaks to welcome page layout 2026-09-01 12:42:13 -04:00
bbedward ae7ed119a6 changelog: enable 1.6 changelog 2026-09-01 11:15:06 -04:00
bbedward 9ea8d2015b polkit: authenticate as the current user when multiple admins are candidates
fixes #3251
2026-09-01 09:13:12 -04:00
bbedward c580aa26c0 shell: fix media text collapse, stale plugin cc detail, theme browser open 2026-08-31 13:19:58 -04:00
Mario Moharandbbedward 9eaaed3203 feat(clipboard): clear only the filtered entries (#3235)
* feat(clipboard): clear only the filtered entries

With a search query or type filter active, the clear button deleted
every unpinned entry regardless of what the list was showing, so
pruning a subset meant deleting entries one by one from the context
menu.

When the recents list is filtered, the button now clears exactly the
entries it shows and the modal stays open on that filter; with no
filter the previous behaviour is untouched. A filter that matches
nothing does nothing at all, rather than falling back to clearing
everything. The tooltip says "Clear Filtered" while a filter is
active, and the confirmation names the count.

Deleting one id at a time would have cost a round trip and a full
state broadcast per entry, so this adds clipboard.deleteEntries: one
bolt transaction, one state update, and it skips pinned entries
server-side so a bulk delete can never take out saved items.

Fixes #3198

* fix(clipboard): add translator context and list deleteEntries in IPC help

Review follow-up.

The two strings this branch adds carried no translator context: the
"Clear Filtered" tooltip in ClipboardHeader and the confirmation body for
a filtered clear in ClipboardHistoryContent. Both now say where they
appear, and the second one names what %1 is, per CONTRIBUTING.

"Clear All" on the same line is deliberately left alone. It is an
existing shared term with four other call sites, so a context there would
attach a clipboard-specific comment to a term the printer tab also uses.

clipboard.deleteEntries was handled but absent from the IPC method
listing that `dms debug-srv` prints, which left it undiscoverable right
next to clipboard.deleteEntry.

Catalogs untouched.

* chore: re-trigger CI

The lint-and-test failure on 21cefbf is an unrelated flake:
TestStoreEntry_CorruptDBReturnsErrorNotPanic died with SIGBUS in the
bbolt mmap. That test comes from master (c68b7e5) and passes there;
this branch only adds TestDeleteEntries_*/TestHandleDeleteEntries_*
and touches manager.go additively.

* clipboard: survive mmap faults from corrupted db pages

---------

Co-authored-by: bbedward <[email protected]>
2026-08-31 10:04:26 -04:00
bbedward 4624ceb8f0 firststart/greeter: add a step for plugins 2026-08-31 09:15:59 -04:00
08dde7f79a add: Cellular Network support (rebase of #2765) (#3096)
* feat: add cellular support

* fix(settings): resolve cellular tab index collision

* fix: address review feedback for cellular support

Bump the network API version, drop the deleted term freeze file,
regenerate the settings search index, and reuse existing translation
terms where they already cover the new cellular UI.

* fix: do not assign readonly userPreference when switching network type

Assigning SettingsData.networkPreference through the readonly alias
threw on click, so the Cellular segment in Control Center did nothing.

* fix: make Cellular selectable and the modem row clickable

The Control Center type switcher was bound only to preference, so
Cellular snapped back to Ethernet. The settings modem row highlighted
on hover but had no click handler. Show the modem as a connect target
when no APN profiles exist.

* fix: create a GSM profile when connecting cellular with none saved

Clicking Mobile broadband only activated an existing profile, so a
modem with no APN did nothing useful. Fall back to AddAndActivate
like ethernet connect.

* fix: address second round of cellular review feedback

- Guard cellularDevices map with devMutex via snapshot/by-iface/set/remove
  helpers, mirroring the existing ethernet and wifi accessors
- Regenerate mocks with mockery v3 instead of hand-editing mock_Backend.go
- Reuse refreshCellularState() in SetCellularEnabled, DisconnectCellular,
  DisconnectCellularDevice, ActivateCellularConnection and device removal
- Wrap retryErr rather than the first error when the auto-config retry fails
- Use Theme.surfaceTextSecondary/primaryHover/primaryHoverLight in the
  cellular-off state instead of hand-rolled Qt.rgba values
- Drop the duplicate "No devices found" label so it can no longer render
  twice with no modem and no saved config

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* fix: address remaining cellular review comments

- Toast "Failed to toggle cellular" instead of always "enable"
- Remove unused enableCellularRadio() wrappers
- Store Control Center network type as a value, not a list index

* fix: pass nmcli connection selector as separate argument

`nmcli con mod uuid:<UUID>` is not valid syntax -- nmcli expects the
selector keyword as its own argument, so every priority update failed
with "no such connection profile". Because practically every NM profile
has a UUID, this silently disabled autoconnect-priority and route-metric
updates for ethernet and WiFi as well as cellular.

Also fix the Cellular settings status text, which said "Connecting..."
for a flag that only tracks radio enable/disable, and add the seven
cellular terms that were missing from the translation catalogs.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>

* apply review comments

* fix lints

* update debug doc

* fix style inconsistencies

---------

Co-authored-by: Yechiel Worenklein <[email protected]>
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>
Co-authored-by: bbedward <[email protected]>
2026-08-29 15:37:36 -04:00
bbedward 93a7c17e59 launcher: add built-in power options plugin to launcher 2026-08-28 13:57:29 -04:00
Huỳnh Thiện Lộc fdabf949f1 fix(clipboard): stack clear dialog above modal (#3207) 2026-08-28 10:22:02 -04:00
Huỳnh Thiện Lộc f0753dcfd8 fix(settings): keep search result visible (#3209)
* fix(settings): keep search result visible

* fix(settings): keep search field visible
2026-08-28 10:17:59 -04:00
bbedward cef1eb962e powermenu: add popout variant for bar widget
fixes #3199
2026-08-28 10:17:26 -04:00
Huỳnh Thiện Lộc d8ceb12164 feat(spotlight): align tab navigation with full launcher (#3205)
* feat(spotlight): align tab navigation

* fix(launcher): wrap reverse action navigation

* fix(launcher): reset spotlight actions on selection
2026-08-28 01:18:27 -04:00
Huỳnh Thiện Lộc 23faed0bd4 feat(launcher): cycle modes with ctrl-tab (#3204) 2026-08-28 01:00:43 -04:00
bbedward fc42786fec settings: scroll the sidebar with the keyboard highlight
fixes #3175
2026-08-27 09:49:31 -04:00
purian23 195ec3bf6e feat(motion): spring-based physics & shell hybrid material animations
- New Spring Motion settings (bounce character, reduce motion)
- Reorganize Typography & Motion tab
2026-08-24 22:41:02 -04:00
zion 7802572193 fix(launcher): support app id and genericName matching in DankLauncherV2 scorer (#3122)
Scorer was dropping apps that only matched on id or genericName, even though AppSearchService.searchApplications already matches both. Add id (350) and genericName (prefix 800, contains 400) tiers so the V2 launcher ranks those apps the same way.
2026-08-24 18:53:48 -04:00
bbedward fb96661092 fix(network): repair 802.1x credential persistence, add EAP method selection 2026-08-24 14:31:59 -04:00
bbedward 2c44b794f1 island: general optimizations and cleanups 2026-08-23 11:46:02 -04:00
purian23 4447d77ba6 feat(Dank Island): add new island bar instance 2026-08-22 22:06:13 -04:00
bbedward f89b21a0ef inputs: consistent usage of Dank* variants, bump qml-common with blink
timeout
2026-08-22 11:53:20 -04:00
bbedward c6083d6177 colorpicker: add popout variant for bar widget 2026-08-21 20:24:36 -04:00
0xNA d22dae1494 feat(wallpaper): add folder-based wallpaper selection and shuffle (#2990)
* feat(wallpaper): add folder-based wallpaper selection and shuffle

* fix(wallpaper): bump 'dank-qml-common' and handle single-wallpaper folders

* fix(wallpaper): address review feedback for folder selection

* fix(wallpaper): persist folder selection across restarts

* fix(wallpaper): pick first wallpaper on folder select and persist folder path

* fix: resolve wallpaper cycling folder persistence issues
2026-08-21 19:09:13 -04:00
bbedward 3630b46ee3 perf(shell): cut idle RAM by a lot
wallpaper: drop redundant FBOs

startup: create QtMultimedia players lazily on-demand instead of at
startup

launcher: fixed animated images in plugins that use them

dms: set MALLOC_CONF=thp:never,narenas:4,dirty_decay_ms:3000 for
quickshell when unset. jemalloc extets no longer round up to 2 MB
huge  pages

Worth keeping an eye on any potential regressions people encounter
2026-08-21 14:12:39 -04:00
Huỳnh Thiện Lộc 20aafebd87 feat(settings): search runtime plugins (#3136)
* feat(settings): search runtime plugins

* fix(settings): handle plugin search metadata
2026-08-21 11:01:28 -04:00
bbedward 9b04b47e98 notifications: add DnD bypass option globally and per-app
fixes #2870
closes #3016
2026-08-20 14:29:39 -04:00
Huỳnh Thiện Lộc 754ebab48d fix(launcher): clip tile labels to rounded corners (#3126) 2026-08-20 08:46:25 -04:00
purian23 327aad210c fix(frame): restore overlay connected chrome
- Frame chrome stays on the overlay layer with connected popouts and modals
2026-08-18 17:53:21 -04:00
Huỳnh Thiện Lộc d83a0037f3 feat(chooser): edit and copy target (#3094)
* feat(chooser): edit and copy target

* fix(chooser): avoid copy shortcut conflict

* fix(chooser): address review feedback
2026-08-18 13:50:17 -04:00
Alexanderandbbedward 0100c64d7a feat(niri): close overview on window focus (#3030)
* feat(niri): close overview on window focus

* feat(niri): close overview on window focus

---------

Co-authored-by: bbedward <[email protected]>
2026-08-17 13:35:10 -04:00
Huỳnh Thiện Lộcandbbedward fc1c9f6c54 fix(frame): align connected chrome layer (#3039)
* fix(frame): align connected chrome layer

Connected chrome now follows overlay modal/popout descriptors so the frame-rendered background stays on the same Wayland layer as the content. This currently moves the frame window as a whole, so hosted bars may follow the overlay layer while such surfaces are presented.

* fix(frame): keep overlay-opted surfaces out of connected chrome

---------

Co-authored-by: bbedward <[email protected]>
2026-08-17 12:42:01 -04:00
Huỳnh Thiện Lộc c93767fdfe fix(launcher): hide spotlight placeholder during IME input (#3071) 2026-08-15 12:26:55 -04:00
bbedward 7974887295 settings: diff-only configs, machine-state split, backup CLI
- settings.json/session.json now store only values that differ from
  spec defaults
- Machine-specific state moves out of settings.json into session.json
- Add dms backup create/restore: tar.gz of ~/.config/DankMaterialShell

fixes #3027
2026-08-11 15:32:01 -04:00
bbedward da664565b1 dgop: drop dgop as an external dependency and use dgop/gops internally
fixes #3033
2026-08-10 18:29:59 -04:00
Huỳnh Thiện Lộc ed4cc1da7b feat(clipboard): add overlay layer option (#3037) 2026-08-10 14:57:14 -04:00
Huỳnh Thiện Lộc b6d051a61f feat(filebrowser): expose folder mode on surface modal (#3022) 2026-08-08 18:14:50 -04:00