mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2025-12-05 21:15:38 -05:00
Compare commits
7 Commits
e5a6a00282
...
de8f2e6a68
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
de8f2e6a68 | ||
|
|
85704e3947 | ||
|
|
4d661ff41d | ||
|
|
d7b39634e6 | ||
|
|
039c98b9e3 | ||
|
|
172c4bf0a9 | ||
|
|
1f2a1c5dec |
@@ -46,3 +46,9 @@ packages:
|
||||
outpkg: mocks_evdev
|
||||
interfaces:
|
||||
EvdevDevice:
|
||||
github.com/AvengeMedia/DankMaterialShell/core/internal/version:
|
||||
config:
|
||||
dir: "internal/mocks/version"
|
||||
outpkg: mocks_version
|
||||
interfaces:
|
||||
VersionFetcher:
|
||||
|
||||
@@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/AvengeMedia/DankMaterialShell/core/internal/dank16"
|
||||
@@ -26,7 +25,6 @@ func init() {
|
||||
dank16Cmd.Flags().Bool("alacritty", false, "Output in Alacritty terminal format")
|
||||
dank16Cmd.Flags().Bool("ghostty", false, "Output in Ghostty terminal format")
|
||||
dank16Cmd.Flags().Bool("wezterm", false, "Output in Wezterm terminal format")
|
||||
dank16Cmd.Flags().String("vscode-enrich", "", "Enrich existing VSCode theme file with terminal colors")
|
||||
dank16Cmd.Flags().String("background", "", "Custom background color")
|
||||
dank16Cmd.Flags().String("contrast", "dps", "Contrast algorithm: dps (Delta Phi Star, default) or wcag")
|
||||
}
|
||||
@@ -44,7 +42,6 @@ func runDank16(cmd *cobra.Command, args []string) {
|
||||
isAlacritty, _ := cmd.Flags().GetBool("alacritty")
|
||||
isGhostty, _ := cmd.Flags().GetBool("ghostty")
|
||||
isWezterm, _ := cmd.Flags().GetBool("wezterm")
|
||||
vscodeEnrich, _ := cmd.Flags().GetString("vscode-enrich")
|
||||
background, _ := cmd.Flags().GetString("background")
|
||||
contrastAlgo, _ := cmd.Flags().GetString("contrast")
|
||||
|
||||
@@ -65,18 +62,7 @@ func runDank16(cmd *cobra.Command, args []string) {
|
||||
|
||||
colors := dank16.GeneratePalette(primaryColor, opts)
|
||||
|
||||
if vscodeEnrich != "" {
|
||||
data, err := os.ReadFile(vscodeEnrich)
|
||||
if err != nil {
|
||||
log.Fatalf("Error reading file: %v", err)
|
||||
}
|
||||
|
||||
enriched, err := dank16.EnrichVSCodeTheme(data, colors)
|
||||
if err != nil {
|
||||
log.Fatalf("Error enriching theme: %v", err)
|
||||
}
|
||||
fmt.Println(string(enriched))
|
||||
} else if isJson {
|
||||
if isJson {
|
||||
fmt.Print(dank16.GenerateJSON(colors))
|
||||
} else if isKitty {
|
||||
fmt.Print(dank16.GenerateKittyTheme(colors))
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package dank16
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"math"
|
||||
"testing"
|
||||
)
|
||||
@@ -373,79 +372,6 @@ func TestGeneratePalette(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnrichVSCodeTheme(t *testing.T) {
|
||||
colors := GeneratePalette("#625690", PaletteOptions{IsLight: false})
|
||||
|
||||
baseTheme := map[string]interface{}{
|
||||
"name": "Test Theme",
|
||||
"type": "dark",
|
||||
"colors": map[string]interface{}{
|
||||
"editor.background": "#000000",
|
||||
},
|
||||
}
|
||||
|
||||
themeJSON, err := json.Marshal(baseTheme)
|
||||
if err != nil {
|
||||
t.Fatalf("Failed to marshal base theme: %v", err)
|
||||
}
|
||||
|
||||
result, err := EnrichVSCodeTheme(themeJSON, colors)
|
||||
if err != nil {
|
||||
t.Fatalf("EnrichVSCodeTheme failed: %v", err)
|
||||
}
|
||||
|
||||
var enriched map[string]interface{}
|
||||
if err := json.Unmarshal(result, &enriched); err != nil {
|
||||
t.Fatalf("Failed to unmarshal result: %v", err)
|
||||
}
|
||||
|
||||
colorsMap, ok := enriched["colors"].(map[string]interface{})
|
||||
if !ok {
|
||||
t.Fatal("colors is not a map")
|
||||
}
|
||||
|
||||
terminalColors := []string{
|
||||
"terminal.ansiBlack",
|
||||
"terminal.ansiRed",
|
||||
"terminal.ansiGreen",
|
||||
"terminal.ansiYellow",
|
||||
"terminal.ansiBlue",
|
||||
"terminal.ansiMagenta",
|
||||
"terminal.ansiCyan",
|
||||
"terminal.ansiWhite",
|
||||
"terminal.ansiBrightBlack",
|
||||
"terminal.ansiBrightRed",
|
||||
"terminal.ansiBrightGreen",
|
||||
"terminal.ansiBrightYellow",
|
||||
"terminal.ansiBrightBlue",
|
||||
"terminal.ansiBrightMagenta",
|
||||
"terminal.ansiBrightCyan",
|
||||
"terminal.ansiBrightWhite",
|
||||
}
|
||||
|
||||
for i, key := range terminalColors {
|
||||
if val, ok := colorsMap[key]; !ok {
|
||||
t.Errorf("Missing terminal color: %s", key)
|
||||
} else if val != colors[i] {
|
||||
t.Errorf("%s = %s, expected %s", key, val, colors[i])
|
||||
}
|
||||
}
|
||||
|
||||
if colorsMap["editor.background"] != "#000000" {
|
||||
t.Error("Original theme colors should be preserved")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnrichVSCodeThemeInvalidJSON(t *testing.T) {
|
||||
colors := GeneratePalette("#625690", PaletteOptions{IsLight: false})
|
||||
invalidJSON := []byte("{invalid json")
|
||||
|
||||
_, err := EnrichVSCodeTheme(invalidJSON, colors)
|
||||
if err == nil {
|
||||
t.Error("Expected error for invalid JSON, got nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTripConversion(t *testing.T) {
|
||||
testColors := []string{"#000000", "#ffffff", "#ff0000", "#00ff00", "#0000ff", "#625690", "#808080"}
|
||||
|
||||
|
||||
@@ -1,250 +0,0 @@
|
||||
package dank16
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type VSCodeTheme struct {
|
||||
Schema string `json:"$schema"`
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
Colors map[string]string `json:"colors"`
|
||||
TokenColors []VSCodeTokenColor `json:"tokenColors"`
|
||||
SemanticHighlighting bool `json:"semanticHighlighting"`
|
||||
SemanticTokenColors map[string]VSCodeTokenSetting `json:"semanticTokenColors"`
|
||||
}
|
||||
|
||||
type VSCodeTokenColor struct {
|
||||
Scope interface{} `json:"scope"`
|
||||
Settings VSCodeTokenSetting `json:"settings"`
|
||||
}
|
||||
|
||||
type VSCodeTokenSetting struct {
|
||||
Foreground string `json:"foreground,omitempty"`
|
||||
FontStyle string `json:"fontStyle,omitempty"`
|
||||
}
|
||||
|
||||
func updateTokenColor(tc interface{}, scopeToColor map[string]string) {
|
||||
tcMap, ok := tc.(map[string]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
scopes, ok := tcMap["scope"].([]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
settings, ok := tcMap["settings"].(map[string]interface{})
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
isYaml := hasScopeContaining(scopes, "yaml")
|
||||
|
||||
for _, scope := range scopes {
|
||||
scopeStr, ok := scope.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if scopeStr == "string" && isYaml {
|
||||
continue
|
||||
}
|
||||
|
||||
if applyColorToScope(settings, scope, scopeToColor) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func applyColorToScope(settings map[string]interface{}, scope interface{}, scopeToColor map[string]string) bool {
|
||||
scopeStr, ok := scope.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
newColor, exists := scopeToColor[scopeStr]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
settings["foreground"] = newColor
|
||||
return true
|
||||
}
|
||||
|
||||
func hasScopeContaining(scopes []interface{}, substring string) bool {
|
||||
for _, scope := range scopes {
|
||||
scopeStr, ok := scope.(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
for i := 0; i <= len(scopeStr)-len(substring); i++ {
|
||||
if scopeStr[i:i+len(substring)] == substring {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func EnrichVSCodeTheme(themeData []byte, colors []string) ([]byte, error) {
|
||||
var theme map[string]interface{}
|
||||
if err := json.Unmarshal(themeData, &theme); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
colorsMap, ok := theme["colors"].(map[string]interface{})
|
||||
if !ok {
|
||||
colorsMap = make(map[string]interface{})
|
||||
theme["colors"] = colorsMap
|
||||
}
|
||||
|
||||
bg := colors[0]
|
||||
isLight := false
|
||||
if len(bg) == 7 && bg[0] == '#' {
|
||||
r, g, b := 0, 0, 0
|
||||
fmt.Sscanf(bg[1:], "%02x%02x%02x", &r, &g, &b)
|
||||
luminance := (0.299*float64(r) + 0.587*float64(g) + 0.114*float64(b)) / 255.0
|
||||
isLight = luminance > 0.5
|
||||
}
|
||||
|
||||
if isLight {
|
||||
theme["type"] = "light"
|
||||
} else {
|
||||
theme["type"] = "dark"
|
||||
}
|
||||
|
||||
colorsMap["terminal.ansiBlack"] = colors[0]
|
||||
colorsMap["terminal.ansiRed"] = colors[1]
|
||||
colorsMap["terminal.ansiGreen"] = colors[2]
|
||||
colorsMap["terminal.ansiYellow"] = colors[3]
|
||||
colorsMap["terminal.ansiBlue"] = colors[4]
|
||||
colorsMap["terminal.ansiMagenta"] = colors[5]
|
||||
colorsMap["terminal.ansiCyan"] = colors[6]
|
||||
colorsMap["terminal.ansiWhite"] = colors[7]
|
||||
colorsMap["terminal.ansiBrightBlack"] = colors[8]
|
||||
colorsMap["terminal.ansiBrightRed"] = colors[9]
|
||||
colorsMap["terminal.ansiBrightGreen"] = colors[10]
|
||||
colorsMap["terminal.ansiBrightYellow"] = colors[11]
|
||||
colorsMap["terminal.ansiBrightBlue"] = colors[12]
|
||||
colorsMap["terminal.ansiBrightMagenta"] = colors[13]
|
||||
colorsMap["terminal.ansiBrightCyan"] = colors[14]
|
||||
colorsMap["terminal.ansiBrightWhite"] = colors[15]
|
||||
|
||||
tokenColors, ok := theme["tokenColors"].([]interface{})
|
||||
if ok {
|
||||
scopeToColor := map[string]string{
|
||||
"comment": colors[8],
|
||||
"punctuation.definition.comment": colors[8],
|
||||
"keyword": colors[5],
|
||||
"storage.type": colors[13],
|
||||
"storage.modifier": colors[5],
|
||||
"variable": colors[15],
|
||||
"variable.parameter": colors[7],
|
||||
"meta.object-literal.key": colors[4],
|
||||
"meta.property.object": colors[4],
|
||||
"variable.other.property": colors[4],
|
||||
"constant.other.symbol": colors[12],
|
||||
"constant.numeric": colors[12],
|
||||
"constant.language": colors[12],
|
||||
"constant.character": colors[3],
|
||||
"entity.name.type": colors[12],
|
||||
"support.type": colors[13],
|
||||
"entity.name.class": colors[12],
|
||||
"entity.name.function": colors[2],
|
||||
"support.function": colors[2],
|
||||
"support.class": colors[15],
|
||||
"support.variable": colors[15],
|
||||
"variable.language": colors[12],
|
||||
"entity.name.tag.yaml": colors[12],
|
||||
"string.unquoted.plain.out.yaml": colors[15],
|
||||
"string.unquoted.yaml": colors[15],
|
||||
"string": colors[3],
|
||||
}
|
||||
|
||||
for i, tc := range tokenColors {
|
||||
updateTokenColor(tc, scopeToColor)
|
||||
tokenColors[i] = tc
|
||||
}
|
||||
|
||||
yamlRules := []VSCodeTokenColor{
|
||||
{
|
||||
Scope: "entity.name.tag.yaml",
|
||||
Settings: VSCodeTokenSetting{Foreground: colors[12]},
|
||||
},
|
||||
{
|
||||
Scope: []string{"string.unquoted.plain.out.yaml", "string.unquoted.yaml"},
|
||||
Settings: VSCodeTokenSetting{Foreground: colors[15]},
|
||||
},
|
||||
}
|
||||
|
||||
for _, rule := range yamlRules {
|
||||
tokenColors = append(tokenColors, rule)
|
||||
}
|
||||
|
||||
theme["tokenColors"] = tokenColors
|
||||
}
|
||||
|
||||
if semanticTokenColors, ok := theme["semanticTokenColors"].(map[string]interface{}); ok {
|
||||
updates := map[string]string{
|
||||
"variable": colors[15],
|
||||
"variable.readonly": colors[12],
|
||||
"property": colors[4],
|
||||
"function": colors[2],
|
||||
"method": colors[2],
|
||||
"type": colors[12],
|
||||
"class": colors[12],
|
||||
"typeParameter": colors[13],
|
||||
"enumMember": colors[12],
|
||||
"string": colors[3],
|
||||
"number": colors[12],
|
||||
"comment": colors[8],
|
||||
"keyword": colors[5],
|
||||
"operator": colors[15],
|
||||
"parameter": colors[7],
|
||||
"namespace": colors[15],
|
||||
}
|
||||
|
||||
for key, color := range updates {
|
||||
if existing, ok := semanticTokenColors[key].(map[string]interface{}); ok {
|
||||
existing["foreground"] = color
|
||||
} else {
|
||||
semanticTokenColors[key] = map[string]interface{}{
|
||||
"foreground": color,
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
semanticTokenColors := make(map[string]interface{})
|
||||
updates := map[string]string{
|
||||
"variable": colors[7],
|
||||
"variable.readonly": colors[12],
|
||||
"property": colors[4],
|
||||
"function": colors[2],
|
||||
"method": colors[2],
|
||||
"type": colors[12],
|
||||
"class": colors[12],
|
||||
"typeParameter": colors[13],
|
||||
"enumMember": colors[12],
|
||||
"string": colors[3],
|
||||
"number": colors[12],
|
||||
"comment": colors[8],
|
||||
"keyword": colors[5],
|
||||
"operator": colors[15],
|
||||
"parameter": colors[7],
|
||||
"namespace": colors[15],
|
||||
}
|
||||
|
||||
for key, color := range updates {
|
||||
semanticTokenColors[key] = map[string]interface{}{
|
||||
"foreground": color,
|
||||
}
|
||||
}
|
||||
theme["semanticTokenColors"] = semanticTokenColors
|
||||
}
|
||||
|
||||
return json.MarshalIndent(theme, "", " ")
|
||||
}
|
||||
@@ -37,6 +37,9 @@ func init() {
|
||||
Register("garuda", "#cba6f7", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewArchDistribution(config, logChan)
|
||||
})
|
||||
Register("artix", "#1793D1", FamilyArch, func(config DistroConfig, logChan chan<- string) Distribution {
|
||||
return NewArchDistribution(config, logChan)
|
||||
})
|
||||
}
|
||||
|
||||
type ArchDistribution struct {
|
||||
|
||||
144
core/internal/mocks/version/mock_VersionFetcher.go
Normal file
144
core/internal/mocks/version/mock_VersionFetcher.go
Normal file
@@ -0,0 +1,144 @@
|
||||
// Code generated by mockery v2.53.5. DO NOT EDIT.
|
||||
|
||||
package mocks_version
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// MockVersionFetcher is an autogenerated mock type for the VersionFetcher type
|
||||
type MockVersionFetcher struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
type MockVersionFetcher_Expecter struct {
|
||||
mock *mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockVersionFetcher) EXPECT() *MockVersionFetcher_Expecter {
|
||||
return &MockVersionFetcher_Expecter{mock: &_m.Mock}
|
||||
}
|
||||
|
||||
// GetCurrentVersion provides a mock function with given fields: dmsPath
|
||||
func (_m *MockVersionFetcher) GetCurrentVersion(dmsPath string) (string, error) {
|
||||
ret := _m.Called(dmsPath)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetCurrentVersion")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string) (string, error)); ok {
|
||||
return rf(dmsPath)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) string); ok {
|
||||
r0 = rf(dmsPath)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(dmsPath)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockVersionFetcher_GetCurrentVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetCurrentVersion'
|
||||
type MockVersionFetcher_GetCurrentVersion_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetCurrentVersion is a helper method to define mock.On call
|
||||
// - dmsPath string
|
||||
func (_e *MockVersionFetcher_Expecter) GetCurrentVersion(dmsPath interface{}) *MockVersionFetcher_GetCurrentVersion_Call {
|
||||
return &MockVersionFetcher_GetCurrentVersion_Call{Call: _e.mock.On("GetCurrentVersion", dmsPath)}
|
||||
}
|
||||
|
||||
func (_c *MockVersionFetcher_GetCurrentVersion_Call) Run(run func(dmsPath string)) *MockVersionFetcher_GetCurrentVersion_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockVersionFetcher_GetCurrentVersion_Call) Return(_a0 string, _a1 error) *MockVersionFetcher_GetCurrentVersion_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockVersionFetcher_GetCurrentVersion_Call) RunAndReturn(run func(string) (string, error)) *MockVersionFetcher_GetCurrentVersion_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// GetLatestVersion provides a mock function with given fields: dmsPath
|
||||
func (_m *MockVersionFetcher) GetLatestVersion(dmsPath string) (string, error) {
|
||||
ret := _m.Called(dmsPath)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for GetLatestVersion")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string) (string, error)); ok {
|
||||
return rf(dmsPath)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string) string); ok {
|
||||
r0 = rf(dmsPath)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string) error); ok {
|
||||
r1 = rf(dmsPath)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// MockVersionFetcher_GetLatestVersion_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLatestVersion'
|
||||
type MockVersionFetcher_GetLatestVersion_Call struct {
|
||||
*mock.Call
|
||||
}
|
||||
|
||||
// GetLatestVersion is a helper method to define mock.On call
|
||||
// - dmsPath string
|
||||
func (_e *MockVersionFetcher_Expecter) GetLatestVersion(dmsPath interface{}) *MockVersionFetcher_GetLatestVersion_Call {
|
||||
return &MockVersionFetcher_GetLatestVersion_Call{Call: _e.mock.On("GetLatestVersion", dmsPath)}
|
||||
}
|
||||
|
||||
func (_c *MockVersionFetcher_GetLatestVersion_Call) Run(run func(dmsPath string)) *MockVersionFetcher_GetLatestVersion_Call {
|
||||
_c.Call.Run(func(args mock.Arguments) {
|
||||
run(args[0].(string))
|
||||
})
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockVersionFetcher_GetLatestVersion_Call) Return(_a0 string, _a1 error) *MockVersionFetcher_GetLatestVersion_Call {
|
||||
_c.Call.Return(_a0, _a1)
|
||||
return _c
|
||||
}
|
||||
|
||||
func (_c *MockVersionFetcher_GetLatestVersion_Call) RunAndReturn(run func(string) (string, error)) *MockVersionFetcher_GetLatestVersion_Call {
|
||||
_c.Call.Return(run)
|
||||
return _c
|
||||
}
|
||||
|
||||
// NewMockVersionFetcher creates a new instance of MockVersionFetcher. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewMockVersionFetcher(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *MockVersionFetcher {
|
||||
mock := &MockVersionFetcher{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
@@ -18,28 +18,27 @@ type VersionInfo struct {
|
||||
HasUpdate bool
|
||||
}
|
||||
|
||||
func GetCurrentDMSVersion() (string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get home directory: %w", err)
|
||||
}
|
||||
// VersionFetcher is an interface for fetching version information
|
||||
type VersionFetcher interface {
|
||||
GetCurrentVersion(dmsPath string) (string, error)
|
||||
GetLatestVersion(dmsPath string) (string, error)
|
||||
}
|
||||
|
||||
dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms")
|
||||
if _, err := os.Stat(dmsPath); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("DMS not installed")
|
||||
}
|
||||
|
||||
originalDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Chdir(originalDir)
|
||||
|
||||
if err := os.Chdir(dmsPath); err != nil {
|
||||
return "", fmt.Errorf("failed to change to DMS directory: %w", err)
|
||||
}
|
||||
// DefaultVersionFetcher is the default implementation that uses git/curl
|
||||
type DefaultVersionFetcher struct{}
|
||||
|
||||
func (d *DefaultVersionFetcher) GetCurrentVersion(dmsPath string) (string, error) {
|
||||
if _, err := os.Stat(filepath.Join(dmsPath, ".git")); err == nil {
|
||||
originalDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Chdir(originalDir)
|
||||
|
||||
if err := os.Chdir(dmsPath); err != nil {
|
||||
return "", fmt.Errorf("failed to change to DMS directory: %w", err)
|
||||
}
|
||||
|
||||
tagCmd := exec.Command("git", "describe", "--exact-match", "--tags", "HEAD")
|
||||
if tagOutput, err := tagCmd.Output(); err == nil {
|
||||
return strings.TrimSpace(string(tagOutput)), nil
|
||||
@@ -65,21 +64,14 @@ func GetCurrentDMSVersion() (string, error) {
|
||||
return "unknown", nil
|
||||
}
|
||||
|
||||
func GetLatestDMSVersion() (string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get home directory: %w", err)
|
||||
}
|
||||
|
||||
dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms")
|
||||
|
||||
originalDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Chdir(originalDir)
|
||||
|
||||
func (d *DefaultVersionFetcher) GetLatestVersion(dmsPath string) (string, error) {
|
||||
if _, err := os.Stat(filepath.Join(dmsPath, ".git")); err == nil {
|
||||
originalDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer os.Chdir(originalDir)
|
||||
|
||||
if err := os.Chdir(dmsPath); err != nil {
|
||||
return "", fmt.Errorf("failed to change to DMS directory: %w", err)
|
||||
}
|
||||
@@ -154,13 +146,54 @@ func GetLatestDMSVersion() (string, error) {
|
||||
return result.TagName, nil
|
||||
}
|
||||
|
||||
// defaultFetcher is used by the public functions
|
||||
var defaultFetcher VersionFetcher = &DefaultVersionFetcher{}
|
||||
|
||||
func GetCurrentDMSVersion() (string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get home directory: %w", err)
|
||||
}
|
||||
|
||||
dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms")
|
||||
if _, err := os.Stat(dmsPath); os.IsNotExist(err) {
|
||||
return "", fmt.Errorf("DMS not installed")
|
||||
}
|
||||
|
||||
return defaultFetcher.GetCurrentVersion(dmsPath)
|
||||
}
|
||||
|
||||
func GetLatestDMSVersion() (string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get home directory: %w", err)
|
||||
}
|
||||
|
||||
dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms")
|
||||
return defaultFetcher.GetLatestVersion(dmsPath)
|
||||
}
|
||||
|
||||
func GetDMSVersionInfo() (*VersionInfo, error) {
|
||||
current, err := GetCurrentDMSVersion()
|
||||
return GetDMSVersionInfoWithFetcher(defaultFetcher)
|
||||
}
|
||||
|
||||
func GetDMSVersionInfoWithFetcher(fetcher VersionFetcher) (*VersionInfo, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get home directory: %w", err)
|
||||
}
|
||||
|
||||
dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms")
|
||||
if _, err := os.Stat(dmsPath); os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("DMS not installed")
|
||||
}
|
||||
|
||||
current, err := fetcher.GetCurrentVersion(dmsPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
latest, err := GetLatestDMSVersion()
|
||||
latest, err := fetcher.GetLatestVersion(dmsPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get latest version: %w", err)
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@ import (
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
mocks_version "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/version"
|
||||
)
|
||||
|
||||
func TestCompareVersions(t *testing.T) {
|
||||
@@ -33,36 +35,107 @@ func TestCompareVersions(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetDMSVersionInfo_Structure(t *testing.T) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
t.Skip("Cannot get home directory")
|
||||
}
|
||||
// Create a temp directory with a fake DMS installation
|
||||
tempDir := t.TempDir()
|
||||
dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms")
|
||||
os.MkdirAll(dmsPath, 0755)
|
||||
|
||||
dmsPath := filepath.Join(homeDir, ".config", "quickshell", "dms")
|
||||
if _, err := os.Stat(dmsPath); os.IsNotExist(err) {
|
||||
t.Skip("DMS not installed, skipping version info test")
|
||||
}
|
||||
// Create a .git directory to simulate git installation
|
||||
os.MkdirAll(filepath.Join(dmsPath, ".git"), 0755)
|
||||
|
||||
info, err := GetDMSVersionInfo()
|
||||
originalHome := os.Getenv("HOME")
|
||||
defer os.Setenv("HOME", originalHome)
|
||||
os.Setenv("HOME", tempDir)
|
||||
|
||||
// Create mock fetcher
|
||||
mockFetcher := mocks_version.NewMockVersionFetcher(t)
|
||||
mockFetcher.EXPECT().GetCurrentVersion(dmsPath).Return("v0.1.0", nil)
|
||||
mockFetcher.EXPECT().GetLatestVersion(dmsPath).Return("v0.1.1", nil)
|
||||
|
||||
info, err := GetDMSVersionInfoWithFetcher(mockFetcher)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDMSVersionInfo() failed: %v", err)
|
||||
t.Fatalf("GetDMSVersionInfoWithFetcher() failed: %v", err)
|
||||
}
|
||||
|
||||
if info == nil {
|
||||
t.Fatal("GetDMSVersionInfo() returned nil")
|
||||
t.Fatal("GetDMSVersionInfoWithFetcher() returned nil")
|
||||
}
|
||||
|
||||
if info.Current == "" {
|
||||
t.Error("Current version is empty")
|
||||
if info.Current != "v0.1.0" {
|
||||
t.Errorf("Current version = %s, expected v0.1.0", info.Current)
|
||||
}
|
||||
|
||||
if info.Latest == "" {
|
||||
t.Error("Latest version is empty")
|
||||
if info.Latest != "v0.1.1" {
|
||||
t.Errorf("Latest version = %s, expected v0.1.1", info.Latest)
|
||||
}
|
||||
|
||||
if !info.HasUpdate {
|
||||
t.Error("HasUpdate should be true when current != latest")
|
||||
}
|
||||
|
||||
if !info.IsTag {
|
||||
t.Error("IsTag should be true for v0.1.0")
|
||||
}
|
||||
|
||||
t.Logf("Current: %s, Latest: %s, HasUpdate: %v", info.Current, info.Latest, info.HasUpdate)
|
||||
}
|
||||
|
||||
func TestGetDMSVersionInfo_BranchVersion(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms")
|
||||
os.MkdirAll(dmsPath, 0755)
|
||||
os.MkdirAll(filepath.Join(dmsPath, ".git"), 0755)
|
||||
|
||||
originalHome := os.Getenv("HOME")
|
||||
defer os.Setenv("HOME", originalHome)
|
||||
os.Setenv("HOME", tempDir)
|
||||
|
||||
mockFetcher := mocks_version.NewMockVersionFetcher(t)
|
||||
mockFetcher.EXPECT().GetCurrentVersion(dmsPath).Return("master@abc1234", nil)
|
||||
mockFetcher.EXPECT().GetLatestVersion(dmsPath).Return("master@def5678", nil)
|
||||
|
||||
info, err := GetDMSVersionInfoWithFetcher(mockFetcher)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDMSVersionInfoWithFetcher() failed: %v", err)
|
||||
}
|
||||
|
||||
if !info.IsBranch {
|
||||
t.Error("IsBranch should be true for branch@commit format")
|
||||
}
|
||||
|
||||
if !info.IsGit {
|
||||
t.Error("IsGit should be true for branch@commit format")
|
||||
}
|
||||
|
||||
if !info.HasUpdate {
|
||||
t.Error("HasUpdate should be true when commits differ")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetDMSVersionInfo_NoUpdate(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
dmsPath := filepath.Join(tempDir, ".config", "quickshell", "dms")
|
||||
os.MkdirAll(dmsPath, 0755)
|
||||
os.MkdirAll(filepath.Join(dmsPath, ".git"), 0755)
|
||||
|
||||
originalHome := os.Getenv("HOME")
|
||||
defer os.Setenv("HOME", originalHome)
|
||||
os.Setenv("HOME", tempDir)
|
||||
|
||||
mockFetcher := mocks_version.NewMockVersionFetcher(t)
|
||||
mockFetcher.EXPECT().GetCurrentVersion(dmsPath).Return("v0.1.0", nil)
|
||||
mockFetcher.EXPECT().GetLatestVersion(dmsPath).Return("v0.1.0", nil)
|
||||
|
||||
info, err := GetDMSVersionInfoWithFetcher(mockFetcher)
|
||||
if err != nil {
|
||||
t.Fatalf("GetDMSVersionInfoWithFetcher() failed: %v", err)
|
||||
}
|
||||
|
||||
if info.HasUpdate {
|
||||
t.Error("HasUpdate should be false when current == latest")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetCurrentDMSVersion_NotInstalled(t *testing.T) {
|
||||
originalHome := os.Getenv("HOME")
|
||||
defer os.Setenv("HOME", originalHome)
|
||||
|
||||
@@ -303,6 +303,7 @@ Singleton {
|
||||
property bool osdPowerProfileEnabled: true
|
||||
|
||||
property bool powerActionConfirm: true
|
||||
property int powerActionHoldDuration: 1
|
||||
property var powerMenuActions: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"]
|
||||
property string powerMenuDefaultAction: "logout"
|
||||
property bool powerMenuGridLayout: false
|
||||
|
||||
@@ -88,7 +88,9 @@ Singleton {
|
||||
property bool gtkThemingEnabled: typeof SettingsData !== "undefined" ? SettingsData.gtkAvailable : false
|
||||
property bool qtThemingEnabled: typeof SettingsData !== "undefined" ? (SettingsData.qt5ctAvailable || SettingsData.qt6ctAvailable) : false
|
||||
property var workerRunning: false
|
||||
property var pendingThemeRequest: null
|
||||
property var matugenColors: ({})
|
||||
property var _pendingGenerateParams: null
|
||||
property var customThemeData: null
|
||||
|
||||
Component.onCompleted: {
|
||||
@@ -784,13 +786,26 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
function setDesiredTheme(kind, value, isLight, iconTheme, matugenType) {
|
||||
function setDesiredTheme(kind, value, isLight, iconTheme, matugenType, stockColors) {
|
||||
if (!matugenAvailable) {
|
||||
console.warn("Theme: matugen not available or disabled - cannot set system theme");
|
||||
return;
|
||||
}
|
||||
|
||||
console.info("Theme: Setting desired theme -", kind, "mode:", isLight ? "light" : "dark", "type:", matugenType);
|
||||
if (workerRunning) {
|
||||
console.info("Theme: Worker already running, queueing request");
|
||||
pendingThemeRequest = {
|
||||
kind,
|
||||
value,
|
||||
isLight,
|
||||
iconTheme,
|
||||
matugenType,
|
||||
stockColors
|
||||
};
|
||||
return;
|
||||
}
|
||||
|
||||
console.info("Theme: Setting desired theme -", kind, "mode:", isLight ? "light" : "dark", stockColors ? "(stock colors)" : "(dynamic)");
|
||||
|
||||
if (typeof NiriService !== "undefined" && CompositorService.isNiri) {
|
||||
NiriService.suppressNextToast();
|
||||
@@ -805,15 +820,18 @@ Singleton {
|
||||
"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";
|
||||
|
||||
Quickshell.execDetached(["sh", "-c", `mkdir -p '${stateDir}' && cat > '${desiredPath}' << 'EOF'\n${json}\nEOF`]);
|
||||
workerRunning = true;
|
||||
const syncModeWithPortal = (typeof SettingsData !== "undefined" && SettingsData.syncModeWithPortal) ? "true" : "false";
|
||||
const terminalsAlwaysDark = (typeof SettingsData !== "undefined" && SettingsData.terminalsAlwaysDark) ? "true" : "false";
|
||||
|
||||
console.log("Theme: Starting matugen worker");
|
||||
systemThemeGenerator.command = [shellDir + "/scripts/matugen-worker.sh", stateDir, shellDir, configDir, syncModeWithPortal, terminalsAlwaysDark, "--run"];
|
||||
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`];
|
||||
systemThemeGenerator.running = true;
|
||||
}
|
||||
|
||||
@@ -821,40 +839,122 @@ Singleton {
|
||||
const isGreeterMode = (typeof SessionData !== "undefined" && SessionData.isGreeterMode);
|
||||
if (!matugenAvailable || isGreeterMode)
|
||||
return;
|
||||
|
||||
_pendingGenerateParams = true;
|
||||
_themeGenerateDebounce.restart();
|
||||
}
|
||||
|
||||
function _executeThemeGeneration() {
|
||||
if (!_pendingGenerateParams)
|
||||
return;
|
||||
_pendingGenerateParams = null;
|
||||
|
||||
const isLight = (typeof SessionData !== "undefined" && SessionData.isLightMode);
|
||||
const iconTheme = (typeof SettingsData !== "undefined" && SettingsData.iconTheme) ? SettingsData.iconTheme : "System Default";
|
||||
|
||||
if (currentTheme === dynamic) {
|
||||
if (!rawWallpaperPath) {
|
||||
if (!rawWallpaperPath)
|
||||
return;
|
||||
}
|
||||
const selectedMatugenType = (typeof SettingsData !== "undefined" && SettingsData.matugenScheme) ? SettingsData.matugenScheme : "scheme-tonal-spot";
|
||||
if (rawWallpaperPath.startsWith("#")) {
|
||||
setDesiredTheme("hex", rawWallpaperPath, isLight, iconTheme, selectedMatugenType);
|
||||
} else {
|
||||
setDesiredTheme("image", rawWallpaperPath, isLight, iconTheme, selectedMatugenType);
|
||||
}
|
||||
} else {
|
||||
let primaryColor;
|
||||
let matugenType;
|
||||
if (currentTheme === "custom") {
|
||||
if (!customThemeData || !customThemeData.primary) {
|
||||
console.warn("Custom theme data not available for system theme generation");
|
||||
return;
|
||||
}
|
||||
primaryColor = customThemeData.primary;
|
||||
matugenType = customThemeData.matugen_type;
|
||||
} else {
|
||||
primaryColor = currentThemeData.primary;
|
||||
matugenType = currentThemeData.matugen_type;
|
||||
}
|
||||
|
||||
if (!primaryColor) {
|
||||
console.warn("No primary color available for theme:", currentTheme);
|
||||
return;
|
||||
}
|
||||
setDesiredTheme("hex", primaryColor, isLight, iconTheme, matugenType);
|
||||
const kind = rawWallpaperPath.startsWith("#") ? "hex" : "image";
|
||||
setDesiredTheme(kind, rawWallpaperPath, isLight, iconTheme, selectedMatugenType, null);
|
||||
return;
|
||||
}
|
||||
|
||||
let darkTheme, lightTheme;
|
||||
if (currentTheme === "custom") {
|
||||
darkTheme = customThemeData;
|
||||
lightTheme = customThemeData;
|
||||
} else {
|
||||
darkTheme = StockThemes.getThemeByName(currentTheme, false);
|
||||
lightTheme = StockThemes.getThemeByName(currentTheme, true);
|
||||
}
|
||||
|
||||
if (!darkTheme || !darkTheme.primary) {
|
||||
console.warn("Theme data not available for:", currentTheme);
|
||||
return;
|
||||
}
|
||||
|
||||
const stockColors = buildMatugenColorsFromTheme(darkTheme, lightTheme);
|
||||
const themeData = isLight ? lightTheme : darkTheme;
|
||||
setDesiredTheme("hex", themeData.primary, isLight, iconTheme, themeData.matugen_type, stockColors);
|
||||
}
|
||||
|
||||
function buildMatugenColorsFromTheme(darkTheme, lightTheme) {
|
||||
const colors = {};
|
||||
|
||||
function addColor(matugenKey, darkVal, lightVal) {
|
||||
if (!darkVal && !lightVal)
|
||||
return;
|
||||
colors[matugenKey] = {
|
||||
"dark": {
|
||||
"color": String(darkVal || lightVal)
|
||||
},
|
||||
"light": {
|
||||
"color": String(lightVal || darkVal)
|
||||
},
|
||||
"default": {
|
||||
"color": String(darkVal || lightVal)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function get(theme, key, fallback) {
|
||||
return theme[key] || fallback;
|
||||
}
|
||||
|
||||
addColor("primary", darkTheme.primary, lightTheme.primary);
|
||||
addColor("on_primary", darkTheme.primaryText, lightTheme.primaryText);
|
||||
addColor("primary_container", darkTheme.primaryContainer, lightTheme.primaryContainer);
|
||||
addColor("on_primary_container", darkTheme.primaryContainerText || darkTheme.surfaceText, lightTheme.primaryContainerText || lightTheme.surfaceText);
|
||||
addColor("secondary", darkTheme.secondary, lightTheme.secondary);
|
||||
addColor("on_secondary", darkTheme.secondaryText || darkTheme.primaryText, lightTheme.secondaryText || lightTheme.primaryText);
|
||||
addColor("secondary_container", darkTheme.secondaryContainer || darkTheme.surfaceContainerHigh, lightTheme.secondaryContainer || lightTheme.surfaceContainerHigh);
|
||||
addColor("on_secondary_container", darkTheme.secondaryContainerText || darkTheme.surfaceText, lightTheme.secondaryContainerText || lightTheme.surfaceText);
|
||||
addColor("tertiary", darkTheme.tertiary || darkTheme.secondary, lightTheme.tertiary || lightTheme.secondary);
|
||||
addColor("on_tertiary", darkTheme.tertiaryText || darkTheme.secondaryText || darkTheme.primaryText, lightTheme.tertiaryText || lightTheme.secondaryText || lightTheme.primaryText);
|
||||
addColor("tertiary_container", darkTheme.tertiaryContainer || darkTheme.secondaryContainer || darkTheme.surfaceContainerHigh, lightTheme.tertiaryContainer || lightTheme.secondaryContainer || lightTheme.surfaceContainerHigh);
|
||||
addColor("on_tertiary_container", darkTheme.tertiaryContainerText || darkTheme.surfaceText, lightTheme.tertiaryContainerText || lightTheme.surfaceText);
|
||||
addColor("error", darkTheme.error || "#F2B8B5", lightTheme.error || "#B3261E");
|
||||
addColor("on_error", darkTheme.errorText || "#601410", lightTheme.errorText || "#FFFFFF");
|
||||
addColor("error_container", darkTheme.errorContainer || "#8C1D18", lightTheme.errorContainer || "#F9DEDC");
|
||||
addColor("on_error_container", darkTheme.errorContainerText || "#F9DEDC", lightTheme.errorContainerText || "#410E0B");
|
||||
addColor("surface", darkTheme.surface, lightTheme.surface);
|
||||
addColor("on_surface", darkTheme.surfaceText, lightTheme.surfaceText);
|
||||
addColor("surface_variant", darkTheme.surfaceVariant, lightTheme.surfaceVariant);
|
||||
addColor("on_surface_variant", darkTheme.surfaceVariantText, lightTheme.surfaceVariantText);
|
||||
addColor("surface_tint", darkTheme.surfaceTint, lightTheme.surfaceTint);
|
||||
addColor("background", darkTheme.background, lightTheme.background);
|
||||
addColor("on_background", darkTheme.backgroundText, lightTheme.backgroundText);
|
||||
addColor("outline", darkTheme.outline, lightTheme.outline);
|
||||
addColor("outline_variant", darkTheme.outlineVariant || darkTheme.surfaceVariant, lightTheme.outlineVariant || lightTheme.surfaceVariant);
|
||||
addColor("surface_container", darkTheme.surfaceContainer, lightTheme.surfaceContainer);
|
||||
addColor("surface_container_high", darkTheme.surfaceContainerHigh, lightTheme.surfaceContainerHigh);
|
||||
addColor("surface_container_highest", darkTheme.surfaceContainerHighest || darkTheme.surfaceContainerHigh, lightTheme.surfaceContainerHighest || lightTheme.surfaceContainerHigh);
|
||||
addColor("surface_container_low", darkTheme.surfaceContainerLow || darkTheme.surface, lightTheme.surfaceContainerLow || lightTheme.surface);
|
||||
addColor("surface_container_lowest", darkTheme.surfaceContainerLowest || darkTheme.background, lightTheme.surfaceContainerLowest || lightTheme.background);
|
||||
addColor("surface_bright", darkTheme.surfaceBright || darkTheme.surfaceContainerHighest || darkTheme.surfaceContainerHigh, lightTheme.surfaceBright || lightTheme.surface);
|
||||
addColor("surface_dim", darkTheme.surfaceDim || darkTheme.background, lightTheme.surfaceDim || lightTheme.surfaceContainer);
|
||||
addColor("inverse_surface", darkTheme.inverseSurface || lightTheme.surface, lightTheme.inverseSurface || darkTheme.surface);
|
||||
addColor("inverse_on_surface", darkTheme.inverseOnSurface || lightTheme.surfaceText, lightTheme.inverseOnSurface || darkTheme.surfaceText);
|
||||
addColor("inverse_primary", darkTheme.inversePrimary || lightTheme.primary, lightTheme.inversePrimary || darkTheme.primary);
|
||||
addColor("scrim", darkTheme.scrim || "#000000", lightTheme.scrim || "#000000");
|
||||
addColor("shadow", darkTheme.shadow || "#000000", lightTheme.shadow || "#000000");
|
||||
addColor("source_color", darkTheme.primary, lightTheme.primary);
|
||||
addColor("primary_fixed", darkTheme.primaryFixed || darkTheme.primaryContainer, lightTheme.primaryFixed || lightTheme.primaryContainer);
|
||||
addColor("primary_fixed_dim", darkTheme.primaryFixedDim || darkTheme.primary, lightTheme.primaryFixedDim || lightTheme.primary);
|
||||
addColor("on_primary_fixed", darkTheme.onPrimaryFixed || darkTheme.primaryText, lightTheme.onPrimaryFixed || lightTheme.primaryText);
|
||||
addColor("on_primary_fixed_variant", darkTheme.onPrimaryFixedVariant || darkTheme.primaryText, lightTheme.onPrimaryFixedVariant || lightTheme.primaryText);
|
||||
addColor("secondary_fixed", darkTheme.secondaryFixed || darkTheme.secondary, lightTheme.secondaryFixed || lightTheme.secondary);
|
||||
addColor("secondary_fixed_dim", darkTheme.secondaryFixedDim || darkTheme.secondary, lightTheme.secondaryFixedDim || lightTheme.secondary);
|
||||
addColor("on_secondary_fixed", darkTheme.onSecondaryFixed || darkTheme.primaryText, lightTheme.onSecondaryFixed || lightTheme.primaryText);
|
||||
addColor("on_secondary_fixed_variant", darkTheme.onSecondaryFixedVariant || darkTheme.primaryText, lightTheme.onSecondaryFixedVariant || lightTheme.primaryText);
|
||||
addColor("tertiary_fixed", darkTheme.tertiaryFixed || darkTheme.tertiary || darkTheme.secondary, lightTheme.tertiaryFixed || lightTheme.tertiary || lightTheme.secondary);
|
||||
addColor("tertiary_fixed_dim", darkTheme.tertiaryFixedDim || darkTheme.tertiary || darkTheme.secondary, lightTheme.tertiaryFixedDim || lightTheme.tertiary || lightTheme.secondary);
|
||||
addColor("on_tertiary_fixed", darkTheme.onTertiaryFixed || darkTheme.primaryText, lightTheme.onTertiaryFixed || lightTheme.primaryText);
|
||||
addColor("on_tertiary_fixed_variant", darkTheme.onTertiaryFixedVariant || darkTheme.primaryText, lightTheme.onTertiaryFixedVariant || lightTheme.primaryText);
|
||||
|
||||
return colors;
|
||||
}
|
||||
|
||||
function applyGtkColors() {
|
||||
@@ -1005,10 +1105,6 @@ Singleton {
|
||||
|
||||
if (exitCode === 0) {
|
||||
console.info("Theme: Matugen worker completed successfully");
|
||||
if (currentTheme === dynamic) {
|
||||
console.log("Theme: Reloading dynamic colors file");
|
||||
dynamicColorsFileView.reload();
|
||||
}
|
||||
} else if (exitCode === 2) {
|
||||
console.log("Theme: Matugen worker completed with code 2 (no changes needed)");
|
||||
} else {
|
||||
@@ -1017,6 +1113,13 @@ Singleton {
|
||||
}
|
||||
console.warn("Theme: Matugen worker failed with exit code:", exitCode);
|
||||
}
|
||||
|
||||
if (pendingThemeRequest) {
|
||||
const req = pendingThemeRequest;
|
||||
pendingThemeRequest = null;
|
||||
console.info("Theme: Processing queued theme request");
|
||||
setDesiredTheme(req.kind, req.value, req.isLight, req.iconTheme, req.matugenType, req.stockColors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1129,6 +1232,13 @@ Singleton {
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: _themeGenerateDebounce
|
||||
interval: 100
|
||||
repeat: false
|
||||
onTriggered: root._executeThemeGeneration()
|
||||
}
|
||||
|
||||
// These timers are for screen transitions, since sometimes QML still beats the niri call
|
||||
Timer {
|
||||
id: themeTransitionTimer
|
||||
|
||||
@@ -205,6 +205,7 @@ var SPEC = {
|
||||
osdPowerProfileEnabled: { def: false },
|
||||
|
||||
powerActionConfirm: { def: true },
|
||||
powerActionHoldDuration: { def: 1 },
|
||||
powerMenuActions: { def: ["reboot", "logout", "poweroff", "lock", "suspend", "restart"] },
|
||||
powerMenuDefaultAction: { def: "logout" },
|
||||
powerMenuGridLayout: { def: false },
|
||||
|
||||
@@ -343,16 +343,6 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: powerConfirmModalLoader
|
||||
|
||||
active: false
|
||||
|
||||
ConfirmModal {
|
||||
id: powerConfirmModal
|
||||
}
|
||||
}
|
||||
|
||||
LazyLoader {
|
||||
id: processListPopoutLoader
|
||||
|
||||
@@ -489,22 +479,6 @@ Item {
|
||||
id: powerMenuModal
|
||||
|
||||
onPowerActionRequested: (action, title, message) => {
|
||||
if (SettingsData.powerActionConfirm) {
|
||||
powerConfirmModalLoader.active = true;
|
||||
if (powerConfirmModalLoader.item) {
|
||||
powerConfirmModalLoader.item.confirmButtonColor = action === "poweroff" ? Theme.error : action === "reboot" ? Theme.warning : Theme.primary;
|
||||
powerConfirmModalLoader.item.show(title, message, () => actionApply(action), function () {});
|
||||
}
|
||||
} else {
|
||||
actionApply(action);
|
||||
}
|
||||
}
|
||||
|
||||
onLockRequested: {
|
||||
lock.activate();
|
||||
}
|
||||
|
||||
function actionApply(action) {
|
||||
switch (action) {
|
||||
case "logout":
|
||||
SessionService.logout();
|
||||
@@ -524,6 +498,10 @@ Item {
|
||||
}
|
||||
}
|
||||
|
||||
onLockRequested: {
|
||||
lock.activate();
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
PopoutService.powerMenuModal = powerMenuModal;
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ DankModal {
|
||||
layerNamespace: "dms:bluetooth-pairing"
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [root]
|
||||
windows: [root.contentWindow]
|
||||
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ DankModal {
|
||||
layerNamespace: "dms:clipboard"
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [clipboardHistoryModal]
|
||||
windows: [clipboardHistoryModal.contentWindow]
|
||||
active: CompositorService.isHyprland && clipboardHistoryModal.shouldHaveFocus
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ DankModal {
|
||||
id: root
|
||||
|
||||
layerNamespace: "dms:confirm-modal"
|
||||
keepPopoutsOpen: true
|
||||
|
||||
property string confirmTitle: ""
|
||||
property string confirmMessage: ""
|
||||
|
||||
@@ -44,6 +44,8 @@ Item {
|
||||
property bool keepContentLoaded: false
|
||||
property bool keepPopoutsOpen: false
|
||||
property var customKeyboardFocus: null
|
||||
readonly property alias contentWindow: contentWindow
|
||||
readonly property alias backgroundWindow: backgroundWindow
|
||||
|
||||
signal opened
|
||||
signal dialogClosed
|
||||
@@ -55,12 +57,15 @@ Item {
|
||||
ModalManager.openModal(root);
|
||||
closeTimer.stop();
|
||||
shouldBeVisible = true;
|
||||
contentWindow.visible = false;
|
||||
if (useBackgroundWindow)
|
||||
backgroundWindow.visible = true;
|
||||
contentWindow.visible = true;
|
||||
shouldHaveFocus = false;
|
||||
Qt.callLater(() => {
|
||||
shouldHaveFocus = Qt.binding(() => shouldBeVisible);
|
||||
contentWindow.visible = true;
|
||||
shouldHaveFocus = false;
|
||||
Qt.callLater(() => {
|
||||
shouldHaveFocus = Qt.binding(() => shouldBeVisible);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ DankModal {
|
||||
layerNamespace: "dms:color-picker"
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [root]
|
||||
windows: [root.contentWindow]
|
||||
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ DankModal {
|
||||
layerNamespace: "dms:notification-center-modal"
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [notificationModal]
|
||||
windows: [notificationModal.contentWindow]
|
||||
active: CompositorService.isHyprland && notificationModal.shouldHaveFocus
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ DankModal {
|
||||
layerNamespace: "dms:polkit"
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [root]
|
||||
windows: [root.contentWindow]
|
||||
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import QtQuick
|
||||
import QtQuick.Effects
|
||||
import Quickshell
|
||||
import Quickshell.Hyprland
|
||||
import qs.Common
|
||||
@@ -10,9 +11,10 @@ DankModal {
|
||||
id: root
|
||||
|
||||
layerNamespace: "dms:power-menu"
|
||||
keepPopoutsOpen: true
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [root]
|
||||
windows: [root.contentWindow]
|
||||
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||
}
|
||||
|
||||
@@ -25,9 +27,94 @@ DankModal {
|
||||
property int gridColumns: 3
|
||||
property int gridRows: 2
|
||||
|
||||
property string holdAction: ""
|
||||
property int holdActionIndex: -1
|
||||
property real holdProgress: 0
|
||||
property bool showHoldHint: false
|
||||
|
||||
readonly property bool needsConfirmation: SettingsData.powerActionConfirm
|
||||
readonly property int holdDurationMs: SettingsData.powerActionHoldDuration * 1000
|
||||
|
||||
signal powerActionRequested(string action, string title, string message)
|
||||
signal lockRequested
|
||||
|
||||
function actionNeedsConfirm(action) {
|
||||
return action !== "lock" && action !== "restart";
|
||||
}
|
||||
|
||||
function startHold(action, actionIndex) {
|
||||
if (!needsConfirmation || !actionNeedsConfirm(action)) {
|
||||
executeAction(action);
|
||||
return;
|
||||
}
|
||||
holdAction = action;
|
||||
holdActionIndex = actionIndex;
|
||||
holdProgress = 0;
|
||||
showHoldHint = false;
|
||||
holdTimer.start();
|
||||
}
|
||||
|
||||
function cancelHold() {
|
||||
if (holdAction === "")
|
||||
return;
|
||||
const wasHolding = holdProgress > 0;
|
||||
holdTimer.stop();
|
||||
if (wasHolding && holdProgress < 1) {
|
||||
showHoldHint = true;
|
||||
hintTimer.restart();
|
||||
}
|
||||
holdAction = "";
|
||||
holdActionIndex = -1;
|
||||
holdProgress = 0;
|
||||
}
|
||||
|
||||
function completeHold() {
|
||||
if (holdProgress < 1) {
|
||||
cancelHold();
|
||||
return;
|
||||
}
|
||||
const action = holdAction;
|
||||
holdTimer.stop();
|
||||
holdAction = "";
|
||||
holdActionIndex = -1;
|
||||
holdProgress = 0;
|
||||
executeAction(action);
|
||||
}
|
||||
|
||||
function executeAction(action) {
|
||||
if (action === "lock") {
|
||||
close();
|
||||
lockRequested();
|
||||
return;
|
||||
}
|
||||
if (action === "restart") {
|
||||
close();
|
||||
Quickshell.execDetached(["dms", "restart"]);
|
||||
return;
|
||||
}
|
||||
close();
|
||||
root.powerActionRequested(action, "", "");
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: holdTimer
|
||||
interval: 16
|
||||
repeat: true
|
||||
onTriggered: {
|
||||
root.holdProgress = Math.min(1, root.holdProgress + (interval / root.holdDurationMs));
|
||||
if (root.holdProgress >= 1) {
|
||||
stop();
|
||||
root.completeHold();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: hintTimer
|
||||
interval: 2000
|
||||
onTriggered: root.showHoldHint = false
|
||||
}
|
||||
|
||||
function openCentered() {
|
||||
parentBounds = Qt.rect(0, 0, 0, 0);
|
||||
parentScreen = null;
|
||||
@@ -39,9 +126,7 @@ DankModal {
|
||||
parentBounds = bounds;
|
||||
parentScreen = targetScreen;
|
||||
backgroundOpacity = 0;
|
||||
keepPopoutsOpen = true;
|
||||
open();
|
||||
keepPopoutsOpen = false;
|
||||
}
|
||||
|
||||
function updateVisibleActions() {
|
||||
@@ -142,44 +227,8 @@ DankModal {
|
||||
}
|
||||
}
|
||||
|
||||
function selectOption(action) {
|
||||
if (action === "lock") {
|
||||
close();
|
||||
lockRequested();
|
||||
return;
|
||||
}
|
||||
if (action === "restart") {
|
||||
close();
|
||||
Quickshell.execDetached(["dms", "restart"]);
|
||||
return;
|
||||
}
|
||||
close();
|
||||
const actions = {
|
||||
"logout": {
|
||||
"title": I18n.tr("Log Out"),
|
||||
"message": I18n.tr("Are you sure you want to log out?")
|
||||
},
|
||||
"suspend": {
|
||||
"title": I18n.tr("Suspend"),
|
||||
"message": I18n.tr("Are you sure you want to suspend the system?")
|
||||
},
|
||||
"hibernate": {
|
||||
"title": I18n.tr("Hibernate"),
|
||||
"message": I18n.tr("Are you sure you want to hibernate the system?")
|
||||
},
|
||||
"reboot": {
|
||||
"title": I18n.tr("Reboot"),
|
||||
"message": I18n.tr("Are you sure you want to reboot the system?")
|
||||
},
|
||||
"poweroff": {
|
||||
"title": I18n.tr("Power Off"),
|
||||
"message": I18n.tr("Are you sure you want to power off the system?")
|
||||
}
|
||||
};
|
||||
const selected = actions[action];
|
||||
if (selected) {
|
||||
root.powerActionRequested(action, selected.title, selected.message);
|
||||
}
|
||||
function selectOption(action, actionIndex) {
|
||||
startHold(action, actionIndex !== undefined ? actionIndex : -1);
|
||||
}
|
||||
|
||||
shouldBeVisible: false
|
||||
@@ -208,8 +257,15 @@ DankModal {
|
||||
}
|
||||
return Qt.point(0, 0);
|
||||
}
|
||||
onBackgroundClicked: () => close()
|
||||
onBackgroundClicked: () => {
|
||||
cancelHold();
|
||||
close();
|
||||
}
|
||||
onOpened: () => {
|
||||
holdAction = "";
|
||||
holdActionIndex = -1;
|
||||
holdProgress = 0;
|
||||
showHoldHint = false;
|
||||
updateVisibleActions();
|
||||
const defaultIndex = getDefaultActionIndex();
|
||||
if (SettingsData.powerMenuGridLayout) {
|
||||
@@ -221,16 +277,42 @@ DankModal {
|
||||
}
|
||||
Qt.callLater(() => modalFocusScope.forceActiveFocus());
|
||||
}
|
||||
onDialogClosed: () => {
|
||||
cancelHold();
|
||||
}
|
||||
Component.onCompleted: updateVisibleActions()
|
||||
modalFocusScope.Keys.onPressed: event => {
|
||||
if (event.isAutoRepeat) {
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
if (SettingsData.powerMenuGridLayout) {
|
||||
handleGridNavigation(event);
|
||||
handleGridNavigation(event, true);
|
||||
} else {
|
||||
handleListNavigation(event);
|
||||
handleListNavigation(event, true);
|
||||
}
|
||||
}
|
||||
modalFocusScope.Keys.onReleased: event => {
|
||||
if (event.isAutoRepeat) {
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
if (SettingsData.powerMenuGridLayout) {
|
||||
handleGridNavigation(event, false);
|
||||
} else {
|
||||
handleListNavigation(event, false);
|
||||
}
|
||||
}
|
||||
|
||||
function handleListNavigation(event) {
|
||||
function handleListNavigation(event, isPressed) {
|
||||
if (!isPressed) {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_R || event.key === Qt.Key_X || event.key === Qt.Key_L || event.key === Qt.Key_S || event.key === Qt.Key_H || event.key === Qt.Key_D || (event.key === Qt.Key_P && !(event.modifiers & Qt.ControlModifier))) {
|
||||
cancelHold();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case Qt.Key_Up:
|
||||
case Qt.Key_Backtab:
|
||||
@@ -244,7 +326,7 @@ DankModal {
|
||||
break;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
selectOption(getActionAtIndex(selectedIndex));
|
||||
startHold(getActionAtIndex(selectedIndex), selectedIndex);
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_N:
|
||||
@@ -255,7 +337,8 @@ DankModal {
|
||||
break;
|
||||
case Qt.Key_P:
|
||||
if (!(event.modifiers & Qt.ControlModifier)) {
|
||||
selectOption("poweroff");
|
||||
const idx = visibleActions.indexOf("poweroff");
|
||||
startHold("poweroff", idx);
|
||||
event.accepted = true;
|
||||
} else {
|
||||
selectedIndex = (selectedIndex - 1 + visibleActions.length) % visibleActions.length;
|
||||
@@ -275,33 +358,41 @@ DankModal {
|
||||
}
|
||||
break;
|
||||
case Qt.Key_R:
|
||||
selectOption("reboot");
|
||||
startHold("reboot", visibleActions.indexOf("reboot"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_X:
|
||||
selectOption("logout");
|
||||
startHold("logout", visibleActions.indexOf("logout"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_L:
|
||||
selectOption("lock");
|
||||
startHold("lock", visibleActions.indexOf("lock"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_S:
|
||||
selectOption("suspend");
|
||||
startHold("suspend", visibleActions.indexOf("suspend"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_H:
|
||||
selectOption("hibernate");
|
||||
startHold("hibernate", visibleActions.indexOf("hibernate"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_D:
|
||||
selectOption("restart");
|
||||
startHold("restart", visibleActions.indexOf("restart"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
function handleGridNavigation(event) {
|
||||
function handleGridNavigation(event, isPressed) {
|
||||
if (!isPressed) {
|
||||
if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter || event.key === Qt.Key_R || event.key === Qt.Key_X || event.key === Qt.Key_L || event.key === Qt.Key_S || event.key === Qt.Key_H || event.key === Qt.Key_D || (event.key === Qt.Key_P && !(event.modifiers & Qt.ControlModifier))) {
|
||||
cancelHold();
|
||||
event.accepted = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.key) {
|
||||
case Qt.Key_Left:
|
||||
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns;
|
||||
@@ -327,7 +418,7 @@ DankModal {
|
||||
break;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
selectOption(getActionAtIndex(selectedIndex));
|
||||
startHold(getActionAtIndex(selectedIndex), selectedIndex);
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_N:
|
||||
@@ -339,7 +430,8 @@ DankModal {
|
||||
break;
|
||||
case Qt.Key_P:
|
||||
if (!(event.modifiers & Qt.ControlModifier)) {
|
||||
selectOption("poweroff");
|
||||
const idx = visibleActions.indexOf("poweroff");
|
||||
startHold("poweroff", idx);
|
||||
event.accepted = true;
|
||||
} else {
|
||||
selectedCol = (selectedCol - 1 + gridColumns) % gridColumns;
|
||||
@@ -362,27 +454,27 @@ DankModal {
|
||||
}
|
||||
break;
|
||||
case Qt.Key_R:
|
||||
selectOption("reboot");
|
||||
startHold("reboot", visibleActions.indexOf("reboot"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_X:
|
||||
selectOption("logout");
|
||||
startHold("logout", visibleActions.indexOf("logout"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_L:
|
||||
selectOption("lock");
|
||||
startHold("lock", visibleActions.indexOf("lock"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_S:
|
||||
selectOption("suspend");
|
||||
startHold("suspend", visibleActions.indexOf("suspend"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_H:
|
||||
selectOption("hibernate");
|
||||
startHold("hibernate", visibleActions.indexOf("hibernate"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
case Qt.Key_D:
|
||||
selectOption("restart");
|
||||
startHold("restart", visibleActions.indexOf("restart"));
|
||||
event.accepted = true;
|
||||
break;
|
||||
}
|
||||
@@ -391,12 +483,14 @@ DankModal {
|
||||
content: Component {
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
implicitHeight: SettingsData.powerMenuGridLayout ? buttonGrid.implicitHeight + Theme.spacingL * 2 : buttonColumn.implicitHeight + Theme.spacingL * 2
|
||||
implicitHeight: (SettingsData.powerMenuGridLayout ? buttonGrid.implicitHeight : buttonColumn.implicitHeight) + Theme.spacingL * 2 + (root.needsConfirmation ? hintRow.height + Theme.spacingM : 0)
|
||||
|
||||
Grid {
|
||||
id: buttonGrid
|
||||
visible: SettingsData.powerMenuGridLayout
|
||||
anchors.centerIn: parent
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Theme.spacingL
|
||||
columns: root.gridColumns
|
||||
columnSpacing: Theme.spacingS
|
||||
rowSpacing: Theme.spacingS
|
||||
@@ -405,12 +499,14 @@ DankModal {
|
||||
model: root.visibleActions
|
||||
|
||||
Rectangle {
|
||||
id: gridButtonRect
|
||||
required property int index
|
||||
required property string modelData
|
||||
|
||||
readonly property var actionData: root.getActionData(modelData)
|
||||
readonly property bool isSelected: root.selectedIndex === index
|
||||
readonly property bool showWarning: modelData === "reboot" || modelData === "poweroff"
|
||||
readonly property bool isHolding: root.holdActionIndex === index && root.holdProgress > 0
|
||||
|
||||
width: (root.modalWidth - Theme.spacingL * 2 - Theme.spacingS * (root.gridColumns - 1)) / root.gridColumns
|
||||
height: 100
|
||||
@@ -425,16 +521,50 @@ DankModal {
|
||||
border.color: isSelected ? Theme.primary : "transparent"
|
||||
border.width: isSelected ? 2 : 0
|
||||
|
||||
Rectangle {
|
||||
id: gridProgressMask
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
visible: false
|
||||
layer.enabled: true
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
visible: gridButtonRect.isHolding
|
||||
layer.enabled: gridButtonRect.isHolding
|
||||
layer.effect: MultiEffect {
|
||||
maskEnabled: true
|
||||
maskSource: gridProgressMask
|
||||
maskSpreadAtMin: 1
|
||||
maskThresholdMin: 0.5
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width * root.holdProgress
|
||||
color: {
|
||||
if (gridButtonRect.modelData === "poweroff")
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3);
|
||||
if (gridButtonRect.modelData === "reboot")
|
||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.3);
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.centerIn: parent
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: parent.parent.actionData.icon
|
||||
name: gridButtonRect.actionData.icon
|
||||
size: Theme.iconSize + 8
|
||||
color: {
|
||||
if (parent.parent.showWarning && mouseArea.containsMouse) {
|
||||
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||
if (gridButtonRect.showWarning && (mouseArea.containsMouse || gridButtonRect.isHolding)) {
|
||||
return gridButtonRect.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||
}
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
@@ -442,11 +572,11 @@ DankModal {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: parent.parent.actionData.label
|
||||
text: gridButtonRect.actionData.label
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: {
|
||||
if (parent.parent.showWarning && mouseArea.containsMouse) {
|
||||
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||
if (gridButtonRect.showWarning && (mouseArea.containsMouse || gridButtonRect.isHolding)) {
|
||||
return gridButtonRect.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||
}
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
@@ -462,7 +592,7 @@ DankModal {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
StyledText {
|
||||
text: parent.parent.parent.actionData.key
|
||||
text: gridButtonRect.actionData.key
|
||||
font.pixelSize: Theme.fontSizeSmall - 1
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||
font.weight: Font.Medium
|
||||
@@ -476,11 +606,14 @@ DankModal {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
onPressed: {
|
||||
root.selectedRow = Math.floor(index / root.gridColumns);
|
||||
root.selectedCol = index % root.gridColumns;
|
||||
root.selectOption(modelData);
|
||||
root.selectedIndex = index;
|
||||
root.startHold(modelData, index);
|
||||
}
|
||||
onReleased: root.cancelHold()
|
||||
onCanceled: root.cancelHold()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -492,9 +625,10 @@ DankModal {
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
top: parent.top
|
||||
leftMargin: Theme.spacingL
|
||||
rightMargin: Theme.spacingL
|
||||
verticalCenter: parent.verticalCenter
|
||||
topMargin: Theme.spacingL
|
||||
}
|
||||
spacing: Theme.spacingS
|
||||
|
||||
@@ -502,12 +636,14 @@ DankModal {
|
||||
model: root.visibleActions
|
||||
|
||||
Rectangle {
|
||||
id: listButtonRect
|
||||
required property int index
|
||||
required property string modelData
|
||||
|
||||
readonly property var actionData: root.getActionData(modelData)
|
||||
readonly property bool isSelected: root.selectedIndex === index
|
||||
readonly property bool showWarning: modelData === "reboot" || modelData === "poweroff"
|
||||
readonly property bool isHolding: root.holdActionIndex === index && root.holdProgress > 0
|
||||
|
||||
width: parent.width
|
||||
height: 56
|
||||
@@ -522,6 +658,40 @@ DankModal {
|
||||
border.color: isSelected ? Theme.primary : "transparent"
|
||||
border.width: isSelected ? 2 : 0
|
||||
|
||||
Rectangle {
|
||||
id: listProgressMask
|
||||
anchors.fill: parent
|
||||
radius: parent.radius
|
||||
visible: false
|
||||
layer.enabled: true
|
||||
}
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
visible: listButtonRect.isHolding
|
||||
layer.enabled: listButtonRect.isHolding
|
||||
layer.effect: MultiEffect {
|
||||
maskEnabled: true
|
||||
maskSource: listProgressMask
|
||||
maskSpreadAtMin: 1
|
||||
maskThresholdMin: 0.5
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
width: parent.width * root.holdProgress
|
||||
color: {
|
||||
if (listButtonRect.modelData === "poweroff")
|
||||
return Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.3);
|
||||
if (listButtonRect.modelData === "reboot")
|
||||
return Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.3);
|
||||
return Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors {
|
||||
left: parent.left
|
||||
@@ -533,11 +703,11 @@ DankModal {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: parent.parent.actionData.icon
|
||||
name: listButtonRect.actionData.icon
|
||||
size: Theme.iconSize + 4
|
||||
color: {
|
||||
if (parent.parent.showWarning && listMouseArea.containsMouse) {
|
||||
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||
if (listButtonRect.showWarning && (listMouseArea.containsMouse || listButtonRect.isHolding)) {
|
||||
return listButtonRect.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||
}
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
@@ -545,11 +715,11 @@ DankModal {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: parent.parent.actionData.label
|
||||
text: listButtonRect.actionData.label
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: {
|
||||
if (parent.parent.showWarning && listMouseArea.containsMouse) {
|
||||
return parent.parent.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||
if (listButtonRect.showWarning && (listMouseArea.containsMouse || listButtonRect.isHolding)) {
|
||||
return listButtonRect.modelData === "poweroff" ? Theme.error : Theme.warning;
|
||||
}
|
||||
return Theme.surfaceText;
|
||||
}
|
||||
@@ -570,7 +740,7 @@ DankModal {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: parent.parent.actionData.key
|
||||
text: listButtonRect.actionData.key
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||
font.weight: Font.Medium
|
||||
@@ -583,14 +753,53 @@ DankModal {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: {
|
||||
onPressed: {
|
||||
root.selectedIndex = index;
|
||||
root.selectOption(modelData);
|
||||
root.startHold(modelData, index);
|
||||
}
|
||||
onReleased: root.cancelHold()
|
||||
onCanceled: root.cancelHold()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: hintRow
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: Theme.spacingS
|
||||
spacing: Theme.spacingXS
|
||||
visible: root.needsConfirmation
|
||||
opacity: root.showHoldHint ? 1 : 0.5
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: 150
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: root.showHoldHint ? "warning" : "touch_app"
|
||||
size: Theme.fontSizeSmall
|
||||
color: root.showHoldHint ? Theme.warning : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
readonly property int remainingSeconds: Math.ceil(SettingsData.powerActionHoldDuration * (1 - root.holdProgress))
|
||||
text: {
|
||||
if (root.showHoldHint)
|
||||
return I18n.tr("Hold longer to confirm");
|
||||
if (root.holdProgress > 0)
|
||||
return I18n.tr("Hold to confirm (%1s)").arg(remainingSeconds);
|
||||
return I18n.tr("Hold to confirm (%1s)").arg(SettingsData.powerActionHoldDuration);
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: root.showHoldHint ? Theme.warning : Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.6)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -583,11 +583,54 @@ Item {
|
||||
|
||||
DankToggle {
|
||||
width: parent.width
|
||||
text: I18n.tr("Show Confirmation on Power Actions")
|
||||
description: I18n.tr("Request confirmation on power off, restart, suspend, hibernate and logout actions")
|
||||
text: I18n.tr("Hold to Confirm Power Actions")
|
||||
description: I18n.tr("Require holding button/key to confirm power off, restart, suspend, hibernate and logout")
|
||||
checked: SettingsData.powerActionConfirm
|
||||
onToggled: checked => SettingsData.set("powerActionConfirm", checked)
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: SettingsData.powerActionConfirm
|
||||
|
||||
Item {
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
height: holdDurationLabel.height
|
||||
|
||||
StyledText {
|
||||
id: holdDurationLabel
|
||||
text: I18n.tr("Hold Duration")
|
||||
font.pixelSize: Appearance.fontSize.normal
|
||||
font.weight: Font.Medium
|
||||
anchors.left: parent.left
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: SettingsData.powerActionHoldDuration + "s"
|
||||
font.pixelSize: Appearance.fontSize.normal
|
||||
font.weight: Font.Medium
|
||||
color: Theme.primary
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
DankSlider {
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
minimum: 1
|
||||
maximum: 10
|
||||
unit: "s"
|
||||
wheelEnabled: false
|
||||
showValue: false
|
||||
thumbOutlineColor: Theme.surfaceContainerHigh
|
||||
value: SettingsData.powerActionHoldDuration
|
||||
onSliderValueChanged: newValue => SettingsData.set("powerActionHoldDuration", newValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ DankModal {
|
||||
layerNamespace: "dms:spotlight"
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [spotlightModal]
|
||||
windows: [spotlightModal.contentWindow]
|
||||
active: CompositorService.isHyprland && spotlightModal.shouldHaveFocus
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ DankModal {
|
||||
keepPopoutsOpen: true
|
||||
|
||||
HyprlandFocusGrab {
|
||||
windows: [root]
|
||||
windows: [root.contentWindow]
|
||||
active: CompositorService.isHyprland && root.shouldHaveFocus
|
||||
}
|
||||
|
||||
|
||||
@@ -354,7 +354,12 @@ Rectangle {
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: AudioService.displayName(modelData)
|
||||
text: {
|
||||
const mediaName = modelData && modelData.properties ? (modelData.properties["media.name"] || "") : "";
|
||||
const max = 30;
|
||||
const truncated = mediaName.length > max ? mediaName.substring(0, max) + "..." : mediaName;
|
||||
return AudioService.displayName(modelData) + (truncated ? ": " + truncated : "");
|
||||
}
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
font.weight: modelData === AudioService.sink ? Font.Medium : Font.Normal
|
||||
|
||||
@@ -12,10 +12,12 @@ Scope {
|
||||
property bool searchActive: false
|
||||
property string searchActiveScreen: ""
|
||||
property bool isClosing: false
|
||||
property bool releaseKeyboard: false
|
||||
property bool overlayActive: (NiriService.inOverview && !(PopoutService.spotlightModal?.spotlightOpen ?? false)) || searchActive
|
||||
|
||||
function showSpotlight(screenName) {
|
||||
isClosing = false;
|
||||
releaseKeyboard = false;
|
||||
searchActive = true;
|
||||
searchActiveScreen = screenName;
|
||||
}
|
||||
@@ -26,10 +28,16 @@ Scope {
|
||||
isClosing = true;
|
||||
}
|
||||
|
||||
function hideAndReleaseKeyboard() {
|
||||
releaseKeyboard = true;
|
||||
hideSpotlight();
|
||||
}
|
||||
|
||||
function completeHide() {
|
||||
searchActive = false;
|
||||
searchActiveScreen = "";
|
||||
isClosing = false;
|
||||
releaseKeyboard = false;
|
||||
}
|
||||
|
||||
Connections {
|
||||
@@ -97,7 +105,7 @@ Scope {
|
||||
return WlrKeyboardFocus.None;
|
||||
if (!isActiveScreen)
|
||||
return WlrKeyboardFocus.None;
|
||||
if (niriOverviewScope.isClosing)
|
||||
if (niriOverviewScope.releaseKeyboard)
|
||||
return WlrKeyboardFocus.None;
|
||||
return WlrKeyboardFocus.Exclusive;
|
||||
}
|
||||
@@ -248,6 +256,20 @@ Scope {
|
||||
Component.onCompleted: {
|
||||
parentModal = fakeParentModal;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: spotlightContent.appLauncher
|
||||
function onAppLaunched() {
|
||||
niriOverviewScope.releaseKeyboard = true;
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: spotlightContent.fileSearchController
|
||||
function onFileOpened() {
|
||||
niriOverviewScope.releaseKeyboard = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[templates.dmscodiumdefault]
|
||||
input_path = 'SHELL_DIR/matugen/templates/vscode-color-theme-default.json'
|
||||
output_path = '~/.vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-default-base.json'
|
||||
output_path = '~/.vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-default.json'
|
||||
|
||||
[templates.dmscodiumdark]
|
||||
input_path = 'SHELL_DIR/matugen/templates/vscode-color-theme-dark.json'
|
||||
output_path = '~/.vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-dark-base.json'
|
||||
output_path = '~/.vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-dark.json'
|
||||
|
||||
[templates.dmscodiumlight]
|
||||
input_path = 'SHELL_DIR/matugen/templates/vscode-color-theme-light.json'
|
||||
output_path = '~/.vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-light-base.json'
|
||||
output_path = '~/.vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-light.json'
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
[templates.dmsvscodedefault]
|
||||
input_path = 'SHELL_DIR/matugen/templates/vscode-color-theme-default.json'
|
||||
output_path = '~/.vscode/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-default-base.json'
|
||||
output_path = '~/.vscode/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-default.json'
|
||||
|
||||
[templates.dmsvscodedark]
|
||||
input_path = 'SHELL_DIR/matugen/templates/vscode-color-theme-dark.json'
|
||||
output_path = '~/.vscode/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-dark-base.json'
|
||||
output_path = '~/.vscode/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-dark.json'
|
||||
|
||||
[templates.dmsvscodelight]
|
||||
input_path = 'SHELL_DIR/matugen/templates/vscode-color-theme-light.json'
|
||||
output_path = '~/.vscode/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-light-base.json'
|
||||
output_path = '~/.vscode/extensions/local.dynamic-base16-dankshell-0.0.1/themes/dankshell-light.json'
|
||||
|
||||
@@ -9,3 +9,23 @@ background = '{{colors.primary_container.default.hex}}'
|
||||
[colors.cursor]
|
||||
text = '{{colors.background.default.hex}}'
|
||||
cursor = '{{colors.primary.default.hex}}'
|
||||
|
||||
[colors.normal]
|
||||
black = '{{dank16.color0}}'
|
||||
red = '{{dank16.color1}}'
|
||||
green = '{{dank16.color2}}'
|
||||
yellow = '{{dank16.color3}}'
|
||||
blue = '{{dank16.color4}}'
|
||||
magenta = '{{dank16.color5}}'
|
||||
cyan = '{{dank16.color6}}'
|
||||
white = '{{dank16.color7}}'
|
||||
|
||||
[colors.bright]
|
||||
black = '{{dank16.color8}}'
|
||||
red = '{{dank16.color9}}'
|
||||
green = '{{dank16.color10}}'
|
||||
yellow = '{{dank16.color11}}'
|
||||
blue = '{{dank16.color12}}'
|
||||
magenta = '{{dank16.color13}}'
|
||||
cyan = '{{dank16.color14}}'
|
||||
white = '{{dank16.color15}}'
|
||||
|
||||
@@ -3,3 +3,20 @@ foreground={{colors.on_surface.default.hex_stripped}}
|
||||
background={{colors.background.default.hex_stripped}}
|
||||
selection-foreground={{colors.on_surface.default.hex_stripped}}
|
||||
selection-background={{colors.primary_container.default.hex_stripped}}
|
||||
|
||||
regular0={{dank16.color0 | remove_prefix: '#'}}
|
||||
regular1={{dank16.color1 | remove_prefix: '#'}}
|
||||
regular2={{dank16.color2 | remove_prefix: '#'}}
|
||||
regular3={{dank16.color3 | remove_prefix: '#'}}
|
||||
regular4={{dank16.color4 | remove_prefix: '#'}}
|
||||
regular5={{dank16.color5 | remove_prefix: '#'}}
|
||||
regular6={{dank16.color6 | remove_prefix: '#'}}
|
||||
regular7={{dank16.color7 | remove_prefix: '#'}}
|
||||
bright0={{dank16.color8 | remove_prefix: '#'}}
|
||||
bright1={{dank16.color9 | remove_prefix: '#'}}
|
||||
bright2={{dank16.color10 | remove_prefix: '#'}}
|
||||
bright3={{dank16.color11 | remove_prefix: '#'}}
|
||||
bright4={{dank16.color12 | remove_prefix: '#'}}
|
||||
bright5={{dank16.color13 | remove_prefix: '#'}}
|
||||
bright6={{dank16.color14 | remove_prefix: '#'}}
|
||||
bright7={{dank16.color15 | remove_prefix: '#'}}
|
||||
|
||||
@@ -2,4 +2,21 @@ background = {{colors.background.default.hex}}
|
||||
foreground = {{colors.on_surface.default.hex}}
|
||||
cursor-color = {{colors.primary.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}}
|
||||
palette = 1={{dank16.color1}}
|
||||
palette = 2={{dank16.color2}}
|
||||
palette = 3={{dank16.color3}}
|
||||
palette = 4={{dank16.color4}}
|
||||
palette = 5={{dank16.color5}}
|
||||
palette = 6={{dank16.color6}}
|
||||
palette = 7={{dank16.color7}}
|
||||
palette = 8={{dank16.color8}}
|
||||
palette = 9={{dank16.color9}}
|
||||
palette = 10={{dank16.color10}}
|
||||
palette = 11={{dank16.color11}}
|
||||
palette = 12={{dank16.color12}}
|
||||
palette = 13={{dank16.color13}}
|
||||
palette = 14={{dank16.color14}}
|
||||
palette = 15={{dank16.color15}}
|
||||
@@ -5,4 +5,21 @@ foreground {{colors.on_surface.default.hex}}
|
||||
background {{colors.background.default.hex}}
|
||||
selection_foreground {{colors.on_secondary.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}}
|
||||
color1 {{dank16.color1}}
|
||||
color2 {{dank16.color2}}
|
||||
color3 {{dank16.color3}}
|
||||
color4 {{dank16.color4}}
|
||||
color5 {{dank16.color5}}
|
||||
color6 {{dank16.color6}}
|
||||
color7 {{dank16.color7}}
|
||||
color8 {{dank16.color8}}
|
||||
color9 {{dank16.color9}}
|
||||
color10 {{dank16.color10}}
|
||||
color11 {{dank16.color11}}
|
||||
color12 {{dank16.color12}}
|
||||
color13 {{dank16.color13}}
|
||||
color14 {{dank16.color14}}
|
||||
color15 {{dank16.color15}}
|
||||
@@ -97,6 +97,22 @@
|
||||
|
||||
"terminal.background": "{{colors.background.dark.hex}}",
|
||||
"terminal.foreground": "{{colors.on_surface.dark.hex}}",
|
||||
"terminal.ansiBlack": "{{dank16.color0}}",
|
||||
"terminal.ansiRed": "{{dank16.color1}}",
|
||||
"terminal.ansiGreen": "{{dank16.color2}}",
|
||||
"terminal.ansiYellow": "{{dank16.color3}}",
|
||||
"terminal.ansiBlue": "{{dank16.color4}}",
|
||||
"terminal.ansiMagenta": "{{dank16.color5}}",
|
||||
"terminal.ansiCyan": "{{dank16.color6}}",
|
||||
"terminal.ansiWhite": "{{dank16.color7}}",
|
||||
"terminal.ansiBrightBlack": "{{dank16.color8}}",
|
||||
"terminal.ansiBrightRed": "{{dank16.color9}}",
|
||||
"terminal.ansiBrightGreen": "{{dank16.color10}}",
|
||||
"terminal.ansiBrightYellow": "{{dank16.color11}}",
|
||||
"terminal.ansiBrightBlue": "{{dank16.color12}}",
|
||||
"terminal.ansiBrightMagenta": "{{dank16.color13}}",
|
||||
"terminal.ansiBrightCyan": "{{dank16.color14}}",
|
||||
"terminal.ansiBrightWhite": "{{dank16.color15}}",
|
||||
|
||||
"gitDecoration.modifiedResourceForeground": "{{colors.primary.dark.hex}}",
|
||||
"gitDecoration.addedResourceForeground": "{{colors.primary.dark.hex}}",
|
||||
@@ -158,19 +174,6 @@
|
||||
},
|
||||
|
||||
"tokenColors": [
|
||||
{
|
||||
"scope": ["comment", "punctuation.definition.comment"],
|
||||
"settings": {
|
||||
"foreground": "{{colors.outline.dark.hex}}",
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["keyword", "storage.type", "storage.modifier"],
|
||||
"settings": {
|
||||
"foreground": "{{colors.primary.dark.hex}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["variable", "meta.object-literal.key"],
|
||||
"settings": {
|
||||
@@ -263,53 +266,166 @@
|
||||
"settings": {
|
||||
"foreground": "{{colors.secondary.dark.hex}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["comment", "punctuation.definition.comment"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color8}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color5}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage.type",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color13}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage.modifier",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color5}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable.parameter",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color7}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["meta.object-literal.key", "meta.property.object", "variable.other.property"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color4}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.other.symbol",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["constant.numeric", "constant.language"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.character",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color3}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.name.type", "entity.name.class"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "support.type",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color13}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.name.function", "support.function"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color2}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["support.class", "support.variable"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable.language",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.name.tag.yaml",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["string.unquoted.plain.out.yaml", "string.unquoted.yaml"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "string",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color3}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"semanticHighlighting": true,
|
||||
"semanticTokenColors": {
|
||||
"variable": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
},
|
||||
"variable.readonly": {
|
||||
"foreground": "{{colors.tertiary.dark.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"property": {
|
||||
"foreground": "{{colors.on_surface.dark.hex}}"
|
||||
"foreground": "{{dank16.color4}}"
|
||||
},
|
||||
"function": {
|
||||
"foreground": "{{colors.primary.dark.hex}}"
|
||||
"foreground": "{{dank16.color2}}"
|
||||
},
|
||||
"method": {
|
||||
"foreground": "{{colors.primary.dark.hex}}"
|
||||
"foreground": "{{dank16.color2}}"
|
||||
},
|
||||
"type": {
|
||||
"foreground": "{{colors.tertiary.dark.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"class": {
|
||||
"foreground": "{{colors.tertiary.dark.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"typeParameter": {
|
||||
"foreground": "{{dank16.color13}}"
|
||||
},
|
||||
"enumMember": {
|
||||
"foreground": "{{colors.tertiary.dark.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"string": {
|
||||
"foreground": "{{colors.secondary.dark.hex}}"
|
||||
"foreground": "{{dank16.color3}}"
|
||||
},
|
||||
"number": {
|
||||
"foreground": "{{colors.tertiary.dark.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"comment": {
|
||||
"foreground": "{{colors.outline.dark.hex}}",
|
||||
"fontStyle": "italic"
|
||||
"foreground": "{{dank16.color8}}"
|
||||
},
|
||||
"keyword": {
|
||||
"foreground": "{{colors.primary.dark.hex}}"
|
||||
"foreground": "{{dank16.color5}}"
|
||||
},
|
||||
"operator": {
|
||||
"foreground": "{{colors.on_surface.dark.hex}}"
|
||||
"foreground": "{{dank16.color15}}"
|
||||
},
|
||||
"parameter": {
|
||||
"foreground": "{{colors.on_surface.dark.hex}}"
|
||||
"foreground": "{{dank16.color7}}"
|
||||
},
|
||||
"namespace": {
|
||||
"foreground": "{{colors.secondary.dark.hex}}"
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,22 @@
|
||||
|
||||
"terminal.background": "{{colors.background.default.hex}}",
|
||||
"terminal.foreground": "{{colors.on_surface.default.hex}}",
|
||||
"terminal.ansiBlack": "{{dank16.color0}}",
|
||||
"terminal.ansiRed": "{{dank16.color1}}",
|
||||
"terminal.ansiGreen": "{{dank16.color2}}",
|
||||
"terminal.ansiYellow": "{{dank16.color3}}",
|
||||
"terminal.ansiBlue": "{{dank16.color4}}",
|
||||
"terminal.ansiMagenta": "{{dank16.color5}}",
|
||||
"terminal.ansiCyan": "{{dank16.color6}}",
|
||||
"terminal.ansiWhite": "{{dank16.color7}}",
|
||||
"terminal.ansiBrightBlack": "{{dank16.color8}}",
|
||||
"terminal.ansiBrightRed": "{{dank16.color9}}",
|
||||
"terminal.ansiBrightGreen": "{{dank16.color10}}",
|
||||
"terminal.ansiBrightYellow": "{{dank16.color11}}",
|
||||
"terminal.ansiBrightBlue": "{{dank16.color12}}",
|
||||
"terminal.ansiBrightMagenta": "{{dank16.color13}}",
|
||||
"terminal.ansiBrightCyan": "{{dank16.color14}}",
|
||||
"terminal.ansiBrightWhite": "{{dank16.color15}}",
|
||||
|
||||
"gitDecoration.modifiedResourceForeground": "{{colors.primary.default.hex}}",
|
||||
"gitDecoration.addedResourceForeground": "{{colors.primary.default.hex}}",
|
||||
@@ -158,19 +174,6 @@
|
||||
},
|
||||
|
||||
"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": {
|
||||
@@ -263,53 +266,166 @@
|
||||
"settings": {
|
||||
"foreground": "{{colors.secondary.default.hex}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["comment", "punctuation.definition.comment"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color8}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color5}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage.type",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color13}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage.modifier",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color5}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable.parameter",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color7}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["meta.object-literal.key", "meta.property.object", "variable.other.property"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color4}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.other.symbol",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["constant.numeric", "constant.language"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.character",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color3}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.name.type", "entity.name.class"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "support.type",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color13}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.name.function", "support.function"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color2}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["support.class", "support.variable"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable.language",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.name.tag.yaml",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["string.unquoted.plain.out.yaml", "string.unquoted.yaml"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "string",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color3}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"semanticHighlighting": true,
|
||||
"semanticTokenColors": {
|
||||
"variable": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
},
|
||||
"variable.readonly": {
|
||||
"foreground": "{{colors.tertiary.default.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"property": {
|
||||
"foreground": "{{colors.on_surface.default.hex}}"
|
||||
"foreground": "{{dank16.color4}}"
|
||||
},
|
||||
"function": {
|
||||
"foreground": "{{colors.primary.default.hex}}"
|
||||
"foreground": "{{dank16.color2}}"
|
||||
},
|
||||
"method": {
|
||||
"foreground": "{{colors.primary.default.hex}}"
|
||||
"foreground": "{{dank16.color2}}"
|
||||
},
|
||||
"type": {
|
||||
"foreground": "{{colors.tertiary.default.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"class": {
|
||||
"foreground": "{{colors.tertiary.default.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"typeParameter": {
|
||||
"foreground": "{{dank16.color13}}"
|
||||
},
|
||||
"enumMember": {
|
||||
"foreground": "{{colors.tertiary.default.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"string": {
|
||||
"foreground": "{{colors.secondary.default.hex}}"
|
||||
"foreground": "{{dank16.color3}}"
|
||||
},
|
||||
"number": {
|
||||
"foreground": "{{colors.tertiary.default.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"comment": {
|
||||
"foreground": "{{colors.outline.default.hex}}",
|
||||
"fontStyle": "italic"
|
||||
"foreground": "{{dank16.color8}}"
|
||||
},
|
||||
"keyword": {
|
||||
"foreground": "{{colors.primary.default.hex}}"
|
||||
"foreground": "{{dank16.color5}}"
|
||||
},
|
||||
"operator": {
|
||||
"foreground": "{{colors.on_surface.default.hex}}"
|
||||
"foreground": "{{dank16.color15}}"
|
||||
},
|
||||
"parameter": {
|
||||
"foreground": "{{colors.on_surface.default.hex}}"
|
||||
"foreground": "{{dank16.color7}}"
|
||||
},
|
||||
"namespace": {
|
||||
"foreground": "{{colors.secondary.default.hex}}"
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,6 +97,22 @@
|
||||
|
||||
"terminal.background": "{{colors.background.light.hex}}",
|
||||
"terminal.foreground": "{{colors.on_surface.light.hex}}",
|
||||
"terminal.ansiBlack": "{{dank16.color0}}",
|
||||
"terminal.ansiRed": "{{dank16.color1}}",
|
||||
"terminal.ansiGreen": "{{dank16.color2}}",
|
||||
"terminal.ansiYellow": "{{dank16.color3}}",
|
||||
"terminal.ansiBlue": "{{dank16.color4}}",
|
||||
"terminal.ansiMagenta": "{{dank16.color5}}",
|
||||
"terminal.ansiCyan": "{{dank16.color6}}",
|
||||
"terminal.ansiWhite": "{{dank16.color7}}",
|
||||
"terminal.ansiBrightBlack": "{{dank16.color8}}",
|
||||
"terminal.ansiBrightRed": "{{dank16.color9}}",
|
||||
"terminal.ansiBrightGreen": "{{dank16.color10}}",
|
||||
"terminal.ansiBrightYellow": "{{dank16.color11}}",
|
||||
"terminal.ansiBrightBlue": "{{dank16.color12}}",
|
||||
"terminal.ansiBrightMagenta": "{{dank16.color13}}",
|
||||
"terminal.ansiBrightCyan": "{{dank16.color14}}",
|
||||
"terminal.ansiBrightWhite": "{{dank16.color15}}",
|
||||
|
||||
"gitDecoration.modifiedResourceForeground": "{{colors.primary.light.hex}}",
|
||||
"gitDecoration.addedResourceForeground": "{{colors.primary.light.hex}}",
|
||||
@@ -158,19 +174,6 @@
|
||||
},
|
||||
|
||||
"tokenColors": [
|
||||
{
|
||||
"scope": ["comment", "punctuation.definition.comment"],
|
||||
"settings": {
|
||||
"foreground": "{{colors.outline.light.hex}}",
|
||||
"fontStyle": "italic"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["keyword", "storage.type", "storage.modifier"],
|
||||
"settings": {
|
||||
"foreground": "{{colors.primary.light.hex}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["variable", "meta.object-literal.key"],
|
||||
"settings": {
|
||||
@@ -263,53 +266,166 @@
|
||||
"settings": {
|
||||
"foreground": "{{colors.secondary.light.hex}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["comment", "punctuation.definition.comment"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color8}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "keyword",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color5}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage.type",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color13}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "storage.modifier",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color5}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable.parameter",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color7}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["meta.object-literal.key", "meta.property.object", "variable.other.property"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color4}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.other.symbol",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["constant.numeric", "constant.language"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "constant.character",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color3}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.name.type", "entity.name.class"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "support.type",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color13}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["entity.name.function", "support.function"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color2}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["support.class", "support.variable"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "variable.language",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "entity.name.tag.yaml",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color12}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": ["string.unquoted.plain.out.yaml", "string.unquoted.yaml"],
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
},
|
||||
{
|
||||
"scope": "string",
|
||||
"settings": {
|
||||
"foreground": "{{dank16.color3}}"
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
"semanticHighlighting": true,
|
||||
"semanticTokenColors": {
|
||||
"variable": {
|
||||
"foreground": "{{dank16.color15}}"
|
||||
},
|
||||
"variable.readonly": {
|
||||
"foreground": "{{colors.tertiary.light.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"property": {
|
||||
"foreground": "{{colors.on_surface.light.hex}}"
|
||||
"foreground": "{{dank16.color4}}"
|
||||
},
|
||||
"function": {
|
||||
"foreground": "{{colors.primary.light.hex}}"
|
||||
"foreground": "{{dank16.color2}}"
|
||||
},
|
||||
"method": {
|
||||
"foreground": "{{colors.primary.light.hex}}"
|
||||
"foreground": "{{dank16.color2}}"
|
||||
},
|
||||
"type": {
|
||||
"foreground": "{{colors.tertiary.light.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"class": {
|
||||
"foreground": "{{colors.tertiary.light.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"typeParameter": {
|
||||
"foreground": "{{dank16.color13}}"
|
||||
},
|
||||
"enumMember": {
|
||||
"foreground": "{{colors.tertiary.light.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"string": {
|
||||
"foreground": "{{colors.secondary.light.hex}}"
|
||||
"foreground": "{{dank16.color3}}"
|
||||
},
|
||||
"number": {
|
||||
"foreground": "{{colors.tertiary.light.hex}}"
|
||||
"foreground": "{{dank16.color12}}"
|
||||
},
|
||||
"comment": {
|
||||
"foreground": "{{colors.outline.light.hex}}",
|
||||
"fontStyle": "italic"
|
||||
"foreground": "{{dank16.color8}}"
|
||||
},
|
||||
"keyword": {
|
||||
"foreground": "{{colors.primary.light.hex}}"
|
||||
"foreground": "{{dank16.color5}}"
|
||||
},
|
||||
"operator": {
|
||||
"foreground": "{{colors.on_surface.light.hex}}"
|
||||
"foreground": "{{dank16.color15}}"
|
||||
},
|
||||
"parameter": {
|
||||
"foreground": "{{colors.on_surface.light.hex}}"
|
||||
"foreground": "{{dank16.color7}}"
|
||||
},
|
||||
"namespace": {
|
||||
"foreground": "{{colors.secondary.light.hex}}"
|
||||
"foreground": "{{dank16.color15}}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,3 +8,6 @@ cursor_border = '{{colors.primary.default.hex}}'
|
||||
|
||||
selection_bg = '{{colors.primary_container.default.hex}}'
|
||||
selection_fg = '{{colors.on_surface.default.hex}}'
|
||||
|
||||
ansi = ['{{dank16.color0}}', '{{dank16.color1}}', '{{dank16.color2}}', '{{dank16.color3}}', '{{dank16.color4}}', '{{dank16.color5}}', '{{dank16.color6}}', '{{dank16.color7}}']
|
||||
brights = ['{{dank16.color8}}', '{{dank16.color9}}', '{{dank16.color10}}', '{{dank16.color11}}', '{{dank16.color12}}', '{{dank16.color13}}', '{{dank16.color14}}', '{{dank16.color15}}']
|
||||
|
||||
@@ -1,467 +1,275 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
set -uo pipefail
|
||||
|
||||
if [ $# -lt 5 ]; then
|
||||
echo "Usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR SYNC_MODE_WITH_PORTAL TERMINALS_ALWAYS_DARK --run" >&2
|
||||
exit 1
|
||||
fi
|
||||
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"
|
||||
|
||||
if [ ! -d "$STATE_DIR" ]; then
|
||||
echo "Error: STATE_DIR '$STATE_DIR' does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$SHELL_DIR" ]; then
|
||||
echo "Error: SHELL_DIR '$SHELL_DIR' does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "$CONFIG_DIR" ]; then
|
||||
echo "Error: CONFIG_DIR '$CONFIG_DIR' does not exist" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
shift 5
|
||||
[[ "${1:-}" != "--run" ]] && { echo "Usage: $0 ... --run" >&2; exit 1; }
|
||||
|
||||
if [[ "${1:-}" != "--run" ]]; then
|
||||
echo "usage: $0 STATE_DIR SHELL_DIR CONFIG_DIR SYNC_MODE_WITH_PORTAL TERMINALS_ALWAYS_DARK --run" >&2
|
||||
exit 1
|
||||
fi
|
||||
[[ ! -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"
|
||||
LAST_JSON="$STATE_DIR/last.json"
|
||||
LOCK="$STATE_DIR/matugen-worker.lock"
|
||||
COLORS_OUTPUT="$STATE_DIR/dms-colors.json"
|
||||
|
||||
exec 9>"$LOCK"
|
||||
flock 9
|
||||
|
||||
rm -f "$BUILT_KEY"
|
||||
|
||||
get_matugen_major_version() {
|
||||
local version_output=$(matugen --version 2>&1)
|
||||
local version=$(echo "$version_output" | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -n1)
|
||||
local major=$(echo "$version" | cut -d. -f1)
|
||||
echo "$major"
|
||||
read_json_field() {
|
||||
local json="$1" field="$2"
|
||||
echo "$json" | sed -n "s/.*\"$field\"[[:space:]]*:[[:space:]]*\"\([^\"]*\)\".*/\1/p" | head -1
|
||||
}
|
||||
|
||||
MATUGEN_VERSION=$(get_matugen_major_version)
|
||||
|
||||
read_desired() {
|
||||
[[ ! -f "$DESIRED_JSON" ]] && { echo "no desired state" >&2; exit 0; }
|
||||
cat "$DESIRED_JSON"
|
||||
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"
|
||||
}
|
||||
|
||||
key_of() {
|
||||
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=$(echo "$json" | sed 's/.*"kind": *"\([^"]*\)".*/\1/')
|
||||
local value=$(echo "$json" | sed 's/.*"value": *"\([^"]*\)".*/\1/')
|
||||
local mode=$(echo "$json" | sed 's/.*"mode": *"\([^"]*\)".*/\1/')
|
||||
local icon=$(echo "$json" | sed 's/.*"iconTheme": *"\([^"]*\)".*/\1/')
|
||||
local matugen_type=$(echo "$json" | sed 's/.*"matugenType": *"\([^"]*\)".*/\1/')
|
||||
local run_user_templates=$(echo "$json" | sed 's/.*"runUserTemplates": *\([^,}]*\).*/\1/')
|
||||
[[ -z "$icon" ]] && icon="System Default"
|
||||
[[ -z "$matugen_type" ]] && matugen_type="scheme-tonal-spot"
|
||||
[[ -z "$run_user_templates" ]] && run_user_templates="true"
|
||||
echo "${kind}|${value}|${mode}|${icon}|${matugen_type}|${run_user_templates}" | sha256sum | cut -d' ' -f1
|
||||
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"
|
||||
}
|
||||
|
||||
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_config "code" "vscode.toml" "$cfg_file"
|
||||
append_config "codium" "codium.toml" "$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
|
||||
}
|
||||
|
||||
if [[ "$SYNC_MODE_WITH_PORTAL" != "true" ]]; then
|
||||
return 0
|
||||
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
|
||||
}
|
||||
|
||||
local target_scheme
|
||||
if [[ "$mode" == "light" ]]; then
|
||||
target_scheme="default"
|
||||
else
|
||||
target_scheme="prefer-dark"
|
||||
fi
|
||||
setup_vscode_extension() {
|
||||
local cmd="$1" ext_dir="$2" config_dir="$3"
|
||||
command -v "$cmd" >/dev/null 2>&1 || return
|
||||
[[ ! -d "$config_dir" ]] && return
|
||||
local theme_dir="$ext_dir/themes"
|
||||
mkdir -p "$theme_dir"
|
||||
cp "$SHELL_DIR/matugen/templates/vscode-package.json" "$ext_dir/package.json" 2>/dev/null || true
|
||||
cp "$SHELL_DIR/matugen/templates/vscode-vsixmanifest.xml" "$ext_dir/.vsixmanifest" 2>/dev/null || true
|
||||
}
|
||||
|
||||
if command -v gsettings >/dev/null 2>&1; then
|
||||
gsettings set org.gnome.desktop.interface color-scheme "$target_scheme" >/dev/null 2>&1 || true
|
||||
elif command -v dconf >/dev/null 2>&1; then
|
||||
dconf write /org/gnome/desktop/interface/color-scheme "'$target_scheme'" >/dev/null 2>&1 || true
|
||||
fi
|
||||
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 value mode icon matugen_type run_user_templates
|
||||
kind=$(echo "$json" | sed 's/.*"kind": *"\([^"]*\)".*/\1/')
|
||||
value=$(echo "$json" | sed 's/.*"value": *"\([^"]*\)".*/\1/')
|
||||
mode=$(echo "$json" | sed 's/.*"mode": *"\([^"]*\)".*/\1/')
|
||||
icon=$(echo "$json" | sed 's/.*"iconTheme": *"\([^"]*\)".*/\1/')
|
||||
matugen_type=$(echo "$json" | sed 's/.*"matugenType": *"\([^"]*\)".*/\1/')
|
||||
run_user_templates=$(echo "$json" | sed 's/.*"runUserTemplates": *\([^,}]*\).*/\1/')
|
||||
[[ -z "$icon" ]] && icon="System Default"
|
||||
[[ -z "$matugen_type" ]] && matugen_type="scheme-tonal-spot"
|
||||
[[ -z "$run_user_templates" ]] && run_user_templates="true"
|
||||
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")
|
||||
|
||||
USER_MATUGEN_DIR="$CONFIG_DIR/matugen/dms"
|
||||
|
||||
TMP_CFG="$(mktemp)"
|
||||
trap 'rm -f "$TMP_CFG"' RETURN
|
||||
[[ -z "$mtype" ]] && mtype="scheme-tonal-spot"
|
||||
[[ -z "$run_user" ]] && run_user="true"
|
||||
|
||||
if [[ "$run_user_templates" == "true" ]] && [[ -f "$CONFIG_DIR/matugen/config.toml" ]]; then
|
||||
awk '/^\[config/{p=1} /^\[templates/{p=0} p' "$CONFIG_DIR/matugen/config.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
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
|
||||
echo "[config]" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
fi
|
||||
log "Using dynamic theme from $kind: $value"
|
||||
|
||||
grep -v '^\[config\]' "$SHELL_DIR/matugen/configs/base.toml" | \
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
local matugen_cmd=("matugen")
|
||||
[[ "$kind" == "hex" ]] && matugen_cmd+=("color" "hex") || matugen_cmd+=("$kind")
|
||||
matugen_cmd+=("$value")
|
||||
|
||||
cat >> "$TMP_CFG" << EOF
|
||||
[templates.dank]
|
||||
input_path = '$SHELL_DIR/matugen/templates/dank.json'
|
||||
output_path = '$STATE_DIR/dms-colors.json'
|
||||
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; }
|
||||
|
||||
EOF
|
||||
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')
|
||||
|
||||
# If light mode, use gtk3 light config
|
||||
if [[ "$mode" == "light" ]]; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/gtk3-light.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
else
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/gtk3-dark.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
fi
|
||||
[[ -z "$primary" ]] && { err "Failed to extract primary color"; return 1; }
|
||||
|
||||
if command -v niri >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/niri.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
fi
|
||||
dank16_dark=$(generate_dank16 "$primary" "$surface" "")
|
||||
dank16_light=$(generate_dank16 "$primary" "$surface" "--light")
|
||||
|
||||
if command -v qt5ct >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/qt5ct.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
fi
|
||||
local dank16_current
|
||||
[[ "$mode" == "light" ]] && dank16_current="$dank16_light" || dank16_current="$dank16_dark"
|
||||
[[ "$TERMINALS_ALWAYS_DARK" == "true" && "$mode" == "light" ]] && dank16_current="$dank16_dark"
|
||||
|
||||
if command -v qt6ct >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/qt6ct.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
fi
|
||||
import_args+=(--import-json-string "{\"dank16\": $dank16_current}")
|
||||
|
||||
if command -v firefox >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/firefox.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
fi
|
||||
|
||||
if command -v pywalfox >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/pywalfox.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
fi
|
||||
|
||||
if command -v vesktop >/dev/null 2>&1 && [[ -d "$CONFIG_DIR/vesktop" ]]; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/vesktop.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
fi
|
||||
|
||||
if [[ "$run_user_templates" == "true" ]] && [[ -f "$CONFIG_DIR/matugen/config.toml" ]]; then
|
||||
awk '/^\[templates/{p=1} p' "$CONFIG_DIR/matugen/config.toml" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
fi
|
||||
|
||||
for config in "$USER_MATUGEN_DIR/configs"/*.toml; do
|
||||
[[ -f "$config" ]] || continue
|
||||
cat "$config" >> "$TMP_CFG"
|
||||
echo "" >> "$TMP_CFG"
|
||||
done
|
||||
|
||||
pushd "$SHELL_DIR" >/dev/null
|
||||
MAT_MODE=(-m "$mode")
|
||||
MAT_TYPE=(-t "$matugen_type")
|
||||
|
||||
case "$kind" in
|
||||
image)
|
||||
[[ -f "$value" ]] || { echo "wallpaper not found: $value" >&2; popd >/dev/null; return 2; }
|
||||
JSON=$(matugen -c "$TMP_CFG" --json hex image "$value" "${MAT_MODE[@]}" "${MAT_TYPE[@]}")
|
||||
matugen -c "$TMP_CFG" image "$value" "${MAT_MODE[@]}" "${MAT_TYPE[@]}" >/dev/null
|
||||
;;
|
||||
hex)
|
||||
[[ "$value" =~ ^#[0-9A-Fa-f]{6}$ ]] || { echo "invalid hex: $value" >&2; popd >/dev/null; return 2; }
|
||||
JSON=$(matugen -c "$TMP_CFG" --json hex color hex "$value" "${MAT_MODE[@]}" "${MAT_TYPE[@]}")
|
||||
matugen -c "$TMP_CFG" color hex "$value" "${MAT_MODE[@]}" "${MAT_TYPE[@]}" >/dev/null
|
||||
;;
|
||||
*)
|
||||
echo "unknown kind: $kind" >&2; popd >/dev/null; return 2;;
|
||||
esac
|
||||
|
||||
TMP_CONTENT_CFG="$(mktemp)"
|
||||
echo "[config]" > "$TMP_CONTENT_CFG"
|
||||
echo "" >> "$TMP_CONTENT_CFG"
|
||||
|
||||
TERMINAL_MODE="$mode"
|
||||
if [[ "$TERMINALS_ALWAYS_DARK" == "true" ]]; then
|
||||
TERMINAL_MODE="dark"
|
||||
fi
|
||||
|
||||
if command -v ghostty >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/ghostty.toml" >> "$TMP_CONTENT_CFG"
|
||||
echo "" >> "$TMP_CONTENT_CFG"
|
||||
fi
|
||||
|
||||
if command -v kitty >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/kitty.toml" >> "$TMP_CONTENT_CFG"
|
||||
echo "" >> "$TMP_CONTENT_CFG"
|
||||
fi
|
||||
|
||||
if command -v foot >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/foot.toml" >> "$TMP_CONTENT_CFG"
|
||||
echo "" >> "$TMP_CONTENT_CFG"
|
||||
fi
|
||||
|
||||
if command -v alacritty >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/alacritty.toml" >> "$TMP_CONTENT_CFG"
|
||||
echo "" >> "$TMP_CONTENT_CFG"
|
||||
fi
|
||||
|
||||
if command -v wezterm >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/wezterm.toml" >>"$TMP_CONTENT_CFG"
|
||||
echo "" >>"$TMP_CONTENT_CFG"
|
||||
fi
|
||||
|
||||
if command -v dgop >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/dgop.toml" >> "$TMP_CONTENT_CFG"
|
||||
echo "" >> "$TMP_CONTENT_CFG"
|
||||
fi
|
||||
|
||||
if command -v code >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/vscode.toml" >> "$TMP_CONTENT_CFG"
|
||||
echo "" >> "$TMP_CONTENT_CFG"
|
||||
fi
|
||||
|
||||
if command -v codium >/dev/null 2>&1; then
|
||||
sed "s|'SHELL_DIR/|'$SHELL_DIR/|g" "$SHELL_DIR/matugen/configs/codium.toml" >> "$TMP_CONTENT_CFG"
|
||||
echo "" >> "$TMP_CONTENT_CFG"
|
||||
fi
|
||||
|
||||
if [[ -s "$TMP_CONTENT_CFG" ]] && grep -q '\[templates\.' "$TMP_CONTENT_CFG"; then
|
||||
MAT_TERMINAL_MODE=(-m "$TERMINAL_MODE")
|
||||
case "$kind" in
|
||||
image)
|
||||
matugen -c "$TMP_CONTENT_CFG" image "$value" "${MAT_TERMINAL_MODE[@]}" "${MAT_TYPE[@]}" >/dev/null
|
||||
;;
|
||||
hex)
|
||||
matugen -c "$TMP_CONTENT_CFG" color hex "$value" "${MAT_TERMINAL_MODE[@]}" "${MAT_TYPE[@]}" >/dev/null
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
rm -f "$TMP_CONTENT_CFG"
|
||||
popd >/dev/null
|
||||
|
||||
echo "$JSON" | grep -q '"primary"' || { echo "matugen JSON missing primary" >&2; set_system_color_scheme "$mode"; return 2; }
|
||||
printf "%s" "$JSON" > "$LAST_JSON"
|
||||
|
||||
GTK_CSS="$CONFIG_DIR/gtk-3.0/gtk.css"
|
||||
SHOULD_RUN_HOOK=false
|
||||
|
||||
if [[ -L "$GTK_CSS" ]]; then
|
||||
LINK_TARGET=$(readlink "$GTK_CSS")
|
||||
if [[ "$LINK_TARGET" == *"dank-colors.css"* ]]; then
|
||||
SHOULD_RUN_HOOK=true
|
||||
fi
|
||||
elif [[ -f "$GTK_CSS" ]] && grep -q "dank-colors.css" "$GTK_CSS"; then
|
||||
SHOULD_RUN_HOOK=true
|
||||
fi
|
||||
|
||||
if [[ "$SHOULD_RUN_HOOK" == "true" ]]; then
|
||||
gsettings set org.gnome.desktop.interface gtk-theme "" >/dev/null 2>&1 || true
|
||||
gsettings set org.gnome.desktop.interface gtk-theme "adw-gtk3-${mode}" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
COLOR_EXTRACT_MODE="$mode"
|
||||
if [[ "$TERMINALS_ALWAYS_DARK" == "true" ]]; then
|
||||
COLOR_EXTRACT_MODE="dark"
|
||||
fi
|
||||
|
||||
if [[ "$MATUGEN_VERSION" == "2" ]]; then
|
||||
PRIMARY=$(echo "$JSON" | sed -n "s/.*\"$COLOR_EXTRACT_MODE\":{[^}]*\"primary\":\"\\(#[0-9a-fA-F]\\{6\\}\\)\".*/\\1/p")
|
||||
SURFACE=$(echo "$JSON" | sed -n "s/.*\"$COLOR_EXTRACT_MODE\":{[^}]*\"surface\":\"\\(#[0-9a-fA-F]\\{6\\}\\)\".*/\\1/p")
|
||||
else
|
||||
JSON_FLAT=$(echo "$JSON" | tr -d '\n')
|
||||
PRIMARY=$(echo "$JSON_FLAT" | sed -n "s/.*\"primary\" *: *{ *[^}]*\"$COLOR_EXTRACT_MODE\" *: *\"\\(#[0-9a-fA-F]\\{6\\}\\)\".*/\\1/p")
|
||||
SURFACE=$(echo "$JSON_FLAT" | sed -n "s/.*\"surface\" *: *{ *[^}]*\"$COLOR_EXTRACT_MODE\" *: *\"\\(#[0-9a-fA-F]\\{6\\}\\)\".*/\\1/p")
|
||||
fi
|
||||
|
||||
if [[ -z "$PRIMARY" ]]; then
|
||||
echo "Error: Failed to extract PRIMARY color from matugen JSON (mode: $mode)" >&2
|
||||
echo "This may indicate an incompatible matugen JSON format" >&2
|
||||
set_system_color_scheme "$mode"
|
||||
return 2
|
||||
fi
|
||||
|
||||
TERMINAL_LIGHT_FLAG=""
|
||||
if [[ "$TERMINALS_ALWAYS_DARK" != "true" ]] && [[ "$mode" == "light" ]]; then
|
||||
TERMINAL_LIGHT_FLAG="--light"
|
||||
fi
|
||||
|
||||
if command -v ghostty >/dev/null 2>&1 && [[ -f "$CONFIG_DIR/ghostty/config-dankcolors" ]]; then
|
||||
OUT=$(dms dank16 "$PRIMARY" $TERMINAL_LIGHT_FLAG ${SURFACE:+--background "$SURFACE"} --ghostty 2>/dev/null || true)
|
||||
if [[ -n "${OUT:-}" ]]; then
|
||||
printf "\n%s\n" "$OUT" >> "$CONFIG_DIR/ghostty/config-dankcolors"
|
||||
if [[ -f "$CONFIG_DIR/ghostty/config" ]] && grep -q "^[^#]*config-dankcolors" "$CONFIG_DIR/ghostty/config" 2>/dev/null; then
|
||||
pkill -USR2 -x 'ghostty|.ghostty-wrappe' >/dev/null 2>&1 || true
|
||||
fi
|
||||
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
|
||||
|
||||
if command -v kitty >/dev/null 2>&1 && [[ -f "$CONFIG_DIR/kitty/dank-theme.conf" ]]; then
|
||||
OUT=$(dms dank16 "$PRIMARY" $TERMINAL_LIGHT_FLAG ${SURFACE:+--background "$SURFACE"} --kitty 2>/dev/null || true)
|
||||
if [[ -n "${OUT:-}" ]]; then
|
||||
printf "\n%s\n" "$OUT" >> "$CONFIG_DIR/kitty/dank-theme.conf"
|
||||
if [[ -f "$CONFIG_DIR/kitty/kitty.conf" ]] && grep -q "^[^#]*dank-theme.conf" "$CONFIG_DIR/kitty/kitty.conf" 2>/dev/null; then
|
||||
pkill -USR1 -x kitty >/dev/null 2>&1 || true
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v foot >/dev/null 2>&1; then
|
||||
FOOT_CONFIG="$CONFIG_DIR/foot/dank-colors.ini"
|
||||
|
||||
if [[ ! -f "$FOOT_CONFIG" ]]; then
|
||||
mkdir -p "$(dirname "$FOOT_CONFIG")"
|
||||
echo "[colors]" > "$FOOT_CONFIG"
|
||||
fi
|
||||
|
||||
OUT=$(dms dank16 "$PRIMARY" $TERMINAL_LIGHT_FLAG ${SURFACE:+--background "$SURFACE"} --foot 2>/dev/null || true)
|
||||
if [[ -n "${OUT:-}" ]]; then
|
||||
printf "\n%s\n" "$OUT" >> "$FOOT_CONFIG"
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v wezterm >/dev/null 2>&1; then
|
||||
WEZTERM_CONFIG="$CONFIG_DIR/wezterm/colors/dank-theme.toml"
|
||||
|
||||
if [[ ! -f "$WEZTERM_CONFIG" ]]; then
|
||||
mkdir -p "$(dirname "$WEZTERM_CONFIG")"
|
||||
touch "$WEZTERM_CONFIG"
|
||||
fi
|
||||
|
||||
OUT=$(dms dank16 "$PRIMARY" $TERMINAL_LIGHT_FLAG ${SURFACE:+--background "$SURFACE"} --wezterm 2>/dev/null || true)
|
||||
if [[ -n "${OUT:-}" ]]; then
|
||||
printf "\n%s\n" "$OUT" >>"$WEZTERM_CONFIG"
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v alacritty >/dev/null 2>&1; then
|
||||
ALACRITTY_CONFIG="$CONFIG_DIR/alacritty/dank-theme.toml"
|
||||
|
||||
if [[ ! -f "$ALACRITTY_CONFIG" ]]; then
|
||||
mkdir -p "$(dirname "$ALACRITTY_CONFIG")"
|
||||
touch "$ALACRITTY_CONFIG"
|
||||
fi
|
||||
|
||||
OUT=$(dms dank16 "$PRIMARY" $TERMINAL_LIGHT_FLAG ${SURFACE:+--background "$SURFACE"} --alacritty 2>/dev/null || true)
|
||||
if [[ -n "${OUT:-}" ]]; then
|
||||
printf "\n%s\n" "$OUT" >> "$ALACRITTY_CONFIG"
|
||||
fi
|
||||
fi
|
||||
|
||||
if command -v code >/dev/null 2>&1; then
|
||||
VSCODE_EXT_DIR="$HOME/.vscode/extensions/local.dynamic-base16-dankshell-0.0.1"
|
||||
VSCODE_THEME_DIR="$VSCODE_EXT_DIR/themes"
|
||||
VSCODE_BASE_THEME="$VSCODE_THEME_DIR/dankshell-color-theme-base.json"
|
||||
VSCODE_FINAL_THEME="$VSCODE_THEME_DIR/dankshell-color-theme.json"
|
||||
|
||||
mkdir -p "$VSCODE_THEME_DIR"
|
||||
|
||||
cp "$SHELL_DIR/matugen/templates/vscode-package.json" "$VSCODE_EXT_DIR/package.json"
|
||||
cp "$SHELL_DIR/matugen/templates/vscode-vsixmanifest.xml" "$VSCODE_EXT_DIR/.vsixmanifest"
|
||||
|
||||
for variant in default dark light; do
|
||||
VSCODE_BASE="$VSCODE_THEME_DIR/dankshell-${variant}-base.json"
|
||||
VSCODE_FINAL="$VSCODE_THEME_DIR/dankshell-${variant}.json"
|
||||
|
||||
if [[ -f "$VSCODE_BASE" ]]; then
|
||||
VARIANT_FLAG=""
|
||||
if [[ "$variant" == "light" ]]; then
|
||||
VARIANT_FLAG="--light"
|
||||
elif [[ "$variant" == "default" && "$mode" == "light" ]]; then
|
||||
VARIANT_FLAG="--light"
|
||||
fi
|
||||
|
||||
dms dank16 "$PRIMARY" $VARIANT_FLAG ${SURFACE:+--background "$SURFACE"} --vscode-enrich "$VSCODE_BASE" > "$VSCODE_FINAL" 2>/dev/null || cp "$VSCODE_BASE" "$VSCODE_FINAL"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if command -v codium >/dev/null 2>&1; then
|
||||
CODIUM_EXT_DIR="$HOME/.vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1"
|
||||
CODIUM_THEME_DIR="$CODIUM_EXT_DIR/themes"
|
||||
CODIUM_BASE_THEME="$CODIUM_THEME_DIR/dankshell-color-theme-base.json"
|
||||
CODIUM_FINAL_THEME="$CODIUM_THEME_DIR/dankshell-color-theme.json"
|
||||
CODIUM_EXTENSIONS_JSON="$HOME/.vscode-oss/extensions/extensions.json"
|
||||
|
||||
mkdir -p "$CODIUM_THEME_DIR"
|
||||
|
||||
cp "$SHELL_DIR/matugen/templates/vscode-package.json" "$CODIUM_EXT_DIR/package.json"
|
||||
cp "$SHELL_DIR/matugen/templates/vscode-vsixmanifest.xml" "$CODIUM_EXT_DIR/.vsixmanifest"
|
||||
|
||||
if [[ -f "$CODIUM_EXTENSIONS_JSON" ]]; then
|
||||
if ! grep -q "local.dynamic-base16-dankshell" "$CODIUM_EXTENSIONS_JSON" 2>/dev/null; then
|
||||
cp "$CODIUM_EXTENSIONS_JSON" "$CODIUM_EXTENSIONS_JSON.backup-$(date +%s)" 2>/dev/null || true
|
||||
|
||||
CODIUM_ENTRY='{"identifier":{"id":"local.dynamic-base16-dankshell"},"version":"0.0.1","location":{"$mid":1,"path":"'"$CODIUM_EXT_DIR"'","scheme":"file"},"relativeLocation":"local.dynamic-base16-dankshell-0.0.1","metadata":{"id":"local.dynamic-base16-dankshell","publisherId":"local","publisherDisplayName":"local","targetPlatform":"undefined","isApplicationScoped":false,"updated":false,"isPreReleaseVersion":false,"installedTimestamp":'"$(date +%s)000"',"preRelease":false}}'
|
||||
|
||||
if [[ "$(cat "$CODIUM_EXTENSIONS_JSON")" == "[]" ]]; then
|
||||
echo "[$CODIUM_ENTRY]" > "$CODIUM_EXTENSIONS_JSON"
|
||||
else
|
||||
TMP_JSON="$(mktemp)"
|
||||
sed 's/]$/,'"$CODIUM_ENTRY"']/' "$CODIUM_EXTENSIONS_JSON" > "$TMP_JSON"
|
||||
mv "$TMP_JSON" "$CODIUM_EXTENSIONS_JSON"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
for variant in default dark light; do
|
||||
CODIUM_BASE="$CODIUM_THEME_DIR/dankshell-${variant}-base.json"
|
||||
CODIUM_FINAL="$CODIUM_THEME_DIR/dankshell-${variant}.json"
|
||||
|
||||
if [[ -f "$CODIUM_BASE" ]]; then
|
||||
VARIANT_FLAG=""
|
||||
if [[ "$variant" == "light" ]]; then
|
||||
VARIANT_FLAG="--light"
|
||||
elif [[ "$variant" == "default" && "$mode" == "light" ]]; then
|
||||
VARIANT_FLAG="--light"
|
||||
fi
|
||||
|
||||
dms dank16 "$PRIMARY" $VARIANT_FLAG ${SURFACE:+--background "$SURFACE"} --vscode-enrich "$CODIUM_BASE" > "$CODIUM_FINAL" 2>/dev/null || cp "$CODIUM_BASE" "$CODIUM_FINAL"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
refresh_gtk "$mode"
|
||||
setup_vscode_extension "code" "$HOME/.vscode/extensions/local.dynamic-base16-dankshell-0.0.1" "$HOME/.vscode"
|
||||
setup_vscode_extension "codium" "$HOME/.vscode-oss/extensions/local.dynamic-base16-dankshell-0.0.1" "$HOME/.vscode-oss"
|
||||
set_system_color_scheme "$mode"
|
||||
signal_terminals
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
while :; do
|
||||
DESIRED="$(read_desired)"
|
||||
WANT_KEY="$(key_of "$DESIRED")"
|
||||
HAVE_KEY=""
|
||||
[[ -f "$BUILT_KEY" ]] && HAVE_KEY="$(cat "$BUILT_KEY" 2>/dev/null || true)"
|
||||
[[ ! -f "$DESIRED_JSON" ]] && { log "No desired state file"; exit 0; }
|
||||
|
||||
if [[ "$WANT_KEY" == "$HAVE_KEY" ]]; then
|
||||
exit 0
|
||||
fi
|
||||
DESIRED=$(cat "$DESIRED_JSON")
|
||||
WANT_KEY=$(compute_key "$DESIRED")
|
||||
HAVE_KEY=""
|
||||
[[ -f "$BUILT_KEY" ]] && HAVE_KEY=$(cat "$BUILT_KEY" 2>/dev/null || true)
|
||||
|
||||
if build_once "$DESIRED"; then
|
||||
echo "$WANT_KEY" > "$BUILT_KEY"
|
||||
else
|
||||
exit 2
|
||||
fi
|
||||
done
|
||||
[[ "$WANT_KEY" == "$HAVE_KEY" ]] && { log "Already up to date"; exit 0; }
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user