1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-05-14 00:02:45 -04:00

Compare commits

...

26 Commits

Author SHA1 Message Date
Marcus Ramberg 8fad2826b1 ci: add flake check 2025-12-08 16:09:16 +01:00
bbedward 57ee0fb2bd bump: failed fprint tries 2025-12-08 10:02:53 -05:00
osscar 3ef10e73a5 nix: remove leading dot in nativeBuildInputs (#948)
Co-authored-by: osscar <osscar.unheard025@passmail.net>
2025-12-08 15:52:32 +01:00
bbedward dc40492fc7 cc: fix audio slider binding 2025-12-08 09:45:25 -05:00
bbedward e606a76a86 screenshot: add screenshot-window support for DWL/MangoWC 2025-12-08 09:39:42 -05:00
Lucas 8838fd67b9 nix: add dev-shell (#944)
* nix: add dev-shell

* docs: add Nix dev shell in contributing docs
2025-12-08 12:22:07 +01:00
Lucas c570e20308 nix: use quickshell from source by default in greeter (#941) 2025-12-08 07:37:29 +01:00
bbedward 0a00ef39e3 ipc: fix bar widget IPCs when screens change 2025-12-07 23:15:24 -05:00
bbedward 9a08b81214 dankinstall: swap to systemd by default, use 90-dms.conf for vars 2025-12-07 22:51:22 -05:00
bbedward c617ae26a2 niri: fix some keybind tab issues
- Fix args for screenshot
- move-column stuff is focus=true by default
- Parsing fixes
part of #914
2025-12-07 22:41:01 -05:00
Lucas f6a776a692 nix: use by default quickshell from source (#939) 2025-12-07 21:11:22 -05:00
bbedward 54b253099d dankinstall: update hyprland syntax
fixes #913
2025-12-07 21:03:24 -05:00
bbedward f662aca58c dankinstall: replace grim+slurp+grimblast with dms 2025-12-07 20:59:46 -05:00
bbedward 76e7755496 consistent icon sizing 2025-12-07 20:21:07 -05:00
bbedward e05ad81c13 displays: remove system tray per-display opt
- superceded by omegabar
2025-12-07 20:13:40 -05:00
bbedward cffb16d7f7 matugen: make signalByName helper not use exec 2025-12-07 20:10:31 -05:00
bbedward 18ca571944 matugen: scrap shell script for proper backend implementation with queue
system
2025-12-07 20:00:43 -05:00
bbedward 3ae1973e21 screenshot/colorpicker: fix scaling, update go-wayland to fix object
destruction, fix hyprland window detection
2025-12-07 13:44:35 -05:00
bbedward 308c8c3ea7 lock screen: fix inconsistency with network status, add VPN
maybe fix #926
2025-12-07 12:33:29 -05:00
bbedward f49b5dd037 media player: replace color quantizer with album art 2025-12-07 12:23:00 -05:00
bbedward f245ba82ad gamma: fix non-automation toggling
fixes #924
2025-12-07 12:02:50 -05:00
arfan 60d22d6973 feat: add workspace index display when app icon enabled (#936) 2025-12-07 11:48:48 -05:00
Farokh d6f48a82d9 Update VSCode color theme templates for improved contrast and readability (#931)
* matugen/vscode-theme: update VSCode templates for contrast and readability

* vscode-theme: rework dark theme, refine light, restore default fallback

* dank16: add variants option, make default vscode consistent, fix termial
always dark

---------

Co-authored-by: bbedward <bbedward@gmail.com>
2025-12-07 11:47:25 -05:00
Marcus Ramberg c0d73dae67 fix: handle ipc arguments (#930) 2025-12-07 11:01:31 -05:00
Marcus Ramberg 49eb60589d fix: also restart ghostty/kitty on nix (#934) 2025-12-07 10:28:26 -05:00
Marcus Ramberg 89993b7421 core: remove unused function after refactors (#935) 2025-12-07 10:27:44 -05:00
81 changed files with 3843 additions and 2477 deletions
+30
View File
@@ -0,0 +1,30 @@
name: Check nix flake
on:
pull_request:
branches: [master, main]
paths:
- "flake.*"
- "distro/nix/**"
jobs:
check-flake:
runs-on: ubuntu-latest
steps:
- name: Create GitHub App token
id: app_token
uses: actions/create-github-app-token@v1
with:
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
token: ${{ steps.app_token.outputs.token }}
- name: Install Nix
uses: cachix/install-nix-action@v31
- name: Update vendorHash in flake.nix
run: nix flake check
+4 -33
View File
@@ -102,39 +102,6 @@ go.work.sum
# .idea/ # .idea/
# .vscode/ # .vscode/
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Code coverage profiles and other test artifacts
*.out
coverage.*
*.coverprofile
profile.cov
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
go.work.sum
# env file
.env
# Editor/IDE
# .idea/
# .vscode/
bin/ bin/
# Extracted source trees in Ubuntu package directories # Extracted source trees in Ubuntu package directories
@@ -142,3 +109,7 @@ distro/ubuntu/*/dms-git-repo/
distro/ubuntu/*/DankMaterialShell-*/ distro/ubuntu/*/DankMaterialShell-*/
distro/ubuntu/danklinux/*/dsearch-*/ distro/ubuntu/danklinux/*/dsearch-*/
distro/ubuntu/danklinux/*/dgop-*/ distro/ubuntu/danklinux/*/dgop-*/
# direnv
.envrc
.direnv/
+15
View File
@@ -12,6 +12,21 @@ Enable pre-commit hooks to catch CI failures before pushing:
git config core.hooksPath .githooks git config core.hooksPath .githooks
``` ```
### Nix Development Shell
If you have Nix installed with flakes enabled, you can use the provided development shell which includes all necessary dependencies:
```bash
nix develop
```
This will provide:
- Go 1.24 toolchain (go, gopls, delve, go-tools) and GNU Make
- Quickshell and required QML packages
- Properly configured QML2_IMPORT_PATH
The dev shell automatically creates the `.qmlls.ini` file in the `quickshell/` directory.
## VSCode Setup ## VSCode Setup
This is a monorepo, the easiest thing to do is to open an editor in either `quickshell`, `core`, or both depending on which part of the project you are working on. This is a monorepo, the easiest thing to do is to open an editor in either `quickshell`, `core`, or both depending on which part of the project you are working on.
+1
View File
@@ -21,6 +21,7 @@ linters:
# Signal handling # Signal handling
- (*os.Process).Signal - (*os.Process).Signal
- (*os.Process).Kill - (*os.Process).Kill
- syscall.Kill
# DBus cleanup # DBus cleanup
- (*github.com/godbus/dbus/v5.Conn).RemoveMatchSignal - (*github.com/godbus/dbus/v5.Conn).RemoveMatchSignal
- (*github.com/godbus/dbus/v5.Conn).RemoveSignal - (*github.com/godbus/dbus/v5.Conn).RemoveSignal
+1 -1
View File
@@ -454,7 +454,6 @@ func uninstallPluginCLI(idOrName string) error {
return nil return nil
} }
// getCommonCommands returns the commands available in all builds
func getCommonCommands() []*cobra.Command { func getCommonCommands() []*cobra.Command {
return []*cobra.Command{ return []*cobra.Command{
versionCmd, versionCmd,
@@ -474,5 +473,6 @@ func getCommonCommands() []*cobra.Command {
colorCmd, colorCmd,
screenshotCmd, screenshotCmd,
notifyActionCmd, notifyActionCmd,
matugenCmd,
} }
} }
+47 -8
View File
@@ -10,15 +10,15 @@ import (
) )
var dank16Cmd = &cobra.Command{ var dank16Cmd = &cobra.Command{
Use: "dank16 <hex_color>", Use: "dank16 [hex_color]",
Short: "Generate Base16 color palettes", Short: "Generate Base16 color palettes",
Long: "Generate Base16 color palettes from a color with support for various output formats", Long: "Generate Base16 color palettes from a color with support for various output formats",
Args: cobra.ExactArgs(1), Args: cobra.MaximumNArgs(1),
Run: runDank16, Run: runDank16,
} }
func init() { func init() {
dank16Cmd.Flags().Bool("light", false, "Generate light theme variant") dank16Cmd.Flags().Bool("light", false, "Generate light theme variant (sets default to light)")
dank16Cmd.Flags().Bool("json", false, "Output in JSON format") dank16Cmd.Flags().Bool("json", false, "Output in JSON format")
dank16Cmd.Flags().Bool("kitty", false, "Output in Kitty terminal format") dank16Cmd.Flags().Bool("kitty", false, "Output in Kitty terminal format")
dank16Cmd.Flags().Bool("foot", false, "Output in Foot terminal format") dank16Cmd.Flags().Bool("foot", false, "Output in Foot terminal format")
@@ -27,17 +27,15 @@ func init() {
dank16Cmd.Flags().Bool("wezterm", false, "Output in Wezterm terminal format") dank16Cmd.Flags().Bool("wezterm", false, "Output in Wezterm terminal format")
dank16Cmd.Flags().String("background", "", "Custom background color") dank16Cmd.Flags().String("background", "", "Custom background color")
dank16Cmd.Flags().String("contrast", "dps", "Contrast algorithm: dps (Delta Phi Star, default) or wcag") dank16Cmd.Flags().String("contrast", "dps", "Contrast algorithm: dps (Delta Phi Star, default) or wcag")
dank16Cmd.Flags().Bool("variants", false, "Output all variants (dark/light/default) in JSON")
dank16Cmd.Flags().String("primary-dark", "", "Primary color for dark mode (use with --variants)")
dank16Cmd.Flags().String("primary-light", "", "Primary color for light mode (use with --variants)")
_ = dank16Cmd.RegisterFlagCompletionFunc("contrast", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { _ = dank16Cmd.RegisterFlagCompletionFunc("contrast", func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
return []string{"dps", "wcag"}, cobra.ShellCompDirectiveNoFileComp return []string{"dps", "wcag"}, cobra.ShellCompDirectiveNoFileComp
}) })
} }
func runDank16(cmd *cobra.Command, args []string) { func runDank16(cmd *cobra.Command, args []string) {
primaryColor := args[0]
if !strings.HasPrefix(primaryColor, "#") {
primaryColor = "#" + primaryColor
}
isLight, _ := cmd.Flags().GetBool("light") isLight, _ := cmd.Flags().GetBool("light")
isJson, _ := cmd.Flags().GetBool("json") isJson, _ := cmd.Flags().GetBool("json")
isKitty, _ := cmd.Flags().GetBool("kitty") isKitty, _ := cmd.Flags().GetBool("kitty")
@@ -47,16 +45,57 @@ func runDank16(cmd *cobra.Command, args []string) {
isWezterm, _ := cmd.Flags().GetBool("wezterm") isWezterm, _ := cmd.Flags().GetBool("wezterm")
background, _ := cmd.Flags().GetString("background") background, _ := cmd.Flags().GetString("background")
contrastAlgo, _ := cmd.Flags().GetString("contrast") contrastAlgo, _ := cmd.Flags().GetString("contrast")
useVariants, _ := cmd.Flags().GetBool("variants")
primaryDark, _ := cmd.Flags().GetString("primary-dark")
primaryLight, _ := cmd.Flags().GetString("primary-light")
if background != "" && !strings.HasPrefix(background, "#") { if background != "" && !strings.HasPrefix(background, "#") {
background = "#" + background background = "#" + background
} }
if primaryDark != "" && !strings.HasPrefix(primaryDark, "#") {
primaryDark = "#" + primaryDark
}
if primaryLight != "" && !strings.HasPrefix(primaryLight, "#") {
primaryLight = "#" + primaryLight
}
contrastAlgo = strings.ToLower(contrastAlgo) contrastAlgo = strings.ToLower(contrastAlgo)
if contrastAlgo != "dps" && contrastAlgo != "wcag" { if contrastAlgo != "dps" && contrastAlgo != "wcag" {
log.Fatalf("Invalid contrast algorithm: %s (must be 'dps' or 'wcag')", contrastAlgo) log.Fatalf("Invalid contrast algorithm: %s (must be 'dps' or 'wcag')", contrastAlgo)
} }
if useVariants {
if primaryDark == "" || primaryLight == "" {
if len(args) == 0 {
log.Fatalf("--variants requires either a positional color argument or both --primary-dark and --primary-light")
}
primaryColor := args[0]
if !strings.HasPrefix(primaryColor, "#") {
primaryColor = "#" + primaryColor
}
primaryDark = primaryColor
primaryLight = primaryColor
}
variantOpts := dank16.VariantOptions{
PrimaryDark: primaryDark,
PrimaryLight: primaryLight,
Background: background,
UseDPS: contrastAlgo == "dps",
IsLightMode: isLight,
}
variantColors := dank16.GenerateVariantPalette(variantOpts)
fmt.Print(dank16.GenerateVariantJSON(variantColors))
return
}
if len(args) == 0 {
log.Fatalf("A color argument is required (or use --variants with --primary-dark and --primary-light)")
}
primaryColor := args[0]
if !strings.HasPrefix(primaryColor, "#") {
primaryColor = "#" + primaryColor
}
opts := dank16.PaletteOptions{ opts := dank16.PaletteOptions{
IsLight: isLight, IsLight: isLight,
Background: background, Background: background,
+182
View File
@@ -0,0 +1,182 @@
package main
import (
"context"
"encoding/json"
"fmt"
"net"
"os"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
"github.com/AvengeMedia/DankMaterialShell/core/internal/matugen"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server"
"github.com/spf13/cobra"
)
var matugenCmd = &cobra.Command{
Use: "matugen",
Short: "Generate Material Design themes",
Long: "Generate Material Design themes using matugen with dank16 color integration",
}
var matugenGenerateCmd = &cobra.Command{
Use: "generate",
Short: "Generate theme synchronously",
Run: runMatugenGenerate,
}
var matugenQueueCmd = &cobra.Command{
Use: "queue",
Short: "Queue theme generation (uses socket if available)",
Run: runMatugenQueue,
}
func init() {
matugenCmd.AddCommand(matugenGenerateCmd)
matugenCmd.AddCommand(matugenQueueCmd)
for _, cmd := range []*cobra.Command{matugenGenerateCmd, matugenQueueCmd} {
cmd.Flags().String("state-dir", "", "State directory for cache files")
cmd.Flags().String("shell-dir", "", "DMS shell installation directory")
cmd.Flags().String("config-dir", "", "User config directory")
cmd.Flags().String("kind", "image", "Source type: image or hex")
cmd.Flags().String("value", "", "Wallpaper path or hex color")
cmd.Flags().String("mode", "dark", "Color mode: dark or light")
cmd.Flags().String("icon-theme", "System Default", "Icon theme name")
cmd.Flags().String("matugen-type", "scheme-tonal-spot", "Matugen scheme type")
cmd.Flags().Bool("run-user-templates", true, "Run user matugen templates")
cmd.Flags().String("stock-colors", "", "Stock theme colors JSON")
cmd.Flags().Bool("sync-mode-with-portal", false, "Sync color scheme with GNOME portal")
cmd.Flags().Bool("terminals-always-dark", false, "Force terminal themes to dark variant")
}
matugenQueueCmd.Flags().Bool("wait", true, "Wait for completion")
matugenQueueCmd.Flags().Duration("timeout", 30*time.Second, "Timeout for waiting")
}
func buildMatugenOptions(cmd *cobra.Command) matugen.Options {
stateDir, _ := cmd.Flags().GetString("state-dir")
shellDir, _ := cmd.Flags().GetString("shell-dir")
configDir, _ := cmd.Flags().GetString("config-dir")
kind, _ := cmd.Flags().GetString("kind")
value, _ := cmd.Flags().GetString("value")
mode, _ := cmd.Flags().GetString("mode")
iconTheme, _ := cmd.Flags().GetString("icon-theme")
matugenType, _ := cmd.Flags().GetString("matugen-type")
runUserTemplates, _ := cmd.Flags().GetBool("run-user-templates")
stockColors, _ := cmd.Flags().GetString("stock-colors")
syncModeWithPortal, _ := cmd.Flags().GetBool("sync-mode-with-portal")
terminalsAlwaysDark, _ := cmd.Flags().GetBool("terminals-always-dark")
return matugen.Options{
StateDir: stateDir,
ShellDir: shellDir,
ConfigDir: configDir,
Kind: kind,
Value: value,
Mode: mode,
IconTheme: iconTheme,
MatugenType: matugenType,
RunUserTemplates: runUserTemplates,
StockColors: stockColors,
SyncModeWithPortal: syncModeWithPortal,
TerminalsAlwaysDark: terminalsAlwaysDark,
}
}
func runMatugenGenerate(cmd *cobra.Command, args []string) {
opts := buildMatugenOptions(cmd)
if err := matugen.Run(opts); err != nil {
log.Fatalf("Theme generation failed: %v", err)
}
}
func runMatugenQueue(cmd *cobra.Command, args []string) {
opts := buildMatugenOptions(cmd)
wait, _ := cmd.Flags().GetBool("wait")
timeout, _ := cmd.Flags().GetDuration("timeout")
socketPath := os.Getenv("DMS_SOCKET")
if socketPath == "" {
var err error
socketPath, err = server.FindSocket()
if err != nil {
log.Info("No socket available, running synchronously")
if err := matugen.Run(opts); err != nil {
log.Fatalf("Theme generation failed: %v", err)
}
return
}
}
conn, err := net.Dial("unix", socketPath)
if err != nil {
log.Info("Socket connection failed, running synchronously")
if err := matugen.Run(opts); err != nil {
log.Fatalf("Theme generation failed: %v", err)
}
return
}
defer conn.Close()
request := map[string]any{
"id": 1,
"method": "matugen.queue",
"params": map[string]any{
"stateDir": opts.StateDir,
"shellDir": opts.ShellDir,
"configDir": opts.ConfigDir,
"kind": opts.Kind,
"value": opts.Value,
"mode": opts.Mode,
"iconTheme": opts.IconTheme,
"matugenType": opts.MatugenType,
"runUserTemplates": opts.RunUserTemplates,
"stockColors": opts.StockColors,
"syncModeWithPortal": opts.SyncModeWithPortal,
"terminalsAlwaysDark": opts.TerminalsAlwaysDark,
"wait": wait,
},
}
if err := json.NewEncoder(conn).Encode(request); err != nil {
log.Fatalf("Failed to send request: %v", err)
}
if !wait {
fmt.Println("Theme generation queued")
return
}
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
resultCh := make(chan error, 1)
go func() {
var response struct {
ID int `json:"id"`
Result any `json:"result"`
Error string `json:"error"`
}
if err := json.NewDecoder(conn).Decode(&response); err != nil {
resultCh <- fmt.Errorf("failed to read response: %w", err)
return
}
if response.Error != "" {
resultCh <- fmt.Errorf("server error: %s", response.Error)
return
}
resultCh <- nil
}()
select {
case err := <-resultCh:
if err != nil {
log.Fatalf("Theme generation failed: %v", err)
}
fmt.Println("Theme generation completed")
case <-ctx.Done():
log.Fatalf("Timeout waiting for theme generation")
}
}
+3 -4
View File
@@ -35,7 +35,7 @@ Modes:
full - Capture the focused output full - Capture the focused output
all - Capture all outputs combined all - Capture all outputs combined
output - Capture a specific output by name output - Capture a specific output by name
window - Capture the focused window (Hyprland only) window - Capture the focused window (Hyprland/DWL)
last - Capture the last selected region last - Capture the last selected region
Output format (--format): Output format (--format):
@@ -91,9 +91,8 @@ If no previous region exists, falls back to interactive selection.`,
var ssWindowCmd = &cobra.Command{ var ssWindowCmd = &cobra.Command{
Use: "window", Use: "window",
Short: "Capture the focused window", Short: "Capture the focused window",
Long: `Capture the currently focused window. Long: `Capture the currently focused window. Supported on Hyprland and DWL.`,
Currently only supported on Hyprland.`, Run: runScreenshotWindow,
Run: runScreenshotWindow,
} }
var ssListCmd = &cobra.Command{ var ssListCmd = &cobra.Command{
+35 -7
View File
@@ -16,7 +16,7 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/server" "github.com/AvengeMedia/DankMaterialShell/core/internal/server"
) )
type ipcTargets map[string][]string type ipcTargets map[string]map[string][]string
var isSessionManaged bool var isSessionManaged bool
@@ -476,28 +476,40 @@ func runShellDaemon(session bool) {
} }
func parseTargetsFromIPCShowOutput(output string) ipcTargets { func parseTargetsFromIPCShowOutput(output string) ipcTargets {
targets := map[string][]string{} targets := make(ipcTargets)
var currentTarget string var currentTarget string
for _, line := range strings.Split(output, "\n") { for _, line := range strings.Split(output, "\n") {
if strings.HasPrefix(line, "target ") { if strings.HasPrefix(line, "target ") {
currentTarget = strings.TrimSpace(strings.TrimPrefix(line, "target ")) currentTarget = strings.TrimSpace(strings.TrimPrefix(line, "target "))
targets[currentTarget] = make(map[string][]string)
} }
if strings.HasPrefix(line, " function") && currentTarget != "" { if strings.HasPrefix(line, " function") && currentTarget != "" {
argsList := []string{}
currentFunc := strings.TrimPrefix(line, " function ") currentFunc := strings.TrimPrefix(line, " function ")
currentFunc = strings.SplitN(currentFunc, "(", 2)[0] funcDef := strings.SplitN(currentFunc, "(", 2)
targets[currentTarget] = append(targets[currentTarget], currentFunc) argList := strings.SplitN(funcDef[1], ")", 2)[0]
args := strings.Split(argList, ",")
if len(args) > 0 && strings.TrimSpace(args[0]) != "" {
argsList = append(argsList, funcDef[0])
for _, arg := range args {
argName := strings.SplitN(strings.TrimSpace(arg), ":", 2)[0]
argsList = append(argsList, argName)
}
targets[currentTarget][funcDef[0]] = argsList
} else {
targets[currentTarget][funcDef[0]] = make([]string, 0)
}
} }
} }
return targets return targets
} }
func getShellIPCCompletions(args []string, toComplete string) []string { func getShellIPCCompletions(args []string, _ string) []string {
cmdArgs := []string{"-p", configPath, "ipc", "show"} cmdArgs := []string{"-p", configPath, "ipc", "show"}
cmd := exec.Command("qs", cmdArgs...) cmd := exec.Command("qs", cmdArgs...)
var targets ipcTargets var targets ipcTargets
if output, err := cmd.Output(); err == nil { if output, err := cmd.Output(); err == nil {
log.Debugf("IPC show output: %s", string(output))
targets = parseTargetsFromIPCShowOutput(string(output)) targets = parseTargetsFromIPCShowOutput(string(output))
} else { } else {
log.Debugf("Error getting IPC show output for completions: %v", err) log.Debugf("Error getting IPC show output for completions: %v", err)
@@ -516,8 +528,24 @@ func getShellIPCCompletions(args []string, toComplete string) []string {
} }
return targetNames return targetNames
} }
if len(args) == 1 {
if targetFuncs, ok := targets[args[0]]; ok {
funcNames := make([]string, 0)
for k := range targetFuncs {
funcNames = append(funcNames, k)
}
return funcNames
}
return nil
}
if len(args) <= len(targets[args[0]]) {
funcArgs := targets[args[0]][args[1]]
if len(funcArgs) >= len(args) {
return []string{fmt.Sprintf("[%s]", funcArgs[len(args)-1])}
}
}
return targets[args[0]] return nil
} }
func runShellIPCCommand(args []string) { func runShellIPCCommand(args []string) {
+30 -23
View File
@@ -2,7 +2,6 @@ package colorpicker
import ( import (
"fmt" "fmt"
"math"
"sync" "sync"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log" "github.com/AvengeMedia/DankMaterialShell/core/internal/log"
@@ -116,6 +115,11 @@ func (p *Picker) Run() (*Color, error) {
return nil, fmt.Errorf("roundtrip: %w", err) return nil, fmt.Errorf("roundtrip: %w", err)
} }
// Extra roundtrip to ensure pointer/keyboard from seat capabilities are registered
if err := p.roundtrip(); err != nil {
return nil, fmt.Errorf("roundtrip after seat: %w", err)
}
if err := p.createSurfaces(); err != nil { if err := p.createSurfaces(); err != nil {
return nil, fmt.Errorf("create surfaces: %w", err) return nil, fmt.Errorf("create surfaces: %w", err)
} }
@@ -405,15 +409,10 @@ func (p *Picker) createLayerSurface(output *Output) (*LayerSurface, error) {
func (p *Picker) computeSurfaceScale(ls *LayerSurface) int32 { func (p *Picker) computeSurfaceScale(ls *LayerSurface) int32 {
out := ls.output out := ls.output
if out == nil || out.fractionalScale <= 0 { if out == nil || out.scale <= 0 {
return 1 return 1
} }
return out.scale
scale := int32(math.Ceil(out.fractionalScale))
if scale <= 0 {
scale = 1
}
return scale
} }
func (p *Picker) ensureShortcutsInhibitor(ls *LayerSurface) { func (p *Picker) ensureShortcutsInhibitor(ls *LayerSurface) {
@@ -485,6 +484,13 @@ func (p *Picker) captureForSurface(ls *LayerSurface) {
frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) { frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
ls.state.OnScreencopyReady() ls.state.OnScreencopyReady()
logicalW, _ := ls.state.LogicalSize()
screenBuf := ls.state.ScreenBuffer()
if logicalW > 0 && screenBuf != nil {
ls.output.fractionalScale = float64(screenBuf.Width) / float64(logicalW)
}
scale := p.computeSurfaceScale(ls) scale := p.computeSurfaceScale(ls)
ls.state.SetScale(scale) ls.state.SetScale(scale)
frame.Destroy() frame.Destroy()
@@ -545,18 +551,17 @@ func (p *Picker) redrawSurface(ls *LayerSurface) {
logicalH = int(ls.output.height) logicalH = int(ls.output.height)
} }
scale := ls.state.Scale()
if scale <= 0 {
scale = 1
}
if ls.viewport != nil { if ls.viewport != nil {
srcW := float64(renderBuf.Width) / float64(scale) _ = ls.wlSurface.SetBufferScale(1)
srcH := float64(renderBuf.Height) / float64(scale) _ = ls.viewport.SetSource(0, 0, float64(renderBuf.Width), float64(renderBuf.Height))
_ = ls.viewport.SetSource(0, 0, srcW, srcH)
_ = ls.viewport.SetDestination(int32(logicalW), int32(logicalH)) _ = ls.viewport.SetDestination(int32(logicalW), int32(logicalH))
} else {
bufferScale := ls.output.scale
if bufferScale <= 0 {
bufferScale = 1
}
_ = ls.wlSurface.SetBufferScale(bufferScale)
} }
_ = ls.wlSurface.SetBufferScale(scale)
_ = ls.wlSurface.Attach(wlBuffer, 0, 0) _ = ls.wlSurface.Attach(wlBuffer, 0, 0)
_ = ls.wlSurface.Damage(0, 0, int32(logicalW), int32(logicalH)) _ = ls.wlSurface.Damage(0, 0, int32(logicalW), int32(logicalH))
_ = ls.wlSurface.Commit() _ = ls.wlSurface.Commit()
@@ -581,17 +586,19 @@ func (p *Picker) setupInput() {
p.seat.SetCapabilitiesHandler(func(e client.SeatCapabilitiesEvent) { p.seat.SetCapabilitiesHandler(func(e client.SeatCapabilitiesEvent) {
if e.Capabilities&uint32(client.SeatCapabilityPointer) != 0 && p.pointer == nil { if e.Capabilities&uint32(client.SeatCapabilityPointer) != 0 && p.pointer == nil {
pointer, err := p.seat.GetPointer() pointer, err := p.seat.GetPointer()
if err == nil { if err != nil {
p.pointer = pointer return
p.setupPointerHandlers()
} }
p.pointer = pointer
p.setupPointerHandlers()
} }
if e.Capabilities&uint32(client.SeatCapabilityKeyboard) != 0 && p.keyboard == nil { if e.Capabilities&uint32(client.SeatCapabilityKeyboard) != 0 && p.keyboard == nil {
keyboard, err := p.seat.GetKeyboard() keyboard, err := p.seat.GetKeyboard()
if err == nil { if err != nil {
p.keyboard = keyboard return
p.setupKeyboardHandlers()
} }
p.keyboard = keyboard
p.setupKeyboardHandlers()
} }
}) })
} }
+20 -7
View File
@@ -269,12 +269,17 @@ func (s *SurfaceState) Redraw() *ShmBuffer {
px = clamp(px, 0, dst.Width-1) px = clamp(px, 0, dst.Width-1)
py = clamp(py, 0, dst.Height-1) py = clamp(py, 0, dst.Height-1)
picked := GetPixelColorWithFormat(s.screenBuf, px, py, s.screenFormat) sampleY := py
if s.yInverted {
sampleY = s.screenBuf.Height - 1 - py
}
drawMagnifier( picked := GetPixelColorWithFormat(s.screenBuf, px, sampleY, s.screenFormat)
drawMagnifierWithInversion(
dst.Data(), dst.Stride, dst.Width, dst.Height, dst.Data(), dst.Stride, dst.Width, dst.Height,
s.screenBuf.Data(), s.screenBuf.Stride, s.screenBuf.Width, s.screenBuf.Height, s.screenBuf.Data(), s.screenBuf.Stride, s.screenBuf.Width, s.screenBuf.Height,
px, py, picked, px, py, picked, s.yInverted,
) )
drawColorPreview(dst.Data(), dst.Stride, dst.Width, dst.Height, px, py, picked, s.displayFormat, s.lowercase) drawColorPreview(dst.Data(), dst.Stride, dst.Width, dst.Height, px, py, picked, s.displayFormat, s.lowercase)
@@ -379,11 +384,12 @@ func blendColors(bg, fg Color, alpha float64) Color {
} }
} }
func drawMagnifier( func drawMagnifierWithInversion(
dst []byte, dstStride, dstW, dstH int, dst []byte, dstStride, dstW, dstH int,
src []byte, srcStride, srcW, srcH int, src []byte, srcStride, srcW, srcH int,
cx, cy int, cx, cy int,
borderColor Color, borderColor Color,
yInverted bool,
) { ) {
if dstW <= 0 || dstH <= 0 || srcW <= 0 || srcH <= 0 { if dstW <= 0 || dstH <= 0 || srcW <= 0 || srcH <= 0 {
return return
@@ -439,10 +445,11 @@ func drawMagnifier(
finalColor = blendColors(bgColor, borderColor, alpha) finalColor = blendColors(bgColor, borderColor, alpha)
case dist > innerRadius: case dist > innerRadius:
if dist > outerRadiusF-aaWidth { switch {
case dist > outerRadiusF-aaWidth:
alpha := clampF((outerRadiusF-dist)/aaWidth, 0, 1) alpha := clampF((outerRadiusF-dist)/aaWidth, 0, 1)
finalColor = blendColors(borderColor, borderColor, alpha) finalColor = blendColors(borderColor, borderColor, alpha)
} else if dist < innerRadius+aaWidth { case dist < innerRadius+aaWidth:
alpha := clampF((dist-innerRadius)/aaWidth, 0, 1) alpha := clampF((dist-innerRadius)/aaWidth, 0, 1)
fx := float64(dx) / zoom fx := float64(dx) / zoom
fy := float64(dy) / zoom fy := float64(dy) / zoom
@@ -450,6 +457,9 @@ func drawMagnifier(
sy := cy + int(math.Round(fy)) sy := cy + int(math.Round(fy))
sx = clamp(sx, 0, srcW-1) sx = clamp(sx, 0, srcW-1)
sy = clamp(sy, 0, srcH-1) sy = clamp(sy, 0, srcH-1)
if yInverted {
sy = srcH - 1 - sy
}
srcOff := sy*srcStride + sx*4 srcOff := sy*srcStride + sx*4
if srcOff+4 <= len(src) { if srcOff+4 <= len(src) {
magColor := Color{B: src[srcOff+0], G: src[srcOff+1], R: src[srcOff+2], A: 255} magColor := Color{B: src[srcOff+0], G: src[srcOff+1], R: src[srcOff+2], A: 255}
@@ -457,7 +467,7 @@ func drawMagnifier(
} else { } else {
finalColor = borderColor finalColor = borderColor
} }
} else { default:
finalColor = borderColor finalColor = borderColor
} }
@@ -468,6 +478,9 @@ func drawMagnifier(
sy := cy + int(math.Round(fy)) sy := cy + int(math.Round(fy))
sx = clamp(sx, 0, srcW-1) sx = clamp(sx, 0, srcW-1)
sy = clamp(sy, 0, srcH-1) sy = clamp(sy, 0, srcH-1)
if yInverted {
sy = srcH - 1 - sy
}
srcOff := sy*srcStride + sx*4 srcOff := sy*srcStride + sx*4
if srcOff+4 <= len(src) { if srcOff+4 <= len(src) {
finalColor = Color{B: src[srcOff+0], G: src[srcOff+1], R: src[srcOff+2], A: 255} finalColor = Color{B: src[srcOff+0], G: src[srcOff+1], R: src[srcOff+2], A: 255}
+5 -7
View File
@@ -435,7 +435,7 @@ func TestHyprlandConfigDeployment(t *testing.T) {
content, err := os.ReadFile(result.Path) content, err := os.ReadFile(result.Path)
require.NoError(t, err) require.NoError(t, err)
assert.Contains(t, string(content), "# MONITOR CONFIG") assert.Contains(t, string(content), "# MONITOR CONFIG")
assert.Contains(t, string(content), "bind = $mod, T, exec, ghostty") assert.Contains(t, string(content), "bind = $mod, T, exec, $TERMINAL")
assert.Contains(t, string(content), "exec-once = ") assert.Contains(t, string(content), "exec-once = ")
}) })
@@ -471,7 +471,7 @@ general {
require.NoError(t, err) require.NoError(t, err)
assert.Contains(t, string(newContent), "monitor = DP-1, 1920x1080@144") assert.Contains(t, string(newContent), "monitor = DP-1, 1920x1080@144")
assert.Contains(t, string(newContent), "monitor = HDMI-A-1, 3840x2160@60") assert.Contains(t, string(newContent), "monitor = HDMI-A-1, 3840x2160@60")
assert.Contains(t, string(newContent), "bind = $mod, T, exec, kitty") assert.Contains(t, string(newContent), "bind = $mod, T, exec, $TERMINAL")
assert.NotContains(t, string(newContent), "monitor = eDP-2") assert.NotContains(t, string(newContent), "monitor = eDP-2")
}) })
} }
@@ -487,16 +487,14 @@ func TestNiriConfigStructure(t *testing.T) {
func TestHyprlandConfigStructure(t *testing.T) { func TestHyprlandConfigStructure(t *testing.T) {
assert.Contains(t, HyprlandConfig, "# MONITOR CONFIG") assert.Contains(t, HyprlandConfig, "# MONITOR CONFIG")
assert.Contains(t, HyprlandConfig, "# ENVIRONMENT VARS")
assert.Contains(t, HyprlandConfig, "# STARTUP APPS") assert.Contains(t, HyprlandConfig, "# STARTUP APPS")
assert.Contains(t, HyprlandConfig, "# INPUT CONFIG") assert.Contains(t, HyprlandConfig, "# INPUT CONFIG")
assert.Contains(t, HyprlandConfig, "# KEYBINDINGS") assert.Contains(t, HyprlandConfig, "# KEYBINDINGS")
assert.Contains(t, HyprlandConfig, "{{POLKIT_AGENT_PATH}}") assert.Contains(t, HyprlandConfig, "{{POLKIT_AGENT_PATH}}")
assert.Contains(t, HyprlandConfig, "{{TERMINAL_COMMAND}}") assert.Contains(t, HyprlandConfig, "bind = $mod, T, exec, $TERMINAL")
assert.Contains(t, HyprlandConfig, "exec-once = dms run")
assert.Contains(t, HyprlandConfig, "bind = $mod, T, exec,")
assert.Contains(t, HyprlandConfig, "bind = $mod, space, exec, dms ipc call spotlight toggle") assert.Contains(t, HyprlandConfig, "bind = $mod, space, exec, dms ipc call spotlight toggle")
assert.Contains(t, HyprlandConfig, "windowrulev2 = noborder, class:^(com\\.mitchellh\\.ghostty)$") assert.Contains(t, HyprlandConfig, "windowrule {")
assert.Contains(t, HyprlandConfig, "match:class = ^(com\\.mitchellh\\.ghostty)$")
} }
func TestGhosttyConfigStructure(t *testing.T) { func TestGhosttyConfigStructure(t *testing.T) {
+123 -40
View File
@@ -7,20 +7,10 @@
# monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1 # monitor = eDP-2, 2560x1600@239.998993, 2560x0, 1, vrr, 1
monitor = , preferred,auto,auto monitor = , preferred,auto,auto
# ==================
# ENVIRONMENT VARS
# ==================
env = QT_QPA_PLATFORM,wayland
env = ELECTRON_OZONE_PLATFORM_HINT,auto
env = QT_QPA_PLATFORMTHEME,gtk3
env = QT_QPA_PLATFORMTHEME_QT6,gtk3
env = TERMINAL,{{TERMINAL_COMMAND}}
# ================== # ==================
# STARTUP APPS # STARTUP APPS
# ================== # ==================
exec-once = bash -c "wl-paste --watch cliphist store &" exec-once = bash -c "wl-paste --watch cliphist store &"
exec-once = dms run
exec-once = {{POLKIT_AGENT_PATH}} exec-once = {{POLKIT_AGENT_PATH}}
# ================== # ==================
@@ -100,36 +90,132 @@ misc {
# ================== # ==================
# WINDOW RULES # WINDOW RULES
# ================== # ==================
windowrulev2 = tile, class:^(org\.wezfurlong\.wezterm)$ windowrule {
name = windowrule-1
tile = on
match:class = ^(org\.wezfurlong\.wezterm)$
border_size = 0
}
windowrulev2 = rounding 12, class:^(org\.gnome\.)
windowrulev2 = noborder, class:^(org\.gnome\.)
windowrulev2 = tile, class:^(gnome-control-center)$ windowrule {
windowrulev2 = tile, class:^(pavucontrol)$ name = windowrule-2
windowrulev2 = tile, class:^(nm-connection-editor)$ rounding = 12
match:class = ^(org\.gnome\.)
border_size = 0
}
windowrulev2 = float, class:^(gnome-calculator)$
windowrulev2 = float, class:^(galculator)$
windowrulev2 = float, class:^(blueman-manager)$
windowrulev2 = float, class:^(org\.gnome\.Nautilus)$
windowrulev2 = float, class:^(steam)$
windowrulev2 = float, class:^(xdg-desktop-portal)$
windowrulev2 = noborder, class:^(org\.wezfurlong\.wezterm)$
windowrulev2 = noborder, class:^(Alacritty)$
windowrulev2 = noborder, class:^(zen)$
windowrulev2 = noborder, class:^(com\.mitchellh\.ghostty)$
windowrulev2 = noborder, class:^(kitty)$
windowrulev2 = float, class:^(firefox)$, title:^(Picture-in-Picture)$ windowrule {
windowrulev2 = float, class:^(zoom)$ name = windowrule-3
tile = on
match:class = ^(gnome-control-center)$
}
# DMS windows floating by default windowrule {
windowrulev2 = float, class:^(org.quickshell)$ name = windowrule-4
windowrulev2 = opacity 0.9 0.9, floating:0, focus:0 tile = on
match:class = ^(pavucontrol)$
}
layerrule = noanim, ^(quickshell)$ windowrule {
name = windowrule-5
tile = on
match:class = ^(nm-connection-editor)$
}
windowrule {
name = windowrule-6
float = on
match:class = ^(gnome-calculator)$
}
windowrule {
name = windowrule-7
float = on
match:class = ^(galculator)$
}
windowrule {
name = windowrule-8
float = on
match:class = ^(blueman-manager)$
}
windowrule {
name = windowrule-9
float = on
match:class = ^(org\.gnome\.Nautilus)$
}
windowrule {
name = windowrule-10
float = on
match:class = ^(steam)$
}
windowrule {
name = windowrule-11
float = on
match:class = ^(xdg-desktop-portal)$
}
windowrule {
name = windowrule-12
border_size = 0
match:class = ^(Alacritty)$
}
windowrule {
name = windowrule-13
border_size = 0
match:class = ^(zen)$
}
windowrule {
name = windowrule-14
border_size = 0
match:class = ^(com\.mitchellh\.ghostty)$
}
windowrule {
name = windowrule-15
border_size = 0
match:class = ^(kitty)$
}
windowrule {
name = windowrule-16
float = on
match:class = ^(firefox)$
match:title = ^(Picture-in-Picture)$
}
windowrule {
name = windowrule-17
float = on
match:class = ^(zoom)$
}
windowrule {
name = windowrule-18
opacity = 0.9 0.9
match:float = 0
match:focus = 0
}
layerrule {
name = layerrule-1
no_anim = on
match:namespace = ^(quickshell)$
}
# ================== # ==================
# KEYBINDINGS # KEYBINDINGS
@@ -137,7 +223,7 @@ layerrule = noanim, ^(quickshell)$
$mod = SUPER $mod = SUPER
# === Application Launchers === # === Application Launchers ===
bind = $mod, T, exec, {{TERMINAL_COMMAND}} bind = $mod, T, exec, $TERMINAL
bind = $mod, space, exec, dms ipc call spotlight toggle bind = $mod, space, exec, dms ipc call spotlight toggle
bind = $mod, V, exec, dms ipc call clipboard toggle bind = $mod, V, exec, dms ipc call clipboard toggle
bind = $mod, M, exec, dms ipc call processlist focusOrToggle bind = $mod, M, exec, dms ipc call processlist focusOrToggle
@@ -281,12 +367,9 @@ binde = $mod SHIFT, minus, resizeactive, 0 -10%
binde = $mod SHIFT, equal, resizeactive, 0 10% binde = $mod SHIFT, equal, resizeactive, 0 10%
# === Screenshots === # === Screenshots ===
bind = , XF86Launch1, exec, grimblast copy area bind = , Print, exec, dms screenshot
bind = CTRL, XF86Launch1, exec, grimblast copy screen bind = CTRL, Print, exec, dms screenshot full
bind = ALT, XF86Launch1, exec, grimblast copy active bind = ALT, Print, exec, dms screenshot window
bind = , Print, exec, grimblast copy area
bind = CTRL, Print, exec, grimblast copy screen
bind = ALT, Print, exec, grimblast copy active
# === System Controls === # === System Controls ===
bind = $mod SHIFT, P, dpms, off bind = $mod SHIFT, P, dpms, off
-6
View File
@@ -116,15 +116,9 @@ overview {
// See the binds section below for more spawn examples. // See the binds section below for more spawn examples.
// This line starts waybar, a commonly used bar for Wayland compositors. // This line starts waybar, a commonly used bar for Wayland compositors.
spawn-at-startup "bash" "-c" "wl-paste --watch cliphist store &" spawn-at-startup "bash" "-c" "wl-paste --watch cliphist store &"
spawn-at-startup "dms" "run"
spawn-at-startup "{{POLKIT_AGENT_PATH}}" spawn-at-startup "{{POLKIT_AGENT_PATH}}"
environment { environment {
XDG_CURRENT_DESKTOP "niri" XDG_CURRENT_DESKTOP "niri"
QT_QPA_PLATFORM "wayland"
ELECTRON_OZONE_PLATFORM_HINT "auto"
QT_QPA_PLATFORMTHEME "gtk3"
QT_QPA_PLATFORMTHEME_QT6 "gtk3"
TERMINAL "{{TERMINAL_COMMAND}}"
} }
hotkey-overlay { hotkey-overlay {
skip-at-startup skip-at-startup
+81
View File
@@ -23,6 +23,17 @@ type ColorInfo struct {
B int `json:"b"` B int `json:"b"`
} }
type VariantColorValue struct {
Hex string `json:"hex"`
HexStripped string `json:"hex_stripped"`
}
type VariantColorInfo struct {
Dark VariantColorValue `json:"dark"`
Light VariantColorValue `json:"light"`
Default VariantColorValue `json:"default"`
}
type Palette struct { type Palette struct {
Color0 ColorInfo `json:"color0"` Color0 ColorInfo `json:"color0"`
Color1 ColorInfo `json:"color1"` Color1 ColorInfo `json:"color1"`
@@ -42,6 +53,25 @@ type Palette struct {
Color15 ColorInfo `json:"color15"` Color15 ColorInfo `json:"color15"`
} }
type VariantPalette struct {
Color0 VariantColorInfo `json:"color0"`
Color1 VariantColorInfo `json:"color1"`
Color2 VariantColorInfo `json:"color2"`
Color3 VariantColorInfo `json:"color3"`
Color4 VariantColorInfo `json:"color4"`
Color5 VariantColorInfo `json:"color5"`
Color6 VariantColorInfo `json:"color6"`
Color7 VariantColorInfo `json:"color7"`
Color8 VariantColorInfo `json:"color8"`
Color9 VariantColorInfo `json:"color9"`
Color10 VariantColorInfo `json:"color10"`
Color11 VariantColorInfo `json:"color11"`
Color12 VariantColorInfo `json:"color12"`
Color13 VariantColorInfo `json:"color13"`
Color14 VariantColorInfo `json:"color14"`
Color15 VariantColorInfo `json:"color15"`
}
func NewColorInfo(hex string) ColorInfo { func NewColorInfo(hex string) ColorInfo {
rgb := HexToRGB(hex) rgb := HexToRGB(hex)
stripped := hex stripped := hex
@@ -492,3 +522,54 @@ func GeneratePalette(primaryColor string, opts PaletteOptions) Palette {
return palette return palette
} }
type VariantOptions struct {
PrimaryDark string
PrimaryLight string
Background string
UseDPS bool
IsLightMode bool
}
func mergeColorInfo(dark, light ColorInfo, isLightMode bool) VariantColorInfo {
darkVal := VariantColorValue{Hex: dark.Hex, HexStripped: dark.HexStripped}
lightVal := VariantColorValue{Hex: light.Hex, HexStripped: light.HexStripped}
defaultVal := darkVal
if isLightMode {
defaultVal = lightVal
}
return VariantColorInfo{
Dark: darkVal,
Light: lightVal,
Default: defaultVal,
}
}
func GenerateVariantPalette(opts VariantOptions) VariantPalette {
darkOpts := PaletteOptions{IsLight: false, Background: opts.Background, UseDPS: opts.UseDPS}
lightOpts := PaletteOptions{IsLight: true, Background: opts.Background, UseDPS: opts.UseDPS}
dark := GeneratePalette(opts.PrimaryDark, darkOpts)
light := GeneratePalette(opts.PrimaryLight, lightOpts)
return VariantPalette{
Color0: mergeColorInfo(dark.Color0, light.Color0, opts.IsLightMode),
Color1: mergeColorInfo(dark.Color1, light.Color1, opts.IsLightMode),
Color2: mergeColorInfo(dark.Color2, light.Color2, opts.IsLightMode),
Color3: mergeColorInfo(dark.Color3, light.Color3, opts.IsLightMode),
Color4: mergeColorInfo(dark.Color4, light.Color4, opts.IsLightMode),
Color5: mergeColorInfo(dark.Color5, light.Color5, opts.IsLightMode),
Color6: mergeColorInfo(dark.Color6, light.Color6, opts.IsLightMode),
Color7: mergeColorInfo(dark.Color7, light.Color7, opts.IsLightMode),
Color8: mergeColorInfo(dark.Color8, light.Color8, opts.IsLightMode),
Color9: mergeColorInfo(dark.Color9, light.Color9, opts.IsLightMode),
Color10: mergeColorInfo(dark.Color10, light.Color10, opts.IsLightMode),
Color11: mergeColorInfo(dark.Color11, light.Color11, opts.IsLightMode),
Color12: mergeColorInfo(dark.Color12, light.Color12, opts.IsLightMode),
Color13: mergeColorInfo(dark.Color13, light.Color13, opts.IsLightMode),
Color14: mergeColorInfo(dark.Color14, light.Color14, opts.IsLightMode),
Color15: mergeColorInfo(dark.Color15, light.Color15, opts.IsLightMode),
}
}
+5
View File
@@ -11,6 +11,11 @@ func GenerateJSON(p Palette) string {
return string(marshalled) return string(marshalled)
} }
func GenerateVariantJSON(p VariantPalette) string {
marshalled, _ := json.Marshal(p)
return string(marshalled)
}
func GenerateKittyTheme(p Palette) string { func GenerateKittyTheme(p Palette) string {
var result strings.Builder var result strings.Builder
fmt.Fprintf(&result, "color0 %s\n", p.Color0.Hex) fmt.Fprintf(&result, "color0 %s\n", p.Color0.Hex)
+9
View File
@@ -357,6 +357,15 @@ func (a *ArchDistribution) InstallPackages(ctx context.Context, dependencies []d
LogOutput: "Starting post-installation configuration...", LogOutput: "Starting post-installation configuration...",
} }
terminal := a.DetectTerminalFromDeps(dependencies)
if err := a.WriteEnvironmentConfig(terminal); err != nil {
a.log(fmt.Sprintf("Warning: failed to write environment config: %v", err))
}
if err := a.EnableDMSService(ctx); err != nil {
a.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
}
// Phase 7: Complete // Phase 7: Complete
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseComplete, Phase: PhaseComplete,
+67 -20
View File
@@ -17,8 +17,10 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/version" "github.com/AvengeMedia/DankMaterialShell/core/internal/version"
) )
const forceQuickshellGit = false const (
const forceDMSGit = false forceQuickshellGit = false
forceDMSGit = false
)
// BaseDistribution provides common functionality for all distributions // BaseDistribution provides common functionality for all distributions
type BaseDistribution struct { type BaseDistribution struct {
@@ -219,20 +221,6 @@ func (b *BaseDistribution) detectClipboardTools() []deps.Dependency {
return dependencies return dependencies
} }
func (b *BaseDistribution) detectHyprpicker() deps.Dependency {
status := deps.StatusMissing
if b.commandExists("hyprpicker") {
status = deps.StatusInstalled
}
return deps.Dependency{
Name: "hyprpicker",
Status: status,
Description: "Color picker for Wayland",
Required: true,
}
}
func (b *BaseDistribution) detectHyprlandTools() []deps.Dependency { func (b *BaseDistribution) detectHyprlandTools() []deps.Dependency {
var dependencies []deps.Dependency var dependencies []deps.Dependency
@@ -240,10 +228,7 @@ func (b *BaseDistribution) detectHyprlandTools() []deps.Dependency {
name string name string
description string description string
}{ }{
{"grim", "Screenshot utility for Wayland"},
{"slurp", "Region selection utility for Wayland"},
{"hyprctl", "Hyprland control utility"}, {"hyprctl", "Hyprland control utility"},
{"grimblast", "Screenshot script for Hyprland"},
{"jq", "JSON processor"}, {"jq", "JSON processor"},
} }
@@ -564,6 +549,68 @@ func (b *BaseDistribution) runWithProgressStepTimeout(cmd *exec.Cmd, progressCha
} }
} }
func (b *BaseDistribution) DetectTerminalFromDeps(dependencies []deps.Dependency) deps.Terminal {
for _, dep := range dependencies {
switch dep.Name {
case "ghostty":
return deps.TerminalGhostty
case "kitty":
return deps.TerminalKitty
case "alacritty":
return deps.TerminalAlacritty
}
}
return deps.TerminalGhostty
}
func (b *BaseDistribution) WriteEnvironmentConfig(terminal deps.Terminal) error {
homeDir, err := os.UserHomeDir()
if err != nil {
return fmt.Errorf("failed to get home directory: %w", err)
}
envDir := filepath.Join(homeDir, ".config", "environment.d")
if err := os.MkdirAll(envDir, 0755); err != nil {
return fmt.Errorf("failed to create environment.d directory: %w", err)
}
var terminalCmd string
switch terminal {
case deps.TerminalGhostty:
terminalCmd = "ghostty"
case deps.TerminalKitty:
terminalCmd = "kitty"
case deps.TerminalAlacritty:
terminalCmd = "alacritty"
default:
terminalCmd = "ghostty"
}
content := fmt.Sprintf(`QT_QPA_PLATFORM=wayland
ELECTRON_OZONE_PLATFORM_HINT=auto
QT_QPA_PLATFORMTHEME=gtk3
QT_QPA_PLATFORMTHEME_QT6=gtk3
TERMINAL=%s
`, terminalCmd)
envFile := filepath.Join(envDir, "90-dms.conf")
if err := os.WriteFile(envFile, []byte(content), 0644); err != nil {
return fmt.Errorf("failed to write environment config: %w", err)
}
b.log(fmt.Sprintf("Wrote environment config to %s", envFile))
return nil
}
func (b *BaseDistribution) EnableDMSService(ctx context.Context) error {
cmd := exec.CommandContext(ctx, "systemctl", "--user", "enable", "--now", "dms")
if err := cmd.Run(); err != nil {
return fmt.Errorf("failed to enable dms service: %w", err)
}
b.log("Enabled dms systemd user service")
return nil
}
// installDMSBinary installs the DMS binary from GitHub releases // installDMSBinary installs the DMS binary from GitHub releases
func (b *BaseDistribution) installDMSBinary(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { func (b *BaseDistribution) installDMSBinary(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
b.log("Installing/updating DMS binary...") b.log("Installing/updating DMS binary...")
@@ -602,7 +649,7 @@ func (b *BaseDistribution) installDMSBinary(ctx context.Context, sudoPassword st
return fmt.Errorf("failed to get user home directory: %w", err) return fmt.Errorf("failed to get user home directory: %w", err)
} }
tmpDir := filepath.Join(homeDir, ".cache", "dankinstall", "manual-builds") tmpDir := filepath.Join(homeDir, ".cache", "dankinstall", "manual-builds")
if err := os.MkdirAll(tmpDir, 0755); err != nil { if err := os.MkdirAll(tmpDir, 0o755); err != nil {
return fmt.Errorf("failed to create temp directory: %w", err) return fmt.Errorf("failed to create temp directory: %w", err)
} }
defer os.RemoveAll(tmpDir) defer os.RemoveAll(tmpDir)
+9
View File
@@ -333,6 +333,15 @@ func (d *DebianDistribution) InstallPackages(ctx context.Context, dependencies [
LogOutput: "Starting post-installation configuration...", LogOutput: "Starting post-installation configuration...",
} }
terminal := d.DetectTerminalFromDeps(dependencies)
if err := d.WriteEnvironmentConfig(terminal); err != nil {
d.log(fmt.Sprintf("Warning: failed to write environment config: %v", err))
}
if err := d.EnableDMSService(ctx); err != nil {
d.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
}
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseComplete, Phase: PhaseComplete,
Progress: 1.0, Progress: 1.0,
+9
View File
@@ -357,6 +357,15 @@ func (f *FedoraDistribution) InstallPackages(ctx context.Context, dependencies [
LogOutput: "Starting post-installation configuration...", LogOutput: "Starting post-installation configuration...",
} }
terminal := f.DetectTerminalFromDeps(dependencies)
if err := f.WriteEnvironmentConfig(terminal); err != nil {
f.log(fmt.Sprintf("Warning: failed to write environment config: %v", err))
}
if err := f.EnableDMSService(ctx); err != nil {
f.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
}
// Phase 7: Complete // Phase 7: Complete
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseComplete, Phase: PhaseComplete,
+9
View File
@@ -451,6 +451,15 @@ func (g *GentooDistribution) InstallPackages(ctx context.Context, dependencies [
LogOutput: "Starting post-installation configuration...", LogOutput: "Starting post-installation configuration...",
} }
terminal := g.DetectTerminalFromDeps(dependencies)
if err := g.WriteEnvironmentConfig(terminal); err != nil {
g.log(fmt.Sprintf("Warning: failed to write environment config: %v", err))
}
if err := g.EnableDMSService(ctx); err != nil {
g.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
}
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseComplete, Phase: PhaseComplete,
Progress: 1.0, Progress: 1.0,
-60
View File
@@ -62,10 +62,6 @@ func (m *ManualPackageInstaller) InstallManualPackages(ctx context.Context, pack
if err := m.installDgop(ctx, sudoPassword, progressChan); err != nil { if err := m.installDgop(ctx, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install dgop: %w", err) return fmt.Errorf("failed to install dgop: %w", err)
} }
case "grimblast":
if err := m.installGrimblast(ctx, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install grimblast: %w", err)
}
case "niri": case "niri":
if err := m.installNiri(ctx, sudoPassword, progressChan); err != nil { if err := m.installNiri(ctx, sudoPassword, progressChan); err != nil {
return fmt.Errorf("failed to install niri: %w", err) return fmt.Errorf("failed to install niri: %w", err)
@@ -166,62 +162,6 @@ func (m *ManualPackageInstaller) installDgop(ctx context.Context, sudoPassword s
return nil return nil
} }
func (m *ManualPackageInstaller) installGrimblast(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
m.log("Installing grimblast script for Hyprland...")
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.1,
Step: "Downloading grimblast script...",
IsComplete: false,
CommandInfo: "curl grimblast script",
}
grimblastURL := "https://raw.githubusercontent.com/hyprwm/contrib/refs/heads/main/grimblast/grimblast"
tmpPath := filepath.Join(os.TempDir(), "grimblast")
downloadCmd := exec.CommandContext(ctx, "curl", "-L", "-o", tmpPath, grimblastURL)
if err := downloadCmd.Run(); err != nil {
m.logError("failed to download grimblast", err)
return fmt.Errorf("failed to download grimblast: %w", err)
}
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.5,
Step: "Making grimblast executable...",
IsComplete: false,
CommandInfo: "chmod +x grimblast",
}
chmodCmd := exec.CommandContext(ctx, "chmod", "+x", tmpPath)
if err := chmodCmd.Run(); err != nil {
m.logError("failed to make grimblast executable", err)
return fmt.Errorf("failed to make grimblast executable: %w", err)
}
progressChan <- InstallProgressMsg{
Phase: PhaseSystemPackages,
Progress: 0.8,
Step: "Installing grimblast to /usr/local/bin...",
IsComplete: false,
NeedsSudo: true,
CommandInfo: "sudo cp grimblast /usr/local/bin/",
}
installCmd := ExecSudoCommand(ctx, sudoPassword,
fmt.Sprintf("cp %s /usr/local/bin/grimblast", tmpPath))
if err := installCmd.Run(); err != nil {
m.logError("failed to install grimblast", err)
return fmt.Errorf("failed to install grimblast: %w", err)
}
os.Remove(tmpPath)
m.log("grimblast installed successfully to /usr/local/bin")
return nil
}
func (m *ManualPackageInstaller) installNiri(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error { func (m *ManualPackageInstaller) installNiri(ctx context.Context, sudoPassword string, progressChan chan<- InstallProgressMsg) error {
m.log("Installing niri from source...") m.log("Installing niri from source...")
+9
View File
@@ -372,6 +372,15 @@ func (o *OpenSUSEDistribution) InstallPackages(ctx context.Context, dependencies
LogOutput: "Starting post-installation configuration...", LogOutput: "Starting post-installation configuration...",
} }
terminal := o.DetectTerminalFromDeps(dependencies)
if err := o.WriteEnvironmentConfig(terminal); err != nil {
o.log(fmt.Sprintf("Warning: failed to write environment config: %v", err))
}
if err := o.EnableDMSService(ctx); err != nil {
o.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
}
// Complete // Complete
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseComplete, Phase: PhaseComplete,
+9
View File
@@ -352,6 +352,15 @@ func (u *UbuntuDistribution) InstallPackages(ctx context.Context, dependencies [
LogOutput: "Starting post-installation configuration...", LogOutput: "Starting post-installation configuration...",
} }
terminal := u.DetectTerminalFromDeps(dependencies)
if err := u.WriteEnvironmentConfig(terminal); err != nil {
u.log(fmt.Sprintf("Warning: failed to write environment config: %v", err))
}
if err := u.EnableDMSService(ctx); err != nil {
u.log(fmt.Sprintf("Warning: failed to enable dms service: %v", err))
}
// Phase 7: Complete // Phase 7: Complete
progressChan <- InstallProgressMsg{ progressChan <- InstallProgressMsg{
Phase: PhaseComplete, Phase: PhaseComplete,
+1 -1
View File
@@ -514,7 +514,7 @@ func (m Model) categorizeDependencies() map[string][]DependencyInfo {
switch dep.Name { switch dep.Name {
case "dms (DankMaterialShell)", "quickshell": case "dms (DankMaterialShell)", "quickshell":
categories["Shell"] = append(categories["Shell"], dep) categories["Shell"] = append(categories["Shell"], dep)
case "hyprland", "grim", "slurp", "hyprctl", "grimblast": case "hyprland", "hyprctl":
categories["Hyprland Components"] = append(categories["Hyprland Components"], dep) categories["Hyprland Components"] = append(categories["Hyprland Components"], dep)
case "niri": case "niri":
categories["Niri Components"] = append(categories["Niri Components"], dep) categories["Niri Components"] = append(categories["Niri Components"], dep)
+21 -3
View File
@@ -371,6 +371,18 @@ func (n *NiriProvider) buildActionNode(action string) *document.Node {
node.SetName(parts[0]) node.SetName(parts[0])
for _, arg := range parts[1:] { for _, arg := range parts[1:] {
if strings.Contains(arg, "=") {
kv := strings.SplitN(arg, "=", 2)
switch kv[1] {
case "true":
node.AddProperty(kv[0], true, "")
case "false":
node.AddProperty(kv[0], false, "")
default:
node.AddProperty(kv[0], kv[1], "")
}
continue
}
node.AddArgument(arg, "") node.AddArgument(arg, "")
} }
return node return node
@@ -379,7 +391,7 @@ func (n *NiriProvider) buildActionNode(action string) *document.Node {
func (n *NiriProvider) parseActionParts(action string) []string { func (n *NiriProvider) parseActionParts(action string) []string {
var parts []string var parts []string
var current strings.Builder var current strings.Builder
var inQuote, escaped bool var inQuote, escaped, wasQuoted bool
for _, r := range action { for _, r := range action {
switch { switch {
@@ -389,17 +401,19 @@ func (n *NiriProvider) parseActionParts(action string) []string {
case r == '\\': case r == '\\':
escaped = true escaped = true
case r == '"': case r == '"':
wasQuoted = true
inQuote = !inQuote inQuote = !inQuote
case r == ' ' && !inQuote: case r == ' ' && !inQuote:
if current.Len() > 0 { if current.Len() > 0 || wasQuoted {
parts = append(parts, current.String()) parts = append(parts, current.String())
current.Reset() current.Reset()
wasQuoted = false
} }
default: default:
current.WriteRune(r) current.WriteRune(r)
} }
} }
if current.Len() > 0 { if current.Len() > 0 || wasQuoted {
parts = append(parts, current.String()) parts = append(parts, current.String())
} }
return parts return parts
@@ -508,6 +522,10 @@ func (n *NiriProvider) writeBindNode(sb *strings.Builder, bind *overrideBind, in
sb.WriteString(" ") sb.WriteString(" ")
n.writeArg(sb, arg.ValueString(), forceQuote) n.writeArg(sb, arg.ValueString(), forceQuote)
} }
if child.Properties.Exist() {
sb.WriteString(" ")
sb.WriteString(strings.TrimLeft(child.Properties.String(), " "))
}
} }
sb.WriteString("; }\n") sb.WriteString("; }\n")
} }
@@ -265,6 +265,11 @@ func (p *NiriParser) parseKeybindNode(node *document.Node, _ string) *NiriKeyBin
for _, arg := range actionNode.Arguments { for _, arg := range actionNode.Arguments {
args = append(args, arg.ValueString()) args = append(args, arg.ValueString())
} }
if actionNode.Properties != nil {
if val, ok := actionNode.Properties.Get("focus"); ok {
args = append(args, "focus="+val.String())
}
}
} }
var description string var description string
@@ -602,8 +602,24 @@ func TestNiriParseActionWithProperties(t *testing.T) {
for _, kb := range result.Section.Keybinds { for _, kb := range result.Section.Keybinds {
switch kb.Action { switch kb.Action {
case "move-column-to-workspace": case "move-column-to-workspace":
if len(kb.Args) != 1 { if len(kb.Args) != 2 {
t.Errorf("move-column-to-workspace should have 1 arg, got %d", len(kb.Args)) t.Errorf("move-column-to-workspace should have 2 args (index + focus), got %d", len(kb.Args))
}
hasIndex := false
hasFocus := false
for _, arg := range kb.Args {
if arg == "1" || arg == "2" {
hasIndex = true
}
if arg == "focus=false" {
hasFocus = true
}
}
if !hasIndex {
t.Errorf("move-column-to-workspace missing index arg")
}
if !hasFocus {
t.Errorf("move-column-to-workspace missing focus=false arg")
} }
case "next-window": case "next-window":
if kb.Key != "Tab" { if kb.Key != "Tab" {
+587
View File
@@ -0,0 +1,587 @@
package matugen
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"sync"
"syscall"
"github.com/AvengeMedia/DankMaterialShell/core/internal/dank16"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
)
var (
matugenVersionOnce sync.Once
matugenSupportsCOE bool
)
type Options struct {
StateDir string
ShellDir string
ConfigDir string
Kind string
Value string
Mode string
IconTheme string
MatugenType string
RunUserTemplates bool
StockColors string
SyncModeWithPortal bool
TerminalsAlwaysDark bool
}
type ColorsOutput struct {
Colors struct {
Dark map[string]string `json:"dark"`
Light map[string]string `json:"light"`
} `json:"colors"`
}
func (o *Options) ColorsOutput() string {
return filepath.Join(o.StateDir, "dms-colors.json")
}
func Run(opts Options) error {
if opts.StateDir == "" {
return fmt.Errorf("state-dir is required")
}
if opts.ShellDir == "" {
return fmt.Errorf("shell-dir is required")
}
if opts.ConfigDir == "" {
return fmt.Errorf("config-dir is required")
}
if opts.Kind == "" {
return fmt.Errorf("kind is required")
}
if opts.Value == "" {
return fmt.Errorf("value is required")
}
if opts.Mode == "" {
opts.Mode = "dark"
}
if opts.MatugenType == "" {
opts.MatugenType = "scheme-tonal-spot"
}
if opts.IconTheme == "" {
opts.IconTheme = "System Default"
}
if err := os.MkdirAll(opts.StateDir, 0755); err != nil {
return fmt.Errorf("failed to create state dir: %w", err)
}
log.Infof("Building theme: %s %s (%s)", opts.Kind, opts.Value, opts.Mode)
if err := buildOnce(&opts); err != nil {
return err
}
if opts.SyncModeWithPortal {
syncColorScheme(opts.Mode)
}
log.Info("Done")
return nil
}
func buildOnce(opts *Options) error {
cfgFile, err := os.CreateTemp("", "matugen-config-*.toml")
if err != nil {
return fmt.Errorf("failed to create temp config: %w", err)
}
defer os.Remove(cfgFile.Name())
defer cfgFile.Close()
tmpDir, err := os.MkdirTemp("", "matugen-templates-*")
if err != nil {
return fmt.Errorf("failed to create temp dir: %w", err)
}
defer os.RemoveAll(tmpDir)
if err := buildMergedConfig(opts, cfgFile, tmpDir); err != nil {
return fmt.Errorf("failed to build config: %w", err)
}
cfgFile.Close()
var primaryDark, primaryLight, surface string
var dank16JSON string
var importArgs []string
if opts.StockColors != "" {
log.Info("Using stock/custom theme colors with matugen base")
primaryDark = extractNestedColor(opts.StockColors, "primary", "dark")
primaryLight = extractNestedColor(opts.StockColors, "primary", "light")
surface = extractNestedColor(opts.StockColors, "surface", "dark")
if primaryDark == "" {
return fmt.Errorf("failed to extract primary dark from stock colors")
}
if primaryLight == "" {
primaryLight = primaryDark
}
dank16JSON = generateDank16Variants(primaryDark, primaryLight, surface, opts.Mode)
importData := fmt.Sprintf(`{"colors": %s, "dank16": %s}`, opts.StockColors, dank16JSON)
importArgs = []string{"--import-json-string", importData}
log.Info("Running matugen color hex with stock color overrides")
args := []string{"color", "hex", primaryDark, "-m", opts.Mode, "-t", opts.MatugenType, "-c", cfgFile.Name()}
args = append(args, importArgs...)
if err := runMatugen(args); err != nil {
return err
}
} else {
log.Infof("Using dynamic theme from %s: %s", opts.Kind, opts.Value)
matJSON, err := runMatugenDryRun(opts)
if err != nil {
return fmt.Errorf("matugen dry-run failed: %w", err)
}
primaryDark = extractMatugenColor(matJSON, "primary", "dark")
primaryLight = extractMatugenColor(matJSON, "primary", "light")
surface = extractMatugenColor(matJSON, "surface", "dark")
if primaryDark == "" {
return fmt.Errorf("failed to extract primary color")
}
if primaryLight == "" {
primaryLight = primaryDark
}
dank16JSON = generateDank16Variants(primaryDark, primaryLight, surface, opts.Mode)
importData := fmt.Sprintf(`{"dank16": %s}`, dank16JSON)
importArgs = []string{"--import-json-string", importData}
log.Infof("Running matugen %s with dank16 injection", opts.Kind)
var args []string
switch opts.Kind {
case "hex":
args = []string{"color", "hex", opts.Value}
default:
args = []string{opts.Kind, opts.Value}
}
args = append(args, "-m", opts.Mode, "-t", opts.MatugenType, "-c", cfgFile.Name())
args = append(args, importArgs...)
if err := runMatugen(args); err != nil {
return err
}
}
refreshGTK(opts.ConfigDir, opts.Mode)
signalTerminals()
return nil
}
func buildMergedConfig(opts *Options, cfgFile *os.File, tmpDir string) error {
userConfigPath := filepath.Join(opts.ConfigDir, "matugen", "config.toml")
wroteConfig := false
if opts.RunUserTemplates {
if data, err := os.ReadFile(userConfigPath); err == nil {
configSection := extractTOMLSection(string(data), "[config]", "[templates]")
if configSection != "" {
cfgFile.WriteString(configSection)
cfgFile.WriteString("\n")
wroteConfig = true
}
}
}
if !wroteConfig {
cfgFile.WriteString("[config]\n\n")
}
baseConfigPath := filepath.Join(opts.ShellDir, "matugen", "configs", "base.toml")
if data, err := os.ReadFile(baseConfigPath); err == nil {
content := string(data)
lines := strings.Split(content, "\n")
for _, line := range lines {
if strings.TrimSpace(line) == "[config]" {
continue
}
cfgFile.WriteString(substituteShellDir(line, opts.ShellDir) + "\n")
}
cfgFile.WriteString("\n")
}
fmt.Fprintf(cfgFile, `[templates.dank]
input_path = '%s/matugen/templates/dank.json'
output_path = '%s'
`, opts.ShellDir, opts.ColorsOutput())
switch opts.Mode {
case "light":
appendConfig(opts, cfgFile, "skip", "gtk3-light.toml")
default:
appendConfig(opts, cfgFile, "skip", "gtk3-dark.toml")
}
appendConfig(opts, cfgFile, "niri", "niri.toml")
appendConfig(opts, cfgFile, "qt5ct", "qt5ct.toml")
appendConfig(opts, cfgFile, "qt6ct", "qt6ct.toml")
appendConfig(opts, cfgFile, "firefox", "firefox.toml")
appendConfig(opts, cfgFile, "pywalfox", "pywalfox.toml")
appendConfig(opts, cfgFile, "vesktop", "vesktop.toml")
appendTerminalConfig(opts, cfgFile, tmpDir, "ghostty", "ghostty.toml")
appendTerminalConfig(opts, cfgFile, tmpDir, "kitty", "kitty.toml")
appendTerminalConfig(opts, cfgFile, tmpDir, "foot", "foot.toml")
appendTerminalConfig(opts, cfgFile, tmpDir, "alacritty", "alacritty.toml")
appendTerminalConfig(opts, cfgFile, tmpDir, "wezterm", "wezterm.toml")
appendConfig(opts, cfgFile, "dgop", "dgop.toml")
homeDir, _ := os.UserHomeDir()
appendVSCodeConfig(cfgFile, "vscode", filepath.Join(homeDir, ".vscode/extensions/local.dynamic-base16-dankshell-0.0.1"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "codium", filepath.Join(homeDir, ".vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "codeoss", filepath.Join(homeDir, ".config/Code - OSS/extensions/local.dynamic-base16-dankshell-0.0.1"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "cursor", filepath.Join(homeDir, ".cursor/extensions/local.dynamic-base16-dankshell-0.0.1"), opts.ShellDir)
appendVSCodeConfig(cfgFile, "windsurf", filepath.Join(homeDir, ".windsurf/extensions/local.dynamic-base16-dankshell-0.0.1"), opts.ShellDir)
if opts.RunUserTemplates {
if data, err := os.ReadFile(userConfigPath); err == nil {
templatesSection := extractTOMLSection(string(data), "[templates]", "")
if templatesSection != "" {
cfgFile.WriteString(templatesSection)
cfgFile.WriteString("\n")
}
}
}
userPluginConfigDir := filepath.Join(opts.ConfigDir, "matugen", "dms", "configs")
if entries, err := os.ReadDir(userPluginConfigDir); err == nil {
for _, entry := range entries {
if !strings.HasSuffix(entry.Name(), ".toml") {
continue
}
if data, err := os.ReadFile(filepath.Join(userPluginConfigDir, entry.Name())); err == nil {
cfgFile.WriteString(string(data))
cfgFile.WriteString("\n")
}
}
}
return nil
}
func appendConfig(opts *Options, cfgFile *os.File, checkCmd, fileName string) {
configPath := filepath.Join(opts.ShellDir, "matugen", "configs", fileName)
if _, err := os.Stat(configPath); err != nil {
return
}
if checkCmd != "skip" && !commandExists(checkCmd) {
return
}
data, err := os.ReadFile(configPath)
if err != nil {
return
}
cfgFile.WriteString(substituteShellDir(string(data), opts.ShellDir))
cfgFile.WriteString("\n")
}
func appendTerminalConfig(opts *Options, cfgFile *os.File, tmpDir, checkCmd, fileName string) {
configPath := filepath.Join(opts.ShellDir, "matugen", "configs", fileName)
if _, err := os.Stat(configPath); err != nil {
return
}
if checkCmd != "skip" && !commandExists(checkCmd) {
return
}
data, err := os.ReadFile(configPath)
if err != nil {
return
}
content := string(data)
if !opts.TerminalsAlwaysDark {
cfgFile.WriteString(substituteShellDir(content, opts.ShellDir))
cfgFile.WriteString("\n")
return
}
lines := strings.Split(content, "\n")
for _, line := range lines {
if !strings.Contains(line, "input_path") || !strings.Contains(line, "SHELL_DIR/matugen/templates/") {
continue
}
start := strings.Index(line, "'SHELL_DIR/matugen/templates/")
if start == -1 {
continue
}
end := strings.Index(line[start+1:], "'")
if end == -1 {
continue
}
templateName := line[start+len("'SHELL_DIR/matugen/templates/") : start+1+end]
origPath := filepath.Join(opts.ShellDir, "matugen", "templates", templateName)
origData, err := os.ReadFile(origPath)
if err != nil {
continue
}
modified := strings.ReplaceAll(string(origData), ".default.", ".dark.")
tmpPath := filepath.Join(tmpDir, templateName)
if err := os.WriteFile(tmpPath, []byte(modified), 0644); err != nil {
continue
}
content = strings.ReplaceAll(content,
fmt.Sprintf("'SHELL_DIR/matugen/templates/%s'", templateName),
fmt.Sprintf("'%s'", tmpPath))
}
cfgFile.WriteString(substituteShellDir(content, opts.ShellDir))
cfgFile.WriteString("\n")
}
func appendVSCodeConfig(cfgFile *os.File, name, extDir, shellDir string) {
if _, err := os.Stat(extDir); err != nil {
return
}
templateDir := filepath.Join(shellDir, "matugen", "templates")
fmt.Fprintf(cfgFile, `[templates.dms%sdefault]
input_path = '%s/vscode-color-theme-default.json'
output_path = '%s/themes/dankshell-default.json'
[templates.dms%sdark]
input_path = '%s/vscode-color-theme-dark.json'
output_path = '%s/themes/dankshell-dark.json'
[templates.dms%slight]
input_path = '%s/vscode-color-theme-light.json'
output_path = '%s/themes/dankshell-light.json'
`, name, templateDir, extDir,
name, templateDir, extDir,
name, templateDir, extDir)
log.Infof("Added %s theme config (extension found at %s)", name, extDir)
}
func substituteShellDir(content, shellDir string) string {
return strings.ReplaceAll(content, "'SHELL_DIR/", "'"+shellDir+"/")
}
func extractTOMLSection(content, startMarker, endMarker string) string {
startIdx := strings.Index(content, startMarker)
if startIdx == -1 {
return ""
}
if endMarker == "" {
return content[startIdx:]
}
endIdx := strings.Index(content[startIdx:], endMarker)
if endIdx == -1 {
return content[startIdx:]
}
return content[startIdx : startIdx+endIdx]
}
func commandExists(name string) bool {
_, err := exec.LookPath(name)
return err == nil
}
func checkMatugenVersion() {
matugenVersionOnce.Do(func() {
cmd := exec.Command("matugen", "--version")
output, err := cmd.Output()
if err != nil {
return
}
versionStr := strings.TrimSpace(string(output))
versionStr = strings.TrimPrefix(versionStr, "matugen ")
parts := strings.Split(versionStr, ".")
if len(parts) < 2 {
return
}
major, err := strconv.Atoi(parts[0])
if err != nil {
return
}
minor, err := strconv.Atoi(parts[1])
if err != nil {
return
}
matugenSupportsCOE = major > 3 || (major == 3 && minor >= 1)
if matugenSupportsCOE {
log.Infof("Matugen %s supports --continue-on-error", versionStr)
}
})
}
func runMatugen(args []string) error {
checkMatugenVersion()
if matugenSupportsCOE {
args = append([]string{"--continue-on-error"}, args...)
}
cmd := exec.Command("matugen", args...)
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
return cmd.Run()
}
func runMatugenDryRun(opts *Options) (string, error) {
var args []string
switch opts.Kind {
case "hex":
args = []string{"color", "hex", opts.Value}
default:
args = []string{opts.Kind, opts.Value}
}
args = append(args, "-m", "dark", "-t", opts.MatugenType, "--json", "hex", "--dry-run")
cmd := exec.Command("matugen", args...)
output, err := cmd.Output()
if err != nil {
return "", err
}
return strings.ReplaceAll(string(output), "\n", ""), nil
}
func extractMatugenColor(jsonStr, colorName, variant string) string {
var data map[string]any
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
return ""
}
colors, ok := data["colors"].(map[string]any)
if !ok {
return ""
}
colorData, ok := colors[colorName].(map[string]any)
if !ok {
return ""
}
variantData, ok := colorData[variant].(string)
if !ok {
return ""
}
return variantData
}
func extractNestedColor(jsonStr, colorName, variant string) string {
var data map[string]any
if err := json.Unmarshal([]byte(jsonStr), &data); err != nil {
return ""
}
colorData, ok := data[colorName].(map[string]any)
if !ok {
return ""
}
variantData, ok := colorData[variant].(map[string]any)
if !ok {
return ""
}
color, ok := variantData["color"].(string)
if !ok {
return ""
}
return color
}
func generateDank16Variants(primaryDark, primaryLight, surface, mode string) string {
variantOpts := dank16.VariantOptions{
PrimaryDark: primaryDark,
PrimaryLight: primaryLight,
Background: surface,
UseDPS: true,
IsLightMode: mode == "light",
}
variantColors := dank16.GenerateVariantPalette(variantOpts)
return dank16.GenerateVariantJSON(variantColors)
}
func refreshGTK(configDir, mode string) {
gtkCSS := filepath.Join(configDir, "gtk-3.0", "gtk.css")
info, err := os.Lstat(gtkCSS)
if err != nil {
return
}
shouldRun := false
if info.Mode()&os.ModeSymlink != 0 {
target, err := os.Readlink(gtkCSS)
if err == nil && strings.Contains(target, "dank-colors.css") {
shouldRun = true
}
} else {
data, err := os.ReadFile(gtkCSS)
if err == nil && strings.Contains(string(data), "dank-colors.css") {
shouldRun = true
}
}
if !shouldRun {
return
}
exec.Command("gsettings", "set", "org.gnome.desktop.interface", "gtk-theme", "").Run()
exec.Command("gsettings", "set", "org.gnome.desktop.interface", "gtk-theme", "adw-gtk3-"+mode).Run()
}
func signalTerminals() {
signalByName("kitty", syscall.SIGUSR1)
signalByName("ghostty", syscall.SIGUSR2)
signalByName(".kitty-wrapped", syscall.SIGUSR1)
signalByName(".ghostty-wrappe", syscall.SIGUSR2)
}
func signalByName(name string, sig syscall.Signal) {
entries, err := os.ReadDir("/proc")
if err != nil {
return
}
for _, entry := range entries {
pid, err := strconv.Atoi(entry.Name())
if err != nil {
continue
}
comm, err := os.ReadFile(filepath.Join("/proc", entry.Name(), "comm"))
if err != nil {
continue
}
if strings.TrimSpace(string(comm)) == name {
syscall.Kill(pid, sig)
}
}
}
func syncColorScheme(mode string) {
scheme := "prefer-dark"
if mode == "light" {
scheme = "default"
}
if err := exec.Command("gsettings", "set", "org.gnome.desktop.interface", "color-scheme", scheme).Run(); err != nil {
exec.Command("dconf", "write", "/org/gnome/desktop/interface/color-scheme", "'"+scheme+"'").Run()
}
}
+139
View File
@@ -0,0 +1,139 @@
package matugen
import (
"context"
"sync"
"github.com/AvengeMedia/DankMaterialShell/core/internal/log"
)
type Result struct {
Success bool
Error error
}
type QueuedJob struct {
Options Options
Done chan Result
Ctx context.Context
Cancel context.CancelFunc
}
type Queue struct {
mu sync.Mutex
current *QueuedJob
pending *QueuedJob
jobDone chan struct{}
}
var globalQueue *Queue
var queueOnce sync.Once
func GetQueue() *Queue {
queueOnce.Do(func() {
globalQueue = &Queue{
jobDone: make(chan struct{}, 1),
}
})
return globalQueue
}
func (q *Queue) Submit(opts Options) <-chan Result {
result := make(chan Result, 1)
ctx, cancel := context.WithCancel(context.Background())
job := &QueuedJob{
Options: opts,
Done: result,
Ctx: ctx,
Cancel: cancel,
}
q.mu.Lock()
if q.pending != nil {
log.Info("Cancelling pending theme request")
q.pending.Cancel()
q.pending.Done <- Result{Success: false, Error: context.Canceled}
close(q.pending.Done)
}
if q.current != nil {
q.pending = job
q.mu.Unlock()
log.Info("Theme request queued (worker running)")
return result
}
q.current = job
q.mu.Unlock()
go q.runWorker()
return result
}
func (q *Queue) runWorker() {
for {
q.mu.Lock()
job := q.current
if job == nil {
q.mu.Unlock()
return
}
q.mu.Unlock()
select {
case <-job.Ctx.Done():
q.finishJob(Result{Success: false, Error: context.Canceled})
continue
default:
}
log.Infof("Processing theme: %s %s (%s)", job.Options.Kind, job.Options.Value, job.Options.Mode)
err := Run(job.Options)
var result Result
if err != nil {
result = Result{Success: false, Error: err}
} else {
result = Result{Success: true}
}
q.finishJob(result)
}
}
func (q *Queue) finishJob(result Result) {
q.mu.Lock()
defer q.mu.Unlock()
if q.current != nil {
select {
case q.current.Done <- result:
default:
}
close(q.current.Done)
}
q.current = q.pending
q.pending = nil
if q.current == nil {
select {
case q.jobDone <- struct{}{}:
default:
}
}
}
func (q *Queue) IsRunning() bool {
q.mu.Lock()
defer q.mu.Unlock()
return q.current != nil
}
func (q *Queue) HasPending() bool {
q.mu.Lock()
defer q.mu.Unlock()
return q.pending != nil
}
+2 -2
View File
@@ -44,7 +44,7 @@ func NewZdwlIpcManagerV2(ctx *client.Context) *ZdwlIpcManagerV2 {
// Indicates that the client will not the dwl_ipc_manager object anymore. // Indicates that the client will not the dwl_ipc_manager object anymore.
// Objects created through this instance are not affected. // Objects created through this instance are not affected.
func (i *ZdwlIpcManagerV2) Release() error { func (i *ZdwlIpcManagerV2) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -188,7 +188,7 @@ func NewZdwlIpcOutputV2(ctx *client.Context) *ZdwlIpcOutputV2 {
// //
// Indicates to that the client no longer needs this dwl_ipc_output. // Indicates to that the client no longer needs this dwl_ipc_output.
func (i *ZdwlIpcOutputV2) Release() error { func (i *ZdwlIpcOutputV2) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -174,7 +174,7 @@ func (i *ExtWorkspaceManagerV1) Stop() error {
} }
func (i *ExtWorkspaceManagerV1) Destroy() error { func (i *ExtWorkspaceManagerV1) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -385,7 +385,7 @@ func (i *ExtWorkspaceGroupHandleV1) CreateWorkspace(workspace string) error {
// use the workspace group object any more or after the removed event to finalize // use the workspace group object any more or after the removed event to finalize
// the destruction of the object. // the destruction of the object.
func (i *ExtWorkspaceGroupHandleV1) Destroy() error { func (i *ExtWorkspaceGroupHandleV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -655,7 +655,7 @@ func NewExtWorkspaceHandleV1(ctx *client.Context) *ExtWorkspaceHandleV1 {
// use the workspace object any more or after the remove event to finalize // use the workspace object any more or after the remove event to finalize
// the destruction of the object. // the destruction of the object.
func (i *ExtWorkspaceHandleV1) Destroy() error { func (i *ExtWorkspaceHandleV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -54,7 +54,7 @@ func NewZwpKeyboardShortcutsInhibitManagerV1(ctx *client.Context) *ZwpKeyboardSh
// //
// Destroy the keyboard shortcuts inhibitor manager. // Destroy the keyboard shortcuts inhibitor manager.
func (i *ZwpKeyboardShortcutsInhibitManagerV1) Destroy() error { func (i *ZwpKeyboardShortcutsInhibitManagerV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -218,7 +218,7 @@ func NewZwpKeyboardShortcutsInhibitorV1(ctx *client.Context) *ZwpKeyboardShortcu
// //
// Remove the keyboard shortcuts inhibitor from the associated wl_surface. // Remove the keyboard shortcuts inhibitor from the associated wl_surface.
func (i *ZwpKeyboardShortcutsInhibitorV1) Destroy() error { func (i *ZwpKeyboardShortcutsInhibitorV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -85,7 +85,7 @@ func (i *ZwlrGammaControlManagerV1) GetGammaControl(output *client.Output) (*Zwl
// All objects created by the manager will still remain valid, until their // All objects created by the manager will still remain valid, until their
// appropriate destroy request has been called. // appropriate destroy request has been called.
func (i *ZwlrGammaControlManagerV1) Destroy() error { func (i *ZwlrGammaControlManagerV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -169,7 +169,7 @@ func (i *ZwlrGammaControlV1) SetGamma(fd int) error {
// Destroys the gamma control object. If the object is still valid, this // Destroys the gamma control object. If the object is still valid, this
// restores the original gamma tables. // restores the original gamma tables.
func (i *ZwlrGammaControlV1) Destroy() error { func (i *ZwlrGammaControlV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -129,7 +129,7 @@ func (i *ZwlrLayerShellV1) GetLayerSurface(surface *client.Surface, output *clie
// object any more. Objects that have been created through this instance // object any more. Objects that have been created through this instance
// are not affected. // are not affected.
func (i *ZwlrLayerShellV1) Destroy() error { func (i *ZwlrLayerShellV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -509,7 +509,7 @@ func (i *ZwlrLayerSurfaceV1) AckConfigure(serial uint32) error {
// //
// This request destroys the layer surface. // This request destroys the layer surface.
func (i *ZwlrLayerSurfaceV1) Destroy() error { func (i *ZwlrLayerSurfaceV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 7 const opcode = 7
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -172,7 +172,7 @@ func (i *ZwlrOutputManagerV1) Stop() error {
} }
func (i *ZwlrOutputManagerV1) Destroy() error { func (i *ZwlrOutputManagerV1) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -334,7 +334,7 @@ func NewZwlrOutputHeadV1(ctx *client.Context) *ZwlrOutputHeadV1 {
// This request indicates that the client will no longer use this head // This request indicates that the client will no longer use this head
// object. // object.
func (i *ZwlrOutputHeadV1) Release() error { func (i *ZwlrOutputHeadV1) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -879,7 +879,7 @@ func NewZwlrOutputModeV1(ctx *client.Context) *ZwlrOutputModeV1 {
// This request indicates that the client will no longer use this mode // This request indicates that the client will no longer use this mode
// object. // object.
func (i *ZwlrOutputModeV1) Release() error { func (i *ZwlrOutputModeV1) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -1132,7 +1132,7 @@ func (i *ZwlrOutputConfigurationV1) Test() error {
// This request also destroys wlr_output_configuration_head objects created // This request also destroys wlr_output_configuration_head objects created
// via this object. // via this object.
func (i *ZwlrOutputConfigurationV1) Destroy() error { func (i *ZwlrOutputConfigurationV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 4 const opcode = 4
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -1415,7 +1415,7 @@ func (i *ZwlrOutputConfigurationHeadV1) SetAdaptiveSync(state uint32) error {
} }
func (i *ZwlrOutputConfigurationHeadV1) Destroy() error { func (i *ZwlrOutputConfigurationHeadV1) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -79,7 +79,7 @@ func (i *ZwlrOutputPowerManagerV1) GetOutputPower(output *client.Output) (*ZwlrO
// All objects created by the manager will still remain valid, until their // All objects created by the manager will still remain valid, until their
// appropriate destroy request has been called. // appropriate destroy request has been called.
func (i *ZwlrOutputPowerManagerV1) Destroy() error { func (i *ZwlrOutputPowerManagerV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -143,7 +143,7 @@ func (i *ZwlrOutputPowerV1) SetMode(mode uint32) error {
// //
// Destroys the output power management mode control object. // Destroys the output power management mode control object.
func (i *ZwlrOutputPowerV1) Destroy() error { func (i *ZwlrOutputPowerV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -120,7 +120,7 @@ func (i *ZwlrScreencopyManagerV1) CaptureOutputRegion(overlayCursor int32, outpu
// All objects created by the manager will still remain valid, until their // All objects created by the manager will still remain valid, until their
// appropriate destroy request has been called. // appropriate destroy request has been called.
func (i *ZwlrScreencopyManagerV1) Destroy() error { func (i *ZwlrScreencopyManagerV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 2 const opcode = 2
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -219,7 +219,7 @@ func (i *ZwlrScreencopyFrameV1) Copy(buffer *client.Buffer) error {
// //
// Destroys the frame. This request can be sent at any time by the client. // Destroys the frame. This request can be sent at any time by the client.
func (i *ZwlrScreencopyFrameV1) Destroy() error { func (i *ZwlrScreencopyFrameV1) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -66,7 +66,7 @@ func NewWpViewporter(ctx *client.Context) *WpViewporter {
// protocol object anymore. This does not affect any other objects, // protocol object anymore. This does not affect any other objects,
// wp_viewport objects included. // wp_viewport objects included.
func (i *WpViewporter) Destroy() error { func (i *WpViewporter) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -267,7 +267,7 @@ func NewWpViewport(ctx *client.Context) *WpViewport {
// The associated wl_surface's crop and scale state is removed. // The associated wl_surface's crop and scale state is removed.
// The change is applied on the next wl_surface.commit. // The change is applied on the next wl_surface.commit.
func (i *WpViewport) Destroy() error { func (i *WpViewport) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
+463 -9
View File
@@ -5,6 +5,10 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"github.com/AvengeMedia/DankMaterialShell/core/internal/proto/dwl_ipc"
wlhelpers "github.com/AvengeMedia/DankMaterialShell/core/internal/wayland/client"
"github.com/AvengeMedia/DankMaterialShell/core/pkg/go-wayland/wayland/client"
) )
type Compositor int type Compositor int
@@ -12,13 +16,76 @@ type Compositor int
const ( const (
CompositorUnknown Compositor = iota CompositorUnknown Compositor = iota
CompositorHyprland CompositorHyprland
CompositorSway
CompositorNiri
CompositorDWL
) )
var detectedCompositor Compositor = -1
func DetectCompositor() Compositor { func DetectCompositor() Compositor {
if os.Getenv("HYPRLAND_INSTANCE_SIGNATURE") != "" { if detectedCompositor >= 0 {
return CompositorHyprland return detectedCompositor
} }
return CompositorUnknown
hyprlandSig := os.Getenv("HYPRLAND_INSTANCE_SIGNATURE")
niriSocket := os.Getenv("NIRI_SOCKET")
swaySocket := os.Getenv("SWAYSOCK")
switch {
case niriSocket != "":
if _, err := os.Stat(niriSocket); err == nil {
detectedCompositor = CompositorNiri
return detectedCompositor
}
case swaySocket != "":
if _, err := os.Stat(swaySocket); err == nil {
detectedCompositor = CompositorSway
return detectedCompositor
}
case hyprlandSig != "":
detectedCompositor = CompositorHyprland
return detectedCompositor
}
if detectDWLProtocol() {
detectedCompositor = CompositorDWL
return detectedCompositor
}
detectedCompositor = CompositorUnknown
return detectedCompositor
}
func detectDWLProtocol() bool {
display, err := client.Connect("")
if err != nil {
return false
}
ctx := display.Context()
defer ctx.Close()
registry, err := display.GetRegistry()
if err != nil {
return false
}
found := false
registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
if e.Interface == dwl_ipc.ZdwlIpcManagerV2InterfaceName {
found = true
}
})
if err := wlhelpers.Roundtrip(display, ctx); err != nil {
return false
}
return found
}
func SetCompositorDWL() {
detectedCompositor = CompositorDWL
} }
type WindowGeometry struct { type WindowGeometry struct {
@@ -26,16 +93,18 @@ type WindowGeometry struct {
Y int32 Y int32
Width int32 Width int32
Height int32 Height int32
Output string
Scale float64
} }
func GetActiveWindow() (*WindowGeometry, error) { func GetActiveWindow() (*WindowGeometry, error) {
compositor := DetectCompositor() switch DetectCompositor() {
switch compositor {
case CompositorHyprland: case CompositorHyprland:
return getHyprlandActiveWindow() return getHyprlandActiveWindow()
case CompositorDWL:
return getDWLActiveWindow()
default: default:
return nil, fmt.Errorf("window capture requires Hyprland (other compositors not yet supported)") return nil, fmt.Errorf("window capture requires Hyprland or DWL")
} }
} }
@@ -45,8 +114,7 @@ type hyprlandWindow struct {
} }
func getHyprlandActiveWindow() (*WindowGeometry, error) { func getHyprlandActiveWindow() (*WindowGeometry, error) {
cmd := exec.Command("hyprctl", "-j", "activewindow") output, err := exec.Command("hyprctl", "-j", "activewindow").Output()
output, err := cmd.Output()
if err != nil { if err != nil {
return nil, fmt.Errorf("hyprctl activewindow: %w", err) return nil, fmt.Errorf("hyprctl activewindow: %w", err)
} }
@@ -67,3 +135,389 @@ func getHyprlandActiveWindow() (*WindowGeometry, error) {
Height: win.Size[1], Height: win.Size[1],
}, nil }, nil
} }
type hyprlandMonitor struct {
Name string `json:"name"`
X int32 `json:"x"`
Y int32 `json:"y"`
Width int32 `json:"width"`
Height int32 `json:"height"`
Scale float64 `json:"scale"`
Focused bool `json:"focused"`
}
func GetHyprlandMonitorScale(name string) float64 {
output, err := exec.Command("hyprctl", "-j", "monitors").Output()
if err != nil {
return 0
}
var monitors []hyprlandMonitor
if err := json.Unmarshal(output, &monitors); err != nil {
return 0
}
for _, m := range monitors {
if m.Name == name {
return m.Scale
}
}
return 0
}
func getHyprlandFocusedMonitor() string {
output, err := exec.Command("hyprctl", "-j", "monitors").Output()
if err != nil {
return ""
}
var monitors []hyprlandMonitor
if err := json.Unmarshal(output, &monitors); err != nil {
return ""
}
for _, m := range monitors {
if m.Focused {
return m.Name
}
}
return ""
}
func GetHyprlandMonitorGeometry(name string) (x, y, w, h int32, ok bool) {
output, err := exec.Command("hyprctl", "-j", "monitors").Output()
if err != nil {
return 0, 0, 0, 0, false
}
var monitors []hyprlandMonitor
if err := json.Unmarshal(output, &monitors); err != nil {
return 0, 0, 0, 0, false
}
for _, m := range monitors {
if m.Name == name {
logicalW := int32(float64(m.Width) / m.Scale)
logicalH := int32(float64(m.Height) / m.Scale)
return m.X, m.Y, logicalW, logicalH, true
}
}
return 0, 0, 0, 0, false
}
type swayWorkspace struct {
Output string `json:"output"`
Focused bool `json:"focused"`
}
func getSwayFocusedMonitor() string {
output, err := exec.Command("swaymsg", "-t", "get_workspaces").Output()
if err != nil {
return ""
}
var workspaces []swayWorkspace
if err := json.Unmarshal(output, &workspaces); err != nil {
return ""
}
for _, ws := range workspaces {
if ws.Focused {
return ws.Output
}
}
return ""
}
type niriWorkspace struct {
Output string `json:"output"`
IsFocused bool `json:"is_focused"`
}
func getNiriFocusedMonitor() string {
output, err := exec.Command("niri", "msg", "-j", "workspaces").Output()
if err != nil {
return ""
}
var workspaces []niriWorkspace
if err := json.Unmarshal(output, &workspaces); err != nil {
return ""
}
for _, ws := range workspaces {
if ws.IsFocused {
return ws.Output
}
}
return ""
}
var dwlActiveOutput string
func SetDWLActiveOutput(name string) {
dwlActiveOutput = name
}
func getDWLFocusedMonitor() string {
if dwlActiveOutput != "" {
return dwlActiveOutput
}
return queryDWLActiveOutput()
}
func queryDWLActiveOutput() string {
display, err := client.Connect("")
if err != nil {
return ""
}
ctx := display.Context()
defer ctx.Close()
registry, err := display.GetRegistry()
if err != nil {
return ""
}
var dwlManager *dwl_ipc.ZdwlIpcManagerV2
outputs := make(map[uint32]*client.Output)
registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
switch e.Interface {
case dwl_ipc.ZdwlIpcManagerV2InterfaceName:
mgr := dwl_ipc.NewZdwlIpcManagerV2(ctx)
if err := registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil {
dwlManager = mgr
}
case client.OutputInterfaceName:
out := client.NewOutput(ctx)
version := e.Version
if version > 4 {
version = 4
}
if err := registry.Bind(e.Name, e.Interface, version, out); err == nil {
outputs[e.Name] = out
}
}
})
if err := wlhelpers.Roundtrip(display, ctx); err != nil {
return ""
}
if dwlManager == nil || len(outputs) == 0 {
return ""
}
outputNames := make(map[uint32]string)
for name, out := range outputs {
n := name
out.SetNameHandler(func(e client.OutputNameEvent) {
outputNames[n] = e.Name
})
}
if err := wlhelpers.Roundtrip(display, ctx); err != nil {
return ""
}
type outputState struct {
name string
active bool
gotFrame bool
}
states := make(map[uint32]*outputState)
for name, out := range outputs {
dwlOut, err := dwlManager.GetOutput(out)
if err != nil {
continue
}
state := &outputState{name: outputNames[name]}
states[name] = state
dwlOut.SetActiveHandler(func(e dwl_ipc.ZdwlIpcOutputV2ActiveEvent) {
state.active = e.Active != 0
})
dwlOut.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) {
state.gotFrame = true
})
}
allFramesReceived := func() bool {
for _, s := range states {
if !s.gotFrame {
return false
}
}
return true
}
for !allFramesReceived() {
if err := ctx.Dispatch(); err != nil {
return ""
}
}
for _, state := range states {
if state.active {
return state.name
}
}
return ""
}
func GetFocusedMonitor() string {
switch DetectCompositor() {
case CompositorHyprland:
return getHyprlandFocusedMonitor()
case CompositorSway:
return getSwayFocusedMonitor()
case CompositorNiri:
return getNiriFocusedMonitor()
case CompositorDWL:
return getDWLFocusedMonitor()
}
return ""
}
func getDWLActiveWindow() (*WindowGeometry, error) {
display, err := client.Connect("")
if err != nil {
return nil, fmt.Errorf("connect: %w", err)
}
ctx := display.Context()
defer ctx.Close()
registry, err := display.GetRegistry()
if err != nil {
return nil, fmt.Errorf("get registry: %w", err)
}
var dwlManager *dwl_ipc.ZdwlIpcManagerV2
outputs := make(map[uint32]*client.Output)
registry.SetGlobalHandler(func(e client.RegistryGlobalEvent) {
switch e.Interface {
case dwl_ipc.ZdwlIpcManagerV2InterfaceName:
mgr := dwl_ipc.NewZdwlIpcManagerV2(ctx)
if err := registry.Bind(e.Name, e.Interface, e.Version, mgr); err == nil {
dwlManager = mgr
}
case client.OutputInterfaceName:
out := client.NewOutput(ctx)
version := e.Version
if version > 4 {
version = 4
}
if err := registry.Bind(e.Name, e.Interface, version, out); err == nil {
outputs[e.Name] = out
}
}
})
if err := wlhelpers.Roundtrip(display, ctx); err != nil {
return nil, fmt.Errorf("roundtrip: %w", err)
}
if dwlManager == nil {
return nil, fmt.Errorf("dwl_ipc_manager not available")
}
if len(outputs) == 0 {
return nil, fmt.Errorf("no outputs found")
}
outputNames := make(map[uint32]string)
for name, out := range outputs {
n := name
out.SetNameHandler(func(e client.OutputNameEvent) {
outputNames[n] = e.Name
})
}
if err := wlhelpers.Roundtrip(display, ctx); err != nil {
return nil, fmt.Errorf("roundtrip: %w", err)
}
type dwlOutputState struct {
output *dwl_ipc.ZdwlIpcOutputV2
name string
active bool
x, y int32
w, h int32
scalefactor uint32
gotFrame bool
}
dwlOutputs := make(map[uint32]*dwlOutputState)
for name, out := range outputs {
dwlOut, err := dwlManager.GetOutput(out)
if err != nil {
continue
}
state := &dwlOutputState{output: dwlOut, name: outputNames[name]}
dwlOutputs[name] = state
dwlOut.SetActiveHandler(func(e dwl_ipc.ZdwlIpcOutputV2ActiveEvent) {
state.active = e.Active != 0
})
dwlOut.SetXHandler(func(e dwl_ipc.ZdwlIpcOutputV2XEvent) {
state.x = e.X
})
dwlOut.SetYHandler(func(e dwl_ipc.ZdwlIpcOutputV2YEvent) {
state.y = e.Y
})
dwlOut.SetWidthHandler(func(e dwl_ipc.ZdwlIpcOutputV2WidthEvent) {
state.w = e.Width
})
dwlOut.SetHeightHandler(func(e dwl_ipc.ZdwlIpcOutputV2HeightEvent) {
state.h = e.Height
})
dwlOut.SetScalefactorHandler(func(e dwl_ipc.ZdwlIpcOutputV2ScalefactorEvent) {
state.scalefactor = e.Scalefactor
})
dwlOut.SetFrameHandler(func(e dwl_ipc.ZdwlIpcOutputV2FrameEvent) {
state.gotFrame = true
})
}
allFramesReceived := func() bool {
for _, s := range dwlOutputs {
if !s.gotFrame {
return false
}
}
return true
}
for !allFramesReceived() {
if err := ctx.Dispatch(); err != nil {
return nil, fmt.Errorf("dispatch: %w", err)
}
}
for _, state := range dwlOutputs {
if !state.active {
continue
}
if state.w <= 0 || state.h <= 0 {
return nil, fmt.Errorf("no active window")
}
scale := float64(state.scalefactor) / 100.0
if scale <= 0 {
scale = 1.0
}
return &WindowGeometry{
X: state.x,
Y: state.y,
Width: state.w,
Height: state.h,
Output: state.name,
Scale: scale,
}, nil
}
return nil, fmt.Errorf("no active output found")
}
+17 -13
View File
@@ -251,9 +251,10 @@ func (r *RegionSelector) handleGlobal(e client.RegistryGlobalEvent) {
if err := r.registry.Bind(e.Name, e.Interface, version, output); err == nil { if err := r.registry.Bind(e.Name, e.Interface, version, output); err == nil {
r.outputsMu.Lock() r.outputsMu.Lock()
r.outputs[e.Name] = &WaylandOutput{ r.outputs[e.Name] = &WaylandOutput{
wlOutput: output, wlOutput: output,
globalName: e.Name, globalName: e.Name,
scale: 1, scale: 1,
fractionalScale: 1.0,
} }
r.outputsMu.Unlock() r.outputsMu.Unlock()
r.setupOutputHandlers(e.Name, output) r.setupOutputHandlers(e.Name, output)
@@ -320,6 +321,7 @@ func (r *RegionSelector) setupOutputHandlers(name uint32, output *client.Output)
r.outputsMu.Lock() r.outputsMu.Lock()
if o, ok := r.outputs[name]; ok { if o, ok := r.outputs[name]; ok {
o.scale = e.Factor o.scale = e.Factor
o.fractionalScale = float64(e.Factor)
} }
r.outputsMu.Unlock() r.outputsMu.Unlock()
}) })
@@ -607,6 +609,10 @@ func (r *RegionSelector) captureForSurface(os *OutputSurface) {
os.screenFormat = pc.format os.screenFormat = pc.format
os.yInverted = pc.yInverted os.yInverted = pc.yInverted
if os.logicalW > 0 && os.screenBuf != nil {
os.output.fractionalScale = float64(os.screenBuf.Width) / float64(os.logicalW)
}
r.initRenderBuffer(os) r.initRenderBuffer(os)
r.applyPreSelection(os) r.applyPreSelection(os)
r.redrawSurface(os) r.redrawSurface(os)
@@ -713,19 +719,17 @@ func (r *RegionSelector) redrawSurface(os *OutputSurface) {
// Draw overlay (dimming + selection) into this slot // Draw overlay (dimming + selection) into this slot
r.drawOverlay(os, slot.shm) r.drawOverlay(os, slot.shm)
// Attach and commit (viewport only needs to be set once, but it's cheap)
scale := os.output.scale
if scale <= 0 {
scale = 1
}
if os.viewport != nil { if os.viewport != nil {
srcW := float64(slot.shm.Width) / float64(scale) _ = os.wlSurface.SetBufferScale(1)
srcH := float64(slot.shm.Height) / float64(scale) _ = os.viewport.SetSource(0, 0, float64(slot.shm.Width), float64(slot.shm.Height))
_ = os.viewport.SetSource(0, 0, srcW, srcH)
_ = os.viewport.SetDestination(int32(os.logicalW), int32(os.logicalH)) _ = os.viewport.SetDestination(int32(os.logicalW), int32(os.logicalH))
} else {
bufferScale := os.output.scale
if bufferScale <= 0 {
bufferScale = 1
}
_ = os.wlSurface.SetBufferScale(bufferScale)
} }
_ = os.wlSurface.SetBufferScale(scale)
_ = os.wlSurface.Attach(slot.wlBuf, 0, 0) _ = os.wlSurface.Attach(slot.wlBuf, 0, 0)
_ = os.wlSurface.Damage(0, 0, int32(os.logicalW), int32(os.logicalH)) _ = os.wlSurface.Damage(0, 0, int32(os.logicalW), int32(os.logicalH))
+12 -5
View File
@@ -223,16 +223,23 @@ func (r *RegionSelector) finishSelection() {
dstData := cropped.Data() dstData := cropped.Data()
for y := 0; y < h; y++ { for y := 0; y < h; y++ {
srcY := by1 + y srcY := by1 + y
if srcY >= srcBuf.Height { if os.yInverted {
break srcY = srcBuf.Height - 1 - (by1 + y)
}
if srcY < 0 || srcY >= srcBuf.Height {
continue
}
dstY := y
if os.yInverted {
dstY = h - 1 - y
} }
for x := 0; x < w; x++ { for x := 0; x < w; x++ {
srcX := bx1 + x srcX := bx1 + x
if srcX >= srcBuf.Width { if srcX < 0 || srcX >= srcBuf.Width {
break continue
} }
si := srcY*srcBuf.Stride + srcX*4 si := srcY*srcBuf.Stride + srcX*4
di := y*cropped.Stride + x*4 di := dstY*cropped.Stride + x*4
if si+3 < len(srcData) && di+3 < len(dstData) { if si+3 < len(srcData) && di+3 < len(dstData) {
dstData[di+0] = srcData[si+0] dstData[di+0] = srcData[si+0]
dstData[di+1] = srcData[si+1] dstData[di+1] = srcData[si+1]
+393 -67
View File
@@ -11,14 +11,15 @@ import (
) )
type WaylandOutput struct { type WaylandOutput struct {
wlOutput *client.Output wlOutput *client.Output
globalName uint32 globalName uint32
name string name string
x, y int32 x, y int32
width int32 width int32
height int32 height int32
scale int32 scale int32
transform int32 fractionalScale float64
transform int32
} }
type CaptureResult struct { type CaptureResult struct {
@@ -134,12 +135,138 @@ func (s *Screenshoter) captureWindow() (*CaptureResult, error) {
Height: geom.Height, Height: geom.Height,
} }
output := s.findOutputForRegion(region) var output *WaylandOutput
if geom.Output != "" {
output = s.findOutputByName(geom.Output)
}
if output == nil {
output = s.findOutputForRegion(region)
}
if output == nil { if output == nil {
return nil, fmt.Errorf("could not find output for window") return nil, fmt.Errorf("could not find output for window")
} }
return s.captureRegionOnOutput(output, region) switch DetectCompositor() {
case CompositorHyprland:
return s.captureAndCrop(output, region)
case CompositorDWL:
return s.captureDWLWindow(output, region, geom.Scale)
default:
return s.captureRegionOnOutput(output, region)
}
}
func (s *Screenshoter) captureDWLWindow(output *WaylandOutput, region Region, dwlScale float64) (*CaptureResult, error) {
result, err := s.captureWholeOutput(output)
if err != nil {
return nil, err
}
scale := dwlScale
if scale <= 0 {
scale = float64(result.Buffer.Width) / float64(output.width)
}
if scale <= 0 {
scale = 1.0
}
localX := int(float64(region.X) * scale)
localY := int(float64(region.Y) * scale)
if localX >= result.Buffer.Width {
localX = localX % result.Buffer.Width
}
if localY >= result.Buffer.Height {
localY = localY % result.Buffer.Height
}
w := int(float64(region.Width) * scale)
h := int(float64(region.Height) * scale)
if localY+h > result.Buffer.Height && h <= result.Buffer.Height {
localY = result.Buffer.Height - h
if localY < 0 {
localY = 0
}
}
if localX+w > result.Buffer.Width && w <= result.Buffer.Width {
localX = result.Buffer.Width - w
if localX < 0 {
localX = 0
}
}
if localX < 0 {
w += localX
localX = 0
}
if localY < 0 {
h += localY
localY = 0
}
if localX+w > result.Buffer.Width {
w = result.Buffer.Width - localX
}
if localY+h > result.Buffer.Height {
h = result.Buffer.Height - localY
}
if w <= 0 || h <= 0 {
result.Buffer.Close()
return nil, fmt.Errorf("window not visible on output")
}
cropped, err := CreateShmBuffer(w, h, w*4)
if err != nil {
result.Buffer.Close()
return nil, fmt.Errorf("create crop buffer: %w", err)
}
srcData := result.Buffer.Data()
dstData := cropped.Data()
for y := 0; y < h; y++ {
srcY := localY + y
if result.YInverted {
srcY = result.Buffer.Height - 1 - (localY + y)
}
if srcY < 0 || srcY >= result.Buffer.Height {
continue
}
dstY := y
if result.YInverted {
dstY = h - 1 - y
}
for x := 0; x < w; x++ {
srcX := localX + x
if srcX < 0 || srcX >= result.Buffer.Width {
continue
}
si := srcY*result.Buffer.Stride + srcX*4
di := dstY*cropped.Stride + x*4
if si+3 >= len(srcData) || di+3 >= len(dstData) {
continue
}
dstData[di+0] = srcData[si+0]
dstData[di+1] = srcData[si+1]
dstData[di+2] = srcData[si+2]
dstData[di+3] = srcData[si+3]
}
}
result.Buffer.Close()
cropped.Format = PixelFormat(result.Format)
return &CaptureResult{
Buffer: cropped,
Region: region,
YInverted: false,
Format: result.Format,
}, nil
} }
func (s *Screenshoter) captureFullScreen() (*CaptureResult, error) { func (s *Screenshoter) captureFullScreen() (*CaptureResult, error) {
@@ -181,33 +308,8 @@ func (s *Screenshoter) captureOutput(name string) (*CaptureResult, error) {
func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) { func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) {
s.outputsMu.Lock() s.outputsMu.Lock()
outputs := make([]*WaylandOutput, 0, len(s.outputs)) outputs := make([]*WaylandOutput, 0, len(s.outputs))
var minX, minY, maxX, maxY int32
first := true
for _, o := range s.outputs { for _, o := range s.outputs {
outputs = append(outputs, o) outputs = append(outputs, o)
right := o.x + o.width
bottom := o.y + o.height
if first {
minX, minY = o.x, o.y
maxX, maxY = right, bottom
first = false
continue
}
if o.x < minX {
minX = o.x
}
if o.y < minY {
minY = o.y
}
if right > maxX {
maxX = right
}
if bottom > maxY {
maxY = bottom
}
} }
s.outputsMu.Unlock() s.outputsMu.Unlock()
@@ -219,18 +321,18 @@ func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) {
return s.captureWholeOutput(outputs[0]) return s.captureWholeOutput(outputs[0])
} }
totalW := maxX - minX // Capture all outputs first to get actual buffer sizes
totalH := maxY - minY type capturedOutput struct {
output *WaylandOutput
compositeStride := int(totalW) * 4 result *CaptureResult
composite, err := CreateShmBuffer(int(totalW), int(totalH), compositeStride) physX int
if err != nil { physY int
return nil, fmt.Errorf("create composite buffer: %w", err)
} }
captured := make([]capturedOutput, 0, len(outputs))
composite.Clear() var minX, minY, maxX, maxY int
first := true
var format uint32
for _, output := range outputs { for _, output := range outputs {
result, err := s.captureWholeOutput(output) result, err := s.captureWholeOutput(output)
if err != nil { if err != nil {
@@ -238,16 +340,88 @@ func (s *Screenshoter) captureAllScreens() (*CaptureResult, error) {
continue continue
} }
if format == 0 { outX, outY := output.x, output.y
format = result.Format scale := float64(output.scale)
if DetectCompositor() == CompositorHyprland {
if hx, hy, _, _, ok := GetHyprlandMonitorGeometry(output.name); ok {
outX, outY = hx, hy
}
if s := GetHyprlandMonitorScale(output.name); s > 0 {
scale = s
}
} }
s.blitBuffer(composite, result.Buffer, int(output.x-minX), int(output.y-minY), result.YInverted) if scale <= 0 {
result.Buffer.Close() scale = 1.0
}
physX := int(float64(outX) * scale)
physY := int(float64(outY) * scale)
captured = append(captured, capturedOutput{
output: output,
result: result,
physX: physX,
physY: physY,
})
right := physX + result.Buffer.Width
bottom := physY + result.Buffer.Height
if first {
minX, minY = physX, physY
maxX, maxY = right, bottom
first = false
continue
}
if physX < minX {
minX = physX
}
if physY < minY {
minY = physY
}
if right > maxX {
maxX = right
}
if bottom > maxY {
maxY = bottom
}
}
if len(captured) == 0 {
return nil, fmt.Errorf("failed to capture any outputs")
}
if len(captured) == 1 {
return captured[0].result, nil
}
totalW := maxX - minX
totalH := maxY - minY
compositeStride := totalW * 4
composite, err := CreateShmBuffer(totalW, totalH, compositeStride)
if err != nil {
for _, c := range captured {
c.result.Buffer.Close()
}
return nil, fmt.Errorf("create composite buffer: %w", err)
}
composite.Clear()
var format uint32
for _, c := range captured {
if format == 0 {
format = c.result.Format
}
s.blitBuffer(composite, c.result.Buffer, c.physX-minX, c.physY-minY, c.result.YInverted)
c.result.Buffer.Close()
} }
return &CaptureResult{ return &CaptureResult{
Buffer: composite, Buffer: composite,
Region: Region{X: minX, Y: minY, Width: totalW, Height: totalH}, Region: Region{X: int32(minX), Y: int32(minY), Width: int32(totalW), Height: int32(totalH)},
Format: format, Format: format,
}, nil }, nil
} }
@@ -311,21 +485,131 @@ func (s *Screenshoter) captureWholeOutput(output *WaylandOutput) (*CaptureResult
}) })
} }
func (s *Screenshoter) captureAndCrop(output *WaylandOutput, region Region) (*CaptureResult, error) {
result, err := s.captureWholeOutput(output)
if err != nil {
return nil, err
}
outX, outY := output.x, output.y
scale := float64(output.scale)
if hx, hy, _, _, ok := GetHyprlandMonitorGeometry(output.name); ok {
outX, outY = hx, hy
}
if s := GetHyprlandMonitorScale(output.name); s > 0 {
scale = s
}
if scale <= 0 {
scale = 1.0
}
localX := int(float64(region.X-outX) * scale)
localY := int(float64(region.Y-outY) * scale)
w := int(float64(region.Width) * scale)
h := int(float64(region.Height) * scale)
cropped, err := CreateShmBuffer(w, h, w*4)
if err != nil {
result.Buffer.Close()
return nil, fmt.Errorf("create crop buffer: %w", err)
}
srcData := result.Buffer.Data()
dstData := cropped.Data()
for y := 0; y < h; y++ {
srcY := localY + y
if result.YInverted {
srcY = result.Buffer.Height - 1 - (localY + y)
}
if srcY < 0 || srcY >= result.Buffer.Height {
continue
}
dstY := y
if result.YInverted {
dstY = h - 1 - y
}
for x := 0; x < w; x++ {
srcX := localX + x
if srcX < 0 || srcX >= result.Buffer.Width {
continue
}
si := srcY*result.Buffer.Stride + srcX*4
di := dstY*cropped.Stride + x*4
if si+3 >= len(srcData) || di+3 >= len(dstData) {
continue
}
dstData[di+0] = srcData[si+0]
dstData[di+1] = srcData[si+1]
dstData[di+2] = srcData[si+2]
dstData[di+3] = srcData[si+3]
}
}
result.Buffer.Close()
cropped.Format = PixelFormat(result.Format)
return &CaptureResult{
Buffer: cropped,
Region: region,
YInverted: false,
Format: result.Format,
}, nil
}
func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Region) (*CaptureResult, error) { func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Region) (*CaptureResult, error) {
localX := region.X - output.x scale := output.fractionalScale
localY := region.Y - output.y if scale <= 0 && DetectCompositor() == CompositorHyprland {
scale = GetHyprlandMonitorScale(output.name)
}
if scale <= 0 {
scale = float64(output.scale)
}
if scale <= 0 {
scale = 1.0
}
localX := int32(float64(region.X-output.x) * scale)
localY := int32(float64(region.Y-output.y) * scale)
w := int32(float64(region.Width) * scale)
h := int32(float64(region.Height) * scale)
if DetectCompositor() == CompositorDWL {
scaledOutW := int32(float64(output.width) * scale)
scaledOutH := int32(float64(output.height) * scale)
if localX >= scaledOutW {
localX = localX % scaledOutW
}
if localY >= scaledOutH {
localY = localY % scaledOutH
}
if localX+w > scaledOutW {
w = scaledOutW - localX
}
if localY+h > scaledOutH {
h = scaledOutH - localY
}
if localX < 0 {
w += localX
localX = 0
}
if localY < 0 {
h += localY
localY = 0
}
}
cursor := int32(0) cursor := int32(0)
if s.config.IncludeCursor { if s.config.IncludeCursor {
cursor = 1 cursor = 1
} }
frame, err := s.screencopy.CaptureOutputRegion( frame, err := s.screencopy.CaptureOutputRegion(cursor, output.wlOutput, localX, localY, w, h)
cursor,
output.wlOutput,
localX, localY,
region.Width, region.Height,
)
if err != nil { if err != nil {
return nil, fmt.Errorf("capture region: %w", err) return nil, fmt.Errorf("capture region: %w", err)
} }
@@ -335,6 +619,8 @@ func (s *Screenshoter) captureRegionOnOutput(output *WaylandOutput, region Regio
func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1, region Region) (*CaptureResult, error) { func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1, region Region) (*CaptureResult, error) {
var buf *ShmBuffer var buf *ShmBuffer
var pool *client.ShmPool
var wlBuf *client.Buffer
var format PixelFormat var format PixelFormat
var yInverted bool var yInverted bool
ready := false ready := false
@@ -360,15 +646,17 @@ func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1,
return return
} }
pool, err := s.shm.CreatePool(buf.Fd(), int32(buf.Size())) var err error
pool, err = s.shm.CreatePool(buf.Fd(), int32(buf.Size()))
if err != nil { if err != nil {
log.Error("failed to create pool", "err", err) log.Error("failed to create pool", "err", err)
return return
} }
wlBuf, err := pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), uint32(format)) wlBuf, err = pool.CreateBuffer(0, int32(buf.Width), int32(buf.Height), int32(buf.Stride), uint32(format))
if err != nil { if err != nil {
pool.Destroy() pool.Destroy()
pool = nil
log.Error("failed to create wl_buffer", "err", err) log.Error("failed to create wl_buffer", "err", err)
return return
} }
@@ -376,8 +664,6 @@ func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1,
if err := frame.Copy(wlBuf); err != nil { if err := frame.Copy(wlBuf); err != nil {
log.Error("failed to copy frame", "err", err) log.Error("failed to copy frame", "err", err)
} }
pool.Destroy()
}) })
frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) { frame.SetReadyHandler(func(e wlr_screencopy.ZwlrScreencopyFrameV1ReadyEvent) {
@@ -396,6 +682,12 @@ func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1,
} }
frame.Destroy() frame.Destroy()
if wlBuf != nil {
wlBuf.Destroy()
}
if pool != nil {
pool.Destroy()
}
if failed { if failed {
if buf != nil { if buf != nil {
@@ -412,6 +704,17 @@ func (s *Screenshoter) processFrame(frame *wlr_screencopy.ZwlrScreencopyFrameV1,
}, nil }, nil
} }
func (s *Screenshoter) findOutputByName(name string) *WaylandOutput {
s.outputsMu.Lock()
defer s.outputsMu.Unlock()
for _, o := range s.outputs {
if o.name == name {
return o
}
}
return nil
}
func (s *Screenshoter) findOutputForRegion(region Region) *WaylandOutput { func (s *Screenshoter) findOutputForRegion(region Region) *WaylandOutput {
s.outputsMu.Lock() s.outputsMu.Lock()
defer s.outputsMu.Unlock() defer s.outputsMu.Unlock()
@@ -420,14 +723,26 @@ func (s *Screenshoter) findOutputForRegion(region Region) *WaylandOutput {
cy := region.Y + region.Height/2 cy := region.Y + region.Height/2
for _, o := range s.outputs { for _, o := range s.outputs {
if cx >= o.x && cx < o.x+o.width && cy >= o.y && cy < o.y+o.height { x, y, w, h := o.x, o.y, o.width, o.height
if DetectCompositor() == CompositorHyprland {
if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok {
x, y, w, h = hx, hy, hw, hh
}
}
if cx >= x && cx < x+w && cy >= y && cy < y+h {
return o return o
} }
} }
for _, o := range s.outputs { for _, o := range s.outputs {
if region.X >= o.x && region.X < o.x+o.width && x, y, w, h := o.x, o.y, o.width, o.height
region.Y >= o.y && region.Y < o.y+o.height { if DetectCompositor() == CompositorHyprland {
if hx, hy, hw, hh, ok := GetHyprlandMonitorGeometry(o.name); ok {
x, y, w, h = hx, hy, hw, hh
}
}
if region.X >= x && region.X < x+w &&
region.Y >= y && region.Y < y+h {
return o return o
} }
} }
@@ -436,6 +751,15 @@ func (s *Screenshoter) findOutputForRegion(region Region) *WaylandOutput {
} }
func (s *Screenshoter) findFocusedOutput() *WaylandOutput { func (s *Screenshoter) findFocusedOutput() *WaylandOutput {
if mon := GetFocusedMonitor(); mon != "" {
s.outputsMu.Lock()
defer s.outputsMu.Unlock()
for _, o := range s.outputs {
if o.name == mon {
return o
}
}
}
s.outputsMu.Lock() s.outputsMu.Lock()
defer s.outputsMu.Unlock() defer s.outputsMu.Unlock()
for _, o := range s.outputs { for _, o := range s.outputs {
@@ -501,9 +825,10 @@ func (s *Screenshoter) handleGlobal(e client.RegistryGlobalEvent) {
if err := s.registry.Bind(e.Name, e.Interface, version, output); err == nil { if err := s.registry.Bind(e.Name, e.Interface, version, output); err == nil {
s.outputsMu.Lock() s.outputsMu.Lock()
s.outputs[e.Name] = &WaylandOutput{ s.outputs[e.Name] = &WaylandOutput{
wlOutput: output, wlOutput: output,
globalName: e.Name, globalName: e.Name,
scale: 1, scale: 1,
fractionalScale: 1.0,
} }
s.outputsMu.Unlock() s.outputsMu.Unlock()
s.setupOutputHandlers(e.Name, output) s.setupOutputHandlers(e.Name, output)
@@ -546,6 +871,7 @@ func (s *Screenshoter) setupOutputHandlers(name uint32, output *client.Output) {
s.outputsMu.Lock() s.outputsMu.Lock()
if o, ok := s.outputs[name]; ok { if o, ok := s.outputs[name]; ok {
o.scale = e.Factor o.scale = e.Factor
o.fractionalScale = float64(e.Factor)
} }
s.outputsMu.Unlock() s.outputsMu.Unlock()
}) })
+91
View File
@@ -0,0 +1,91 @@
package server
import (
"context"
"net"
"time"
"github.com/AvengeMedia/DankMaterialShell/core/internal/matugen"
"github.com/AvengeMedia/DankMaterialShell/core/internal/server/models"
)
type MatugenQueueResult struct {
Success bool `json:"success"`
Message string `json:"message,omitempty"`
}
func handleMatugenQueue(conn net.Conn, req models.Request) {
getString := func(key string) string {
if v, ok := req.Params[key].(string); ok {
return v
}
return ""
}
getBool := func(key string, def bool) bool {
if v, ok := req.Params[key].(bool); ok {
return v
}
return def
}
opts := matugen.Options{
StateDir: getString("stateDir"),
ShellDir: getString("shellDir"),
ConfigDir: getString("configDir"),
Kind: getString("kind"),
Value: getString("value"),
Mode: getString("mode"),
IconTheme: getString("iconTheme"),
MatugenType: getString("matugenType"),
RunUserTemplates: getBool("runUserTemplates", true),
StockColors: getString("stockColors"),
SyncModeWithPortal: getBool("syncModeWithPortal", false),
TerminalsAlwaysDark: getBool("terminalsAlwaysDark", false),
}
wait := getBool("wait", true)
queue := matugen.GetQueue()
resultCh := queue.Submit(opts)
if !wait {
models.Respond(conn, req.ID, MatugenQueueResult{
Success: true,
Message: "queued",
})
return
}
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
defer cancel()
select {
case result := <-resultCh:
if result.Error != nil {
if result.Error == context.Canceled {
models.Respond(conn, req.ID, MatugenQueueResult{
Success: false,
Message: "cancelled",
})
return
}
models.RespondError(conn, req.ID, result.Error.Error())
return
}
models.Respond(conn, req.ID, MatugenQueueResult{
Success: true,
Message: "completed",
})
case <-ctx.Done():
models.RespondError(conn, req.ID, "timeout waiting for theme generation")
}
}
func handleMatugenStatus(conn net.Conn, req models.Request) {
queue := matugen.GetQueue()
models.Respond(conn, req.ID, map[string]bool{
"running": queue.IsRunning(),
"pending": queue.HasPending(),
})
}
+4
View File
@@ -215,6 +215,10 @@ func RouteRequest(conn net.Conn, req models.Request) {
models.Respond(conn, req.ID, info) models.Respond(conn, req.ID, info)
case "subscribe": case "subscribe":
handleSubscribe(conn, req) handleSubscribe(conn, req)
case "matugen.queue":
handleMatugenQueue(conn, req)
case "matugen.status":
handleMatugenStatus(conn, req)
default: default:
models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method)) models.RespondError(conn, req.ID, fmt.Sprintf("unknown method: %s", req.Method))
} }
+28 -27
View File
@@ -113,7 +113,7 @@ func (i *Display) GetRegistry() (*Registry, error) {
} }
func (i *Display) Destroy() error { func (i *Display) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -224,15 +224,16 @@ func (i *Display) Dispatch(opcode uint32, fd int, data []byte) {
i.errorHandler(e) i.errorHandler(e)
case 1: case 1:
if i.deleteIdHandler == nil {
return
}
var e DisplayDeleteIdEvent var e DisplayDeleteIdEvent
l := 0 l := 0
e.Id = Uint32(data[l : l+4]) e.Id = Uint32(data[l : l+4])
l += 4 l += 4
i.deleteIdHandler(e) i.Context().DeleteID(e.Id)
if i.deleteIdHandler != nil {
i.deleteIdHandler(e)
}
} }
} }
@@ -326,7 +327,7 @@ func (i *Registry) Bind(name uint32, iface string, version uint32, id Proxy) err
} }
func (i *Registry) Destroy() error { func (i *Registry) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -433,7 +434,7 @@ func NewCallback(ctx *Context) *Callback {
} }
func (i *Callback) Destroy() error { func (i *Callback) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -529,7 +530,7 @@ func (i *Compositor) CreateRegion() (*Region, error) {
} }
func (i *Compositor) Destroy() error { func (i *Compositor) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -619,7 +620,7 @@ func (i *ShmPool) CreateBuffer(offset, width, height, stride int32, format uint3
// buffers that have been created from this pool // buffers that have been created from this pool
// are gone. // are gone.
func (i *ShmPool) Destroy() error { func (i *ShmPool) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -735,7 +736,7 @@ func (i *Shm) CreatePool(fd int, size int32) (*ShmPool, error) {
// //
// Objects created via this interface remain unaffected. // Objects created via this interface remain unaffected.
func (i *Shm) Release() error { func (i *Shm) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -1642,7 +1643,7 @@ func NewBuffer(ctx *Context) *Buffer {
// //
// For possible side-effects to a surface, see wl_surface.attach. // For possible side-effects to a surface, see wl_surface.attach.
func (i *Buffer) Destroy() error { func (i *Buffer) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -1803,7 +1804,7 @@ func (i *DataOffer) Receive(mimeType string, fd int) error {
// //
// Destroy the data offer. // Destroy the data offer.
func (i *DataOffer) Destroy() error { func (i *DataOffer) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 2 const opcode = 2
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -2120,7 +2121,7 @@ func (i *DataSource) Offer(mimeType string) error {
// //
// Destroy the data source. // Destroy the data source.
func (i *DataSource) Destroy() error { func (i *DataSource) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -2540,7 +2541,7 @@ func (i *DataDevice) SetSelection(source *DataSource, serial uint32) error {
// //
// This request destroys the data device. // This request destroys the data device.
func (i *DataDevice) Release() error { func (i *DataDevice) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 2 const opcode = 2
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -2859,7 +2860,7 @@ func (i *DataDeviceManager) GetDataDevice(seat *Seat) (*DataDevice, error) {
} }
func (i *DataDeviceManager) Destroy() error { func (i *DataDeviceManager) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -3000,7 +3001,7 @@ func (i *Shell) GetShellSurface(surface *Surface) (*ShellSurface, error) {
} }
func (i *Shell) Destroy() error { func (i *Shell) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -3421,7 +3422,7 @@ func (i *ShellSurface) SetClass(class string) error {
} }
func (i *ShellSurface) Destroy() error { func (i *ShellSurface) Destroy() error {
i.Context().Unregister(i) i.MarkZombie()
return nil return nil
} }
@@ -3798,7 +3799,7 @@ func NewSurface(ctx *Context) *Surface {
// //
// Deletes the surface and invalidates its object ID. // Deletes the surface and invalidates its object ID.
func (i *Surface) Destroy() error { func (i *Surface) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -4618,7 +4619,7 @@ func (i *Seat) GetTouch() (*Touch, error) {
// Using this request a client can tell the server that it is not going to // Using this request a client can tell the server that it is not going to
// use the seat object anymore. // use the seat object anymore.
func (i *Seat) Release() error { func (i *Seat) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 3 const opcode = 3
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -4920,7 +4921,7 @@ func (i *Pointer) SetCursor(serial uint32, surface *Surface, hotspotX, hotspotY
// This request destroys the pointer proxy object, so clients must not call // This request destroys the pointer proxy object, so clients must not call
// wl_pointer_destroy() after using this request. // wl_pointer_destroy() after using this request.
func (i *Pointer) Release() error { func (i *Pointer) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 1 const opcode = 1
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -5685,7 +5686,7 @@ func NewKeyboard(ctx *Context) *Keyboard {
// Release : release the keyboard object // Release : release the keyboard object
func (i *Keyboard) Release() error { func (i *Keyboard) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -6091,7 +6092,7 @@ func NewTouch(ctx *Context) *Touch {
// Release : release the touch object // Release : release the touch object
func (i *Touch) Release() error { func (i *Touch) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -6406,7 +6407,7 @@ func NewOutput(ctx *Context) *Output {
// Using this request a client can tell the server that it is not going to // Using this request a client can tell the server that it is not going to
// use the output object anymore. // use the output object anymore.
func (i *Output) Release() error { func (i *Output) Release() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -6923,7 +6924,7 @@ func NewRegion(ctx *Context) *Region {
// //
// Destroy the region. This will invalidate the object ID. // Destroy the region. This will invalidate the object ID.
func (i *Region) Destroy() error { func (i *Region) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -7057,7 +7058,7 @@ func NewSubcompositor(ctx *Context) *Subcompositor {
// protocol object anymore. This does not affect any other // protocol object anymore. This does not affect any other
// objects, wl_subsurface objects included. // objects, wl_subsurface objects included.
func (i *Subcompositor) Destroy() error { func (i *Subcompositor) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -7280,7 +7281,7 @@ func NewSubsurface(ctx *Context) *Subsurface {
// wl_subcompositor.get_subsurface request. The wl_surface's association // wl_subcompositor.get_subsurface request. The wl_surface's association
// to the parent is deleted. The wl_surface is unmapped immediately. // to the parent is deleted. The wl_surface is unmapped immediately.
func (i *Subsurface) Destroy() error { func (i *Subsurface) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
@@ -7499,7 +7500,7 @@ func NewFixes(ctx *Context) *Fixes {
// Destroy : destroys this object // Destroy : destroys this object
func (i *Fixes) Destroy() error { func (i *Fixes) Destroy() error {
defer i.Context().Unregister(i) defer i.MarkZombie()
const opcode = 0 const opcode = 0
const _reqBufLen = 8 const _reqBufLen = 8
var _reqBuf [_reqBufLen]byte var _reqBuf [_reqBufLen]byte
+15 -2
View File
@@ -1,5 +1,7 @@
package client package client
import "sync/atomic"
type Dispatcher interface { type Dispatcher interface {
Dispatch(opcode uint32, fd int, data []byte) Dispatch(opcode uint32, fd int, data []byte)
} }
@@ -9,11 +11,14 @@ type Proxy interface {
SetContext(ctx *Context) SetContext(ctx *Context)
ID() uint32 ID() uint32
SetID(id uint32) SetID(id uint32)
IsZombie() bool
MarkZombie()
} }
type BaseProxy struct { type BaseProxy struct {
ctx *Context ctx *Context
id uint32 id uint32
zombie atomic.Bool
} }
func (p *BaseProxy) ID() uint32 { func (p *BaseProxy) ID() uint32 {
@@ -31,3 +36,11 @@ func (p *BaseProxy) Context() *Context {
func (p *BaseProxy) SetContext(ctx *Context) { func (p *BaseProxy) SetContext(ctx *Context) {
p.ctx = ctx p.ctx = ctx
} }
func (p *BaseProxy) IsZombie() bool {
return p.zombie.Load()
}
func (p *BaseProxy) MarkZombie() {
p.zombie.Store(true)
}
@@ -32,6 +32,10 @@ func (ctx *Context) Unregister(p Proxy) {
ctx.objects.Delete(p.ID()) ctx.objects.Delete(p.ID())
} }
func (ctx *Context) DeleteID(id uint32) {
ctx.objects.Delete(id)
}
func (ctx *Context) GetProxy(id uint32) Proxy { func (ctx *Context) GetProxy(id uint32) Proxy {
if val, ok := ctx.objects.Load(id); ok { if val, ok := ctx.objects.Load(id); ok {
return val return val
@@ -72,7 +76,11 @@ func (ctx *Context) GetDispatch() func() error {
return func() error { return func() error {
proxy, ok := ctx.objects.Load(senderID) proxy, ok := ctx.objects.Load(senderID)
if !ok { if !ok {
return fmt.Errorf("%w (senderID=%d)", ErrDispatchSenderNotFound, senderID) return nil // Proxy already deleted via delete_id, silently ignore
}
if proxy.IsZombie() {
return nil // Zombie proxy, discard late events
} }
sender, ok := proxy.(Dispatcher) sender, ok := proxy.(Dispatcher)
+3 -1
View File
@@ -61,7 +61,9 @@ in {
''; '';
}; };
quickshell = { quickshell = {
package = lib.mkPackageOption pkgs "quickshell" {}; package = lib.mkPackageOption dmsPkgs "quickshell" {
extraDescription = "The quickshell package to use (defaults to be built from source, in the commit 26531f due to unreleased features used by DMS).";
};
}; };
logs.save = lib.mkEnableOption "saving logs from DMS greeter to file"; logs.save = lib.mkEnableOption "saving logs from DMS greeter to file";
logs.path = lib.mkOption { logs.path = lib.mkOption {
+2 -2
View File
@@ -4,13 +4,13 @@
lib, lib,
dmsPkgs, dmsPkgs,
... ...
}: let } @ args: let
cfg = config.programs.dankMaterialShell; cfg = config.programs.dankMaterialShell;
jsonFormat = pkgs.formats.json {}; jsonFormat = pkgs.formats.json {};
common = import ./common.nix {inherit config pkgs lib dmsPkgs;}; common = import ./common.nix {inherit config pkgs lib dmsPkgs;};
in { in {
imports = [ imports = [
./options.nix (import ./options.nix args)
(lib.mkRemovedOptionModule ["programs" "dankMaterialShell" "enableNightMode"] "Night mode is now always available.") (lib.mkRemovedOptionModule ["programs" "dankMaterialShell" "enableNightMode"] "Night mode is now always available.")
(lib.mkRenamedOptionModule ["programs" "dankMaterialShell" "enableSystemd"] ["programs" "dankMaterialShell" "systemd" "enable"]) (lib.mkRenamedOptionModule ["programs" "dankMaterialShell" "enableSystemd"] ["programs" "dankMaterialShell" "systemd" "enable"])
]; ];
+2 -2
View File
@@ -4,12 +4,12 @@
lib, lib,
dmsPkgs, dmsPkgs,
... ...
}: let } @ args: let
cfg = config.programs.dankMaterialShell; cfg = config.programs.dankMaterialShell;
common = import ./common.nix {inherit config pkgs lib dmsPkgs;}; common = import ./common.nix {inherit config pkgs lib dmsPkgs;};
in { in {
imports = [ imports = [
./options.nix (import ./options.nix args)
]; ];
config = lib.mkIf cfg.enable config = lib.mkIf cfg.enable
+4 -2
View File
@@ -1,6 +1,6 @@
{ {
pkgs,
lib, lib,
dmsPkgs,
... ...
}: let }: let
inherit (lib) types; inherit (lib) types;
@@ -62,7 +62,9 @@ in {
description = "Add needed dependencies to have system sound support"; description = "Add needed dependencies to have system sound support";
}; };
quickshell = { quickshell = {
package = lib.mkPackageOption pkgs "quickshell" {}; package = lib.mkPackageOption dmsPkgs "quickshell" {
extraDescription = "The quickshell package to use (defaults to be built from source, in the commit 26531f due to unreleased features used by DMS).";
};
}; };
}; };
} }
Generated
+29 -7
View File
@@ -7,11 +7,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1762435535, "lastModified": 1762835999,
"narHash": "sha256-QhzRn7pYN35IFpKjjxJAj3GPJECuC+VLhoGem3ezycc=", "narHash": "sha256-UykYGrGFOFTmDpKTLNxj1wvd1gbDG4TkqLNSbV0TYwk=",
"owner": "AvengeMedia", "owner": "AvengeMedia",
"repo": "dgop", "repo": "dgop",
"rev": "6cf638dde818f9f8a2e26d0243179c43cb3458d7", "rev": "799301991cd5dcea9b64245f9d500dcc76615653",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -22,11 +22,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1762363567, "lastModified": 1764950072,
"narHash": "sha256-YRqMDEtSMbitIMj+JLpheSz0pwEr0Rmy5mC7myl17xs=", "narHash": "sha256-BmPWzogsG2GsXZtlT+MTcAWeDK5hkbGRZTeZNW42fwA=",
"owner": "nixos", "owner": "nixos",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "ae814fd3904b621d8ab97418f1d0f2eb0d3716f4", "rev": "f61125a668a320878494449750330ca58b78c557",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -36,10 +36,32 @@
"type": "github" "type": "github"
} }
}, },
"quickshell": {
"inputs": {
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1764663772,
"narHash": "sha256-sHqLmm0wAt3PC4vczJeBozI1/f4rv9yp3IjkClHDXDs=",
"ref": "refs/heads/master",
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460",
"revCount": 713,
"type": "git",
"url": "https://git.outfoxxed.me/quickshell/quickshell"
},
"original": {
"rev": "26531fc46ef17e9365b03770edd3fb9206fcb460",
"type": "git",
"url": "https://git.outfoxxed.me/quickshell/quickshell"
}
},
"root": { "root": {
"inputs": { "inputs": {
"dgop": "dgop", "dgop": "dgop",
"nixpkgs": "nixpkgs" "nixpkgs": "nixpkgs",
"quickshell": "quickshell"
} }
} }
}, },
+90 -49
View File
@@ -7,12 +7,17 @@
url = "github:AvengeMedia/dgop"; url = "github:AvengeMedia/dgop";
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
quickshell = {
url = "git+https://git.outfoxxed.me/quickshell/quickshell?rev=26531fc46ef17e9365b03770edd3fb9206fcb460";
inputs.nixpkgs.follows = "nixpkgs";
};
}; };
outputs = { outputs = {
self, self,
nixpkgs, nixpkgs,
dgop, dgop,
quickshell,
... ...
}: let }: let
forEachSystem = fn: forEachSystem = fn:
@@ -22,6 +27,7 @@
buildDmsPkgs = pkgs: { buildDmsPkgs = pkgs: {
dms-shell = self.packages.${pkgs.stdenv.hostPlatform.system}.default; dms-shell = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
dgop = dgop.packages.${pkgs.stdenv.hostPlatform.system}.dgop; dgop = dgop.packages.${pkgs.stdenv.hostPlatform.system}.dgop;
quickshell = quickshell.packages.${pkgs.stdenv.hostPlatform.system}.default;
}; };
mkModuleWithDmsPkgs = path: args @ {pkgs, ...}: { mkModuleWithDmsPkgs = path: args @ {pkgs, ...}: {
imports = [ imports = [
@@ -46,67 +52,69 @@
+ "_" + "_"
+ (self.shortRev or "dirty"); + (self.shortRev or "dirty");
in { in {
dms-shell = pkgs.buildGoModule (let dms-shell = pkgs.buildGoModule (
rootSrc = ./.; let
in { rootSrc = ./.;
inherit version; in {
pname = "dms-shell"; inherit version;
src = ./core; pname = "dms-shell";
vendorHash = "sha256-2PCqiW4frxME8IlmwWH5ktznhd/G1bah5Ae4dp0HPTQ="; src = ./core;
vendorHash = "sha256-2PCqiW4frxME8IlmwWH5ktznhd/G1bah5Ae4dp0HPTQ=";
subPackages = ["cmd/dms"]; subPackages = ["cmd/dms"];
ldflags = [ ldflags = [
"-s" "-s"
"-w" "-w"
"-X main.Version=${version}" "-X main.Version=${version}"
]; ];
nativeBuildInputs = [ nativeBuildInputs = with pkgs; [
pkgs.installShellFiles installShellFiles
pkgs.makeWrapper makeWrapper
]; ];
postInstall = '' postInstall = ''
mkdir -p $out/share/quickshell/dms mkdir -p $out/share/quickshell/dms
cp -r ${rootSrc}/quickshell/. $out/share/quickshell/dms/ cp -r ${rootSrc}/quickshell/. $out/share/quickshell/dms/
chmod u+w $out/share/quickshell/dms/VERSION chmod u+w $out/share/quickshell/dms/VERSION
echo "${version}" > $out/share/quickshell/dms/VERSION echo "${version}" > $out/share/quickshell/dms/VERSION
# Install desktop file and icon # Install desktop file and icon
install -D ${rootSrc}/assets/dms-open.desktop \ install -D ${rootSrc}/assets/dms-open.desktop \
$out/share/applications/dms-open.desktop $out/share/applications/dms-open.desktop
install -D ${rootSrc}/core/assets/danklogo.svg \ install -D ${rootSrc}/core/assets/danklogo.svg \
$out/share/hicolor/scalable/apps/danklogo.svg $out/share/hicolor/scalable/apps/danklogo.svg
wrapProgram $out/bin/dms --add-flags "-c $out/share/quickshell/dms" wrapProgram $out/bin/dms --add-flags "-c $out/share/quickshell/dms"
install -Dm644 ${rootSrc}/assets/systemd/dms.service \ install -Dm644 ${rootSrc}/assets/systemd/dms.service \
$out/lib/systemd/user/dms.service $out/lib/systemd/user/dms.service
substituteInPlace $out/lib/systemd/user/dms.service \ substituteInPlace $out/lib/systemd/user/dms.service \
--replace-fail /usr/bin/dms $out/bin/dms \ --replace-fail /usr/bin/dms $out/bin/dms \
--replace-fail /usr/bin/pkill ${pkgs.procps}/bin/pkill --replace-fail /usr/bin/pkill ${pkgs.procps}/bin/pkill
substituteInPlace $out/share/quickshell/dms/Modules/Greetd/assets/dms-greeter \ substituteInPlace $out/share/quickshell/dms/Modules/Greetd/assets/dms-greeter \
--replace-fail /bin/bash ${pkgs.bashInteractive}/bin/bash --replace-fail /bin/bash ${pkgs.bashInteractive}/bin/bash
installShellCompletion --cmd dms \ installShellCompletion --cmd dms \
--bash <($out/bin/dms completion bash) \ --bash <($out/bin/dms completion bash) \
--fish <($out/bin/dms completion fish) \ --fish <($out/bin/dms completion fish) \
--zsh <($out/bin/dms completion zsh) --zsh <($out/bin/dms completion zsh)
''; '';
meta = { meta = {
description = "Desktop shell for wayland compositors built with Quickshell & GO"; description = "Desktop shell for wayland compositors built with Quickshell & GO";
homepage = "https://danklinux.com"; homepage = "https://danklinux.com";
changelog = "https://github.com/AvengeMedia/DankMaterialShell/releases/tag/v${version}"; changelog = "https://github.com/AvengeMedia/DankMaterialShell/releases/tag/v${version}";
license = pkgs.lib.licenses.mit; license = pkgs.lib.licenses.mit;
mainProgram = "dms"; mainProgram = "dms";
platforms = pkgs.lib.platforms.linux; platforms = pkgs.lib.platforms.linux;
}; };
}); }
);
default = self.packages.${system}.dms-shell; default = self.packages.${system}.dms-shell;
} }
@@ -119,5 +127,38 @@
nixosModules.dankMaterialShell = mkModuleWithDmsPkgs ./distro/nix/nixos.nix; nixosModules.dankMaterialShell = mkModuleWithDmsPkgs ./distro/nix/nixos.nix;
nixosModules.greeter = mkModuleWithDmsPkgs ./distro/nix/greeter.nix; nixosModules.greeter = mkModuleWithDmsPkgs ./distro/nix/greeter.nix;
devShells = forEachSystem (
system: pkgs: let
qmlPkgs =
[
quickshell.packages.${system}.default
]
++ (with pkgs.kdePackages; [
qtdeclarative
kirigami.unwrapped
sonnet
qtmultimedia
]);
in {
default = pkgs.mkShell {
buildInputs = with pkgs;
[
go_1_24
gopls
delve
go-tools
gnumake
]
++ qmlPkgs;
shellHook = ''
touch quickshell/.qmlls.ini 2>/dev/null
'';
QML2_IMPORT_PATH = pkgs.lib.concatStringsSep ":" (map (o: "${o}/lib/qt-6/qml") qmlPkgs);
};
}
);
}; };
} }
+58 -29
View File
@@ -197,14 +197,26 @@ const ACTION_ARGS = {
{ name: "focus", type: "bool", label: "Follow focus", default: false } { name: "focus", type: "bool", label: "Follow focus", default: false }
] ]
}, },
"move-column-to-workspace-down": {
args: [{ name: "focus", type: "bool", label: "Follow focus", default: false }]
},
"move-column-to-workspace-up": {
args: [{ name: "focus", type: "bool", label: "Follow focus", default: false }]
},
"screenshot": { "screenshot": {
args: [{ name: "opts", type: "screenshot", label: "Options" }] args: [{ name: "show-pointer", type: "bool", label: "Show pointer" }]
}, },
"screenshot-screen": { "screenshot-screen": {
args: [{ name: "opts", type: "screenshot", label: "Options" }] args: [
{ name: "show-pointer", type: "bool", label: "Show pointer" },
{ name: "write-to-disk", type: "bool", label: "Save to disk" }
]
}, },
"screenshot-window": { "screenshot-window": {
args: [{ name: "opts", type: "screenshot", label: "Options" }] args: [
{ name: "show-pointer", type: "bool", label: "Show pointer" },
{ name: "write-to-disk", type: "bool", label: "Save to disk" }
]
} }
}; };
@@ -288,11 +300,12 @@ function getActionLabel(action) {
if (!action) if (!action)
return ""; return "";
const dmsAct = findDmsAction(action); var dmsAct = findDmsAction(action);
if (dmsAct) if (dmsAct)
return dmsAct.label; return dmsAct.label;
const compAct = findCompositorAction(action); var base = action.split(" ")[0];
var compAct = findCompositorAction(base);
if (compAct) if (compAct)
return compAct.label; return compAct.label;
@@ -337,7 +350,8 @@ function isValidAction(action) {
function isKnownCompositorAction(action) { function isKnownCompositorAction(action) {
if (!action) if (!action)
return false; return false;
return findCompositorAction(action) !== null; var base = action.split(" ")[0];
return findCompositorAction(base) !== null;
} }
function buildSpawnAction(command, args) { function buildSpawnAction(command, args) {
@@ -404,10 +418,10 @@ function parseCompositorActionArgs(action) {
if (!ACTION_ARGS[base]) if (!ACTION_ARGS[base])
return { base: action, args: {} }; return { base: action, args: {} };
var argConfig = ACTION_ARGS[base];
var argParts = parts.slice(1); var argParts = parts.slice(1);
if (base === "move-column-to-workspace") { switch (base) {
case "move-column-to-workspace":
for (var i = 0; i < argParts.length; i++) { for (var i = 0; i < argParts.length; i++) {
if (argParts[i] === "focus=true" || argParts[i] === "focus=false") { if (argParts[i] === "focus=true" || argParts[i] === "focus=false") {
args.focus = argParts[i] === "focus=true"; args.focus = argParts[i] === "focus=true";
@@ -415,14 +429,24 @@ function parseCompositorActionArgs(action) {
args.index = argParts[i]; args.index = argParts[i];
} }
} }
} else if (base.startsWith("screenshot")) { break;
args.opts = {}; case "move-column-to-workspace-down":
for (var j = 0; j < argParts.length; j += 2) { case "move-column-to-workspace-up":
if (j + 1 < argParts.length) for (var k = 0; k < argParts.length; k++) {
args.opts[argParts[j]] = argParts[j + 1]; if (argParts[k] === "focus=true" || argParts[k] === "focus=false")
args.focus = argParts[k] === "focus=true";
}
break;
default:
if (base.startsWith("screenshot")) {
for (var j = 0; j < argParts.length; j++) {
var kv = argParts[j].split("=");
if (kv.length === 2)
args[kv[0]] = kv[1] === "true";
}
} else if (argParts.length > 0) {
args.value = argParts.join(" ");
} }
} else if (argParts.length > 0) {
args.value = argParts.join(" ");
} }
return { base: base, args: args }; return { base: base, args: args };
@@ -437,24 +461,29 @@ function buildCompositorAction(base, args) {
if (!args || Object.keys(args).length === 0) if (!args || Object.keys(args).length === 0)
return base; return base;
if (base === "move-column-to-workspace") { switch (base) {
case "move-column-to-workspace":
if (args.index) if (args.index)
parts.push(args.index); parts.push(args.index);
if (args.focus === true) if (args.focus === false)
parts.push("focus=true");
else if (args.focus === false)
parts.push("focus=false"); parts.push("focus=false");
} else if (base.startsWith("screenshot") && args.opts) { break;
for (var key in args.opts) { case "move-column-to-workspace-down":
if (args.opts[key] !== undefined && args.opts[key] !== "") { case "move-column-to-workspace-up":
parts.push(key); if (args.focus === false)
parts.push(args.opts[key]); parts.push("focus=false");
} break;
default:
if (base.startsWith("screenshot")) {
if (args["show-pointer"] === true)
parts.push("show-pointer=true");
if (args["write-to-disk"] === true)
parts.push("write-to-disk=true");
} else if (args.value) {
parts.push(args.value);
} else if (args.index) {
parts.push(args.index);
} }
} else if (args.value) {
parts.push(args.value);
} else if (args.index) {
parts.push(args.index);
} }
return parts.join(" "); return parts.join(" ");
+1 -1
View File
@@ -295,7 +295,7 @@ Singleton {
property bool lockScreenShowPowerActions: true property bool lockScreenShowPowerActions: true
property bool enableFprint: false property bool enableFprint: false
property int maxFprintTries: 3 property int maxFprintTries: 15
property bool fprintdAvailable: false property bool fprintdAvailable: false
property string lockScreenActiveMonitor: "all" property string lockScreenActiveMonitor: "all"
property string lockScreenInactiveColor: "#000000" property string lockScreenInactiveColor: "#000000"
+27 -10
View File
@@ -820,18 +820,35 @@ Singleton {
"runUserTemplates": (typeof SettingsData !== "undefined") ? SettingsData.runUserMatugenTemplates : true "runUserTemplates": (typeof SettingsData !== "undefined") ? SettingsData.runUserMatugenTemplates : true
}; };
if (stockColors) {
desired.stockColors = JSON.stringify(stockColors);
}
const json = JSON.stringify(desired);
const desiredPath = stateDir + "/matugen.desired.json";
const syncModeWithPortal = (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal) ? "true" : "false";
const terminalsAlwaysDark = (typeof SettingsData !== "undefined" && SettingsData.terminalsAlwaysDark) ? "true" : "false";
console.log("Theme: Starting matugen worker"); console.log("Theme: Starting matugen worker");
workerRunning = true; workerRunning = true;
systemThemeGenerator.command = ["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF\nexec '${shellDir}/scripts/matugen-worker.sh' '${stateDir}' '${shellDir}' '${configDir}' '${syncModeWithPortal}' '${terminalsAlwaysDark}' --run`];
const args = [
"dms", "matugen", "queue",
"--state-dir", stateDir,
"--shell-dir", shellDir,
"--config-dir", configDir,
"--kind", desired.kind,
"--value", desired.value,
"--mode", desired.mode,
"--icon-theme", desired.iconTheme,
"--matugen-type", desired.matugenType,
];
if (!desired.runUserTemplates) {
args.push("--run-user-templates=false");
}
if (stockColors) {
args.push("--stock-colors", JSON.stringify(stockColors));
}
if (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal) {
args.push("--sync-mode-with-portal");
}
if (typeof SettingsData !== "undefined" && SettingsData.terminalsAlwaysDark) {
args.push("--terminals-always-dark");
}
systemThemeGenerator.command = args;
systemThemeGenerator.running = true; systemThemeGenerator.running = true;
} }
+1 -1
View File
@@ -194,7 +194,7 @@ var SPEC = {
lockScreenShowPowerActions: { def: true }, lockScreenShowPowerActions: { def: true },
enableFprint: { def: false }, enableFprint: { def: false },
maxFprintTries: { def: 3 }, maxFprintTries: { def: 15 },
fprintdAvailable: { def: false, persist: false }, fprintdAvailable: { def: false, persist: false },
lockScreenActiveMonitor: { def: "all" }, lockScreenActiveMonitor: { def: "all" },
lockScreenInactiveColor: { def: "#000000" }, lockScreenInactiveColor: { def: "#000000" },
@@ -56,6 +56,8 @@ Row {
} }
DankSlider { DankSlider {
id: volumeSlider
readonly property real actualVolumePercent: defaultSink ? Math.round(defaultSink.audio.volume * 100) : 0 readonly property real actualVolumePercent: defaultSink ? Math.round(defaultSink.audio.volume * 100) : 0
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
@@ -63,7 +65,6 @@ Row {
enabled: defaultSink !== null enabled: defaultSink !== null
minimum: 0 minimum: 0
maximum: 100 maximum: 100
value: defaultSink ? Math.min(100, Math.round(defaultSink.audio.volume * 100)) : 0
showValue: true showValue: true
unit: "%" unit: "%"
valueOverride: actualVolumePercent valueOverride: actualVolumePercent
@@ -81,4 +82,11 @@ Row {
} }
} }
} }
Binding {
target: volumeSlider
property: "value"
value: defaultSink ? Math.min(100, Math.round(defaultSink.audio.volume * 100)) : 0
when: !volumeSlider.isDragging
}
} }
+7 -3
View File
@@ -23,6 +23,7 @@ Loader {
property bool isRightBarEdge: false property bool isRightBarEdge: false
property bool isTopBarEdge: false property bool isTopBarEdge: false
property bool isBottomBarEdge: false property bool isBottomBarEdge: false
property string _registeredScreenName: ""
asynchronous: false asynchronous: false
@@ -198,13 +199,16 @@ Loader {
if (!hasPopout) if (!hasPopout)
return; return;
BarWidgetService.registerWidget(widgetId, parentScreen.name, item); _registeredScreenName = parentScreen.name;
BarWidgetService.registerWidget(widgetId, _registeredScreenName, item);
} }
function unregisterWidget() { function unregisterWidget() {
if (!widgetId || !parentScreen?.name) if (!widgetId || !_registeredScreenName)
return; return;
BarWidgetService.unregisterWidget(widgetId, parentScreen.name);
BarWidgetService.unregisterWidget(widgetId, _registeredScreenName);
_registeredScreenName = "";
} }
function getWidgetComponent(widgetId, components) { function getWidgetComponent(widgetId, components) {
@@ -17,7 +17,7 @@ BasePill {
DankIcon { DankIcon {
anchors.centerIn: parent anchors.centerIn: parent
name: "content_paste" name: "content_paste"
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: Theme.widgetIconColor color: Theme.widgetIconColor
} }
} }
@@ -173,7 +173,7 @@ BasePill {
DankIcon { DankIcon {
name: root.getNetworkIconName() name: root.getNetworkIconName()
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: root.getNetworkIconColor() color: root.getNetworkIconColor()
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: root.showNetworkIcon && NetworkService.networkAvailable visible: root.showNetworkIcon && NetworkService.networkAvailable
@@ -181,7 +181,7 @@ BasePill {
DankIcon { DankIcon {
name: "vpn_lock" name: "vpn_lock"
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: NetworkService.vpnConnected ? Theme.primary : Theme.outlineButton color: NetworkService.vpnConnected ? Theme.primary : Theme.outlineButton
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected visible: root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected
@@ -189,7 +189,7 @@ BasePill {
DankIcon { DankIcon {
name: "bluetooth" name: "bluetooth"
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: BluetoothService.connected ? Theme.primary : Theme.outlineButton color: BluetoothService.connected ? Theme.primary : Theme.outlineButton
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
@@ -205,7 +205,7 @@ BasePill {
DankIcon { DankIcon {
id: audioIconV id: audioIconV
name: root.getVolumeIconName() name: root.getVolumeIconName()
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: Theme.widgetIconColor color: Theme.widgetIconColor
anchors.centerIn: parent anchors.centerIn: parent
} }
@@ -230,7 +230,7 @@ BasePill {
DankIcon { DankIcon {
id: micIconV id: micIconV
name: root.getMicIconName() name: root.getMicIconName()
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: root.getMicIconColor() color: root.getMicIconColor()
anchors.centerIn: parent anchors.centerIn: parent
} }
@@ -255,7 +255,7 @@ BasePill {
DankIcon { DankIcon {
id: brightnessIconV id: brightnessIconV
name: root.getBrightnessIconName() name: root.getBrightnessIconName()
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: Theme.widgetIconColor color: Theme.widgetIconColor
anchors.centerIn: parent anchors.centerIn: parent
} }
@@ -272,7 +272,7 @@ BasePill {
DankIcon { DankIcon {
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable) name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: root.getBatteryIconColor() color: root.getBatteryIconColor()
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: root.showBatteryIcon && BatteryService.batteryAvailable visible: root.showBatteryIcon && BatteryService.batteryAvailable
@@ -280,7 +280,7 @@ BasePill {
DankIcon { DankIcon {
name: "print" name: "print"
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: Theme.primary color: Theme.primary
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs() visible: root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs()
@@ -288,7 +288,7 @@ BasePill {
DankIcon { DankIcon {
name: "settings" name: "settings"
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: root.isActive ? Theme.primary : Theme.widgetIconColor color: root.isActive ? Theme.primary : Theme.widgetIconColor
anchors.horizontalCenter: parent.horizontalCenter anchors.horizontalCenter: parent.horizontalCenter
visible: root.hasNoVisibleIcons() visible: root.hasNoVisibleIcons()
@@ -304,7 +304,7 @@ BasePill {
DankIcon { DankIcon {
id: networkIcon id: networkIcon
name: root.getNetworkIconName() name: root.getNetworkIconName()
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: root.getNetworkIconColor() color: root.getNetworkIconColor()
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: root.showNetworkIcon && NetworkService.networkAvailable visible: root.showNetworkIcon && NetworkService.networkAvailable
@@ -313,7 +313,7 @@ BasePill {
DankIcon { DankIcon {
id: vpnIcon id: vpnIcon
name: "vpn_lock" name: "vpn_lock"
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: NetworkService.vpnConnected ? Theme.primary : Theme.outlineButton color: NetworkService.vpnConnected ? Theme.primary : Theme.outlineButton
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected visible: root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected
@@ -322,7 +322,7 @@ BasePill {
DankIcon { DankIcon {
id: bluetoothIcon id: bluetoothIcon
name: "bluetooth" name: "bluetooth"
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: BluetoothService.connected ? Theme.primary : Theme.outlineButton color: BluetoothService.connected ? Theme.primary : Theme.outlineButton
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
@@ -338,7 +338,7 @@ BasePill {
DankIcon { DankIcon {
id: audioIcon id: audioIcon
name: root.getVolumeIconName() name: root.getVolumeIconName()
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: Theme.widgetIconColor color: Theme.widgetIconColor
anchors.centerIn: parent anchors.centerIn: parent
} }
@@ -364,7 +364,7 @@ BasePill {
DankIcon { DankIcon {
id: micIcon id: micIcon
name: root.getMicIconName() name: root.getMicIconName()
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: root.getMicIconColor() color: root.getMicIconColor()
anchors.centerIn: parent anchors.centerIn: parent
} }
@@ -390,7 +390,7 @@ BasePill {
DankIcon { DankIcon {
id: brightnessIcon id: brightnessIcon
name: root.getBrightnessIconName() name: root.getBrightnessIconName()
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: Theme.widgetIconColor color: Theme.widgetIconColor
anchors.centerIn: parent anchors.centerIn: parent
} }
@@ -409,7 +409,7 @@ BasePill {
DankIcon { DankIcon {
id: batteryIcon id: batteryIcon
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable) name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: root.getBatteryIconColor() color: root.getBatteryIconColor()
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: root.showBatteryIcon && BatteryService.batteryAvailable visible: root.showBatteryIcon && BatteryService.batteryAvailable
@@ -418,7 +418,7 @@ BasePill {
DankIcon { DankIcon {
id: printerIcon id: printerIcon
name: "print" name: "print"
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: Theme.primary color: Theme.primary
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs() visible: root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs()
@@ -426,7 +426,7 @@ BasePill {
DankIcon { DankIcon {
name: "settings" name: "settings"
size: Theme.barIconSize(root.barThickness) size: Theme.barIconSize(root.barThickness, -4)
color: root.isActive ? Theme.primary : Theme.widgetIconColor color: root.isActive ? Theme.primary : Theme.widgetIconColor
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: root.hasNoVisibleIcons() visible: root.hasNoVisibleIcons()
@@ -541,6 +541,34 @@ Item {
} }
} }
function getWorkspaceIndex(modelData) {
let isPlaceholder;
if (root.useExtWorkspace) {
isPlaceholder = modelData?.hidden === true;
} else if (CompositorService.isHyprland) {
isPlaceholder = modelData?.id === -1;
} else if (CompositorService.isDwl) {
isPlaceholder = modelData?.tag === -1;
} else if (CompositorService.isSway) {
isPlaceholder = modelData?.num === -1;
} else {
isPlaceholder = modelData === -1;
}
if (isPlaceholder)
return index + 1;
if (root.useExtWorkspace)
return index + 1;
if (CompositorService.isHyprland)
return modelData?.id || "";
if (CompositorService.isDwl)
return (modelData?.tag !== undefined) ? (modelData.tag + 1) : "";
if (CompositorService.isSway)
return modelData?.num || "";
return modelData - 1;
}
readonly property bool hasNativeWorkspaceSupport: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isSway readonly property bool hasNativeWorkspaceSupport: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isSway
readonly property bool hasWorkspaces: getRealWorkspaces().length > 0 readonly property bool hasWorkspaces: getRealWorkspaces().length > 0
readonly property bool shouldShow: hasNativeWorkspaceSupport || (useExtWorkspace && hasWorkspaces) readonly property bool shouldShow: hasNativeWorkspaceSupport || (useExtWorkspace && hasWorkspaces)
@@ -862,7 +890,18 @@ Item {
id: rowLayout id: rowLayout
Row { Row {
spacing: 4 spacing: 4
visible: loadedIcons.length > 0 visible: loadedIcons.length > 0 || SettingsData.showWorkspaceIndex
StyledText {
topPadding: 2
rightPadding: isActive ? 4 : 0
visible: SettingsData.showWorkspaceIndex
text: {
return root.getWorkspaceIndex(modelData);
}
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
font.weight: (isActive && !isPlaceholder) ? Font.DemiBold : Font.Normal
}
Repeater { Repeater {
model: ScriptModel { model: ScriptModel {
@@ -1045,31 +1084,7 @@ Item {
StyledText { StyledText {
anchors.centerIn: parent anchors.centerIn: parent
text: { text: {
let isPlaceholder; return root.getWorkspaceIndex(modelData);
if (root.useExtWorkspace) {
isPlaceholder = modelData?.hidden === true;
} else if (CompositorService.isHyprland) {
isPlaceholder = modelData?.id === -1;
} else if (CompositorService.isDwl) {
isPlaceholder = modelData?.tag === -1;
} else if (CompositorService.isSway) {
isPlaceholder = modelData?.num === -1;
} else {
isPlaceholder = modelData === -1;
}
if (isPlaceholder)
return index + 1;
if (root.useExtWorkspace)
return index + 1;
if (CompositorService.isHyprland)
return modelData?.id || "";
if (CompositorService.isDwl)
return (modelData?.tag !== undefined) ? (modelData.tag + 1) : "";
if (CompositorService.isSway)
return modelData?.num || "";
return modelData - 1;
} }
color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium color: (isActive || isUrgent) ? Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.95) : isPlaceholder ? Theme.surfaceTextAlpha : Theme.surfaceTextMedium
font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale) font.pixelSize: Theme.barTextSize(barThickness, barConfig?.fontScale)
+66 -129
View File
@@ -51,17 +51,9 @@ Item {
readonly property bool usePlayerVolume: activePlayer && activePlayer.volumeSupported && !__isChromeBrowser readonly property bool usePlayerVolume: activePlayer && activePlayer.volumeSupported && !__isChromeBrowser
readonly property real currentVolume: usePlayerVolume ? activePlayer.volume : (AudioService.sink?.audio?.volume ?? 0) readonly property real currentVolume: usePlayerVolume ? activePlayer.volume : (AudioService.sink?.audio?.volume ?? 0)
// Palette that stays stable across track switches until new colors are ready
property color dom: Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, 1.0)
property color acc: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.25)
property color _nextDom: dom
property color _nextAcc: acc
// Track-switch hold (prevents banner flicker only during switches)
property bool isSwitching: false property bool isSwitching: false
property bool paletteReady: false
property string _lastArtUrl: "" property string _lastArtUrl: ""
property url _cqSource: "" property string _bgArtSource: ""
// Derived "no players" state: always correct, no timers. // Derived "no players" state: always correct, no timers.
readonly property int _playerCount: allPlayers ? allPlayers.length : 0 readonly property int _playerCount: allPlayers ? allPlayers.length : 0
@@ -69,7 +61,6 @@ Item {
readonly property bool _trulyIdle: activePlayer && activePlayer.playbackState === MprisPlaybackState.Stopped && !activePlayer.trackTitle && !activePlayer.trackArtist readonly property bool _trulyIdle: activePlayer && activePlayer.playbackState === MprisPlaybackState.Stopped && !activePlayer.trackTitle && !activePlayer.trackArtist
readonly property bool showNoPlayerNow: (!_switchHold) && (_noneAvailable || _trulyIdle) readonly property bool showNoPlayerNow: (!_switchHold) && (_noneAvailable || _trulyIdle)
// Short hold only during track switches (not when players disappear)
property bool _switchHold: false property bool _switchHold: false
Timer { Timer {
id: _switchHoldTimer id: _switchHoldTimer
@@ -86,11 +77,9 @@ Item {
} }
isSwitching = true; isSwitching = true;
_switchHold = true; _switchHold = true;
paletteReady = false;
_switchHoldTimer.restart(); _switchHoldTimer.restart();
if (activePlayer.trackArtUrl) { if (activePlayer.trackArtUrl)
loadArtwork(activePlayer.trackArtUrl); loadArtwork(activePlayer.trackArtUrl);
}
} }
property string activeTrackArtFile: "" property string activeTrackArtFile: ""
@@ -108,13 +97,13 @@ Item {
imageDownloader.command = ["curl", "-L", "-s", "--user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36", "-o", filename, url]; imageDownloader.command = ["curl", "-L", "-s", "--user-agent", "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36", "-o", filename, url];
imageDownloader.targetFile = filename; imageDownloader.targetFile = filename;
imageDownloader.running = true; imageDownloader.running = true;
} else { return;
_preloadImage.source = url;
} }
_bgArtSource = url;
} }
function maybeFinishSwitch() { function maybeFinishSwitch() {
if (activePlayer && activePlayer.trackTitle !== "" && paletteReady) { if (activePlayer && activePlayer.trackTitle !== "") {
isSwitching = false; isSwitching = false;
_switchHold = false; _switchHold = false;
} }
@@ -219,9 +208,8 @@ Item {
property string targetFile: "" property string targetFile: ""
onExited: exitCode => { onExited: exitCode => {
if (exitCode === 0 && targetFile) { if (exitCode === 0 && targetFile)
_preloadImage.source = "file://" + targetFile; _bgArtSource = "file://" + targetFile;
}
} }
} }
@@ -230,121 +218,70 @@ Item {
running: false running: false
} }
Image {
id: _preloadImage
source: ""
asynchronous: true
cache: true
visible: false
onStatusChanged: {
if (status === Image.Ready) {
_cqSource = source;
colorQuantizer.source = _cqSource;
} else if (status === Image.Error) {
_cqSource = "";
}
}
}
ColorQuantizer {
id: colorQuantizer
source: _cqSource !== "" ? _cqSource : undefined
depth: 8
rescaleSize: 32
onColorsChanged: {
if (!colors || colors.length === 0)
return;
function enhanceColor(color) {
const satBoost = 1.4;
const valueBoost = 1.2;
return Qt.hsva(color.hsvHue, Math.min(1, color.hsvSaturation * satBoost), Math.min(1, color.hsvValue * valueBoost), color.a);
}
function getExtremeColor(startIdx, direction = 1) {
let bestColor = colors[startIdx];
let bestScore = 0;
for (let i = startIdx; i >= 0 && i < colors.length; i += direction) {
const c = colors[i];
const saturation = c.hsvSaturation;
const brightness = c.hsvValue;
const contrast = Math.abs(brightness - 0.5) * 2;
const score = saturation * 0.7 + contrast * 0.3;
if (score > bestScore) {
bestScore = score;
bestColor = c;
}
}
return enhanceColor(bestColor);
}
_pendingDom = getExtremeColor(Math.floor(colors.length * 0.2), 1);
_pendingAcc = getExtremeColor(Math.floor(colors.length * 0.8), -1);
paletteApplyDelay.restart();
}
}
property color _pendingDom: dom
property color _pendingAcc: acc
Timer {
id: paletteApplyDelay
interval: 90
repeat: false
onTriggered: {
const dist = (c1, c2) => {
const dr = c1.r - c2.r, dg = c1.g - c2.g, db = c1.b - c2.b;
return Math.sqrt(dr * dr + dg * dg + db * db);
};
const domChanged = dist(_pendingDom, dom) > 0.02;
const accChanged = dist(_pendingAcc, acc) > 0.02;
if (domChanged || accChanged) {
dom = _pendingDom;
acc = _pendingAcc;
}
paletteReady = true;
maybeFinishSwitch();
}
}
property bool isSeeking: false property bool isSeeking: false
Rectangle { Item {
id: bgContainer
anchors.fill: parent anchors.fill: parent
radius: Theme.cornerRadius visible: _bgArtSource !== ""
opacity: 1.0
gradient: Gradient {
GradientStop {
position: 0.0
color: Qt.rgba(dom.r, dom.g, dom.b, paletteReady ? 0.38 : 0.06)
}
GradientStop {
position: 0.3
color: Qt.rgba(acc.r, acc.g, acc.b, paletteReady ? 0.28 : 0.05)
}
GradientStop {
position: 1.0
color: Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, paletteReady ? 0.92 : 0.985)
}
}
Behavior on opacity {
NumberAnimation {
duration: 160
}
}
}
Behavior on dom { Image {
ColorAnimation { id: bgImage
duration: 220 anchors.centerIn: parent
easing.type: Easing.InOutQuad width: Math.max(parent.width, parent.height) * 1.1
height: width
source: _bgArtSource
fillMode: Image.PreserveAspectCrop
asynchronous: true
cache: true
visible: false
onStatusChanged: {
if (status === Image.Ready)
maybeFinishSwitch();
}
} }
}
Behavior on acc { Item {
ColorAnimation { id: blurredBg
duration: 220 anchors.fill: parent
easing.type: Easing.InOutQuad visible: false
MultiEffect {
anchors.centerIn: parent
width: bgImage.width
height: bgImage.height
source: bgImage
blurEnabled: true
blurMax: 64
blur: 0.8
saturation: -0.2
brightness: -0.25
}
}
Rectangle {
id: bgMask
anchors.fill: parent
radius: Theme.cornerRadius
visible: false
layer.enabled: true
}
MultiEffect {
anchors.fill: parent
source: blurredBg
maskEnabled: true
maskSource: bgMask
maskThresholdMin: 0.5
maskSpreadAtMin: 1.0
opacity: 0.7
}
Rectangle {
anchors.fill: parent
radius: Theme.cornerRadius
color: Theme.surface
opacity: 0.3
} }
} }
+22 -3
View File
@@ -1086,14 +1086,33 @@ Item {
Row { Row {
spacing: Theme.spacingM spacing: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.networkStatus !== "disconnected" || (BluetoothService.available && BluetoothService.enabled) || (AudioService.sink && AudioService.sink.audio) visible: NetworkService.networkAvailable || (BluetoothService.available && BluetoothService.enabled) || (AudioService.sink && AudioService.sink.audio)
DankIcon { DankIcon {
name: NetworkService.networkStatus === "ethernet" ? "lan" : NetworkService.wifiSignalIcon name: {
if (NetworkService.wifiToggling)
return "sync";
switch (NetworkService.networkStatus) {
case "ethernet":
return "lan";
case "vpn":
return NetworkService.ethernetConnected ? "lan" : NetworkService.wifiSignalIcon;
default:
return NetworkService.wifiSignalIcon;
}
}
size: Theme.iconSize - 2 size: Theme.iconSize - 2
color: NetworkService.networkStatus !== "disconnected" ? "white" : Qt.rgba(255, 255, 255, 0.5) color: NetworkService.networkStatus !== "disconnected" ? "white" : Qt.rgba(255, 255, 255, 0.5)
anchors.verticalCenter: parent.verticalCenter anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.networkStatus !== "disconnected" visible: NetworkService.networkAvailable
}
DankIcon {
name: "vpn_lock"
size: Theme.iconSize - 2
color: NetworkService.vpnConnected ? Theme.primary : Qt.rgba(255, 255, 255, 0.5)
anchors.verticalCenter: parent.verticalCenter
visible: NetworkService.vpnAvailable && NetworkService.vpnConnected
} }
DankIcon { DankIcon {
+5 -10
View File
@@ -59,12 +59,6 @@ Item {
"description": I18n.tr("Quick note-taking slideout panel"), "description": I18n.tr("Quick note-taking slideout panel"),
"icon": "sticky_note_2" "icon": "sticky_note_2"
}, },
{
"id": "systemTray",
"name": I18n.tr("System Tray"),
"description": I18n.tr("System tray icons"),
"icon": "notifications"
}
]; ];
} }
@@ -199,8 +193,8 @@ Item {
DankDropdown { DankDropdown {
width: parent.width - parent.leftPadding - parent.rightPadding width: parent.width - parent.leftPadding - parent.rightPadding
text: I18n.tr("Night Temperature") text: SessionData.nightModeAutoEnabled ? I18n.tr("Night Temperature") : I18n.tr("Color Temperature")
description: I18n.tr("Color temperature for night mode") description: SessionData.nightModeAutoEnabled ? I18n.tr("Color temperature for night mode") : I18n.tr("Warm color temperature to apply")
currentValue: SessionData.nightModeTemperature + "K" currentValue: SessionData.nightModeTemperature + "K"
options: { options: {
var temps = []; var temps = [];
@@ -223,6 +217,7 @@ Item {
text: I18n.tr("Day Temperature") text: I18n.tr("Day Temperature")
description: I18n.tr("Color temperature for day time") description: I18n.tr("Color temperature for day time")
currentValue: SessionData.nightModeHighTemperature + "K" currentValue: SessionData.nightModeHighTemperature + "K"
visible: SessionData.nightModeAutoEnabled
options: { options: {
var temps = []; var temps = [];
var minTemp = SessionData.nightModeTemperature; var minTemp = SessionData.nightModeTemperature;
@@ -821,7 +816,7 @@ Item {
const prefs = displaysTab.getScreenPreferences(parent.componentId); const prefs = displaysTab.getScreenPreferences(parent.componentId);
const isAll = prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all"); const isAll = prefs.includes("all") || (typeof prefs[0] === "string" && prefs[0] === "all");
const cid = parent.componentId; const cid = parent.componentId;
const isRelevantComponent = ["dankBar", "dock", "notifications", "osd", "toast", "notepad", "systemTray"].includes(cid) || cid.startsWith("bar:"); const isRelevantComponent = ["dankBar", "dock", "notifications", "osd", "toast", "notepad"].includes(cid) || cid.startsWith("bar:");
return !isAll && isRelevantComponent; return !isAll && isRelevantComponent;
} }
onToggled: checked => { onToggled: checked => {
@@ -905,4 +900,4 @@ Item {
} }
} }
} }
} }
+1 -1
View File
@@ -597,7 +597,7 @@ Item {
onSaveBind: (originalKey, newData) => { onSaveBind: (originalKey, newData) => {
KeybindsService.saveBind(originalKey, newData); KeybindsService.saveBind(originalKey, newData);
keybindsTab._editingKey = newData.key; keybindsTab._editingKey = newData.key;
keybindsTab.expandedKey = modelData.action; keybindsTab.expandedKey = newData.action;
} }
onRemoveBind: key => { onRemoveBind: key => {
const remainingKey = bindItem.keys.find(k => k.key !== key)?.key ?? ""; const remainingKey = bindItem.keys.find(k => k.key !== key)?.key ?? "";
+1 -1
View File
@@ -506,7 +506,7 @@ Singleton {
DMSService.sendRequest("wayland.gamma.setTemperature", { DMSService.sendRequest("wayland.gamma.setTemperature", {
"low": temperature, "low": temperature,
"high": 6500 "high": temperature
}, response => { }, response => {
if (response.error) { if (response.error) {
console.error("DisplayService: Failed to set temperature:", response.error); console.error("DisplayService: Failed to set temperature:", response.error);
+87 -72
View File
@@ -650,9 +650,10 @@ Item {
} }
onWheel: wheel => { onWheel: wheel => {
if (!root.recording) if (!root.recording) {
wheel.accepted = false;
return; return;
}
wheel.accepted = true; wheel.accepted = true;
const mods = []; const mods = [];
@@ -959,12 +960,12 @@ Item {
Layout.preferredWidth: 120 Layout.preferredWidth: 120
compactMode: true compactMode: true
currentValue: { currentValue: {
const action = root.editAction; const base = root.editAction.split(" ")[0];
const cats = KeybindsService.getCompositorCategories(); const cats = KeybindsService.getCompositorCategories();
for (const cat of cats) { for (const cat of cats) {
const actions = KeybindsService.getCompositorActions(cat); const actions = KeybindsService.getCompositorActions(cat);
for (const act of actions) { for (const act of actions) {
if (act.id === action) if (act.id === base)
return cat; return cat;
} }
} }
@@ -1024,12 +1025,13 @@ Item {
} }
RowLayout { RowLayout {
id: optionsRow
Layout.fillWidth: true Layout.fillWidth: true
spacing: Theme.spacingM spacing: Theme.spacingM
visible: root._actionType === "compositor" && !root.useCustomCompositor && Actions.getActionArgConfig(root.editAction) visible: root._actionType === "compositor" && !root.useCustomCompositor && Actions.getActionArgConfig(root.editAction)
property var argConfig: Actions.getActionArgConfig(root.editAction) readonly property var argConfig: Actions.getActionArgConfig(root.editAction)
property var parsedArgs: Actions.parseCompositorActionArgs(root.editAction) readonly property var parsedArgs: Actions.parseCompositorActionArgs(root.editAction)
StyledText { StyledText {
text: I18n.tr("Options") text: I18n.tr("Options")
@@ -1048,56 +1050,75 @@ Item {
Layout.fillWidth: true Layout.fillWidth: true
Layout.preferredHeight: 40 Layout.preferredHeight: 40
visible: { visible: {
const cfg = parent.parent.argConfig; const cfg = optionsRow.argConfig;
if (!cfg || !cfg.config || !cfg.config.args) if (!cfg?.config?.args)
return false; return false;
const firstArg = cfg.config.args[0]; const firstArg = cfg.config.args[0];
return firstArg && (firstArg.type === "text" || firstArg.type === "number"); return firstArg && (firstArg.type === "text" || firstArg.type === "number");
} }
placeholderText: { placeholderText: optionsRow.argConfig?.config?.args?.[0]?.placeholder || ""
const cfg = parent.parent.argConfig;
if (!cfg || !cfg.config || !cfg.config.args) Connections {
return ""; target: optionsRow
return cfg.config.args[0]?.placeholder || ""; function onParsedArgsChanged() {
const newText = optionsRow.parsedArgs?.args?.value || optionsRow.parsedArgs?.args?.index || "";
if (argValueField.text !== newText)
argValueField.text = newText;
}
} }
text: parent.parent.parsedArgs?.args?.value || parent.parent.parsedArgs?.args?.index || ""
onTextChanged: { Component.onCompleted: {
const cfg = parent.parent.argConfig; text = optionsRow.parsedArgs?.args?.value || optionsRow.parsedArgs?.args?.index || "";
}
onEditingFinished: {
const cfg = optionsRow.argConfig;
if (!cfg) if (!cfg)
return; return;
const base = parent.parent.parsedArgs?.base || root.editAction.split(" ")[0]; const parsed = optionsRow.parsedArgs;
const args = cfg.config.args[0]?.type === "number" ? { const args = {};
index: text if (cfg.config.args[0]?.type === "number")
} : { args.index = text;
value: text else
}; args.value = text;
if (parsed?.args?.focus === false)
args.focus = false;
root.updateEdit({ root.updateEdit({
action: Actions.buildCompositorAction(base, args) action: Actions.buildCompositorAction(parsed?.base || cfg.base, args)
}); });
} }
} }
RowLayout { RowLayout {
visible: { visible: {
const cfg = parent.parent.argConfig; const cfg = optionsRow.argConfig;
return cfg && cfg.base === "move-column-to-workspace"; if (!cfg)
return false;
switch (cfg.base) {
case "move-column-to-workspace":
case "move-column-to-workspace-down":
case "move-column-to-workspace-up":
return true;
}
return false;
} }
spacing: Theme.spacingXS spacing: Theme.spacingXS
DankToggle { DankToggle {
id: focusToggle id: focusToggle
checked: parent.parent.parent.parsedArgs?.args?.focus === true checked: optionsRow.parsedArgs?.args?.focus !== false
onCheckedChanged: { onToggled: newChecked => {
const cfg = parent.parent.parent.argConfig; const cfg = optionsRow.argConfig;
if (!cfg) if (!cfg)
return; return;
const parsed = parent.parent.parent.parsedArgs; const parsed = optionsRow.parsedArgs;
const args = { const args = {};
index: parsed?.args?.index || "", if (cfg.base === "move-column-to-workspace")
focus: checked args.index = parsed?.args?.index || "";
}; if (!newChecked)
args.focus = false;
root.updateEdit({ root.updateEdit({
action: Actions.buildCompositorAction("move-column-to-workspace", args) action: Actions.buildCompositorAction(cfg.base, args)
}); });
} }
} }
@@ -1110,53 +1131,22 @@ Item {
} }
RowLayout { RowLayout {
visible: { visible: optionsRow.argConfig?.base?.startsWith("screenshot") ?? false
const cfg = parent.parent.argConfig;
return cfg && cfg.base && cfg.base.startsWith("screenshot");
}
spacing: Theme.spacingM spacing: Theme.spacingM
RowLayout {
spacing: Theme.spacingXS
DankToggle {
id: writeToDiskToggle
checked: parent.parent.parent.parent.parsedArgs?.args?.opts?.["write-to-disk"] === "true"
onCheckedChanged: {
const parsed = parent.parent.parent.parent.parsedArgs;
const base = parsed?.base || "screenshot";
const opts = parsed?.args?.opts || {};
opts["write-to-disk"] = checked ? "true" : "";
root.updateEdit({
action: Actions.buildCompositorAction(base, {
opts: opts
})
});
}
}
StyledText {
text: I18n.tr("Save")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
RowLayout { RowLayout {
spacing: Theme.spacingXS spacing: Theme.spacingXS
DankToggle { DankToggle {
id: showPointerToggle id: showPointerToggle
checked: parent.parent.parent.parent.parsedArgs?.args?.opts?.["show-pointer"] === "true" checked: optionsRow.parsedArgs?.args?.["show-pointer"] === true
onCheckedChanged: { onToggled: newChecked => {
const parsed = parent.parent.parent.parent.parsedArgs; const parsed = optionsRow.parsedArgs;
const base = parsed?.base || "screenshot"; const base = parsed?.base || "screenshot";
const opts = parsed?.args?.opts || {}; const args = Object.assign({}, parsed?.args || {});
opts["show-pointer"] = checked ? "true" : ""; args["show-pointer"] = newChecked;
root.updateEdit({ root.updateEdit({
action: Actions.buildCompositorAction(base, { action: Actions.buildCompositorAction(base, args)
opts: opts
})
}); });
} }
} }
@@ -1167,6 +1157,31 @@ Item {
color: Theme.surfaceVariantText color: Theme.surfaceVariantText
} }
} }
RowLayout {
visible: optionsRow.argConfig?.base !== "screenshot"
spacing: Theme.spacingXS
DankToggle {
id: writeToDiskToggle
checked: optionsRow.parsedArgs?.args?.["write-to-disk"] === true
onToggled: newChecked => {
const parsed = optionsRow.parsedArgs;
const base = parsed?.base || "screenshot-screen";
const args = Object.assign({}, parsed?.args || {});
args["write-to-disk"] = newChecked;
root.updateEdit({
action: Actions.buildCompositorAction(base, args)
});
}
}
StyledText {
text: I18n.tr("Save")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
} }
} }
} }
+78 -40
View File
@@ -17,12 +17,19 @@ Item {
property color playheadColor: Theme.primary property color playheadColor: Theme.primary
property real dpr: (root.window ? root.window.devicePixelRatio : 1) property real dpr: (root.window ? root.window.devicePixelRatio : 1)
function snap(v) { return Math.round(v * dpr) / dpr } function snap(v) {
return Math.round(v * dpr) / dpr;
}
readonly property real playX: snap(root.width * root.value) readonly property real playX: snap(root.width * root.value)
readonly property real midY: snap(height / 2) readonly property real midY: snap(height / 2)
Behavior on currentAmp { NumberAnimation { duration: 300; easing.type: Easing.OutCubic } } Behavior on currentAmp {
NumberAnimation {
duration: 300
easing.type: Easing.OutCubic
}
}
onIsPlayingChanged: currentAmp = isPlaying ? amp : 0 onIsPlayingChanged: currentAmp = isPlaying ? amp : 0
Shape { Shape {
@@ -38,8 +45,16 @@ Item {
capStyle: ShapePath.RoundCap capStyle: ShapePath.RoundCap
joinStyle: ShapePath.RoundJoin joinStyle: ShapePath.RoundJoin
fillColor: "transparent" fillColor: "transparent"
PathMove { id: flatStart; x: 0; y: root.midY } PathMove {
PathLine { id: flatEnd; x: root.width; y: root.midY } id: flatStart
x: Math.min(root.width, snap(root.playX + playhead.width / 2))
y: root.midY
}
PathLine {
id: flatEnd
x: root.width
y: root.midY
}
} }
} }
@@ -48,7 +63,7 @@ Item {
anchors.fill: parent anchors.fill: parent
clip: true clip: true
readonly property real startX: snap(root.lineWidth/2) readonly property real startX: snap(root.lineWidth / 2)
readonly property real aaBias: (0.25 / root.dpr) readonly property real aaBias: (0.25 / root.dpr)
readonly property real endX: Math.max(startX, Math.min(root.playX - startX - aaBias, width)) readonly property real endX: Math.max(startX, Math.min(root.playX - startX - aaBias, width))
@@ -77,7 +92,10 @@ Item {
capStyle: ShapePath.RoundCap capStyle: ShapePath.RoundCap
joinStyle: ShapePath.RoundJoin joinStyle: ShapePath.RoundJoin
fillColor: "transparent" fillColor: "transparent"
PathSvg { id: waveSvg; path: "" } PathSvg {
id: waveSvg
path: ""
}
} }
} }
} }
@@ -88,8 +106,8 @@ Item {
height: snap(root.lineWidth) height: snap(root.lineWidth)
radius: width / 2 radius: width / 2
color: root.fillColor color: root.fillColor
x: waveClip.startX - width/2 x: waveClip.startX - width / 2
y: root.midY - height/2 + root.currentAmp * Math.sin((waveClip.startX / root.wavelength) * 2 * Math.PI + root.phase) y: root.midY - height / 2 + root.currentAmp * Math.sin((waveClip.startX / root.wavelength) * 2 * Math.PI + root.phase)
visible: waveClip.endX > waveClip.startX visible: waveClip.endX > waveClip.startX
z: 2 z: 2
} }
@@ -100,8 +118,8 @@ Item {
height: snap(root.lineWidth) height: snap(root.lineWidth)
radius: width / 2 radius: width / 2
color: root.fillColor color: root.fillColor
x: waveClip.endX - width/2 x: waveClip.endX - width / 2
y: root.midY - height/2 + root.currentAmp * Math.sin((waveClip.endX / root.wavelength) * 2 * Math.PI + root.phase) y: root.midY - height / 2 + root.currentAmp * Math.sin((waveClip.endX / root.wavelength) * 2 * Math.PI + root.phase)
visible: waveClip.endX > waveClip.startX visible: waveClip.endX > waveClip.startX
z: 2 z: 2
} }
@@ -119,48 +137,68 @@ Item {
} }
property real k: (2 * Math.PI) / Math.max(1e-6, wavelength) property real k: (2 * Math.PI) / Math.max(1e-6, wavelength)
function wrapMod(a, m) { let r = a % m; return r < 0 ? r + m : r } function wrapMod(a, m) {
let r = a % m;
return r < 0 ? r + m : r;
}
readonly property real waveOffsetX: -wrapMod(phase / k, wavelength) readonly property real waveOffsetX: -wrapMod(phase / k, wavelength)
FrameAnimation { FrameAnimation {
running: root.visible && (root.isPlaying || root.currentAmp > 0) running: root.visible && (root.isPlaying || root.currentAmp > 0)
onTriggered: { onTriggered: {
if (root.isPlaying) root.phase += 0.03 * frameTime * 60 if (root.isPlaying)
startCap.y = root.midY - startCap.height/2 + root.currentAmp * Math.sin((waveClip.startX / root.wavelength) * 2 * Math.PI + root.phase) root.phase += 0.03 * frameTime * 60;
endCap.y = root.midY - endCap.height/2 + root.currentAmp * Math.sin((waveClip.endX / root.wavelength) * 2 * Math.PI + root.phase) startCap.y = root.midY - startCap.height / 2 + root.currentAmp * Math.sin((waveClip.startX / root.wavelength) * 2 * Math.PI + root.phase);
endCap.y = root.midY - endCap.height / 2 + root.currentAmp * Math.sin((waveClip.endX / root.wavelength) * 2 * Math.PI + root.phase);
} }
} }
function buildStaticWave() { function buildStaticWave() {
const start = waveClip.startX - 2 * root.wavelength const start = waveClip.startX - 2 * root.wavelength;
const end = width + 2 * root.wavelength const end = width + 2 * root.wavelength;
if (end <= start) { waveSvg.path = ""; return } if (end <= start) {
waveSvg.path = "";
const kLocal = k return;
const halfPeriod = root.wavelength / 2
function y0(x) { return root.midY + root.currentAmp * Math.sin(kLocal * x) }
function dy0(x) { return root.currentAmp * Math.cos(kLocal * x) * kLocal }
let x0 = start
let d = `M ${x0} ${y0(x0)}`
while (x0 < end) {
const x1 = Math.min(x0 + halfPeriod, end)
const dx = x1 - x0
const yA = y0(x0), yB = y0(x1)
const dyA = dy0(x0), dyB = dy0(x1)
const c1x = x0 + dx/3
const c1y = yA + (dyA * dx)/3
const c2x = x1 - dx/3
const c2y = yB - (dyB * dx)/3
d += ` C ${c1x} ${c1y} ${c2x} ${c2y} ${x1} ${yB}`
x0 = x1
} }
waveSvg.path = d
const kLocal = k;
const halfPeriod = root.wavelength / 2;
function y0(x) {
return root.midY + root.currentAmp * Math.sin(kLocal * x);
}
function dy0(x) {
return root.currentAmp * Math.cos(kLocal * x) * kLocal;
}
let x0 = start;
let d = `M ${x0} ${y0(x0)}`;
while (x0 < end) {
const x1 = Math.min(x0 + halfPeriod, end);
const dx = x1 - x0;
const yA = y0(x0), yB = y0(x1);
const dyA = dy0(x0), dyB = dy0(x1);
const c1x = x0 + dx / 3;
const c1y = yA + (dyA * dx) / 3;
const c2x = x1 - dx / 3;
const c2y = yB - (dyB * dx) / 3;
d += ` C ${c1x} ${c1y} ${c2x} ${c2y} ${x1} ${yB}`;
x0 = x1;
}
waveSvg.path = d;
} }
Component.onCompleted: { currentAmp = isPlaying ? amp : 0; buildStaticWave() } Component.onCompleted: {
onWidthChanged: { flatStart.x = 0; flatEnd.x = width; buildStaticWave() } currentAmp = isPlaying ? amp : 0;
buildStaticWave();
}
onWidthChanged: {
flatEnd.x = width;
buildStaticWave();
}
onHeightChanged: buildStaticWave() onHeightChanged: buildStaticWave()
onCurrentAmpChanged: buildStaticWave() onCurrentAmpChanged: buildStaticWave()
onWavelengthChanged: { k = (2 * Math.PI) / Math.max(1e-6, wavelength); buildStaticWave() } onWavelengthChanged: {
k = (2 * Math.PI) / Math.max(1e-6, wavelength);
buildStaticWave();
}
} }
Binary file not shown.
+16 -16
View File
@@ -11,21 +11,21 @@ text = '{{colors.background.default.hex}}'
cursor = '{{colors.primary.default.hex}}' cursor = '{{colors.primary.default.hex}}'
[colors.normal] [colors.normal]
black = '{{dank16.color0.hex}}' black = '{{dank16.color0.default.hex}}'
red = '{{dank16.color1.hex}}' red = '{{dank16.color1.default.hex}}'
green = '{{dank16.color2.hex}}' green = '{{dank16.color2.default.hex}}'
yellow = '{{dank16.color3.hex}}' yellow = '{{dank16.color3.default.hex}}'
blue = '{{dank16.color4.hex}}' blue = '{{dank16.color4.default.hex}}'
magenta = '{{dank16.color5.hex}}' magenta = '{{dank16.color5.default.hex}}'
cyan = '{{dank16.color6.hex}}' cyan = '{{dank16.color6.default.hex}}'
white = '{{dank16.color7.hex}}' white = '{{dank16.color7.default.hex}}'
[colors.bright] [colors.bright]
black = '{{dank16.color8.hex}}' black = '{{dank16.color8.default.hex}}'
red = '{{dank16.color9.hex}}' red = '{{dank16.color9.default.hex}}'
green = '{{dank16.color10.hex}}' green = '{{dank16.color10.default.hex}}'
yellow = '{{dank16.color11.hex}}' yellow = '{{dank16.color11.default.hex}}'
blue = '{{dank16.color12.hex}}' blue = '{{dank16.color12.default.hex}}'
magenta = '{{dank16.color13.hex}}' magenta = '{{dank16.color13.default.hex}}'
cyan = '{{dank16.color14.hex}}' cyan = '{{dank16.color14.default.hex}}'
white = '{{dank16.color15.hex}}' white = '{{dank16.color15.default.hex}}'
+16 -16
View File
@@ -4,19 +4,19 @@ background={{colors.background.default.hex_stripped}}
selection-foreground={{colors.on_surface.default.hex_stripped}} selection-foreground={{colors.on_surface.default.hex_stripped}}
selection-background={{colors.primary_container.default.hex_stripped}} selection-background={{colors.primary_container.default.hex_stripped}}
regular0={{dank16.color0.hex_stripped}} regular0={{dank16.color0.default.hex_stripped}}
regular1={{dank16.color1.hex_stripped}} regular1={{dank16.color1.default.hex_stripped}}
regular2={{dank16.color2.hex_stripped}} regular2={{dank16.color2.default.hex_stripped}}
regular3={{dank16.color3.hex_stripped}} regular3={{dank16.color3.default.hex_stripped}}
regular4={{dank16.color4.hex_stripped}} regular4={{dank16.color4.default.hex_stripped}}
regular5={{dank16.color5.hex_stripped}} regular5={{dank16.color5.default.hex_stripped}}
regular6={{dank16.color6.hex_stripped}} regular6={{dank16.color6.default.hex_stripped}}
regular7={{dank16.color7.hex_stripped}} regular7={{dank16.color7.default.hex_stripped}}
bright0={{dank16.color8.hex_stripped}} bright0={{dank16.color8.default.hex_stripped}}
bright1={{dank16.color9.hex_stripped}} bright1={{dank16.color9.default.hex_stripped}}
bright2={{dank16.color10.hex_stripped}} bright2={{dank16.color10.default.hex_stripped}}
bright3={{dank16.color11.hex_stripped}} bright3={{dank16.color11.default.hex_stripped}}
bright4={{dank16.color12.hex_stripped}} bright4={{dank16.color12.default.hex_stripped}}
bright5={{dank16.color13.hex_stripped}} bright5={{dank16.color13.default.hex_stripped}}
bright6={{dank16.color14.hex_stripped}} bright6={{dank16.color14.default.hex_stripped}}
bright7={{dank16.color15.hex_stripped}} bright7={{dank16.color15.default.hex_stripped}}
+16 -16
View File
@@ -4,19 +4,19 @@ cursor-color = {{colors.primary.default.hex}}
selection-background = {{colors.primary_container.default.hex}} selection-background = {{colors.primary_container.default.hex}}
selection-foreground = {{colors.on_surface.default.hex}} selection-foreground = {{colors.on_surface.default.hex}}
palette = 0={{dank16.color0.hex}} palette = 0={{dank16.color0.default.hex}}
palette = 1={{dank16.color1.hex}} palette = 1={{dank16.color1.default.hex}}
palette = 2={{dank16.color2.hex}} palette = 2={{dank16.color2.default.hex}}
palette = 3={{dank16.color3.hex}} palette = 3={{dank16.color3.default.hex}}
palette = 4={{dank16.color4.hex}} palette = 4={{dank16.color4.default.hex}}
palette = 5={{dank16.color5.hex}} palette = 5={{dank16.color5.default.hex}}
palette = 6={{dank16.color6.hex}} palette = 6={{dank16.color6.default.hex}}
palette = 7={{dank16.color7.hex}} palette = 7={{dank16.color7.default.hex}}
palette = 8={{dank16.color8.hex}} palette = 8={{dank16.color8.default.hex}}
palette = 9={{dank16.color9.hex}} palette = 9={{dank16.color9.default.hex}}
palette = 10={{dank16.color10.hex}} palette = 10={{dank16.color10.default.hex}}
palette = 11={{dank16.color11.hex}} palette = 11={{dank16.color11.default.hex}}
palette = 12={{dank16.color12.hex}} palette = 12={{dank16.color12.default.hex}}
palette = 13={{dank16.color13.hex}} palette = 13={{dank16.color13.default.hex}}
palette = 14={{dank16.color14.hex}} palette = 14={{dank16.color14.default.hex}}
palette = 15={{dank16.color15.hex}} palette = 15={{dank16.color15.default.hex}}
+16 -16
View File
@@ -7,19 +7,19 @@ selection_foreground {{colors.on_secondary.default.hex}}
selection_background {{colors.secondary_fixed_dim.default.hex}} selection_background {{colors.secondary_fixed_dim.default.hex}}
url_color {{colors.primary.default.hex}} url_color {{colors.primary.default.hex}}
color0 {{dank16.color0.hex}} color0 {{dank16.color0.default.hex}}
color1 {{dank16.color1.hex}} color1 {{dank16.color1.default.hex}}
color2 {{dank16.color2.hex}} color2 {{dank16.color2.default.hex}}
color3 {{dank16.color3.hex}} color3 {{dank16.color3.default.hex}}
color4 {{dank16.color4.hex}} color4 {{dank16.color4.default.hex}}
color5 {{dank16.color5.hex}} color5 {{dank16.color5.default.hex}}
color6 {{dank16.color6.hex}} color6 {{dank16.color6.default.hex}}
color7 {{dank16.color7.hex}} color7 {{dank16.color7.default.hex}}
color8 {{dank16.color8.hex}} color8 {{dank16.color8.default.hex}}
color9 {{dank16.color9.hex}} color9 {{dank16.color9.default.hex}}
color10 {{dank16.color10.hex}} color10 {{dank16.color10.default.hex}}
color11 {{dank16.color11.hex}} color11 {{dank16.color11.default.hex}}
color12 {{dank16.color12.hex}} color12 {{dank16.color12.default.hex}}
color13 {{dank16.color13.hex}} color13 {{dank16.color13.default.hex}}
color14 {{dank16.color14.hex}} color14 {{dank16.color14.default.hex}}
color15 {{dank16.color15.hex}} color15 {{dank16.color15.default.hex}}
@@ -1,431 +1,299 @@
{ {
"$schema": "vscode://schemas/color-theme", "$schema": "vscode://schemas/color-theme",
"name": "Dynamic Base16 DankShell", "name": "Dynamic Base16 DankShell Dark",
"semanticHighlighting": true,
"colors": { "colors": {
//
// Core foreground + background hierarchy
//
"foreground": "{{colors.on_surface.dark.hex}}",
"editor.background": "{{colors.background.dark.hex}}", "editor.background": "{{colors.background.dark.hex}}",
"editor.foreground": "{{colors.on_surface.dark.hex}}", "editor.foreground": "{{colors.on_surface.dark.hex}}",
"editorLineNumber.foreground": "{{colors.outline.dark.hex}}", "errorForeground": "{{colors.error.dark.hex}}",
"editorLineNumber.activeForeground": "{{colors.on_surface.dark.hex}}", //
"editorCursor.foreground": "{{colors.primary.dark.hex}}", // Borders + dividers
"editor.selectionBackground": "{{colors.primary_container.dark.hex}}", //
"editor.inactiveSelectionBackground": "{{colors.background.dark.hex}}", "panel.border": "{{colors.outline_variant.dark.hex}}",
"editor.lineHighlightBackground": "{{colors.background.dark.hex}}", "panelTitle.activeBorder": "{{colors.primary.dark.hex}}",
"editorIndentGuide.background": "{{colors.background.dark.hex}}", "sideBar.border": "{{colors.outline_variant.dark.hex}}",
"editorIndentGuide.activeBackground": "{{colors.outline.dark.hex}}", "editorGroup.border": "{{colors.outline_variant.dark.hex}}",
"editorWhitespace.foreground": "{{colors.outline_variant.dark.hex}}", "tab.border": "{{colors.outline_variant.dark.hex}}",
"editorBracketMatch.background": "{{colors.background.dark.hex}}", "titleBar.border": "{{colors.outline_variant.dark.hex}}",
"editorBracketMatch.border": "{{colors.primary.dark.hex}}", //
// Focus + active indicators
//
"focusBorder": "{{colors.primary.dark.hex}}",
"selection.background": "{{colors.primary_container.dark.hex}}66",
//
// Title bar, activity bar, sidebar
//
"titleBar.activeBackground": "{{colors.background.dark.hex}}",
"titleBar.activeForeground": "{{colors.on_surface.dark.hex}}",
"titleBar.inactiveForeground": "{{colors.outline.dark.hex}}",
"activityBar.background": "{{colors.background.dark.hex}}", "activityBar.background": "{{colors.background.dark.hex}}",
"activityBar.foreground": "{{colors.on_surface.dark.hex}}", "activityBar.foreground": "{{colors.on_surface.dark.hex}}",
"activityBar.inactiveForeground": "{{colors.outline.dark.hex}}",
"activityBar.activeBorder": "{{colors.primary.dark.hex}}", "activityBar.activeBorder": "{{colors.primary.dark.hex}}",
"activityBar.activeBackground": "{{colors.background.dark.hex}}",
"activityBarBadge.background": "{{colors.primary.dark.hex}}", "activityBarBadge.background": "{{colors.primary.dark.hex}}",
"activityBarBadge.foreground": "{{colors.on_primary.dark.hex}}", "activityBarBadge.foreground": "{{colors.on_primary.dark.hex}}",
"sideBar.background": "{{colors.surface_container_low.dark.hex}}",
"sideBar.background": "{{colors.background.dark.hex}}",
"sideBar.foreground": "{{colors.on_surface.dark.hex}}", "sideBar.foreground": "{{colors.on_surface.dark.hex}}",
"sideBar.border": "{{colors.background.dark.hex}}", "statusBarItem.hoverBackground": "{{colors.surface_container_low.dark.hex}}",
"statusBarItem.activeBackground": "{{colors.surface_container.dark.hex}}",
"sideBarTitle.foreground": "{{colors.on_surface.dark.hex}}", "sideBarTitle.foreground": "{{colors.on_surface.dark.hex}}",
"sideBarSectionHeader.background": "{{colors.background.dark.hex}}", "panel.background": "{{colors.surface_container_low.dark.hex}}",
"panelTitle.activeForeground": "{{colors.on_surface.dark.hex}}",
"panelTitle.inactiveForeground": "{{colors.outline.dark.hex}}",
"sideBarSectionHeader.background": "{{colors.surface_container.dark.hex}}",
"sideBarSectionHeader.foreground": "{{colors.on_surface.dark.hex}}", "sideBarSectionHeader.foreground": "{{colors.on_surface.dark.hex}}",
//
"list.activeSelectionBackground": "{{colors.primary_container.dark.hex}}", // Tabs + editor groups
"list.activeSelectionForeground": "{{colors.on_primary_container.dark.hex}}", //
"list.inactiveSelectionBackground": "{{colors.surface_container.dark.hex}}", "editorGroupHeader.tabsBackground": "{{colors.background.dark.hex}}",
"list.inactiveSelectionForeground": "{{colors.on_surface.dark.hex}}", "tab.activeBackground": "{{colors.surface_container_low.dark.hex}}",
"list.hoverBackground": "{{colors.surface_container.dark.hex}}",
"list.hoverForeground": "{{colors.on_surface.dark.hex}}",
"list.focusBackground": "{{colors.surface_container_high.dark.hex}}",
"list.focusForeground": "{{colors.on_surface.dark.hex}}",
"list.highlightForeground": "{{colors.primary.dark.hex}}",
"statusBar.background": "{{colors.background.dark.hex}}",
"statusBar.foreground": "{{colors.on_surface.dark.hex}}",
"statusBar.border": "{{colors.background.dark.hex}}",
"statusBar.noFolderBackground": "{{colors.background.dark.hex}}",
"statusBar.debuggingBackground": "{{colors.error.dark.hex}}",
"statusBar.debuggingForeground": "{{colors.on_error.dark.hex}}",
"tab.activeBackground": "{{colors.background.dark.hex}}",
"tab.inactiveBackground": "{{colors.background.dark.hex}}", "tab.inactiveBackground": "{{colors.background.dark.hex}}",
"tab.activeForeground": "{{colors.on_surface.dark.hex}}", "tab.activeForeground": "{{colors.on_surface.dark.hex}}",
"tab.inactiveForeground": "{{colors.outline.dark.hex}}", "tab.inactiveForeground": "{{colors.outline.dark.hex}}",
"tab.border": "{{colors.background.dark.hex}}", "tab.activeBorderTop": "{{colors.primary.dark.hex}}",
"tab.activeBorder": "{{colors.primary.dark.hex}}", //
"tab.unfocusedActiveBorder": "{{colors.outline.dark.hex}}", // Lists (files, search results, etc.)
//
"editorGroupHeader.tabsBackground": "{{colors.background.dark.hex}}", "list.activeSelectionBackground": "{{colors.primary_container.dark.hex}}",
"editorGroupHeader.noTabsBackground": "{{colors.background.dark.hex}}", "list.activeSelectionForeground": "{{colors.on_primary_container.dark.hex}}",
"list.inactiveSelectionBackground": "{{colors.surface_container.dark.hex}}",
"titleBar.activeBackground": "{{colors.background.dark.hex}}", "list.hoverBackground": "{{colors.surface_container.dark.hex}}",
"titleBar.activeForeground": "{{colors.on_surface.dark.hex}}", "list.hoverForeground": "{{colors.on_surface.dark.hex}}",
"titleBar.inactiveBackground": "{{colors.background.dark.hex}}", "list.focusForeground": "{{colors.on_surface.dark.hex}}",
"titleBar.inactiveForeground": "{{colors.outline.dark.hex}}", "list.focusOutline": "{{colors.primary.dark.hex}}",
"titleBar.border": "{{colors.background.dark.hex}}", "list.focusBackground": "{{colors.surface_container_high.dark.hex}}",
"list.highlightForeground": "{{colors.primary.dark.hex}}",
"input.background": "{{colors.background.dark.hex}}", "list.errorForeground": "{{colors.error.dark.hex}}",
"list.warningForeground": "{{colors.secondary.dark.hex}}",
//
// Inputs + dropdowns
//
"input.background": "{{colors.surface_container_low.dark.hex}}",
"input.foreground": "{{colors.on_surface.dark.hex}}", "input.foreground": "{{colors.on_surface.dark.hex}}",
"input.border": "{{colors.outline.dark.hex}}", "input.border": "{{colors.outline_variant.dark.hex}}",
"input.placeholderForeground": "{{colors.outline.dark.hex}}", "input.placeholderForeground": "{{colors.outline.dark.hex}}",
"inputOption.activeBorder": "{{colors.primary.dark.hex}}", "dropdown.background": "{{colors.surface_container_low.dark.hex}}",
"inputValidation.errorBackground": "{{colors.error.dark.hex}}",
"inputValidation.errorBorder": "{{colors.error.dark.hex}}",
"dropdown.background": "{{colors.background.dark.hex}}",
"dropdown.foreground": "{{colors.on_surface.dark.hex}}", "dropdown.foreground": "{{colors.on_surface.dark.hex}}",
"dropdown.border": "{{colors.outline.dark.hex}}", "dropdown.border": "{{colors.outline_variant.dark.hex}}",
"quickInput.background": "{{colors.surface_container_low.dark.hex}}",
"quickInput.background": "{{colors.background.dark.hex}}",
"quickInput.foreground": "{{colors.on_surface.dark.hex}}", "quickInput.foreground": "{{colors.on_surface.dark.hex}}",
"quickInputList.focusBackground": "{{colors.surface_container_high.dark.hex}}", "widget.shadow": "{{colors.background.dark.hex}}80",
"quickInputList.focusForeground": "{{colors.on_surface.dark.hex}}", //
// Buttons
//
"button.background": "{{colors.primary.dark.hex}}", "button.background": "{{colors.primary.dark.hex}}",
"button.foreground": "{{colors.on_primary.dark.hex}}", "button.foreground": "{{colors.on_primary.dark.hex}}",
"button.hoverBackground": "{{colors.primary_container.dark.hex}}", "button.hoverBackground": "{{colors.primary_container.dark.hex}}",
//
"focusBorder": "{{colors.primary.dark.hex}}", // Status bar
"badge.background": "{{colors.secondary.dark.hex}}", //
"badge.foreground": "{{colors.on_secondary.dark.hex}}", "statusBar.background": "{{colors.background.dark.hex}}",
"statusBar.foreground": "{{colors.on_surface.dark.hex}}",
"panel.background": "{{colors.background.dark.hex}}", "statusBar.border": "{{colors.outline_variant.dark.hex}}",
"panel.border": "{{colors.primary.dark.hex}}", "statusBar.noFolderBackground": "{{colors.background.dark.hex}}",
"panelTitle.activeBorder": "{{colors.primary.dark.hex}}", "statusBar.noFolderForeground": "{{colors.on_surface.dark.hex}}",
"panelTitle.activeForeground": "{{colors.on_surface.dark.hex}}", "statusBar.debuggingBackground": "{{colors.error.dark.hex}}",
"panelTitle.inactiveForeground": "{{colors.outline.dark.hex}}", "statusBar.debuggingForeground": "{{colors.on_error.dark.hex}}",
//
"terminal.background": "{{colors.background.dark.hex}}", // Notifications
"terminal.foreground": "{{colors.on_surface.dark.hex}}", //
"terminal.ansiBlack": "{{dank16.color0.hex}}", "notificationCenterHeader.background": "{{colors.surface_container_low.dark.hex}}",
"terminal.ansiRed": "{{dank16.color1.hex}}", "notificationCenterHeader.foreground": "{{colors.on_surface.dark.hex}}",
"terminal.ansiGreen": "{{dank16.color2.hex}}", "notificationCenter.border": "{{colors.outline_variant.dark.hex}}",
"terminal.ansiYellow": "{{dank16.color3.hex}}", "notifications.background": "{{colors.surface_container_low.dark.hex}}",
"terminal.ansiBlue": "{{dank16.color4.hex}}", "notifications.foreground": "{{colors.on_surface.dark.hex}}",
"terminal.ansiMagenta": "{{dank16.color5.hex}}", "notifications.border": "{{colors.outline_variant.dark.hex}}",
"terminal.ansiCyan": "{{dank16.color6.hex}}", "notificationsErrorIcon.foreground": "{{colors.error.dark.hex}}",
"terminal.ansiWhite": "{{dank16.color7.hex}}", "notificationsWarningIcon.foreground": "{{colors.secondary.dark.hex}}",
"terminal.ansiBrightBlack": "{{dank16.color8.hex}}", "notificationsInfoIcon.foreground": "{{colors.primary.dark.hex}}",
"terminal.ansiBrightRed": "{{dank16.color9.hex}}", //
"terminal.ansiBrightGreen": "{{dank16.color10.hex}}", // Breadcrumbs
"terminal.ansiBrightYellow": "{{dank16.color11.hex}}", //
"terminal.ansiBrightBlue": "{{dank16.color12.hex}}", "breadcrumb.background": "{{colors.surface_container.dark.hex}}",
"terminal.ansiBrightMagenta": "{{dank16.color13.hex}}",
"terminal.ansiBrightCyan": "{{dank16.color14.hex}}",
"terminal.ansiBrightWhite": "{{dank16.color15.hex}}",
"gitDecoration.modifiedResourceForeground": "{{colors.primary.dark.hex}}",
"gitDecoration.addedResourceForeground": "{{colors.primary.dark.hex}}",
"gitDecoration.stageModifiedResourceForeground": "{{colors.primary.dark.hex}}",
"gitDecoration.stageDeletedResourceForeground": "{{colors.error.dark.hex}}",
"gitDecoration.deletedResourceForeground": "{{colors.error.dark.hex}}",
"gitDecoration.untrackedResourceForeground": "{{colors.secondary.dark.hex}}",
"gitDecoration.ignoredResourceForeground": "{{colors.outline.dark.hex}}",
"gitDecoration.conflictingResourceForeground": "{{colors.error_container.dark.hex}}",
"gitDecoration.submoduleResourceForeground": "{{colors.primary.dark.hex}}",
"editorWidget.background": "{{colors.background.dark.hex}}",
"editorWidget.border": "{{colors.outline.dark.hex}}",
"editorSuggestWidget.background": "{{colors.background.dark.hex}}",
"editorSuggestWidget.border": "{{colors.outline.dark.hex}}",
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.dark.hex}}",
"editorSuggestWidget.highlightForeground": "{{colors.primary.dark.hex}}",
"peekView.border": "{{colors.primary.dark.hex}}",
"peekViewEditor.background": "{{colors.background.dark.hex}}",
"peekViewResult.background": "{{colors.background.dark.hex}}",
"peekViewTitle.background": "{{colors.background.dark.hex}}",
"notificationCenter.border": "{{colors.outline.dark.hex}}",
"notifications.background": "{{colors.background.dark.hex}}",
"notifications.border": "{{colors.outline.dark.hex}}",
"breadcrumb.foreground": "{{colors.outline.dark.hex}}", "breadcrumb.foreground": "{{colors.outline.dark.hex}}",
"breadcrumb.focusForeground": "{{colors.on_surface.dark.hex}}", "breadcrumb.focusForeground": "{{colors.on_surface.dark.hex}}",
"breadcrumb.activeSelectionForeground": "{{colors.primary.dark.hex}}", "breadcrumb.activeSelectionForeground": "{{colors.primary.dark.hex}}",
//
// Editor highlights + guides
//
"editorLineNumber.foreground": "{{colors.outline.dark.hex}}",
"editorLineNumber.activeForeground": "{{colors.primary.dark.hex}}",
"editorCursor.foreground": "{{colors.primary.dark.hex}}",
"editor.lineHighlightBackground": "{{colors.surface_container.dark.hex}}66",
"editor.wordHighlightBackground": "{{colors.secondary.dark.hex}}22",
"editor.wordHighlightStrongBackground": "{{colors.tertiary.dark.hex}}22",
"editor.selectionBackground": "{{colors.primary_container.dark.hex}}66",
"editor.inactiveSelectionBackground": "{{colors.primary_container.dark.hex}}33",
"editorWhitespace.foreground": "{{colors.outline.dark.hex}}66",
"editorIndentGuide.background1": "{{colors.outline.dark.hex}}33",
"editorIndentGuide.activeBackground1": "{{colors.primary.dark.hex}}99",
"editorOverviewRuler.border": "{{colors.outline_variant.dark.hex}}",
"editorOverviewRuler.errorForeground": "{{colors.error.dark.hex}}88",
"editorOverviewRuler.warningForeground": "{{colors.tertiary.dark.hex}}88",
"editorOverviewRuler.infoForeground": "{{colors.primary.dark.hex}}88",
"editorStickyScroll.background": "{{colors.background.dark.hex}}",
"editorStickyScrollHover.background": "{{colors.surface_container_low.dark.hex}}",
"editorBracketHighlight.unexpectedBracket.foreground": "{{colors.outline.dark.hex}}",
//
// Diff editor
//
"diffEditor.insertedTextBackground": "{{colors.secondary.dark.hex}}20",
"diffEditor.removedTextBackground": "{{colors.error.dark.hex}}20",
//
// Git
//
"gitDecoration.modifiedResourceForeground": "{{colors.primary.dark.hex}}",
"gitDecoration.addedResourceForeground": "{{colors.secondary.dark.hex}}",
"gitDecoration.deletedResourceForeground": "{{colors.error.dark.hex}}",
"gitDecoration.ignoredResourceForeground": "{{colors.outline.dark.hex}}",
//
// Peek, Hover, Widgets
//
"editorHoverWidget.background": "{{colors.surface_container_high.dark.hex}}",
"editorHoverWidget.border": "{{colors.outline.dark.hex}}",
"editorSuggestWidget.background": "{{colors.surface_container.dark.hex}}",
"editorSuggestWidget.foreground": "{{colors.on_surface.dark.hex}}",
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.dark.hex}}",
"editorSuggestWidget.highlightForeground": "{{colors.primary.dark.hex}}",
//
// Scrollbar
//
"scrollbarSlider.background": "{{colors.outline.dark.hex}}40", "scrollbarSlider.background": "{{colors.outline.dark.hex}}40",
"scrollbarSlider.hoverBackground": "{{colors.outline.dark.hex}}60", "scrollbarSlider.hoverBackground": "{{colors.outline.dark.hex}}60",
"scrollbarSlider.activeBackground": "{{colors.outline.dark.hex}}80", "scrollbarSlider.activeBackground": "{{colors.outline.dark.hex}}80",
//
"editorError.foreground": "{{colors.error.dark.hex}}", // Terminal (Dank16)
"editorWarning.foreground": "{{colors.tertiary.dark.hex}}", //
"editorInfo.foreground": "{{colors.primary.dark.hex}}", "terminal.background": "{{colors.background.dark.hex}}",
"terminal.foreground": "{{colors.on_surface.dark.hex}}",
"editorGutter.addedBackground": "{{colors.secondary.dark.hex}}", "terminal.ansiBlack": "{{dank16.color0.dark.hex}}",
"editorGutter.modifiedBackground": "{{colors.tertiary.dark.hex}}", "terminal.ansiRed": "{{dank16.color1.dark.hex}}",
"editorGutter.deletedBackground": "{{colors.error.dark.hex}}", "terminal.ansiGreen": "{{dank16.color2.dark.hex}}",
"terminal.ansiYellow": "{{dank16.color3.dark.hex}}",
"diffEditor.insertedTextBackground": "{{colors.secondary.dark.hex}}20", "terminal.ansiBlue": "{{dank16.color4.dark.hex}}",
"diffEditor.removedTextBackground": "{{colors.error.dark.hex}}20", "terminal.ansiMagenta": "{{dank16.color5.dark.hex}}",
"terminal.ansiCyan": "{{dank16.color6.dark.hex}}",
"merge.currentHeaderBackground": "{{colors.primary.dark.hex}}40", "terminal.ansiWhite": "{{dank16.color7.dark.hex}}",
"merge.incomingHeaderBackground": "{{colors.secondary.dark.hex}}40", "terminal.ansiBrightBlack": "{{dank16.color8.dark.hex}}",
"terminal.ansiBrightRed": "{{dank16.color9.dark.hex}}",
"menubar.selectionBackground": "{{colors.surface_container.dark.hex}}", "terminal.ansiBrightGreen": "{{dank16.color10.dark.hex}}",
"menu.background": "{{colors.background.dark.hex}}", "terminal.ansiBrightYellow": "{{dank16.color11.dark.hex}}",
"menu.foreground": "{{colors.on_surface.dark.hex}}", "terminal.ansiBrightBlue": "{{dank16.color12.dark.hex}}",
"menu.selectionBackground": "{{colors.surface_container_high.dark.hex}}", "terminal.ansiBrightMagenta": "{{dank16.color13.dark.hex}}",
"menu.selectionForeground": "{{colors.on_surface.dark.hex}}", "terminal.ansiBrightCyan": "{{dank16.color14.dark.hex}}",
"terminal.ansiBrightWhite": "{{dank16.color15.dark.hex}}"
"debugToolBar.background": "{{colors.background.dark.hex}}",
"debugExceptionWidget.background": "{{colors.background.dark.hex}}",
"debugExceptionWidget.border": "{{colors.error.dark.hex}}"
}, },
//
// Token colors
//
"tokenColors": [ "tokenColors": [
{ {
"scope": ["variable", "meta.object-literal.key"], "scope": [
"comment"
],
"settings": { "settings": {
"foreground": "{{colors.on_surface.dark.hex}}" "foreground": "{{dank16.color8.dark.hex}}"
} }
}, },
{ {
"scope": ["string", "constant.other.symbol"], "scope": [
"keyword"
],
"settings": { "settings": {
"foreground": "{{colors.secondary.dark.hex}}" "foreground": "{{dank16.color5.dark.hex}}"
} }
}, },
{ {
"scope": ["constant.numeric", "constant.language", "constant.character"], "scope": [
"string"
],
"settings": { "settings": {
"foreground": "{{colors.tertiary.dark.hex}}" "foreground": "{{dank16.color3.dark.hex}}"
} }
}, },
{ {
"scope": ["entity.name.type", "support.type", "entity.name.class"], "scope": [
"constant",
"number"
],
"settings": { "settings": {
"foreground": "{{colors.tertiary.dark.hex}}" "foreground": "{{dank16.color12.dark.hex}}"
} }
}, },
{ {
"scope": ["entity.name.function", "support.function"], "scope": [
"variable"
],
"settings": { "settings": {
"foreground": "{{colors.primary.dark.hex}}" "foreground": "{{dank16.color15.dark.hex}}"
} }
}, },
{ {
"scope": ["support.class", "support.variable", "variable.language"], "scope": [
"entity.name.function"
],
"settings": { "settings": {
"foreground": "{{colors.secondary.dark.hex}}" "foreground": "{{dank16.color2.dark.hex}}"
} }
}, },
{ {
"scope": ["invalid"], "scope": [
"entity.name.class",
"support.type"
],
"settings": {
"foreground": "{{dank16.color12.dark.hex}}"
}
},
{
"scope": [
"invalid"
],
"settings": { "settings": {
"foreground": "{{colors.error.dark.hex}}" "foreground": "{{colors.error.dark.hex}}"
} }
}, },
{ {
"scope": ["invalid.deprecated"], "scope": [
"settings": { "markup.heading"
"foreground": "{{colors.outline.dark.hex}}" ],
}
},
{
"scope": ["markup.heading"],
"settings": { "settings": {
"foreground": "{{colors.primary.dark.hex}}", "foreground": "{{colors.primary.dark.hex}}",
"fontStyle": "bold" "fontStyle": "bold"
} }
},
{
"scope": ["markup.bold"],
"settings": {
"foreground": "{{colors.tertiary.dark.hex}}",
"fontStyle": "bold"
}
},
{
"scope": ["markup.italic"],
"settings": {
"foreground": "{{colors.primary.dark.hex}}",
"fontStyle": "italic"
}
},
{
"scope": ["markup.underline"],
"settings": {
"fontStyle": "underline"
}
},
{
"scope": ["markup.quote"],
"settings": {
"foreground": "{{colors.secondary.dark.hex}}"
}
},
{
"scope": ["markup.list"],
"settings": {
"foreground": "{{colors.on_surface.dark.hex}}"
}
},
{
"scope": ["markup.raw", "markup.inline.raw"],
"settings": {
"foreground": "{{colors.secondary.dark.hex}}"
}
},
{
"scope": ["comment", "punctuation.definition.comment"],
"settings": {
"foreground": "{{dank16.color8.hex}}"
}
},
{
"scope": "keyword",
"settings": {
"foreground": "{{dank16.color5.hex}}"
}
},
{
"scope": "storage.type",
"settings": {
"foreground": "{{dank16.color13.hex}}"
}
},
{
"scope": "storage.modifier",
"settings": {
"foreground": "{{dank16.color5.hex}}"
}
},
{
"scope": "variable",
"settings": {
"foreground": "{{dank16.color15.hex}}"
}
},
{
"scope": "variable.parameter",
"settings": {
"foreground": "{{dank16.color7.hex}}"
}
},
{
"scope": ["meta.object-literal.key", "meta.property.object", "variable.other.property"],
"settings": {
"foreground": "{{dank16.color4.hex}}"
}
},
{
"scope": "constant.other.symbol",
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": ["constant.numeric", "constant.language"],
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": "constant.character",
"settings": {
"foreground": "{{dank16.color3.hex}}"
}
},
{
"scope": ["entity.name.type", "entity.name.class"],
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": "support.type",
"settings": {
"foreground": "{{dank16.color13.hex}}"
}
},
{
"scope": ["entity.name.function", "support.function"],
"settings": {
"foreground": "{{dank16.color2.hex}}"
}
},
{
"scope": ["support.class", "support.variable"],
"settings": {
"foreground": "{{dank16.color15.hex}}"
}
},
{
"scope": "variable.language",
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": "entity.name.tag.yaml",
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": ["string.unquoted.plain.out.yaml", "string.unquoted.yaml"],
"settings": {
"foreground": "{{dank16.color15.hex}}"
}
},
{
"scope": "string",
"settings": {
"foreground": "{{dank16.color3.hex}}"
}
} }
], ],
//
"semanticHighlighting": true, // Semantic tokens
//
"semanticTokenColors": { "semanticTokenColors": {
"variable": { "variable": {
"foreground": "{{dank16.color15.hex}}" "foreground": "{{dank16.color15.dark.hex}}"
},
"variable.readonly": {
"foreground": "{{dank16.color12.hex}}"
},
"property": {
"foreground": "{{dank16.color4.hex}}"
},
"function": {
"foreground": "{{dank16.color2.hex}}"
},
"method": {
"foreground": "{{dank16.color2.hex}}"
},
"type": {
"foreground": "{{dank16.color12.hex}}"
},
"class": {
"foreground": "{{dank16.color12.hex}}"
},
"typeParameter": {
"foreground": "{{dank16.color13.hex}}"
},
"enumMember": {
"foreground": "{{dank16.color12.hex}}"
},
"string": {
"foreground": "{{dank16.color3.hex}}"
},
"number": {
"foreground": "{{dank16.color12.hex}}"
},
"comment": {
"foreground": "{{dank16.color8.hex}}"
},
"keyword": {
"foreground": "{{dank16.color5.hex}}"
},
"operator": {
"foreground": "{{dank16.color15.hex}}"
}, },
"parameter": { "parameter": {
"foreground": "{{dank16.color7.hex}}" "foreground": "{{dank16.color7.dark.hex}}"
}, },
"namespace": { "property": {
"foreground": "{{dank16.color15.hex}}" "foreground": "{{dank16.color4.dark.hex}}"
},
"function": {
"foreground": "{{dank16.color2.dark.hex}}"
},
"string": {
"foreground": "{{dank16.color3.dark.hex}}"
},
"number": {
"foreground": "{{dank16.color12.dark.hex}}"
},
"keyword": {
"foreground": "{{dank16.color5.dark.hex}}"
},
"comment": {
"foreground": "{{dank16.color8.dark.hex}}"
} }
} }
} }
@@ -1,431 +1,251 @@
{ {
"$schema": "vscode://schemas/color-theme", "$schema": "vscode://schemas/color-theme",
"name": "Dynamic Base16 DankShell", "name": "Dynamic Base16 DankShell",
"semanticHighlighting": true,
"colors": { "colors": {
"foreground": "{{colors.on_surface.default.hex}}",
"editor.background": "{{colors.background.default.hex}}", "editor.background": "{{colors.background.default.hex}}",
"editor.foreground": "{{colors.on_surface.default.hex}}", "editor.foreground": "{{colors.on_surface.default.hex}}",
"editorLineNumber.foreground": "{{colors.outline.default.hex}}", "errorForeground": "{{colors.error.default.hex}}",
"editorLineNumber.activeForeground": "{{colors.on_surface.default.hex}}", "panel.border": "{{colors.outline_variant.default.hex}}",
"editorCursor.foreground": "{{colors.primary.default.hex}}", "panelTitle.activeBorder": "{{colors.primary.default.hex}}",
"editor.selectionBackground": "{{colors.primary_container.default.hex}}", "sideBar.border": "{{colors.outline_variant.default.hex}}",
"editor.inactiveSelectionBackground": "{{colors.background.default.hex}}", "editorGroup.border": "{{colors.outline_variant.default.hex}}",
"editor.lineHighlightBackground": "{{colors.background.default.hex}}", "tab.border": "{{colors.outline_variant.default.hex}}",
"editorIndentGuide.background": "{{colors.background.default.hex}}", "titleBar.border": "{{colors.outline_variant.default.hex}}",
"editorIndentGuide.activeBackground": "{{colors.outline.default.hex}}", "focusBorder": "{{colors.primary.default.hex}}",
"editorWhitespace.foreground": "{{colors.outline_variant.default.hex}}", "selection.background": "{{colors.primary_container.default.hex}}66",
"editorBracketMatch.background": "{{colors.background.default.hex}}", "titleBar.activeBackground": "{{colors.background.default.hex}}",
"editorBracketMatch.border": "{{colors.primary.default.hex}}", "titleBar.activeForeground": "{{colors.on_surface.default.hex}}",
"titleBar.inactiveForeground": "{{colors.outline.default.hex}}",
"activityBar.background": "{{colors.background.default.hex}}", "activityBar.background": "{{colors.background.default.hex}}",
"activityBar.foreground": "{{colors.on_surface.default.hex}}", "activityBar.foreground": "{{colors.on_surface.default.hex}}",
"activityBar.inactiveForeground": "{{colors.outline.default.hex}}",
"activityBar.activeBorder": "{{colors.primary.default.hex}}", "activityBar.activeBorder": "{{colors.primary.default.hex}}",
"activityBar.activeBackground": "{{colors.background.default.hex}}",
"activityBarBadge.background": "{{colors.primary.default.hex}}", "activityBarBadge.background": "{{colors.primary.default.hex}}",
"activityBarBadge.foreground": "{{colors.on_primary.default.hex}}", "activityBarBadge.foreground": "{{colors.on_primary.default.hex}}",
"sideBar.background": "{{colors.surface_container_low.default.hex}}",
"sideBar.background": "{{colors.background.default.hex}}",
"sideBar.foreground": "{{colors.on_surface.default.hex}}", "sideBar.foreground": "{{colors.on_surface.default.hex}}",
"sideBar.border": "{{colors.background.default.hex}}", "statusBarItem.hoverBackground": "{{colors.surface_container_low.default.hex}}",
"statusBarItem.activeBackground": "{{colors.surface_container.default.hex}}",
"sideBarTitle.foreground": "{{colors.on_surface.default.hex}}", "sideBarTitle.foreground": "{{colors.on_surface.default.hex}}",
"sideBarSectionHeader.background": "{{colors.background.default.hex}}", "panel.background": "{{colors.surface_container_low.default.hex}}",
"panelTitle.activeForeground": "{{colors.on_surface.default.hex}}",
"panelTitle.inactiveForeground": "{{colors.outline.default.hex}}",
"sideBarSectionHeader.background": "{{colors.surface_container.default.hex}}",
"sideBarSectionHeader.foreground": "{{colors.on_surface.default.hex}}", "sideBarSectionHeader.foreground": "{{colors.on_surface.default.hex}}",
"editorGroupHeader.tabsBackground": "{{colors.background.default.hex}}",
"list.activeSelectionBackground": "{{colors.primary_container.default.hex}}", "tab.activeBackground": "{{colors.surface_container_low.default.hex}}",
"list.activeSelectionForeground": "{{colors.on_primary_container.default.hex}}",
"list.inactiveSelectionBackground": "{{colors.surface_container.default.hex}}",
"list.inactiveSelectionForeground": "{{colors.on_surface.default.hex}}",
"list.hoverBackground": "{{colors.surface_container.default.hex}}",
"list.hoverForeground": "{{colors.on_surface.default.hex}}",
"list.focusBackground": "{{colors.surface_container_high.default.hex}}",
"list.focusForeground": "{{colors.on_surface.default.hex}}",
"list.highlightForeground": "{{colors.primary.default.hex}}",
"statusBar.background": "{{colors.background.default.hex}}",
"statusBar.foreground": "{{colors.on_surface.default.hex}}",
"statusBar.border": "{{colors.background.default.hex}}",
"statusBar.noFolderBackground": "{{colors.background.default.hex}}",
"statusBar.debuggingBackground": "{{colors.error.default.hex}}",
"statusBar.debuggingForeground": "{{colors.on_error.default.hex}}",
"tab.activeBackground": "{{colors.background.default.hex}}",
"tab.inactiveBackground": "{{colors.background.default.hex}}", "tab.inactiveBackground": "{{colors.background.default.hex}}",
"tab.activeForeground": "{{colors.on_surface.default.hex}}", "tab.activeForeground": "{{colors.on_surface.default.hex}}",
"tab.inactiveForeground": "{{colors.outline.default.hex}}", "tab.inactiveForeground": "{{colors.outline.default.hex}}",
"tab.border": "{{colors.background.default.hex}}", "tab.activeBorderTop": "{{colors.primary.default.hex}}",
"tab.activeBorder": "{{colors.primary.default.hex}}", "list.activeSelectionBackground": "{{colors.primary_container.default.hex}}",
"tab.unfocusedActiveBorder": "{{colors.outline.default.hex}}", "list.activeSelectionForeground": "{{colors.on_primary_container.default.hex}}",
"list.inactiveSelectionBackground": "{{colors.surface_container.default.hex}}",
"editorGroupHeader.tabsBackground": "{{colors.background.default.hex}}", "list.hoverBackground": "{{colors.surface_container.default.hex}}",
"editorGroupHeader.noTabsBackground": "{{colors.background.default.hex}}", "list.hoverForeground": "{{colors.on_surface.default.hex}}",
"list.focusForeground": "{{colors.on_surface.default.hex}}",
"titleBar.activeBackground": "{{colors.background.default.hex}}", "list.focusOutline": "{{colors.primary.default.hex}}",
"titleBar.activeForeground": "{{colors.on_surface.default.hex}}", "list.focusBackground": "{{colors.surface_container_high.default.hex}}",
"titleBar.inactiveBackground": "{{colors.background.default.hex}}", "list.highlightForeground": "{{colors.primary.default.hex}}",
"titleBar.inactiveForeground": "{{colors.outline.default.hex}}", "list.errorForeground": "{{colors.error.default.hex}}",
"titleBar.border": "{{colors.background.default.hex}}", "list.warningForeground": "{{colors.secondary.default.hex}}",
"input.background": "{{colors.surface_container_low.default.hex}}",
"input.background": "{{colors.background.default.hex}}",
"input.foreground": "{{colors.on_surface.default.hex}}", "input.foreground": "{{colors.on_surface.default.hex}}",
"input.border": "{{colors.outline.default.hex}}", "input.border": "{{colors.outline_variant.default.hex}}",
"input.placeholderForeground": "{{colors.outline.default.hex}}", "input.placeholderForeground": "{{colors.outline.default.hex}}",
"inputOption.activeBorder": "{{colors.primary.default.hex}}", "dropdown.background": "{{colors.surface_container_low.default.hex}}",
"inputValidation.errorBackground": "{{colors.error.default.hex}}",
"inputValidation.errorBorder": "{{colors.error.default.hex}}",
"dropdown.background": "{{colors.background.default.hex}}",
"dropdown.foreground": "{{colors.on_surface.default.hex}}", "dropdown.foreground": "{{colors.on_surface.default.hex}}",
"dropdown.border": "{{colors.outline.default.hex}}", "dropdown.border": "{{colors.outline_variant.default.hex}}",
"quickInput.background": "{{colors.surface_container_low.default.hex}}",
"quickInput.background": "{{colors.background.default.hex}}",
"quickInput.foreground": "{{colors.on_surface.default.hex}}", "quickInput.foreground": "{{colors.on_surface.default.hex}}",
"quickInputList.focusBackground": "{{colors.surface_container_high.default.hex}}", "widget.shadow": "{{colors.background.default.hex}}80",
"quickInputList.focusForeground": "{{colors.on_surface.default.hex}}",
"button.background": "{{colors.primary.default.hex}}", "button.background": "{{colors.primary.default.hex}}",
"button.foreground": "{{colors.on_primary.default.hex}}", "button.foreground": "{{colors.on_primary.default.hex}}",
"button.hoverBackground": "{{colors.primary_container.default.hex}}", "button.hoverBackground": "{{colors.primary_container.default.hex}}",
"statusBar.background": "{{colors.background.default.hex}}",
"focusBorder": "{{colors.primary.default.hex}}", "statusBar.foreground": "{{colors.on_surface.default.hex}}",
"badge.background": "{{colors.secondary.default.hex}}", "statusBar.border": "{{colors.outline_variant.default.hex}}",
"badge.foreground": "{{colors.on_secondary.default.hex}}", "statusBar.noFolderBackground": "{{colors.background.default.hex}}",
"statusBar.noFolderForeground": "{{colors.on_surface.default.hex}}",
"panel.background": "{{colors.background.default.hex}}", "statusBar.debuggingBackground": "{{colors.error.default.hex}}",
"panel.border": "{{colors.primary.default.hex}}", "statusBar.debuggingForeground": "{{colors.on_error.default.hex}}",
"panelTitle.activeBorder": "{{colors.primary.default.hex}}", "notificationCenterHeader.background": "{{colors.surface_container_low.default.hex}}",
"panelTitle.activeForeground": "{{colors.on_surface.default.hex}}", "notificationCenterHeader.foreground": "{{colors.on_surface.default.hex}}",
"panelTitle.inactiveForeground": "{{colors.outline.default.hex}}", "notificationCenter.border": "{{colors.outline_variant.default.hex}}",
"notifications.background": "{{colors.surface_container_low.default.hex}}",
"terminal.background": "{{colors.background.default.hex}}", "notifications.foreground": "{{colors.on_surface.default.hex}}",
"terminal.foreground": "{{colors.on_surface.default.hex}}", "notifications.border": "{{colors.outline_variant.default.hex}}",
"terminal.ansiBlack": "{{dank16.color0.hex}}", "notificationsErrorIcon.foreground": "{{colors.error.default.hex}}",
"terminal.ansiRed": "{{dank16.color1.hex}}", "notificationsWarningIcon.foreground": "{{colors.secondary.default.hex}}",
"terminal.ansiGreen": "{{dank16.color2.hex}}", "notificationsInfoIcon.foreground": "{{colors.primary.default.hex}}",
"terminal.ansiYellow": "{{dank16.color3.hex}}", "breadcrumb.background": "{{colors.surface_container.default.hex}}",
"terminal.ansiBlue": "{{dank16.color4.hex}}",
"terminal.ansiMagenta": "{{dank16.color5.hex}}",
"terminal.ansiCyan": "{{dank16.color6.hex}}",
"terminal.ansiWhite": "{{dank16.color7.hex}}",
"terminal.ansiBrightBlack": "{{dank16.color8.hex}}",
"terminal.ansiBrightRed": "{{dank16.color9.hex}}",
"terminal.ansiBrightGreen": "{{dank16.color10.hex}}",
"terminal.ansiBrightYellow": "{{dank16.color11.hex}}",
"terminal.ansiBrightBlue": "{{dank16.color12.hex}}",
"terminal.ansiBrightMagenta": "{{dank16.color13.hex}}",
"terminal.ansiBrightCyan": "{{dank16.color14.hex}}",
"terminal.ansiBrightWhite": "{{dank16.color15.hex}}",
"gitDecoration.modifiedResourceForeground": "{{colors.primary.default.hex}}",
"gitDecoration.addedResourceForeground": "{{colors.primary.default.hex}}",
"gitDecoration.stageModifiedResourceForeground": "{{colors.primary.default.hex}}",
"gitDecoration.stageDeletedResourceForeground": "{{colors.error.default.hex}}",
"gitDecoration.deletedResourceForeground": "{{colors.error.default.hex}}",
"gitDecoration.untrackedResourceForeground": "{{colors.secondary.default.hex}}",
"gitDecoration.ignoredResourceForeground": "{{colors.outline.default.hex}}",
"gitDecoration.conflictingResourceForeground": "{{colors.error_container.default.hex}}",
"gitDecoration.submoduleResourceForeground": "{{colors.primary.default.hex}}",
"editorWidget.background": "{{colors.background.default.hex}}",
"editorWidget.border": "{{colors.outline.default.hex}}",
"editorSuggestWidget.background": "{{colors.background.default.hex}}",
"editorSuggestWidget.border": "{{colors.outline.default.hex}}",
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.default.hex}}",
"editorSuggestWidget.highlightForeground": "{{colors.primary.default.hex}}",
"peekView.border": "{{colors.primary.default.hex}}",
"peekViewEditor.background": "{{colors.background.default.hex}}",
"peekViewResult.background": "{{colors.background.default.hex}}",
"peekViewTitle.background": "{{colors.background.default.hex}}",
"notificationCenter.border": "{{colors.outline.default.hex}}",
"notifications.background": "{{colors.background.default.hex}}",
"notifications.border": "{{colors.outline.default.hex}}",
"breadcrumb.foreground": "{{colors.outline.default.hex}}", "breadcrumb.foreground": "{{colors.outline.default.hex}}",
"breadcrumb.focusForeground": "{{colors.on_surface.default.hex}}", "breadcrumb.focusForeground": "{{colors.on_surface.default.hex}}",
"breadcrumb.activeSelectionForeground": "{{colors.primary.default.hex}}", "breadcrumb.activeSelectionForeground": "{{colors.primary.default.hex}}",
"editorLineNumber.foreground": "{{colors.outline.default.hex}}",
"editorLineNumber.activeForeground": "{{colors.primary.default.hex}}",
"editorCursor.foreground": "{{colors.primary.default.hex}}",
"editor.lineHighlightBackground": "{{colors.surface_container.default.hex}}66",
"editor.wordHighlightBackground": "{{colors.secondary.default.hex}}22",
"editor.wordHighlightStrongBackground": "{{colors.tertiary.default.hex}}22",
"editor.selectionBackground": "{{colors.primary_container.default.hex}}66",
"editor.inactiveSelectionBackground": "{{colors.primary_container.default.hex}}33",
"editorWhitespace.foreground": "{{colors.outline.default.hex}}66",
"editorIndentGuide.background1": "{{colors.outline.default.hex}}33",
"editorIndentGuide.activeBackground1": "{{colors.primary.default.hex}}99",
"editorOverviewRuler.border": "{{colors.outline_variant.default.hex}}",
"editorOverviewRuler.errorForeground": "{{colors.error.default.hex}}88",
"editorOverviewRuler.warningForeground": "{{colors.tertiary.default.hex}}88",
"editorOverviewRuler.infoForeground": "{{colors.primary.default.hex}}88",
"editorStickyScroll.background": "{{colors.background.default.hex}}",
"editorStickyScrollHover.background": "{{colors.surface_container_low.default.hex}}",
"editorBracketHighlight.unexpectedBracket.foreground": "{{colors.outline.default.hex}}",
"diffEditor.insertedTextBackground": "{{colors.secondary.default.hex}}20",
"diffEditor.removedTextBackground": "{{colors.error.default.hex}}20",
"gitDecoration.modifiedResourceForeground": "{{colors.primary.default.hex}}",
"gitDecoration.addedResourceForeground": "{{colors.secondary.default.hex}}",
"gitDecoration.deletedResourceForeground": "{{colors.error.default.hex}}",
"gitDecoration.ignoredResourceForeground": "{{colors.outline.default.hex}}",
"editorHoverWidget.background": "{{colors.surface_container_high.default.hex}}",
"editorHoverWidget.border": "{{colors.outline.default.hex}}",
"editorSuggestWidget.background": "{{colors.surface_container.default.hex}}",
"editorSuggestWidget.foreground": "{{colors.on_surface.default.hex}}",
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.default.hex}}",
"editorSuggestWidget.highlightForeground": "{{colors.primary.default.hex}}",
"scrollbarSlider.background": "{{colors.outline.default.hex}}40", "scrollbarSlider.background": "{{colors.outline.default.hex}}40",
"scrollbarSlider.hoverBackground": "{{colors.outline.default.hex}}60", "scrollbarSlider.hoverBackground": "{{colors.outline.default.hex}}60",
"scrollbarSlider.activeBackground": "{{colors.outline.default.hex}}80", "scrollbarSlider.activeBackground": "{{colors.outline.default.hex}}80",
"terminal.background": "{{colors.background.default.hex}}",
"editorError.foreground": "{{colors.error.default.hex}}", "terminal.foreground": "{{colors.on_surface.default.hex}}",
"editorWarning.foreground": "{{colors.tertiary.default.hex}}", "terminal.ansiBlack": "{{dank16.color0.default.hex}}",
"editorInfo.foreground": "{{colors.primary.default.hex}}", "terminal.ansiRed": "{{dank16.color1.default.hex}}",
"terminal.ansiGreen": "{{dank16.color2.default.hex}}",
"editorGutter.addedBackground": "{{colors.secondary.default.hex}}", "terminal.ansiYellow": "{{dank16.color3.default.hex}}",
"editorGutter.modifiedBackground": "{{colors.tertiary.default.hex}}", "terminal.ansiBlue": "{{dank16.color4.default.hex}}",
"editorGutter.deletedBackground": "{{colors.error.default.hex}}", "terminal.ansiMagenta": "{{dank16.color5.default.hex}}",
"terminal.ansiCyan": "{{dank16.color6.default.hex}}",
"diffEditor.insertedTextBackground": "{{colors.secondary.default.hex}}20", "terminal.ansiWhite": "{{dank16.color7.default.hex}}",
"diffEditor.removedTextBackground": "{{colors.error.default.hex}}20", "terminal.ansiBrightBlack": "{{dank16.color8.default.hex}}",
"terminal.ansiBrightRed": "{{dank16.color9.default.hex}}",
"merge.currentHeaderBackground": "{{colors.primary.default.hex}}40", "terminal.ansiBrightGreen": "{{dank16.color10.default.hex}}",
"merge.incomingHeaderBackground": "{{colors.secondary.default.hex}}40", "terminal.ansiBrightYellow": "{{dank16.color11.default.hex}}",
"terminal.ansiBrightBlue": "{{dank16.color12.default.hex}}",
"menubar.selectionBackground": "{{colors.surface_container.default.hex}}", "terminal.ansiBrightMagenta": "{{dank16.color13.default.hex}}",
"menu.background": "{{colors.background.default.hex}}", "terminal.ansiBrightCyan": "{{dank16.color14.default.hex}}",
"menu.foreground": "{{colors.on_surface.default.hex}}", "terminal.ansiBrightWhite": "{{dank16.color15.default.hex}}"
"menu.selectionBackground": "{{colors.surface_container_high.default.hex}}",
"menu.selectionForeground": "{{colors.on_surface.default.hex}}",
"debugToolBar.background": "{{colors.background.default.hex}}",
"debugExceptionWidget.background": "{{colors.background.default.hex}}",
"debugExceptionWidget.border": "{{colors.error.default.hex}}"
}, },
"tokenColors": [ "tokenColors": [
{ {
"scope": ["variable", "meta.object-literal.key"], "scope": [
"settings": { "comment"
"foreground": "{{colors.on_surface.default.hex}}" ],
}
},
{
"scope": ["string", "constant.other.symbol"],
"settings": {
"foreground": "{{colors.secondary.default.hex}}"
}
},
{
"scope": ["constant.numeric", "constant.language", "constant.character"],
"settings": {
"foreground": "{{colors.tertiary.default.hex}}"
}
},
{
"scope": ["entity.name.type", "support.type", "entity.name.class"],
"settings": {
"foreground": "{{colors.tertiary.default.hex}}"
}
},
{
"scope": ["entity.name.function", "support.function"],
"settings": {
"foreground": "{{colors.primary.default.hex}}"
}
},
{
"scope": ["support.class", "support.variable", "variable.language"],
"settings": {
"foreground": "{{colors.secondary.default.hex}}"
}
},
{
"scope": ["invalid"],
"settings": {
"foreground": "{{colors.error.default.hex}}"
}
},
{
"scope": ["invalid.deprecated"],
"settings": { "settings": {
"foreground": "{{colors.outline.default.hex}}" "foreground": "{{colors.outline.default.hex}}"
} }
}, },
{ {
"scope": ["markup.heading"], "scope": [
"keyword"
],
"settings": { "settings": {
"foreground": "{{colors.primary.default.hex}}", "foreground": "{{dank16.color5.default.hex}}"
"fontStyle": "bold"
} }
}, },
{ {
"scope": ["markup.bold"], "scope": [
"string"
],
"settings": { "settings": {
"foreground": "{{colors.tertiary.default.hex}}", "foreground": "{{dank16.color2.default.hex}}"
"fontStyle": "bold"
} }
}, },
{ {
"scope": ["markup.italic"], "scope": [
"constant",
"number"
],
"settings": { "settings": {
"foreground": "{{colors.primary.default.hex}}", "foreground": "{{dank16.color12.default.hex}}"
"fontStyle": "italic"
} }
}, },
{ {
"scope": ["markup.underline"], "scope": [
"settings": { "variable"
"fontStyle": "underline" ],
}
},
{
"scope": ["markup.quote"],
"settings": {
"foreground": "{{colors.secondary.default.hex}}"
}
},
{
"scope": ["markup.list"],
"settings": { "settings": {
"foreground": "{{colors.on_surface.default.hex}}" "foreground": "{{colors.on_surface.default.hex}}"
} }
}, },
{ {
"scope": ["markup.raw", "markup.inline.raw"], "scope": [
"entity.name.function"
],
"settings": {
"foreground": "{{dank16.color4.default.hex}}"
}
},
{
"scope": [
"entity.name.class",
"support.type"
],
"settings": { "settings": {
"foreground": "{{colors.secondary.default.hex}}" "foreground": "{{colors.secondary.default.hex}}"
} }
}, },
{ {
"scope": ["comment", "punctuation.definition.comment"], "scope": [
"invalid"
],
"settings": { "settings": {
"foreground": "{{dank16.color8.hex}}" "foreground": "{{colors.error.default.hex}}"
} }
}, },
{ {
"scope": "keyword", "scope": [
"markup.heading"
],
"settings": { "settings": {
"foreground": "{{dank16.color5.hex}}" "foreground": "{{colors.primary.default.hex}}",
} "fontStyle": "bold"
},
{
"scope": "storage.type",
"settings": {
"foreground": "{{dank16.color13.hex}}"
}
},
{
"scope": "storage.modifier",
"settings": {
"foreground": "{{dank16.color5.hex}}"
}
},
{
"scope": "variable",
"settings": {
"foreground": "{{dank16.color15.hex}}"
}
},
{
"scope": "variable.parameter",
"settings": {
"foreground": "{{dank16.color7.hex}}"
}
},
{
"scope": ["meta.object-literal.key", "meta.property.object", "variable.other.property"],
"settings": {
"foreground": "{{dank16.color4.hex}}"
}
},
{
"scope": "constant.other.symbol",
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": ["constant.numeric", "constant.language"],
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": "constant.character",
"settings": {
"foreground": "{{dank16.color3.hex}}"
}
},
{
"scope": ["entity.name.type", "entity.name.class"],
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": "support.type",
"settings": {
"foreground": "{{dank16.color13.hex}}"
}
},
{
"scope": ["entity.name.function", "support.function"],
"settings": {
"foreground": "{{dank16.color2.hex}}"
}
},
{
"scope": ["support.class", "support.variable"],
"settings": {
"foreground": "{{dank16.color15.hex}}"
}
},
{
"scope": "variable.language",
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": "entity.name.tag.yaml",
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": ["string.unquoted.plain.out.yaml", "string.unquoted.yaml"],
"settings": {
"foreground": "{{dank16.color15.hex}}"
}
},
{
"scope": "string",
"settings": {
"foreground": "{{dank16.color3.hex}}"
} }
} }
], ],
"semanticHighlighting": true,
"semanticTokenColors": { "semanticTokenColors": {
"variable": { "variable": {
"foreground": "{{dank16.color15.hex}}" "foreground": "{{colors.on_surface.default.hex}}"
},
"variable.readonly": {
"foreground": "{{dank16.color12.hex}}"
},
"property": {
"foreground": "{{dank16.color4.hex}}"
},
"function": {
"foreground": "{{dank16.color2.hex}}"
},
"method": {
"foreground": "{{dank16.color2.hex}}"
},
"type": {
"foreground": "{{dank16.color12.hex}}"
},
"class": {
"foreground": "{{dank16.color12.hex}}"
},
"typeParameter": {
"foreground": "{{dank16.color13.hex}}"
},
"enumMember": {
"foreground": "{{dank16.color12.hex}}"
},
"string": {
"foreground": "{{dank16.color3.hex}}"
},
"number": {
"foreground": "{{dank16.color12.hex}}"
},
"comment": {
"foreground": "{{dank16.color8.hex}}"
},
"keyword": {
"foreground": "{{dank16.color5.hex}}"
},
"operator": {
"foreground": "{{dank16.color15.hex}}"
}, },
"parameter": { "parameter": {
"foreground": "{{dank16.color7.hex}}" "foreground": "{{colors.on_surface.default.hex}}"
}, },
"namespace": { "property": {
"foreground": "{{dank16.color15.hex}}" "foreground": "{{dank16.color4.default.hex}}"
},
"function": {
"foreground": "{{dank16.color4.default.hex}}"
},
"method": {
"foreground": "{{dank16.color4.default.hex}}"
},
"type": {
"foreground": "{{colors.secondary.default.hex}}"
},
"class": {
"foreground": "{{colors.secondary.default.hex}}"
},
"string": {
"foreground": "{{dank16.color2.default.hex}}"
},
"number": {
"foreground": "{{dank16.color12.default.hex}}"
},
"keyword": {
"foreground": "{{dank16.color5.default.hex}}"
},
"comment": {
"foreground": "{{colors.outline.default.hex}}"
} }
} }
} }
@@ -1,35 +1,74 @@
{ {
"$schema": "vscode://schemas/color-theme", "$schema": "vscode://schemas/color-theme",
"name": "Dynamic Base16 DankShell", "name": "Dynamic Base16 DankShell Light",
"semanticHighlighting": true,
"colors": { "colors": {
"foreground": "{{colors.on_surface.light.hex}}",
"selection.background": "{{colors.primary_container.light.hex}}66",
"errorForeground": "{{colors.error.light.hex}}",
"focusBorder": "{{colors.primary.light.hex}}",
"editor.background": "{{colors.background.light.hex}}", "editor.background": "{{colors.background.light.hex}}",
"editor.foreground": "{{colors.on_surface.light.hex}}", "editor.foreground": "{{colors.on_surface.light.hex}}",
"editorLineNumber.foreground": "{{colors.outline.light.hex}}", "editorLineNumber.foreground": "{{colors.outline.light.hex}}",
"editorLineNumber.activeForeground": "{{colors.on_surface.light.hex}}", "editorLineNumber.activeForeground": "{{colors.primary.light.hex}}",
"editorCursor.foreground": "{{colors.primary.light.hex}}", "editorCursor.foreground": "{{colors.primary.light.hex}}",
"editor.selectionBackground": "{{colors.primary_container.light.hex}}", "editor.selectionBackground": "{{colors.primary_container.light.hex}}B3",
"editor.inactiveSelectionBackground": "{{colors.background.light.hex}}", "editor.inactiveSelectionBackground": "{{colors.primary_container.light.hex}}33",
"editor.lineHighlightBackground": "{{colors.background.light.hex}}", "editor.lineHighlightBackground": "{{colors.surface_container.light.hex}}66",
"editorIndentGuide.background": "{{colors.background.light.hex}}", "editor.lineHighlightBorder": "{{colors.outline_variant.light.hex}}18",
"editorIndentGuide.activeBackground": "{{colors.outline.light.hex}}", "editor.wordHighlightBackground": "{{colors.secondary.light.hex}}22",
"editorWhitespace.foreground": "{{colors.outline_variant.light.hex}}", "editor.wordHighlightStrongBackground": "{{colors.tertiary.light.hex}}22",
"editorBracketMatch.background": "{{colors.background.light.hex}}", "editor.findMatchBackground": "{{colors.secondary.light.hex}}33",
"editor.findMatchHighlightBackground": "{{colors.secondary.light.hex}}22",
"editor.findRangeHighlightBackground": "{{colors.surface_container.light.hex}}33",
"editorWhitespace.foreground": "{{colors.outline.light.hex}}66",
"editorIndentGuide.background1": "{{colors.outline.light.hex}}99",
"editorIndentGuide.activeBackground1": "{{colors.primary.light.hex}}CC",
"editorBracketMatch.background": "{{colors.primary_container.light.hex}}33",
"editorBracketMatch.border": "{{colors.primary.light.hex}}", "editorBracketMatch.border": "{{colors.primary.light.hex}}",
"editorGutter.addedBackground": "{{colors.secondary.light.hex}}AA",
"editorGutter.modifiedBackground": "{{colors.tertiary.light.hex}}AA",
"editorGutter.deletedBackground": "{{colors.error.light.hex}}AA",
"editorError.foreground": "{{colors.error.light.hex}}",
"editorWarning.foreground": "{{colors.tertiary.light.hex}}",
"editorInfo.foreground": "{{colors.primary.light.hex}}",
"editorOverviewRuler.border": "{{colors.outline_variant.light.hex}}",
"editorOverviewRuler.errorForeground": "{{colors.error.light.hex}}88",
"editorOverviewRuler.warningForeground": "{{colors.tertiary.light.hex}}88",
"editorOverviewRuler.infoForeground": "{{colors.primary.light.hex}}88",
"editorWidget.background": "{{colors.surface_container.light.hex}}",
"editorWidget.border": "{{colors.outline_variant.light.hex}}",
"editorHoverWidget.background": "{{colors.surface_container_high.light.hex}}",
"editorHoverWidget.border": "{{colors.outline.light.hex}}",
"editorSuggestWidget.background": "{{colors.surface_container.light.hex}}",
"editorSuggestWidget.foreground": "{{colors.on_surface.light.hex}}",
"editorSuggestWidget.border": "{{colors.outline_variant.light.hex}}",
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.light.hex}}",
"editorSuggestWidget.highlightForeground": "{{colors.primary.light.hex}}",
"editorGroup.border": "{{colors.outline_variant.light.hex}}",
"editorGroup.dropBackground": "{{colors.primary_container.light.hex}}33",
"editorGroupHeader.tabsBackground": "{{colors.background.light.hex}}",
"editorGroupHeader.noTabsBackground": "{{colors.background.light.hex}}",
"tab.border": "{{colors.outline_variant.light.hex}}",
"tab.activeBackground": "{{colors.surface_container_high.light.hex}}",
"tab.inactiveBackground": "{{colors.surface_container.light.hex}}",
"tab.activeForeground": "{{colors.on_surface.light.hex}}",
"tab.inactiveForeground": "{{colors.outline.light.hex}}",
"tab.activeBorder": "{{colors.primary.light.hex}}",
"tab.unfocusedActiveBorder": "{{colors.outline.light.hex}}",
"activityBar.background": "{{colors.background.light.hex}}", "activityBar.background": "{{colors.background.light.hex}}",
"activityBar.foreground": "{{colors.on_surface.light.hex}}", "activityBar.foreground": "{{colors.on_surface.light.hex}}",
"activityBar.inactiveForeground": "{{colors.outline.light.hex}}",
"activityBar.activeBorder": "{{colors.primary.light.hex}}", "activityBar.activeBorder": "{{colors.primary.light.hex}}",
"activityBar.activeBackground": "{{colors.background.light.hex}}", "activityBar.activeBackground": "{{colors.background.light.hex}}",
"activityBarBadge.background": "{{colors.primary.light.hex}}", "activityBarBadge.background": "{{colors.primary.light.hex}}",
"activityBarBadge.foreground": "{{colors.on_primary.light.hex}}", "activityBarBadge.foreground": "{{colors.on_primary.light.hex}}",
"sideBar.background": "{{colors.surface_container.light.hex}}",
"sideBar.background": "{{colors.background.light.hex}}",
"sideBar.foreground": "{{colors.on_surface.light.hex}}", "sideBar.foreground": "{{colors.on_surface.light.hex}}",
"sideBar.border": "{{colors.background.light.hex}}", "sideBar.border": "{{colors.outline_variant.light.hex}}",
"sideBarTitle.foreground": "{{colors.on_surface.light.hex}}", "sideBarTitle.foreground": "{{colors.on_surface.light.hex}}",
"sideBarSectionHeader.background": "{{colors.background.light.hex}}", "sideBarSectionHeader.background": "{{colors.surface_container_low.light.hex}}",
"sideBarSectionHeader.foreground": "{{colors.on_surface.light.hex}}", "sideBarSectionHeader.foreground": "{{colors.on_surface.light.hex}}",
"list.activeSelectionBackground": "{{colors.primary_container.light.hex}}", "list.activeSelectionBackground": "{{colors.primary_container.light.hex}}",
"list.activeSelectionForeground": "{{colors.on_primary_container.light.hex}}", "list.activeSelectionForeground": "{{colors.on_primary_container.light.hex}}",
"list.inactiveSelectionBackground": "{{colors.surface_container.light.hex}}", "list.inactiveSelectionBackground": "{{colors.surface_container.light.hex}}",
@@ -39,83 +78,64 @@
"list.focusBackground": "{{colors.surface_container_high.light.hex}}", "list.focusBackground": "{{colors.surface_container_high.light.hex}}",
"list.focusForeground": "{{colors.on_surface.light.hex}}", "list.focusForeground": "{{colors.on_surface.light.hex}}",
"list.highlightForeground": "{{colors.primary.light.hex}}", "list.highlightForeground": "{{colors.primary.light.hex}}",
"list.errorForeground": "{{colors.error.light.hex}}",
"statusBar.background": "{{colors.background.light.hex}}", "list.warningForeground": "{{colors.tertiary.light.hex}}",
"statusBar.background": "{{colors.surface_container.light.hex}}",
"statusBar.foreground": "{{colors.on_surface.light.hex}}", "statusBar.foreground": "{{colors.on_surface.light.hex}}",
"statusBar.border": "{{colors.background.light.hex}}", "statusBar.border": "{{colors.outline_variant.light.hex}}",
"statusBar.noFolderBackground": "{{colors.background.light.hex}}", "statusBar.noFolderBackground": "{{colors.surface_container.light.hex}}",
"statusBar.noFolderForeground": "{{colors.on_surface.light.hex}}",
"statusBar.debuggingBackground": "{{colors.error.light.hex}}", "statusBar.debuggingBackground": "{{colors.error.light.hex}}",
"statusBar.debuggingForeground": "{{colors.on_error.light.hex}}", "statusBar.debuggingForeground": "{{colors.on_error.light.hex}}",
"tab.activeBackground": "{{colors.background.light.hex}}",
"tab.inactiveBackground": "{{colors.background.light.hex}}",
"tab.activeForeground": "{{colors.on_surface.light.hex}}",
"tab.inactiveForeground": "{{colors.outline.light.hex}}",
"tab.border": "{{colors.background.light.hex}}",
"tab.activeBorder": "{{colors.primary.light.hex}}",
"tab.unfocusedActiveBorder": "{{colors.outline.light.hex}}",
"editorGroupHeader.tabsBackground": "{{colors.background.light.hex}}",
"editorGroupHeader.noTabsBackground": "{{colors.background.light.hex}}",
"titleBar.activeBackground": "{{colors.background.light.hex}}", "titleBar.activeBackground": "{{colors.background.light.hex}}",
"titleBar.activeForeground": "{{colors.on_surface.light.hex}}", "titleBar.activeForeground": "{{colors.on_surface.light.hex}}",
"titleBar.inactiveBackground": "{{colors.background.light.hex}}", "titleBar.inactiveBackground": "{{colors.background.light.hex}}",
"titleBar.inactiveForeground": "{{colors.outline.light.hex}}", "titleBar.inactiveForeground": "{{colors.outline.light.hex}}",
"titleBar.border": "{{colors.background.light.hex}}", "titleBar.border": "{{colors.outline_variant.light.hex}}",
"input.background": "{{colors.surface_container_low.light.hex}}",
"input.background": "{{colors.background.light.hex}}",
"input.foreground": "{{colors.on_surface.light.hex}}", "input.foreground": "{{colors.on_surface.light.hex}}",
"input.border": "{{colors.outline.light.hex}}", "input.border": "{{colors.outline_variant.light.hex}}",
"input.placeholderForeground": "{{colors.outline.light.hex}}", "input.placeholderForeground": "{{colors.outline.light.hex}}",
"inputOption.activeBorder": "{{colors.primary.light.hex}}", "inputOption.activeBorder": "{{colors.primary.light.hex}}",
"inputValidation.errorBackground": "{{colors.error.light.hex}}", "inputValidation.errorBackground": "{{colors.error_container.light.hex}}",
"inputValidation.errorBorder": "{{colors.error.light.hex}}", "inputValidation.errorBorder": "{{colors.error.light.hex}}",
"dropdown.background": "{{colors.surface_container_low.light.hex}}",
"dropdown.background": "{{colors.background.light.hex}}",
"dropdown.foreground": "{{colors.on_surface.light.hex}}", "dropdown.foreground": "{{colors.on_surface.light.hex}}",
"dropdown.border": "{{colors.outline.light.hex}}", "dropdown.border": "{{colors.outline_variant.light.hex}}",
"quickInput.background": "{{colors.surface_container.light.hex}}",
"quickInput.background": "{{colors.background.light.hex}}",
"quickInput.foreground": "{{colors.on_surface.light.hex}}", "quickInput.foreground": "{{colors.on_surface.light.hex}}",
"quickInputList.focusBackground": "{{colors.surface_container_high.light.hex}}", "quickInputList.focusBackground": "{{colors.surface_container_high.light.hex}}",
"quickInputList.focusForeground": "{{colors.on_surface.light.hex}}", "quickInputList.focusForeground": "{{colors.on_surface.light.hex}}",
"button.background": "{{colors.primary.light.hex}}", "button.background": "{{colors.primary.light.hex}}",
"button.foreground": "{{colors.on_primary.light.hex}}", "button.foreground": "{{colors.on_primary.light.hex}}",
"button.hoverBackground": "{{colors.primary_container.light.hex}}", "button.hoverBackground": "{{colors.primary_container.light.hex}}",
"focusBorder": "{{colors.primary.light.hex}}",
"badge.background": "{{colors.secondary.light.hex}}", "badge.background": "{{colors.secondary.light.hex}}",
"badge.foreground": "{{colors.on_secondary.light.hex}}", "badge.foreground": "{{colors.on_secondary.light.hex}}",
"panel.background": "{{colors.surface_container.light.hex}}",
"panel.background": "{{colors.background.light.hex}}", "panel.border": "{{colors.outline_variant.light.hex}}",
"panel.border": "{{colors.primary.light.hex}}",
"panelTitle.activeBorder": "{{colors.primary.light.hex}}", "panelTitle.activeBorder": "{{colors.primary.light.hex}}",
"panelTitle.activeForeground": "{{colors.on_surface.light.hex}}", "panelTitle.activeForeground": "{{colors.on_surface.light.hex}}",
"panelTitle.inactiveForeground": "{{colors.outline.light.hex}}", "panelTitle.inactiveForeground": "{{colors.outline.light.hex}}",
"terminal.background": "{{colors.background.light.hex}}", "terminal.background": "{{colors.background.light.hex}}",
"terminal.foreground": "{{colors.on_surface.light.hex}}", "terminal.foreground": "{{colors.on_surface.light.hex}}",
"terminal.ansiBlack": "{{dank16.color0.hex}}", "terminal.ansiBlack": "{{dank16.color0.light.hex}}",
"terminal.ansiRed": "{{dank16.color1.hex}}", "terminal.ansiRed": "{{dank16.color1.light.hex}}",
"terminal.ansiGreen": "{{dank16.color2.hex}}", "terminal.ansiGreen": "{{dank16.color2.light.hex}}",
"terminal.ansiYellow": "{{dank16.color3.hex}}", "terminal.ansiYellow": "{{dank16.color3.light.hex}}",
"terminal.ansiBlue": "{{dank16.color4.hex}}", "terminal.ansiBlue": "{{dank16.color4.light.hex}}",
"terminal.ansiMagenta": "{{dank16.color5.hex}}", "terminal.ansiMagenta": "{{dank16.color5.light.hex}}",
"terminal.ansiCyan": "{{dank16.color6.hex}}", "terminal.ansiCyan": "{{dank16.color6.light.hex}}",
"terminal.ansiWhite": "{{dank16.color7.hex}}", "terminal.ansiWhite": "{{dank16.color7.light.hex}}",
"terminal.ansiBrightBlack": "{{dank16.color8.hex}}", "terminal.ansiBrightBlack": "{{dank16.color8.light.hex}}",
"terminal.ansiBrightRed": "{{dank16.color9.hex}}", "terminal.ansiBrightRed": "{{dank16.color9.light.hex}}",
"terminal.ansiBrightGreen": "{{dank16.color10.hex}}", "terminal.ansiBrightGreen": "{{dank16.color10.light.hex}}",
"terminal.ansiBrightYellow": "{{dank16.color11.hex}}", "terminal.ansiBrightYellow": "{{dank16.color11.light.hex}}",
"terminal.ansiBrightBlue": "{{dank16.color12.hex}}", "terminal.ansiBrightBlue": "{{dank16.color12.light.hex}}",
"terminal.ansiBrightMagenta": "{{dank16.color13.hex}}", "terminal.ansiBrightMagenta": "{{dank16.color13.light.hex}}",
"terminal.ansiBrightCyan": "{{dank16.color14.hex}}", "terminal.ansiBrightCyan": "{{dank16.color14.light.hex}}",
"terminal.ansiBrightWhite": "{{dank16.color15.hex}}", "terminal.ansiBrightWhite": "{{dank16.color15.light.hex}}",
"gitDecoration.modifiedResourceForeground": "{{colors.primary.light.hex}}", "gitDecoration.modifiedResourceForeground": "{{colors.primary.light.hex}}",
"gitDecoration.addedResourceForeground": "{{colors.primary.light.hex}}", "gitDecoration.addedResourceForeground": "{{colors.secondary.light.hex}}",
"gitDecoration.stageModifiedResourceForeground": "{{colors.primary.light.hex}}", "gitDecoration.stageModifiedResourceForeground": "{{colors.primary.light.hex}}",
"gitDecoration.stageDeletedResourceForeground": "{{colors.error.light.hex}}", "gitDecoration.stageDeletedResourceForeground": "{{colors.error.light.hex}}",
"gitDecoration.deletedResourceForeground": "{{colors.error.light.hex}}", "gitDecoration.deletedResourceForeground": "{{colors.error.light.hex}}",
@@ -123,309 +143,276 @@
"gitDecoration.ignoredResourceForeground": "{{colors.outline.light.hex}}", "gitDecoration.ignoredResourceForeground": "{{colors.outline.light.hex}}",
"gitDecoration.conflictingResourceForeground": "{{colors.error_container.light.hex}}", "gitDecoration.conflictingResourceForeground": "{{colors.error_container.light.hex}}",
"gitDecoration.submoduleResourceForeground": "{{colors.primary.light.hex}}", "gitDecoration.submoduleResourceForeground": "{{colors.primary.light.hex}}",
"editorWidget.background": "{{colors.background.light.hex}}",
"editorWidget.border": "{{colors.outline.light.hex}}",
"editorSuggestWidget.background": "{{colors.background.light.hex}}",
"editorSuggestWidget.border": "{{colors.outline.light.hex}}",
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.light.hex}}",
"editorSuggestWidget.highlightForeground": "{{colors.primary.light.hex}}",
"peekView.border": "{{colors.primary.light.hex}}", "peekView.border": "{{colors.primary.light.hex}}",
"peekViewEditor.background": "{{colors.background.light.hex}}", "peekViewEditor.background": "{{colors.surface_container_high.light.hex}}",
"peekViewResult.background": "{{colors.background.light.hex}}", "peekViewResult.background": "{{colors.surface_container.light.hex}}",
"peekViewTitle.background": "{{colors.background.light.hex}}", "peekViewTitle.background": "{{colors.surface_container_low.light.hex}}",
"notificationCenter.border": "{{colors.outline_variant.light.hex}}",
"notificationCenter.border": "{{colors.outline.light.hex}}", "notifications.background": "{{colors.surface_container.light.hex}}",
"notifications.background": "{{colors.background.light.hex}}", "notifications.border": "{{colors.outline_variant.light.hex}}",
"notifications.border": "{{colors.outline.light.hex}}", "breadcrumb.background": "{{colors.surface_container_high.light.hex}}",
"breadcrumb.foreground": "{{colors.outline.light.hex}}", "breadcrumb.foreground": "{{colors.outline.light.hex}}",
"breadcrumb.focusForeground": "{{colors.on_surface.light.hex}}", "breadcrumb.focusForeground": "{{colors.on_surface.light.hex}}",
"breadcrumb.activeSelectionForeground": "{{colors.primary.light.hex}}", "breadcrumb.activeSelectionForeground": "{{colors.primary.light.hex}}",
"scrollbarSlider.background": "{{colors.outline.light.hex}}40", "scrollbarSlider.background": "{{colors.outline.light.hex}}40",
"scrollbarSlider.hoverBackground": "{{colors.outline.light.hex}}60", "scrollbarSlider.hoverBackground": "{{colors.outline.light.hex}}60",
"scrollbarSlider.activeBackground": "{{colors.outline.light.hex}}80", "scrollbarSlider.activeBackground": "{{colors.outline.light.hex}}80",
"menubar.selectionBackground": "{{colors.primary_container.light.hex}}",
"editorError.foreground": "{{colors.error.light.hex}}", "menubar.selectionForeground": "{{colors.on_primary_container.light.hex}}",
"editorWarning.foreground": "{{colors.tertiary.light.hex}}", "menu.background": "{{colors.surface_container.light.hex}}",
"editorInfo.foreground": "{{colors.primary.light.hex}}",
"editorGutter.addedBackground": "{{colors.secondary.light.hex}}",
"editorGutter.modifiedBackground": "{{colors.tertiary.light.hex}}",
"editorGutter.deletedBackground": "{{colors.error.light.hex}}",
"diffEditor.insertedTextBackground": "{{colors.secondary.light.hex}}20",
"diffEditor.removedTextBackground": "{{colors.error.light.hex}}20",
"merge.currentHeaderBackground": "{{colors.primary.light.hex}}40",
"merge.incomingHeaderBackground": "{{colors.secondary.light.hex}}40",
"menubar.selectionBackground": "{{colors.surface_container.light.hex}}",
"menu.background": "{{colors.background.light.hex}}",
"menu.foreground": "{{colors.on_surface.light.hex}}", "menu.foreground": "{{colors.on_surface.light.hex}}",
"menu.selectionBackground": "{{colors.surface_container_high.light.hex}}", "menu.selectionBackground": "{{colors.primary_container.light.hex}}",
"menu.selectionForeground": "{{colors.on_surface.light.hex}}", "menu.selectionForeground": "{{colors.on_primary_container.light.hex}}",
"debugToolBar.background": "{{colors.surface_container.light.hex}}",
"debugToolBar.background": "{{colors.background.light.hex}}", "debugExceptionWidget.background": "{{colors.surface_container.light.hex}}",
"debugExceptionWidget.background": "{{colors.background.light.hex}}",
"debugExceptionWidget.border": "{{colors.error.light.hex}}" "debugExceptionWidget.border": "{{colors.error.light.hex}}"
}, },
"tokenColors": [ "tokenColors": [
{ {
"scope": ["variable", "meta.object-literal.key"], "name": "Comments",
"settings": { "scope": [
"foreground": "{{colors.on_surface.light.hex}}" "comment",
} "punctuation.definition.comment",
}, "string.comment"
{ ],
"scope": ["string", "constant.other.symbol"],
"settings": {
"foreground": "{{colors.secondary.light.hex}}"
}
},
{
"scope": ["constant.numeric", "constant.language", "constant.character"],
"settings": {
"foreground": "{{colors.tertiary.light.hex}}"
}
},
{
"scope": ["entity.name.type", "support.type", "entity.name.class"],
"settings": {
"foreground": "{{colors.tertiary.light.hex}}"
}
},
{
"scope": ["entity.name.function", "support.function"],
"settings": {
"foreground": "{{colors.primary.light.hex}}"
}
},
{
"scope": ["support.class", "support.variable", "variable.language"],
"settings": {
"foreground": "{{colors.secondary.light.hex}}"
}
},
{
"scope": ["invalid"],
"settings": {
"foreground": "{{colors.error.light.hex}}"
}
},
{
"scope": ["invalid.deprecated"],
"settings": { "settings": {
"foreground": "{{colors.outline.light.hex}}" "foreground": "{{colors.outline.light.hex}}"
} }
}, },
{ {
"scope": ["markup.heading"], "name": "Keywords and storage",
"scope": [
"keyword",
"punctuation.definition.keyword",
"storage",
"storage.type"
],
"settings": {
"foreground": "{{dank16.color5.light.hex}}"
}
},
{
"name": "Strings",
"scope": [
"string",
"string punctuation.section.embedded source"
],
"settings": {
"foreground": "{{dank16.color2.light.hex}}"
}
},
{
"name": "Constants and numbers",
"scope": [
"constant.numeric",
"constant.language",
"constant.other",
"constant.character"
],
"settings": {
"foreground": "{{dank16.color0.light.hex}}"
}
},
{
"name": "Variables (plain)",
"scope": [
"variable",
"variable.other"
],
"settings": {
"foreground": "{{colors.on_surface.light.hex}}"
}
},
{
"name": "Parameters",
"scope": [
"variable.parameter",
"variable.parameter.function"
],
"settings": {
"foreground": "{{colors.on_surface.light.hex}}"
}
},
{
"name": "Object properties and keys",
"scope": [
"meta.object-literal.key",
"meta.property.object",
"variable.other.property",
"entity.name.tag.yaml"
],
"settings": {
"foreground": "{{dank16.color4.light.hex}}"
}
},
{
"name": "Functions",
"scope": [
"entity.name.function",
"support.function"
],
"settings": {
"foreground": "{{dank16.color4.light.hex}}"
}
},
{
"name": "Types and classes",
"scope": [
"entity.name.type",
"entity.name.class",
"support.type"
],
"settings": {
"foreground": "{{colors.secondary.light.hex}}"
}
},
{
"name": "Language builtins and special variables",
"scope": [
"support.class",
"support.variable",
"variable.language",
"support.constant"
],
"settings": {
"foreground": "{{dank16.color4.light.hex}}"
}
},
{
"name": "Invalid",
"scope": [
"invalid"
],
"settings": {
"foreground": "{{colors.error.light.hex}}"
}
},
{
"name": "Invalid deprecated",
"scope": [
"invalid.deprecated"
],
"settings": {
"foreground": "{{colors.outline.light.hex}}"
}
},
{
"name": "Headings",
"scope": [
"markup.heading",
"markup.heading entity.name"
],
"settings": { "settings": {
"foreground": "{{colors.primary.light.hex}}", "foreground": "{{colors.primary.light.hex}}",
"fontStyle": "bold" "fontStyle": "bold"
} }
}, },
{ {
"scope": ["markup.bold"], "name": "Bold",
"scope": [
"markup.bold"
],
"settings": { "settings": {
"foreground": "{{colors.tertiary.light.hex}}", "foreground": "{{colors.tertiary.light.hex}}",
"fontStyle": "bold" "fontStyle": "bold"
} }
}, },
{ {
"scope": ["markup.italic"], "name": "Italic",
"scope": [
"markup.italic"
],
"settings": { "settings": {
"foreground": "{{colors.primary.light.hex}}", "foreground": "{{colors.primary.light.hex}}",
"fontStyle": "italic" "fontStyle": "italic"
} }
}, },
{ {
"scope": ["markup.underline"], "name": "Underline",
"scope": [
"markup.underline"
],
"settings": { "settings": {
"fontStyle": "underline" "fontStyle": "underline"
} }
}, },
{ {
"scope": ["markup.quote"], "name": "Quotes",
"scope": [
"markup.quote"
],
"settings": { "settings": {
"foreground": "{{colors.secondary.light.hex}}" "foreground": "{{colors.outline.light.hex}}"
} }
}, },
{ {
"scope": ["markup.list"], "name": "Lists",
"scope": [
"markup.list"
],
"settings": { "settings": {
"foreground": "{{colors.on_surface.light.hex}}" "foreground": "{{colors.on_surface.light.hex}}"
} }
}, },
{ {
"scope": ["markup.raw", "markup.inline.raw"], "name": "Inline code in prose",
"scope": [
"markup.raw",
"markup.inline.raw"
],
"settings": { "settings": {
"foreground": "{{colors.secondary.light.hex}}" "foreground": "{{dank16.color4.light.hex}}"
}
},
{
"scope": ["comment", "punctuation.definition.comment"],
"settings": {
"foreground": "{{dank16.color8.hex}}"
}
},
{
"scope": "keyword",
"settings": {
"foreground": "{{dank16.color5.hex}}"
}
},
{
"scope": "storage.type",
"settings": {
"foreground": "{{dank16.color13.hex}}"
}
},
{
"scope": "storage.modifier",
"settings": {
"foreground": "{{dank16.color5.hex}}"
}
},
{
"scope": "variable",
"settings": {
"foreground": "{{dank16.color15.hex}}"
}
},
{
"scope": "variable.parameter",
"settings": {
"foreground": "{{dank16.color7.hex}}"
}
},
{
"scope": ["meta.object-literal.key", "meta.property.object", "variable.other.property"],
"settings": {
"foreground": "{{dank16.color4.hex}}"
}
},
{
"scope": "constant.other.symbol",
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": ["constant.numeric", "constant.language"],
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": "constant.character",
"settings": {
"foreground": "{{dank16.color3.hex}}"
}
},
{
"scope": ["entity.name.type", "entity.name.class"],
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": "support.type",
"settings": {
"foreground": "{{dank16.color13.hex}}"
}
},
{
"scope": ["entity.name.function", "support.function"],
"settings": {
"foreground": "{{dank16.color2.hex}}"
}
},
{
"scope": ["support.class", "support.variable"],
"settings": {
"foreground": "{{dank16.color15.hex}}"
}
},
{
"scope": "variable.language",
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": "entity.name.tag.yaml",
"settings": {
"foreground": "{{dank16.color12.hex}}"
}
},
{
"scope": ["string.unquoted.plain.out.yaml", "string.unquoted.yaml"],
"settings": {
"foreground": "{{dank16.color15.hex}}"
}
},
{
"scope": "string",
"settings": {
"foreground": "{{dank16.color3.hex}}"
} }
} }
], ],
"semanticHighlighting": true,
"semanticTokenColors": { "semanticTokenColors": {
"variable": { "variable": {
"foreground": "{{dank16.color15.hex}}" "foreground": "{{colors.on_surface.light.hex}}"
}, },
"variable.readonly": { "variable.readonly": {
"foreground": "{{dank16.color12.hex}}" "foreground": "{{dank16.color1.light.hex}}"
},
"property": {
"foreground": "{{dank16.color4.hex}}"
},
"function": {
"foreground": "{{dank16.color2.hex}}"
},
"method": {
"foreground": "{{dank16.color2.hex}}"
},
"type": {
"foreground": "{{dank16.color12.hex}}"
},
"class": {
"foreground": "{{dank16.color12.hex}}"
},
"typeParameter": {
"foreground": "{{dank16.color13.hex}}"
},
"enumMember": {
"foreground": "{{dank16.color12.hex}}"
},
"string": {
"foreground": "{{dank16.color3.hex}}"
},
"number": {
"foreground": "{{dank16.color12.hex}}"
},
"comment": {
"foreground": "{{dank16.color8.hex}}"
},
"keyword": {
"foreground": "{{dank16.color5.hex}}"
},
"operator": {
"foreground": "{{dank16.color15.hex}}"
}, },
"parameter": { "parameter": {
"foreground": "{{dank16.color7.hex}}" "foreground": "{{colors.on_surface.light.hex}}"
},
"property": {
"foreground": "{{dank16.color4.light.hex}}"
},
"function": {
"foreground": "{{dank16.color4.light.hex}}"
},
"method": {
"foreground": "{{dank16.color4.light.hex}}"
},
"type": {
"foreground": "{{colors.secondary.light.hex}}"
},
"class": {
"foreground": "{{colors.secondary.light.hex}}"
},
"typeParameter": {
"foreground": "{{colors.tertiary.light.hex}}"
},
"enumMember": {
"foreground": "{{dank16.color1.light.hex}}"
},
"string": {
"foreground": "{{dank16.color2.light.hex}}"
},
"number": {
"foreground": "{{dank16.color1.light.hex}}"
},
"comment": {
"foreground": "{{colors.outline.light.hex}}"
},
"keyword": {
"foreground": "{{dank16.color5.light.hex}}"
},
"operator": {
"foreground": "{{colors.on_surface.light.hex}}"
}, },
"namespace": { "namespace": {
"foreground": "{{dank16.color15.hex}}" "foreground": "{{colors.on_surface.light.hex}}"
} }
} }
} }
@@ -1,315 +0,0 @@
{
"$schema": "vscode://schemas/color-theme",
"name": "Dynamic Base16 DankShell",
"colors": {
"editor.background": "{{colors.background.default.hex}}",
"editor.foreground": "{{colors.on_surface.default.hex}}",
"editorLineNumber.foreground": "{{colors.outline.default.hex}}",
"editorLineNumber.activeForeground": "{{colors.on_surface.default.hex}}",
"editorCursor.foreground": "{{colors.primary.default.hex}}",
"editor.selectionBackground": "{{colors.primary_container.default.hex}}",
"editor.inactiveSelectionBackground": "{{colors.background.default.hex}}",
"editor.lineHighlightBackground": "{{colors.background.default.hex}}",
"editorIndentGuide.background": "{{colors.background.default.hex}}",
"editorIndentGuide.activeBackground": "{{colors.outline.default.hex}}",
"editorWhitespace.foreground": "{{colors.outline_variant.default.hex}}",
"editorBracketMatch.background": "{{colors.background.default.hex}}",
"editorBracketMatch.border": "{{colors.primary.default.hex}}",
"activityBar.background": "{{colors.background.default.hex}}",
"activityBar.foreground": "{{colors.on_surface.default.hex}}",
"activityBar.activeBorder": "{{colors.primary.default.hex}}",
"activityBar.activeBackground": "{{colors.background.default.hex}}",
"activityBarBadge.background": "{{colors.primary.default.hex}}",
"activityBarBadge.foreground": "{{colors.on_primary.default.hex}}",
"sideBar.background": "{{colors.background.default.hex}}",
"sideBar.foreground": "{{colors.on_surface.default.hex}}",
"sideBar.border": "{{colors.background.default.hex}}",
"sideBarTitle.foreground": "{{colors.on_surface.default.hex}}",
"sideBarSectionHeader.background": "{{colors.background.default.hex}}",
"sideBarSectionHeader.foreground": "{{colors.on_surface.default.hex}}",
"list.activeSelectionBackground": "{{colors.primary_container.default.hex}}",
"list.activeSelectionForeground": "{{colors.on_primary_container.default.hex}}",
"list.inactiveSelectionBackground": "{{colors.surface_container.default.hex}}",
"list.inactiveSelectionForeground": "{{colors.on_surface.default.hex}}",
"list.hoverBackground": "{{colors.surface_container.default.hex}}",
"list.hoverForeground": "{{colors.on_surface.default.hex}}",
"list.focusBackground": "{{colors.surface_container_high.default.hex}}",
"list.focusForeground": "{{colors.on_surface.default.hex}}",
"list.highlightForeground": "{{colors.primary.default.hex}}",
"statusBar.background": "{{colors.background.default.hex}}",
"statusBar.foreground": "{{colors.on_surface.default.hex}}",
"statusBar.border": "{{colors.background.default.hex}}",
"statusBar.noFolderBackground": "{{colors.background.default.hex}}",
"statusBar.debuggingBackground": "{{colors.error.default.hex}}",
"statusBar.debuggingForeground": "{{colors.on_error.default.hex}}",
"tab.activeBackground": "{{colors.background.default.hex}}",
"tab.inactiveBackground": "{{colors.background.default.hex}}",
"tab.activeForeground": "{{colors.on_surface.default.hex}}",
"tab.inactiveForeground": "{{colors.outline.default.hex}}",
"tab.border": "{{colors.background.default.hex}}",
"tab.activeBorder": "{{colors.primary.default.hex}}",
"tab.unfocusedActiveBorder": "{{colors.outline.default.hex}}",
"editorGroupHeader.tabsBackground": "{{colors.background.default.hex}}",
"editorGroupHeader.noTabsBackground": "{{colors.background.default.hex}}",
"titleBar.activeBackground": "{{colors.background.default.hex}}",
"titleBar.activeForeground": "{{colors.on_surface.default.hex}}",
"titleBar.inactiveBackground": "{{colors.background.default.hex}}",
"titleBar.inactiveForeground": "{{colors.outline.default.hex}}",
"titleBar.border": "{{colors.background.default.hex}}",
"input.background": "{{colors.background.default.hex}}",
"input.foreground": "{{colors.on_surface.default.hex}}",
"input.border": "{{colors.outline.default.hex}}",
"input.placeholderForeground": "{{colors.outline.default.hex}}",
"inputOption.activeBorder": "{{colors.primary.default.hex}}",
"inputValidation.errorBackground": "{{colors.error.default.hex}}",
"inputValidation.errorBorder": "{{colors.error.default.hex}}",
"dropdown.background": "{{colors.background.default.hex}}",
"dropdown.foreground": "{{colors.on_surface.default.hex}}",
"dropdown.border": "{{colors.outline.default.hex}}",
"quickInput.background": "{{colors.background.default.hex}}",
"quickInput.foreground": "{{colors.on_surface.default.hex}}",
"quickInputList.focusBackground": "{{colors.surface_container_high.default.hex}}",
"quickInputList.focusForeground": "{{colors.on_surface.default.hex}}",
"button.background": "{{colors.primary.default.hex}}",
"button.foreground": "{{colors.on_primary.default.hex}}",
"button.hoverBackground": "{{colors.primary_container.default.hex}}",
"focusBorder": "{{colors.primary.default.hex}}",
"badge.background": "{{colors.secondary.default.hex}}",
"badge.foreground": "{{colors.on_secondary.default.hex}}",
"panel.background": "{{colors.background.default.hex}}",
"panel.border": "{{colors.primary.default.hex}}",
"panelTitle.activeBorder": "{{colors.primary.default.hex}}",
"panelTitle.activeForeground": "{{colors.on_surface.default.hex}}",
"panelTitle.inactiveForeground": "{{colors.outline.default.hex}}",
"terminal.background": "{{colors.background.default.hex}}",
"terminal.foreground": "{{colors.on_surface.default.hex}}",
"gitDecoration.modifiedResourceForeground": "{{colors.primary.default.hex}}",
"gitDecoration.addedResourceForeground": "{{colors.primary.default.hex}}",
"gitDecoration.stageModifiedResourceForeground": "{{colors.primary.default.hex}}",
"gitDecoration.stageDeletedResourceForeground": "{{colors.error.default.hex}}",
"gitDecoration.deletedResourceForeground": "{{colors.error.default.hex}}",
"gitDecoration.untrackedResourceForeground": "{{colors.secondary.default.hex}}",
"gitDecoration.ignoredResourceForeground": "{{colors.outline.default.hex}}",
"gitDecoration.conflictingResourceForeground": "{{colors.error_container.default.hex}}",
"gitDecoration.submoduleResourceForeground": "{{colors.primary.default.hex}}",
"editorWidget.background": "{{colors.background.default.hex}}",
"editorWidget.border": "{{colors.outline.default.hex}}",
"editorSuggestWidget.background": "{{colors.background.default.hex}}",
"editorSuggestWidget.border": "{{colors.outline.default.hex}}",
"editorSuggestWidget.selectedBackground": "{{colors.surface_container_high.default.hex}}",
"editorSuggestWidget.highlightForeground": "{{colors.primary.default.hex}}",
"peekView.border": "{{colors.primary.default.hex}}",
"peekViewEditor.background": "{{colors.background.default.hex}}",
"peekViewResult.background": "{{colors.background.default.hex}}",
"peekViewTitle.background": "{{colors.background.default.hex}}",
"notificationCenter.border": "{{colors.outline.default.hex}}",
"notifications.background": "{{colors.background.default.hex}}",
"notifications.border": "{{colors.outline.default.hex}}",
"breadcrumb.foreground": "{{colors.outline.default.hex}}",
"breadcrumb.focusForeground": "{{colors.on_surface.default.hex}}",
"breadcrumb.activeSelectionForeground": "{{colors.primary.default.hex}}",
"scrollbarSlider.background": "{{colors.outline.default.hex}}40",
"scrollbarSlider.hoverBackground": "{{colors.outline.default.hex}}60",
"scrollbarSlider.activeBackground": "{{colors.outline.default.hex}}80",
"editorError.foreground": "{{colors.error.default.hex}}",
"editorWarning.foreground": "{{colors.tertiary.default.hex}}",
"editorInfo.foreground": "{{colors.primary.default.hex}}",
"editorGutter.addedBackground": "{{colors.secondary.default.hex}}",
"editorGutter.modifiedBackground": "{{colors.tertiary.default.hex}}",
"editorGutter.deletedBackground": "{{colors.error.default.hex}}",
"diffEditor.insertedTextBackground": "{{colors.secondary.default.hex}}20",
"diffEditor.removedTextBackground": "{{colors.error.default.hex}}20",
"merge.currentHeaderBackground": "{{colors.primary.default.hex}}40",
"merge.incomingHeaderBackground": "{{colors.secondary.default.hex}}40",
"menubar.selectionBackground": "{{colors.surface_container.default.hex}}",
"menu.background": "{{colors.background.default.hex}}",
"menu.foreground": "{{colors.on_surface.default.hex}}",
"menu.selectionBackground": "{{colors.surface_container_high.default.hex}}",
"menu.selectionForeground": "{{colors.on_surface.default.hex}}",
"debugToolBar.background": "{{colors.background.default.hex}}",
"debugExceptionWidget.background": "{{colors.background.default.hex}}",
"debugExceptionWidget.border": "{{colors.error.default.hex}}"
},
"tokenColors": [
{
"scope": ["comment", "punctuation.definition.comment"],
"settings": {
"foreground": "{{colors.outline.default.hex}}",
"fontStyle": "italic"
}
},
{
"scope": ["keyword", "storage.type", "storage.modifier"],
"settings": {
"foreground": "{{colors.primary.default.hex}}"
}
},
{
"scope": ["variable", "meta.object-literal.key"],
"settings": {
"foreground": "{{colors.on_surface.default.hex}}"
}
},
{
"scope": ["string", "constant.other.symbol"],
"settings": {
"foreground": "{{colors.secondary.default.hex}}"
}
},
{
"scope": ["constant.numeric", "constant.language", "constant.character"],
"settings": {
"foreground": "{{colors.tertiary.default.hex}}"
}
},
{
"scope": ["entity.name.type", "support.type", "entity.name.class"],
"settings": {
"foreground": "{{colors.tertiary.default.hex}}"
}
},
{
"scope": ["entity.name.function", "support.function"],
"settings": {
"foreground": "{{colors.primary.default.hex}}"
}
},
{
"scope": ["support.class", "support.variable", "variable.language"],
"settings": {
"foreground": "{{colors.secondary.default.hex}}"
}
},
{
"scope": ["invalid"],
"settings": {
"foreground": "{{colors.error.default.hex}}"
}
},
{
"scope": ["invalid.deprecated"],
"settings": {
"foreground": "{{colors.outline.default.hex}}"
}
},
{
"scope": ["markup.heading"],
"settings": {
"foreground": "{{colors.primary.default.hex}}",
"fontStyle": "bold"
}
},
{
"scope": ["markup.bold"],
"settings": {
"foreground": "{{colors.tertiary.default.hex}}",
"fontStyle": "bold"
}
},
{
"scope": ["markup.italic"],
"settings": {
"foreground": "{{colors.primary.default.hex}}",
"fontStyle": "italic"
}
},
{
"scope": ["markup.underline"],
"settings": {
"fontStyle": "underline"
}
},
{
"scope": ["markup.quote"],
"settings": {
"foreground": "{{colors.secondary.default.hex}}"
}
},
{
"scope": ["markup.list"],
"settings": {
"foreground": "{{colors.on_surface.default.hex}}"
}
},
{
"scope": ["markup.raw", "markup.inline.raw"],
"settings": {
"foreground": "{{colors.secondary.default.hex}}"
}
}
],
"semanticHighlighting": true,
"semanticTokenColors": {
"variable.readonly": {
"foreground": "{{colors.tertiary.default.hex}}"
},
"property": {
"foreground": "{{colors.on_surface.default.hex}}"
},
"function": {
"foreground": "{{colors.primary.default.hex}}"
},
"method": {
"foreground": "{{colors.primary.default.hex}}"
},
"type": {
"foreground": "{{colors.tertiary.default.hex}}"
},
"class": {
"foreground": "{{colors.tertiary.default.hex}}"
},
"enumMember": {
"foreground": "{{colors.tertiary.default.hex}}"
},
"string": {
"foreground": "{{colors.secondary.default.hex}}"
},
"number": {
"foreground": "{{colors.tertiary.default.hex}}"
},
"comment": {
"foreground": "{{colors.outline.default.hex}}",
"fontStyle": "italic"
},
"keyword": {
"foreground": "{{colors.primary.default.hex}}"
},
"operator": {
"foreground": "{{colors.on_surface.default.hex}}"
},
"parameter": {
"foreground": "{{colors.on_surface.default.hex}}"
},
"namespace": {
"foreground": "{{colors.secondary.default.hex}}"
}
}
}
+2 -2
View File
@@ -9,5 +9,5 @@ cursor_border = '{{colors.primary.default.hex}}'
selection_bg = '{{colors.primary_container.default.hex}}' selection_bg = '{{colors.primary_container.default.hex}}'
selection_fg = '{{colors.on_surface.default.hex}}' selection_fg = '{{colors.on_surface.default.hex}}'
ansi = ['{{dank16.color0.hex}}', '{{dank16.color1.hex}}', '{{dank16.color2.hex}}', '{{dank16.color3.hex}}', '{{dank16.color4.hex}}', '{{dank16.color5.hex}}', '{{dank16.color6.hex}}', '{{dank16.color7.hex}}'] ansi = ['{{dank16.color0.default.hex}}', '{{dank16.color1.default.hex}}', '{{dank16.color2.default.hex}}', '{{dank16.color3.default.hex}}', '{{dank16.color4.default.hex}}', '{{dank16.color5.default.hex}}', '{{dank16.color6.default.hex}}', '{{dank16.color7.default.hex}}']
brights = ['{{dank16.color8.hex}}', '{{dank16.color9.hex}}', '{{dank16.color10.hex}}', '{{dank16.color11.hex}}', '{{dank16.color12.hex}}', '{{dank16.color13.hex}}', '{{dank16.color14.hex}}', '{{dank16.color15.hex}}'] brights = ['{{dank16.color8.default.hex}}', '{{dank16.color9.default.hex}}', '{{dank16.color10.default.hex}}', '{{dank16.color11.default.hex}}', '{{dank16.color12.default.hex}}', '{{dank16.color13.default.hex}}', '{{dank16.color14.default.hex}}', '{{dank16.color15.default.hex}}']
-298
View File
@@ -1,298 +0,0 @@
#!/usr/bin/env bash
set -uo pipefail
log() { echo "[matugen-worker] $*" >&2; }
err() { echo "[matugen-worker] ERROR: $*" >&2; }
[[ $# -lt 6 ]] && { echo "Usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR SYNC_MODE_WITH_PORTAL TERMINALS_ALWAYS_DARK --run" >&2; exit 1; }
STATE_DIR="$1"
SHELL_DIR="$2"
CONFIG_DIR="$3"
SYNC_MODE_WITH_PORTAL="$4"
TERMINALS_ALWAYS_DARK="$5"
shift 5
[[ "${1:-}" != "--run" ]] && { echo "Usage: $0 ... --run" >&2; exit 1; }
[[ ! -d "$STATE_DIR" ]] && { err "STATE_DIR '$STATE_DIR' does not exist"; exit 1; }
[[ ! -d "$SHELL_DIR" ]] && { err "SHELL_DIR '$SHELL_DIR' does not exist"; exit 1; }
[[ ! -d "$CONFIG_DIR" ]] && { err "CONFIG_DIR '$CONFIG_DIR' does not exist"; exit 1; }
DESIRED_JSON="$STATE_DIR/matugen.desired.json"
BUILT_KEY="$STATE_DIR/matugen.key"
LOCK="$STATE_DIR/matugen-worker.lock"
COLORS_OUTPUT="$STATE_DIR/dms-colors.json"
exec 9>"$LOCK"
flock 9
rm -f "$BUILT_KEY"
read_json_field() {
local json="$1" field="$2"
echo "$json" | sed -n "s/.*\"$field\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" | head -1
}
read_json_escaped_field() {
local json="$1" field="$2"
local after="${json#*\"$field\":\"}"
[[ "$after" == "$json" ]] && return
local result=""
while [[ -n "$after" ]]; do
local char="${after:0:1}"
after="${after:1}"
[[ "$char" == '"' ]] && break
[[ "$char" == '\' ]] && { result+="${after:0:1}"; after="${after:1}"; continue; }
result+="$char"
done
echo "$result"
}
read_json_bool() {
local json="$1" field="$2"
echo "$json" | sed -n "s/.*\"$field\"[[:space:]]*:[[:space:]]*\([^,}]*\).*/\1/p" | head -1 | tr -d ' '
}
compute_key() {
local json="$1"
local kind=$(read_json_field "$json" "kind")
local value=$(read_json_field "$json" "value")
local mode=$(read_json_field "$json" "mode")
local icon=$(read_json_field "$json" "iconTheme")
local mtype=$(read_json_field "$json" "matugenType")
local run_user=$(read_json_bool "$json" "runUserTemplates")
local stock_colors=$(read_json_escaped_field "$json" "stockColors")
echo "${kind}|${value}|${mode}|${icon:-default}|${mtype:-scheme-tonal-spot}|${run_user:-true}|${stock_colors:-}" | sha256sum | cut -d' ' -f1
}
append_config() {
local check_cmd="$1" file_name="$2" cfg_file="$3"
local target="$SHELL_DIR/matugen/configs/$file_name"
[[ ! -f "$target" ]] && return
[[ "$check_cmd" != "skip" ]] && ! command -v "$check_cmd" >/dev/null 2>&1 && return
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$target" >> "$cfg_file"
echo "" >> "$cfg_file"
}
append_vscode_config() {
local name="$1" ext_dir="$2" cfg_file="$3"
[[ ! -d "$ext_dir" ]] && return
local template_dir="$SHELL_DIR/matugen/templates"
cat >> "$cfg_file" << EOF
[templates.dms${name}default]
input_path = '$template_dir/vscode-color-theme-default.json'
output_path = '$ext_dir/themes/dankshell-default.json'
[templates.dms${name}dark]
input_path = '$template_dir/vscode-color-theme-dark.json'
output_path = '$ext_dir/themes/dankshell-dark.json'
[templates.dms${name}light]
input_path = '$template_dir/vscode-color-theme-light.json'
output_path = '$ext_dir/themes/dankshell-light.json'
EOF
log "Added $name theme config (extension found at $ext_dir)"
}
build_merged_config() {
local mode="$1" run_user="$2" cfg_file="$3"
if [[ "$run_user" == "true" && -f "$CONFIG_DIR/matugen/config.toml" ]]; then
awk '/^\[config\]/{p=1} /^\[templates\]/{p=0} p' "$CONFIG_DIR/matugen/config.toml" >> "$cfg_file"
else
echo "[config]" >> "$cfg_file"
fi
echo "" >> "$cfg_file"
grep -v '^\[config\]' "$SHELL_DIR/matugen/configs/base.toml" | sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" >> "$cfg_file"
echo "" >> "$cfg_file"
cat >> "$cfg_file" << EOF
[templates.dank]
input_path = '$SHELL_DIR/matugen/templates/dank.json'
output_path = '$COLORS_OUTPUT'
EOF
[[ "$mode" == "light" ]] && append_config "skip" "gtk3-light.toml" "$cfg_file" || append_config "skip" "gtk3-dark.toml" "$cfg_file"
append_config "niri" "niri.toml" "$cfg_file"
append_config "qt5ct" "qt5ct.toml" "$cfg_file"
append_config "qt6ct" "qt6ct.toml" "$cfg_file"
append_config "firefox" "firefox.toml" "$cfg_file"
append_config "pywalfox" "pywalfox.toml" "$cfg_file"
append_config "vesktop" "vesktop.toml" "$cfg_file"
append_config "ghostty" "ghostty.toml" "$cfg_file"
append_config "kitty" "kitty.toml" "$cfg_file"
append_config "foot" "foot.toml" "$cfg_file"
append_config "alacritty" "alacritty.toml" "$cfg_file"
append_config "wezterm" "wezterm.toml" "$cfg_file"
append_config "dgop" "dgop.toml" "$cfg_file"
append_vscode_config "vscode" "$HOME/.vscode/extensions/local.dynamic-base16-dankshell-0.0.1" "$cfg_file"
append_vscode_config "codium" "$HOME/.vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1" "$cfg_file"
append_vscode_config "codeoss" "$HOME/.config/Code - OSS/extensions/local.dynamic-base16-dankshell-0.0.1" "$cfg_file"
append_vscode_config "cursor" "$HOME/.cursor/extensions/local.dynamic-base16-dankshell-0.0.1" "$cfg_file"
append_vscode_config "windsurf" "$HOME/.windsurf/extensions/local.dynamic-base16-dankshell-0.0.1" "$cfg_file"
if [[ "$run_user" == "true" && -f "$CONFIG_DIR/matugen/config.toml" ]]; then
awk '/^\[templates\]/{p=1} p' "$CONFIG_DIR/matugen/config.toml" >> "$cfg_file"
echo "" >> "$cfg_file"
fi
if [[ -d "$CONFIG_DIR/matugen/dms/configs" ]]; then
for config in "$CONFIG_DIR/matugen/dms/configs"/*.toml; do
[[ -f "$config" ]] || continue
cat "$config" >> "$cfg_file"
echo "" >> "$cfg_file"
done
fi
}
generate_dank16() {
local primary="$1" surface="$2" light_flag="$3"
local args=("$primary" --json)
[[ -n "$light_flag" ]] && args+=("$light_flag")
[[ -n "$surface" ]] && args+=(--background "$surface")
dms dank16 "${args[@]}" 2>/dev/null || echo '{}'
}
set_system_color_scheme() {
[[ "$SYNC_MODE_WITH_PORTAL" != "true" ]] && return
local mode="$1"
local scheme="prefer-dark"
[[ "$mode" == "light" ]] && scheme="default"
gsettings set org.gnome.desktop.interface color-scheme "$scheme" 2>/dev/null || \
dconf write /org/gnome/desktop/interface/color-scheme "'$scheme'" 2>/dev/null || true
}
sync_color_scheme_on_exit() {
[[ "$SYNC_MODE_WITH_PORTAL" != "true" ]] && return
[[ ! -f "$DESIRED_JSON" ]] && return
local json mode
json=$(cat "$DESIRED_JSON" 2>/dev/null) || return
mode=$(read_json_field "$json" "mode")
[[ -n "$mode" ]] && set_system_color_scheme "$mode"
}
trap sync_color_scheme_on_exit EXIT
refresh_gtk() {
local mode="$1"
local gtk_css="$CONFIG_DIR/gtk-3.0/gtk.css"
[[ ! -e "$gtk_css" ]] && return
local should_run=false
if [[ -L "$gtk_css" ]]; then
[[ "$(readlink "$gtk_css")" == *"dank-colors.css"* ]] && should_run=true
elif grep -q "dank-colors.css" "$gtk_css" 2>/dev/null; then
should_run=true
fi
[[ "$should_run" != "true" ]] && return
gsettings set org.gnome.desktop.interface gtk-theme "" 2>/dev/null || true
gsettings set org.gnome.desktop.interface gtk-theme "adw-gtk3-${mode}" 2>/dev/null || true
}
signal_terminals() {
pgrep -x kitty >/dev/null 2>&1 && pkill -USR1 kitty
pgrep -x ghostty >/dev/null 2>&1 && pkill -USR2 ghostty
}
build_once() {
local json="$1"
local kind=$(read_json_field "$json" "kind")
local value=$(read_json_field "$json" "value")
local mode=$(read_json_field "$json" "mode")
local mtype=$(read_json_field "$json" "matugenType")
local run_user=$(read_json_bool "$json" "runUserTemplates")
local stock_colors=$(read_json_escaped_field "$json" "stockColors")
[[ -z "$mtype" ]] && mtype="scheme-tonal-spot"
[[ -z "$run_user" ]] && run_user="true"
local TMP_CFG=$(mktemp)
trap "rm -f '$TMP_CFG'" RETURN
build_merged_config "$mode" "$run_user" "$TMP_CFG"
local light_flag=""
[[ "$mode" == "light" ]] && light_flag="--light"
local primary surface dank16_dark dank16_light import_args=()
if [[ -n "$stock_colors" ]]; then
log "Using stock/custom theme colors with matugen base"
primary=$(echo "$stock_colors" | sed -n 's/.*"primary"[^{]*{[^}]*"dark"[^{]*{[^}]*"color"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
surface=$(echo "$stock_colors" | sed -n 's/.*"surface"[^{]*{[^}]*"dark"[^{]*{[^}]*"color"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' | head -1)
[[ -z "$primary" ]] && { err "Failed to extract primary from stock colors"; return 1; }
dank16_dark=$(generate_dank16 "$primary" "$surface" "")
dank16_light=$(generate_dank16 "$primary" "$surface" "--light")
local dank16_current
[[ "$mode" == "light" ]] && dank16_current="$dank16_light" || dank16_current="$dank16_dark"
[[ "$TERMINALS_ALWAYS_DARK" == "true" && "$mode" == "light" ]] && dank16_current="$dank16_dark"
import_args+=(--import-json-string "{\"colors\": $stock_colors, \"dank16\": $dank16_current}")
log "Running matugen color hex with stock color overrides"
if ! matugen color hex "$primary" -m "$mode" -t "${mtype:-scheme-tonal-spot}" -c "$TMP_CFG" "${import_args[@]}"; then
err "matugen failed"
return 1
fi
else
log "Using dynamic theme from $kind: $value"
local matugen_cmd=("matugen")
[[ "$kind" == "hex" ]] && matugen_cmd+=("color" "hex") || matugen_cmd+=("$kind")
matugen_cmd+=("$value")
local mat_json
mat_json=$("${matugen_cmd[@]}" -m dark -t "$mtype" --json hex --dry-run 2>/dev/null | tr -d '\n')
[[ -z "$mat_json" ]] && { err "matugen dry-run failed"; return 1; }
primary=$(echo "$mat_json" | sed -n 's/.*"primary"[[:space:]]*:[[:space:]]*{[^}]*"dark"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
surface=$(echo "$mat_json" | sed -n 's/.*"surface"[[:space:]]*:[[:space:]]*{[^}]*"dark"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p')
[[ -z "$primary" ]] && { err "Failed to extract primary color"; return 1; }
dank16_dark=$(generate_dank16 "$primary" "$surface" "")
dank16_light=$(generate_dank16 "$primary" "$surface" "--light")
local dank16_current
[[ "$mode" == "light" ]] && dank16_current="$dank16_light" || dank16_current="$dank16_dark"
[[ "$TERMINALS_ALWAYS_DARK" == "true" && "$mode" == "light" ]] && dank16_current="$dank16_dark"
import_args+=(--import-json-string "{\"dank16\": $dank16_current}")
log "Running matugen $kind with dank16 injection"
if ! "${matugen_cmd[@]}" -m "$mode" -t "$mtype" -c "$TMP_CFG" "${import_args[@]}"; then
err "matugen failed"
return 1
fi
fi
refresh_gtk "$mode"
signal_terminals
return 0
}
[[ ! -f "$DESIRED_JSON" ]] && { log "No desired state file"; exit 0; }
DESIRED=$(cat "$DESIRED_JSON")
WANT_KEY=$(compute_key "$DESIRED")
HAVE_KEY=""
[[ -f "$BUILT_KEY" ]] && HAVE_KEY=$(cat "$BUILT_KEY" 2>/dev/null || true)
[[ "$WANT_KEY" == "$HAVE_KEY" ]] && { log "Already up to date"; exit 0; }
log "Building theme (key: ${WANT_KEY:0:12}...)"
if build_once "$DESIRED"; then
echo "$WANT_KEY" > "$BUILT_KEY"
log "Done"
exit 0
else
err "Build failed"
exit 2
fi