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

Compare commits

...

4 Commits

Author SHA1 Message Date
bbedward
e9fa2c78ee greeter: remove variable assignments 2026-03-17 15:28:29 -04:00
purian23
59dae954cd theme(greeter): fix auto theme accent variants & update selections 2026-03-17 13:27:57 -04:00
Youseffo13
5c4ce86da4 Wrapped all missing i18n strings (#2013)
* Feat(i18n): wrapped missing strings

* Feat(i18n): wrapped missing strings

* feat(i18n): added pluralization to some strings

* feat: updated en.json and template.json

* Update en.json

* Update template.json
2026-03-17 12:43:23 -04:00
Maddison Hellstrom
0cf2c40377 feat(color-picker): add openColor IPC handler to set color on open (#2017) 2026-03-17 12:42:59 -04:00
30 changed files with 5258 additions and 6226 deletions

View File

@@ -1497,6 +1497,20 @@ func checkGreeterStatus() error {
}
fmt.Println("\nConfiguration Symlinks:")
colorSyncInfo, colorSyncErr := greeter.ResolveGreeterColorSyncInfo(homeDir)
if colorSyncErr != nil {
fmt.Printf(" ✗ Failed to resolve expected greeter color source: %v\n", colorSyncErr)
allGood = false
colorSyncInfo = greeter.GreeterColorSyncInfo{
SourcePath: filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json"),
}
}
colorThemeDesc := "Color theme"
if colorSyncInfo.UsesDynamicWallpaperOverride {
colorThemeDesc = "Color theme (greeter wallpaper override)"
}
symlinks := []struct {
source string
target string
@@ -1513,9 +1527,9 @@ func checkGreeterStatus() error {
desc: "Session state",
},
{
source: filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json"),
source: colorSyncInfo.SourcePath,
target: filepath.Join(cacheDir, "colors.json"),
desc: "Color theme",
desc: colorThemeDesc,
},
}
@@ -1557,6 +1571,10 @@ func checkGreeterStatus() error {
fmt.Printf(" ✓ %s: synced correctly\n", link.desc)
}
if colorSyncInfo.UsesDynamicWallpaperOverride {
fmt.Printf(" Dynamic theme uses greeter override colors from %s\n", colorSyncInfo.SourcePath)
}
fmt.Println("\nGreeter Wallpaper Override:")
overridePath := filepath.Join(cacheDir, "greeter_wallpaper_override.jpg")
if stat, err := os.Stat(overridePath); err == nil && !stat.IsDir() {

View File

@@ -5,6 +5,7 @@ import (
"context"
_ "embed"
"encoding/json"
"errors"
"fmt"
"os"
"os/exec"
@@ -14,6 +15,7 @@ import (
"github.com/AvengeMedia/DankMaterialShell/core/internal/config"
"github.com/AvengeMedia/DankMaterialShell/core/internal/distros"
"github.com/AvengeMedia/DankMaterialShell/core/internal/matugen"
"github.com/AvengeMedia/DankMaterialShell/core/internal/utils"
"github.com/sblinch/kdl-go"
"github.com/sblinch/kdl-go/document"
@@ -1074,6 +1076,7 @@ func SetupDMSGroup(logFunc func(string), sudoPassword string) error {
}{
{filepath.Join(homeDir, ".config", "DankMaterialShell"), "DankMaterialShell config"},
{filepath.Join(homeDir, ".local", "state", "DankMaterialShell"), "DankMaterialShell state"},
{filepath.Join(homeDir, ".cache", "DankMaterialShell"), "DankMaterialShell cache"},
{filepath.Join(homeDir, ".cache", "quickshell"), "quickshell cache"},
{filepath.Join(homeDir, ".config", "quickshell"), "quickshell config"},
{filepath.Join(homeDir, ".local", "share", "wayland-sessions"), "wayland sessions"},
@@ -1108,6 +1111,217 @@ func SetupDMSGroup(logFunc func(string), sudoPassword string) error {
return nil
}
type GreeterColorSyncInfo struct {
SourcePath string
ThemeName string
UsesDynamicWallpaperOverride bool
}
type greeterThemeSyncSettings struct {
CurrentThemeName string `json:"currentThemeName"`
GreeterWallpaperPath string `json:"greeterWallpaperPath"`
MatugenScheme string `json:"matugenScheme"`
IconTheme string `json:"iconTheme"`
}
type greeterThemeSyncSession struct {
IsLightMode bool `json:"isLightMode"`
}
type greeterThemeSyncState struct {
ThemeName string
GreeterWallpaperPath string
ResolvedGreeterWallpaperPath string
MatugenScheme string
IconTheme string
IsLightMode bool
UsesDynamicWallpaperOverride bool
}
func defaultGreeterColorsSource(homeDir string) string {
return filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json")
}
func greeterOverrideColorsStateDir(homeDir string) string {
return filepath.Join(homeDir, ".cache", "DankMaterialShell", "greeter-colors")
}
func greeterOverrideColorsSource(homeDir string) string {
return filepath.Join(greeterOverrideColorsStateDir(homeDir), "dms-colors.json")
}
func readOptionalJSONFile(path string, dst any) error {
data, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
if strings.TrimSpace(string(data)) == "" {
return nil
}
return json.Unmarshal(data, dst)
}
func readGreeterThemeSyncSettings(homeDir string) (greeterThemeSyncSettings, error) {
settings := greeterThemeSyncSettings{
CurrentThemeName: "purple",
MatugenScheme: "scheme-tonal-spot",
IconTheme: "System Default",
}
settingsPath := filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json")
if err := readOptionalJSONFile(settingsPath, &settings); err != nil {
return greeterThemeSyncSettings{}, fmt.Errorf("failed to parse settings at %s: %w", settingsPath, err)
}
return settings, nil
}
func readGreeterThemeSyncSession(homeDir string) (greeterThemeSyncSession, error) {
session := greeterThemeSyncSession{}
sessionPath := filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json")
if err := readOptionalJSONFile(sessionPath, &session); err != nil {
return greeterThemeSyncSession{}, fmt.Errorf("failed to parse session at %s: %w", sessionPath, err)
}
return session, nil
}
func resolveGreeterThemeSyncState(homeDir string) (greeterThemeSyncState, error) {
settings, err := readGreeterThemeSyncSettings(homeDir)
if err != nil {
return greeterThemeSyncState{}, err
}
session, err := readGreeterThemeSyncSession(homeDir)
if err != nil {
return greeterThemeSyncState{}, err
}
resolvedWallpaperPath := ""
if settings.GreeterWallpaperPath != "" {
resolvedWallpaperPath = settings.GreeterWallpaperPath
if !filepath.IsAbs(resolvedWallpaperPath) {
resolvedWallpaperPath = filepath.Join(homeDir, resolvedWallpaperPath)
}
}
usesDynamicWallpaperOverride := strings.EqualFold(strings.TrimSpace(settings.CurrentThemeName), "dynamic") && resolvedWallpaperPath != ""
return greeterThemeSyncState{
ThemeName: settings.CurrentThemeName,
GreeterWallpaperPath: settings.GreeterWallpaperPath,
ResolvedGreeterWallpaperPath: resolvedWallpaperPath,
MatugenScheme: settings.MatugenScheme,
IconTheme: settings.IconTheme,
IsLightMode: session.IsLightMode,
UsesDynamicWallpaperOverride: usesDynamicWallpaperOverride,
}, nil
}
func (s greeterThemeSyncState) effectiveColorsSource(homeDir string) string {
if s.UsesDynamicWallpaperOverride {
return greeterOverrideColorsSource(homeDir)
}
return defaultGreeterColorsSource(homeDir)
}
func ResolveGreeterColorSyncInfo(homeDir string) (GreeterColorSyncInfo, error) {
state, err := resolveGreeterThemeSyncState(homeDir)
if err != nil {
return GreeterColorSyncInfo{}, err
}
return GreeterColorSyncInfo{
SourcePath: state.effectiveColorsSource(homeDir),
ThemeName: state.ThemeName,
UsesDynamicWallpaperOverride: state.UsesDynamicWallpaperOverride,
}, nil
}
func ensureGreeterSyncSourceFile(path string) error {
sourceDir := filepath.Dir(path)
if err := os.MkdirAll(sourceDir, 0o755); err != nil {
return fmt.Errorf("failed to create source directory %s: %w", sourceDir, err)
}
if _, err := os.Stat(path); os.IsNotExist(err) {
if err := os.WriteFile(path, []byte("{}"), 0o644); err != nil {
return fmt.Errorf("failed to create source file %s: %w", path, err)
}
} else if err != nil {
return fmt.Errorf("failed to inspect source file %s: %w", path, err)
}
return nil
}
func syncGreeterDynamicOverrideColors(dmsPath, homeDir string, state greeterThemeSyncState, logFunc func(string)) error {
if !state.UsesDynamicWallpaperOverride {
return nil
}
st, err := os.Stat(state.ResolvedGreeterWallpaperPath)
if err != nil {
return fmt.Errorf("configured greeter wallpaper not found at %s: %w", state.ResolvedGreeterWallpaperPath, err)
}
if st.IsDir() {
return fmt.Errorf("configured greeter wallpaper path points to a directory: %s", state.ResolvedGreeterWallpaperPath)
}
mode := matugen.ColorModeDark
if state.IsLightMode {
mode = matugen.ColorModeLight
}
opts := matugen.Options{
StateDir: greeterOverrideColorsStateDir(homeDir),
ShellDir: dmsPath,
ConfigDir: filepath.Join(homeDir, ".config"),
Kind: "image",
Value: state.ResolvedGreeterWallpaperPath,
Mode: mode,
IconTheme: state.IconTheme,
MatugenType: state.MatugenScheme,
RunUserTemplates: false,
ColorsOnly: true,
}
err = matugen.Run(opts)
switch {
case errors.Is(err, matugen.ErrNoChanges):
logFunc("✓ Greeter dynamic override colors already up to date")
return nil
case err != nil:
return fmt.Errorf("failed to generate greeter dynamic colors from wallpaper override: %w", err)
default:
logFunc("✓ Generated greeter dynamic colors from wallpaper override")
return nil
}
}
func syncGreeterColorSource(homeDir, cacheDir string, state greeterThemeSyncState, logFunc func(string), sudoPassword string) error {
source := state.effectiveColorsSource(homeDir)
if !state.UsesDynamicWallpaperOverride {
if err := ensureGreeterSyncSourceFile(source); err != nil {
return err
}
} else if _, err := os.Stat(source); err != nil {
return fmt.Errorf("expected generated greeter colors at %s: %w", source, err)
}
target := filepath.Join(cacheDir, "colors.json")
_ = runSudoCmd(sudoPassword, "rm", "-f", target)
if err := runSudoCmd(sudoPassword, "ln", "-sf", source, target); err != nil {
return fmt.Errorf("failed to create symlink for wallpaper based theming (%s -> %s): %w", target, source, err)
}
if state.UsesDynamicWallpaperOverride {
logFunc("✓ Synced wallpaper based theming (greeter wallpaper override)")
} else {
logFunc("✓ Synced wallpaper based theming")
}
return nil
}
func SyncDMSConfigs(dmsPath, compositor string, logFunc func(string), sudoPassword string, forceAuth bool) error {
homeDir, err := os.UserHomeDir()
if err != nil {
@@ -1131,11 +1345,6 @@ func SyncDMSConfigs(dmsPath, compositor string, logFunc func(string), sudoPasswo
target: filepath.Join(cacheDir, "session.json"),
desc: "state (wallpaper configuration)",
},
{
source: filepath.Join(homeDir, ".cache", "DankMaterialShell", "dms-colors.json"),
target: filepath.Join(cacheDir, "colors.json"),
desc: "wallpaper based theming",
},
}
for _, link := range symlinks {
@@ -1161,7 +1370,20 @@ func SyncDMSConfigs(dmsPath, compositor string, logFunc func(string), sudoPasswo
logFunc(fmt.Sprintf("✓ Synced %s", link.desc))
}
if err := syncGreeterWallpaperOverride(homeDir, cacheDir, logFunc, sudoPassword); err != nil {
state, err := resolveGreeterThemeSyncState(homeDir)
if err != nil {
return fmt.Errorf("failed to resolve greeter color source: %w", err)
}
if err := syncGreeterDynamicOverrideColors(dmsPath, homeDir, state, logFunc); err != nil {
return err
}
if err := syncGreeterColorSource(homeDir, cacheDir, state, logFunc, sudoPassword); err != nil {
return err
}
if err := syncGreeterWallpaperOverride(cacheDir, logFunc, sudoPassword, state); err != nil {
return fmt.Errorf("greeter wallpaper override sync failed: %w", err)
}
@@ -1180,23 +1402,9 @@ func SyncDMSConfigs(dmsPath, compositor string, logFunc func(string), sudoPasswo
return nil
}
func syncGreeterWallpaperOverride(homeDir, cacheDir string, logFunc func(string), sudoPassword string) error {
settingsPath := filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json")
data, err := os.ReadFile(settingsPath)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return fmt.Errorf("failed to read settings at %s: %w", settingsPath, err)
}
var settings struct {
GreeterWallpaperPath string `json:"greeterWallpaperPath"`
}
if err := json.Unmarshal(data, &settings); err != nil {
return fmt.Errorf("failed to parse settings at %s: %w", settingsPath, err)
}
func syncGreeterWallpaperOverride(cacheDir string, logFunc func(string), sudoPassword string, state greeterThemeSyncState) error {
destPath := filepath.Join(cacheDir, "greeter_wallpaper_override.jpg")
if settings.GreeterWallpaperPath == "" {
if state.ResolvedGreeterWallpaperPath == "" {
if err := runSudoCmd(sudoPassword, "rm", "-f", destPath); err != nil {
return fmt.Errorf("failed to clear override file %s: %w", destPath, err)
}
@@ -1206,10 +1414,7 @@ func syncGreeterWallpaperOverride(homeDir, cacheDir string, logFunc func(string)
if err := runSudoCmd(sudoPassword, "rm", "-f", destPath); err != nil {
return fmt.Errorf("failed to remove old override file %s: %w", destPath, err)
}
src := settings.GreeterWallpaperPath
if !filepath.IsAbs(src) {
src = filepath.Join(homeDir, src)
}
src := state.ResolvedGreeterWallpaperPath
st, err := os.Stat(src)
if err != nil {
return fmt.Errorf("configured greeter wallpaper not found at %s: %w", src, err)

View File

@@ -0,0 +1,98 @@
package greeter
import (
"os"
"path/filepath"
"testing"
)
func writeTestJSON(t *testing.T, path string, content string) {
t.Helper()
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
t.Fatalf("failed to create parent dir for %s: %v", path, err)
}
if err := os.WriteFile(path, []byte(content), 0o644); err != nil {
t.Fatalf("failed to write %s: %v", path, err)
}
}
func TestResolveGreeterThemeSyncState(t *testing.T) {
t.Parallel()
tests := []struct {
name string
settingsJSON string
sessionJSON string
wantSourcePath string
wantResolvedWallpaper string
wantDynamicOverrideUsed bool
}{
{
name: "dynamic theme with greeter wallpaper override uses generated greeter colors",
settingsJSON: `{
"currentThemeName": "dynamic",
"greeterWallpaperPath": "Pictures/blue.jpg",
"matugenScheme": "scheme-tonal-spot",
"iconTheme": "Papirus"
}`,
sessionJSON: `{"isLightMode":true}`,
wantSourcePath: filepath.Join(".cache", "DankMaterialShell", "greeter-colors", "dms-colors.json"),
wantResolvedWallpaper: filepath.Join("Pictures", "blue.jpg"),
wantDynamicOverrideUsed: true,
},
{
name: "dynamic theme without override uses desktop colors",
settingsJSON: `{
"currentThemeName": "dynamic",
"greeterWallpaperPath": ""
}`,
sessionJSON: `{"isLightMode":false}`,
wantSourcePath: filepath.Join(".cache", "DankMaterialShell", "dms-colors.json"),
wantResolvedWallpaper: "",
wantDynamicOverrideUsed: false,
},
{
name: "non-dynamic theme keeps desktop colors even with override wallpaper",
settingsJSON: `{
"currentThemeName": "purple",
"greeterWallpaperPath": "/tmp/blue.jpg"
}`,
sessionJSON: `{"isLightMode":false}`,
wantSourcePath: filepath.Join(".cache", "DankMaterialShell", "dms-colors.json"),
wantResolvedWallpaper: "/tmp/blue.jpg",
wantDynamicOverrideUsed: false,
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
homeDir := t.TempDir()
writeTestJSON(t, filepath.Join(homeDir, ".config", "DankMaterialShell", "settings.json"), tt.settingsJSON)
writeTestJSON(t, filepath.Join(homeDir, ".local", "state", "DankMaterialShell", "session.json"), tt.sessionJSON)
state, err := resolveGreeterThemeSyncState(homeDir)
if err != nil {
t.Fatalf("resolveGreeterThemeSyncState returned error: %v", err)
}
if got := state.effectiveColorsSource(homeDir); got != filepath.Join(homeDir, tt.wantSourcePath) {
t.Fatalf("effectiveColorsSource = %q, want %q", got, filepath.Join(homeDir, tt.wantSourcePath))
}
wantResolvedWallpaper := tt.wantResolvedWallpaper
if wantResolvedWallpaper != "" && !filepath.IsAbs(wantResolvedWallpaper) {
wantResolvedWallpaper = filepath.Join(homeDir, wantResolvedWallpaper)
}
if state.ResolvedGreeterWallpaperPath != wantResolvedWallpaper {
t.Fatalf("ResolvedGreeterWallpaperPath = %q, want %q", state.ResolvedGreeterWallpaperPath, wantResolvedWallpaper)
}
if state.UsesDynamicWallpaperOverride != tt.wantDynamicOverrideUsed {
t.Fatalf("UsesDynamicWallpaperOverride = %v, want %v", state.UsesDynamicWallpaperOverride, tt.wantDynamicOverrideUsed)
}
})
}
}

View File

@@ -100,6 +100,7 @@ type Options struct {
IconTheme string
MatugenType string
RunUserTemplates bool
ColorsOnly bool
StockColors string
SyncModeWithPortal bool
TerminalsAlwaysDark bool
@@ -274,6 +275,10 @@ func buildOnce(opts *Options) (bool, error) {
return false, nil
}
if opts.ColorsOnly {
return true, nil
}
if isDMSGTKActive(opts.ConfigDir) {
switch opts.Mode {
case ColorModeLight:
@@ -331,6 +336,10 @@ output_path = '%s'
`, opts.ShellDir, opts.ColorsOutput())
if opts.ColorsOnly {
return nil
}
homeDir, _ := os.UserHomeDir()
for _, tmpl := range templateRegistry {
if opts.ShouldSkipTemplate(tmpl.ID) {
@@ -597,10 +606,10 @@ func detectMatugenVersionLocked() (matugenFlags, error) {
matugenVersionOK = true
if matugenSupportsCOE {
log.Infof("Matugen %s supports --continue-on-error", versionStr)
log.Debugf("Matugen %s detected: continue-on-error support enabled", versionStr)
}
if matugenIsV4 {
log.Infof("Matugen %s: using v4 flags", versionStr)
log.Debugf("Matugen %s detected: using v4 compatibility flags", versionStr)
}
return matugenFlags{matugenSupportsCOE, matugenIsV4}, nil
}

View File

@@ -3,6 +3,7 @@ package matugen
import (
"os"
"path/filepath"
"strings"
"testing"
mocks_utils "github.com/AvengeMedia/DankMaterialShell/core/internal/mocks/utils"
@@ -392,3 +393,51 @@ func TestSubstituteVars(t *testing.T) {
})
}
}
func TestBuildMergedConfigColorsOnly(t *testing.T) {
tempDir := t.TempDir()
shellDir := filepath.Join(tempDir, "shell")
configsDir := filepath.Join(shellDir, "matugen", "configs")
if err := os.MkdirAll(configsDir, 0o755); err != nil {
t.Fatalf("failed to create configs dir: %v", err)
}
baseConfig := "[config]\ncustom_keywords = []\n"
if err := os.WriteFile(filepath.Join(configsDir, "base.toml"), []byte(baseConfig), 0o644); err != nil {
t.Fatalf("failed to write base config: %v", err)
}
cfgFile, err := os.CreateTemp(tempDir, "merged-*.toml")
if err != nil {
t.Fatalf("failed to create temp config: %v", err)
}
defer os.Remove(cfgFile.Name())
defer cfgFile.Close()
opts := &Options{
ShellDir: shellDir,
ConfigDir: filepath.Join(tempDir, "config"),
StateDir: filepath.Join(tempDir, "state"),
ColorsOnly: true,
}
if err := buildMergedConfig(opts, cfgFile, filepath.Join(tempDir, "templates")); err != nil {
t.Fatalf("buildMergedConfig failed: %v", err)
}
if err := cfgFile.Close(); err != nil {
t.Fatalf("failed to close merged config: %v", err)
}
output, err := os.ReadFile(cfgFile.Name())
if err != nil {
t.Fatalf("failed to read merged config: %v", err)
}
content := string(output)
assert.Contains(t, content, "[templates.dank]")
assert.Contains(t, content, "output_path = '"+filepath.Join(opts.StateDir, "dms-colors.json")+"'")
assert.NotContains(t, content, "[templates.gtk]")
assert.False(t, strings.Contains(content, "output_path = 'CONFIG_DIR/"), "colors-only config should not emit app template outputs")
}

View File

@@ -538,6 +538,8 @@ Color picker modal control.
**Functions:**
- `open` - Show color picker modal
- `openColor <color>` - Show color picker modal with a pre-selected color
- Parameters: `color` - Color string (e.g. "#ff0000", "#3f51b5")
- `close` - Hide color picker modal
- `closeInstant` - Hide color picker modal without animation
- `toggle` - Toggle color picker modal visibility

View File

@@ -1278,7 +1278,10 @@ Singleton {
}
if (themeData.variants.options && themeData.variants.options.length > 0) {
const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, themeData.variants.default) : themeData.variants.default;
const isGreeterMode = typeof SessionData !== "undefined" && SessionData.isGreeterMode;
const selectedVariantId = isGreeterMode
? (typeof GreetdSettings.registryThemeVariants[themeId] === "string" ? GreetdSettings.registryThemeVariants[themeId] : themeData.variants.default)
: (typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, themeData.variants.default) : themeData.variants.default);
const variant = findVariant(themeData.variants.options, selectedVariantId);
if (variant) {
const variantColors = variant[colorMode] || variant.dark || variant.light || {};
@@ -1650,8 +1653,13 @@ Singleton {
const defaults = customThemeRawData.variants.defaults || {};
const darkDefaults = defaults.dark || {};
const lightDefaults = defaults.light || defaults.dark || {};
const storedDark = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, darkDefaults, "dark") : darkDefaults;
const storedLight = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, lightDefaults, "light") : lightDefaults;
const isGreeterMode = typeof SessionData !== "undefined" && SessionData.isGreeterMode;
const storedDark = isGreeterMode
? (GreetdSettings.registryThemeVariants[themeId]?.dark || darkDefaults)
: (typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, darkDefaults, "dark") : darkDefaults);
const storedLight = isGreeterMode
? (GreetdSettings.registryThemeVariants[themeId]?.light || lightDefaults)
: (typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeMultiVariant(themeId, lightDefaults, "light") : lightDefaults);
const darkFlavorId = storedDark.flavor || darkDefaults.flavor || "";
const lightFlavorId = storedLight.flavor || lightDefaults.flavor || "";
const accentId = storedDark.accent || darkDefaults.accent || "";
@@ -1669,7 +1677,10 @@ Singleton {
lightTheme = mergeColors(lightTheme, accent[lightFlavor.id] || {});
}
} else if (customThemeRawData.variants.options) {
const selectedVariantId = typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, customThemeRawData.variants.default) : customThemeRawData.variants.default;
const isGreeterMode = typeof SessionData !== "undefined" && SessionData.isGreeterMode;
const selectedVariantId = isGreeterMode
? (typeof GreetdSettings.registryThemeVariants[themeId] === "string" ? GreetdSettings.registryThemeVariants[themeId] : customThemeRawData.variants.default)
: (typeof SettingsData !== "undefined" ? SettingsData.getRegistryThemeVariant(themeId, customThemeRawData.variants.default) : customThemeRawData.variants.default);
const variant = findVariant(customThemeRawData.variants.options, selectedVariantId);
if (variant) {
darkTheme = mergeColors(darkTheme, variant.dark || {});
@@ -1997,6 +2008,7 @@ Singleton {
const colorsPath = SessionData.isGreeterMode ? greetCfgDir + "/colors.json" : stateDir + "/dms-colors.json";
return colorsPath;
}
blockLoading: false
watchChanges: !SessionData.isGreeterMode
function parseAndLoadColors() {

View File

@@ -26,7 +26,9 @@ Rectangle {
spacing: 2
StyledText {
text: keyboardHints.enterToPaste ? I18n.tr("↑/↓: Navigate • Enter: Paste • Del: Delete • F10: Help", "Keyboard hints when enter-to-paste is enabled") : "↑/↓: Navigate • Enter/Ctrl+C: Copy • Del: Delete • F10: Help"
text: keyboardHints.enterToPaste
? I18n.tr("↑/↓: Navigate • Enter: Paste • Del: Delete • F10: Help", "Keyboard hints when enter-to-paste is enabled")
: I18n.tr("↑/↓: Navigate • Enter/Ctrl+C: Copy • Del: Delete • F10: Help")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter

View File

@@ -147,6 +147,13 @@ DankModal {
return "COLOR_PICKER_MODAL_OPEN_SUCCESS";
}
function openColor(color: string): string {
root.selectedColor = Qt.color(color);
root.currentColor = Qt.color(color);
root.updateFromColor(Qt.color(color));
return open();
}
function close(): string {
root.hide();
return "COLOR_PICKER_MODAL_CLOSE_SUCCESS";

View File

@@ -304,7 +304,17 @@ DankModal {
anchors.right: parent.right
anchors.rightMargin: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
text: I18n.tr("%1 active, %2 filtered").arg(MuxService.sessions.length).arg(muxModal.filteredSessions.length)
text: {
const total = MuxService.sessions.length;
const filtered = muxModal.filteredSessions.length;
const activePart = total === 1
? I18n.tr("%1 active session").arg(total)
: I18n.tr("%1 active sessions").arg(total);
const filteredPart = filtered === 1
? I18n.tr("%1 filtered").arg(filtered)
: I18n.tr("%1 filtered").arg(filtered);
return activePart + ", " + filteredPart;
}
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
}
@@ -468,7 +478,9 @@ DankModal {
text: {
var parts = []
if (modelData.windows !== "N/A")
parts.push(I18n.tr("%1 windows").arg(modelData.windows))
parts.push(modelData.windows === 1
? I18n.tr("%1 window").arg(modelData.windows)
: I18n.tr("%1 windows").arg(modelData.windows))
parts.push(modelData.attached ? I18n.tr("attached") : I18n.tr("detached"))
return parts.join(" \u2022 ")
}

View File

@@ -66,7 +66,7 @@ DankModal {
}
StyledText {
text: `Details for "${networkSSID}"`
text: I18n.tr("Details for \"%1\"").arg(networkSSID)
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceTextMedium
width: parent.width
@@ -102,7 +102,7 @@ DankModal {
id: detailsText
width: parent.width
text: NetworkService.networkInfoDetails && NetworkService.networkInfoDetails.replace(/\\n/g, '\n') || "No information available"
text: NetworkService.networkInfoDetails && NetworkService.networkInfoDetails.replace(/\\n/g, '\n') || I18n.tr("No information available")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
wrapMode: Text.WordWrap

View File

@@ -66,7 +66,7 @@ DankModal {
}
StyledText {
text: `Details for "${networkID}"`
text: I18n.tr("Details for \"%1\"").arg(networkSSID)
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceTextMedium
width: parent.width
@@ -102,7 +102,7 @@ DankModal {
id: detailsText
width: parent.width
text: NetworkService.networkWiredInfoDetails && NetworkService.networkWiredInfoDetails.replace(/\\n/g, '\n') || "No information available"
text: NetworkService.networkWiredInfoDetails && NetworkService.networkWiredInfoDetails.replace(/\\n/g, '\n') || I18n.tr("No information available")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
wrapMode: Text.WordWrap

View File

@@ -22,12 +22,12 @@ DankPopout {
function setProfile(profile) {
if (typeof PowerProfiles === "undefined") {
ToastService.showError("power-profiles-daemon not available");
ToastService.showError(I18n.tr("power-profiles-daemon not available"));
return;
}
PowerProfiles.profile = profile;
if (PowerProfiles.profile !== profile) {
ToastService.showError("Failed to set power profile");
ToastService.showError(I18n.tr("Failed to set power profile"));
}
}
@@ -173,10 +173,10 @@ DankPopout {
width: parent.width - Theme.iconSizeLarge - 32 - Theme.spacingM * 2
readonly property string timeInfoText: {
if (!BatteryService.batteryAvailable)
return "Power profile management available";
return I18n.tr("Power profile management available");
const time = BatteryService.formatTimeRemaining();
if (time !== "Unknown") {
return BatteryService.isCharging ? `Time until full: ${time}` : `Time remaining: ${time}`;
return BatteryService.isCharging ? I18n.tr("Time until full: %1").arg(time) : I18n.tr("Time remaining: %1").arg(time);
}
return "";
}
@@ -188,7 +188,7 @@ DankPopout {
spacing: Theme.spacingS
StyledText {
text: BatteryService.batteryAvailable ? `${BatteryService.batteryLevel}%` : "Power"
text: BatteryService.batteryAvailable ? `${BatteryService.batteryLevel}%` : I18n.tr("Power")
font.pixelSize: Theme.fontSizeXLarge
color: {
if (BatteryService.isLowBattery && !BatteryService.isCharging) {
@@ -338,7 +338,7 @@ DankPopout {
}
StyledText {
text: BatteryService.batteryCapacity > 0 ? `${BatteryService.batteryCapacity.toFixed(1)} Wh` : "Unknown"
text: BatteryService.batteryCapacity > 0 ? `${BatteryService.batteryCapacity.toFixed(1)} Wh` : I18n.tr("Unknown")
font.pixelSize: Theme.fontSizeLarge
color: Theme.surfaceText
font.weight: Font.Bold
@@ -393,7 +393,7 @@ DankPopout {
width: parent.width - percentText.width - chargingIcon.width - Theme.spacingM * 2
StyledText {
text: modelData.model || `Battery ${index + 1}`
text: modelData.model || I18n.tr("Battery %1").arg(index + 1)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
font.weight: Font.Medium

View File

@@ -320,7 +320,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
StyledText {
text: activePlayer?.trackTitle || "Unknown Track"
text: activePlayer?.trackTitle || I18n.tr("Unknown Track")
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Bold
color: Theme.surfaceText
@@ -332,7 +332,7 @@ Item {
}
StyledText {
text: activePlayer?.trackArtist || "Unknown Artist"
text: activePlayer?.trackTitle || I18n.tr("Unknown Artist")
font.pixelSize: Theme.fontSizeMedium
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.8)
width: parent.width
@@ -669,7 +669,7 @@ Item {
const screenY = popoutY + contentOffsetY + btnY;
showPlayersDropdown(Qt.point(screenX, screenY), targetScreen, buttonsOnRight, activePlayer, allPlayers);
}
onEntered: sharedTooltip.show("Media Players", playerSelectorButton, 0, 0, isRightEdge ? "right" : "left")
onEntered: sharedTooltip.show(I18n.tr("Media Players"), playerSelectorButton, 0, 0, isRightEdge ? "right" : "left")
onExited: sharedTooltip.hide()
}
}
@@ -788,7 +788,7 @@ Item {
const screenY = popoutY + contentOffsetY + btnY;
showAudioDevicesDropdown(Qt.point(screenX, screenY), targetScreen, buttonsOnRight);
}
onEntered: sharedTooltip.show("Output Device", audioDevicesButton, 0, 0, isRightEdge ? "right" : "left")
onEntered: sharedTooltip.show(I18n.tr("Output Device"), audioDevicesButton, 0, 0, isRightEdge ? "right" : "left")
onExited: sharedTooltip.hide()
}
}

View File

@@ -88,7 +88,7 @@ Card {
topPadding: Theme.spacingL
StyledText {
text: activePlayer?.trackTitle || "Unknown"
text: activePlayer?.trackTitle || I18n.tr("Unknown")
font.pixelSize: Theme.fontSizeSmall
font.weight: Font.Medium
color: Theme.surfaceText
@@ -99,7 +99,7 @@ Card {
}
StyledText {
text: activePlayer?.trackArtist || "Unknown Artist"
text: activePlayer?.trackArtist || I18n.tr("Unknown Artist")
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
width: parent.width

View File

@@ -28,7 +28,7 @@ Card {
}
StyledText {
text: WeatherService.weather.loading ? "Loading..." : "No Weather"
text: WeatherService.weather.loading ? I18n.tr("Loading...") : I18n.tr("No Weather")
font.pixelSize: Theme.fontSizeSmall
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.7)
anchors.horizontalCenter: parent.horizontalCenter

View File

@@ -538,7 +538,11 @@ Item {
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: wallpaperFolderModel.count > 0 ? `${wallpaperFolderModel.count} wallpapers ${currentPage + 1} / ${totalPages}` : "No wallpapers"
text: wallpaperFolderModel.count > 0
? (wallpaperFolderModel.count === 1
? I18n.tr("%1 wallpaper • %2 / %3").arg(wallpaperFolderModel.count).arg(currentPage + 1).arg(totalPages)
: I18n.tr("%1 wallpapers • %2 / %3").arg(wallpaperFolderModel.count).arg(currentPage + 1).arg(totalPages))
: I18n.tr("No wallpapers")
font.pixelSize: 14
color: Theme.surfaceText
opacity: 0.7

View File

@@ -230,19 +230,19 @@ Item {
function currentAuthMessage() {
if (GreeterState.pamState === "error")
return "Authentication error - try again";
return I18n.tr("Authentication error - try again");
if (GreeterState.pamState === "max")
return "Too many failed attempts - account may be locked";
return I18n.tr("Too many failed attempts - account may be locked");
if (GreeterState.pamState === "fail") {
if (passwordAttemptLimitHint > 0) {
const attempt = Math.max(1, Math.min(passwordFailureCount, passwordAttemptLimitHint));
const remaining = Math.max(passwordAttemptLimitHint - attempt, 0);
if (remaining > 0) {
return "Incorrect password - attempt " + attempt + " of " + passwordAttemptLimitHint + " (lockout may follow)";
return I18n.tr("Incorrect password - attempt %1 of %2 (lockout may follow)").arg(attempt).arg(passwordAttemptLimitHint);
}
return "Incorrect password - next failures may trigger account lockout";
return I18n.tr("Incorrect password - next failures may trigger account lockout");
}
return "Incorrect password";
return I18n.tr("Incorrect password");
}
return "";
}
@@ -1012,15 +1012,15 @@ Item {
anchors.verticalCenter: parent.verticalCenter
text: {
if (GreeterState.unlocking) {
return "Logging in...";
return I18n.tr("Logging in...");
}
if (Greetd.state !== GreetdState.Inactive && !awaitingExternalAuth && !pendingPasswordResponse) {
return "Authenticating...";
return I18n.tr("Authenticating...");
}
if (GreeterState.showPasswordInput) {
return "Password...";
return I18n.tr("Password...");
}
return "Username...";
return I18n.tr("Username...");
}
color: (GreeterState.unlocking || (Greetd.state !== GreetdState.Inactive && !awaitingExternalAuth && !pendingPasswordResponse)) ? Theme.primary : Theme.outline
font.pixelSize: Theme.fontSizeMedium

View File

@@ -271,10 +271,6 @@ hotkey-overlay {
environment {
DMS_RUN_GREETER "1"
HOME "$CACHE_DIR"
XDG_STATE_HOME "$CACHE_DIR/.local/state"
XDG_DATA_HOME "$CACHE_DIR/.local/share"
XDG_CACHE_HOME "$CACHE_DIR/.cache"
}
debug {
@@ -328,16 +324,11 @@ NIRI_EOF
TEMP_CONFIG=$(mktemp)
cat > "$TEMP_CONFIG" << HYPRLAND_EOF
env = DMS_RUN_GREETER,1
env = HOME,$CACHE_DIR
env = XDG_STATE_HOME,$CACHE_DIR/.local/state
env = XDG_DATA_HOME,$CACHE_DIR/.local/share
env = XDG_CACHE_HOME,$CACHE_DIR/.cache
misc {
disable_hyprland_logo = true
}
exec-once = systemctl --user import-environment HOME XDG_STATE_HOME XDG_DATA_HOME XDG_CACHE_HOME 2>/dev/null || true
exec-once = sh -c "$QS_CMD; hyprctl dispatch exit"
HYPRLAND_EOF
COMPOSITOR_CONFIG="$TEMP_CONFIG"
@@ -346,11 +337,6 @@ HYPRLAND_EOF
cat "$COMPOSITOR_CONFIG" > "$TEMP_CONFIG"
cat >> "$TEMP_CONFIG" << HYPRLAND_EOF
env = HOME,$CACHE_DIR
env = XDG_STATE_HOME,$CACHE_DIR/.local/state
env = XDG_DATA_HOME,$CACHE_DIR/.local/share
env = XDG_CACHE_HOME,$CACHE_DIR/.cache
exec-once = systemctl --user import-environment HOME XDG_STATE_HOME XDG_DATA_HOME XDG_CACHE_HOME 2>/dev/null || true
exec-once = sh -c "$QS_CMD; hyprctl dispatch exit"
HYPRLAND_EOF
COMPOSITOR_CONFIG="$TEMP_CONFIG"
@@ -367,7 +353,6 @@ HYPRLAND_EOF
if [[ -z "$COMPOSITOR_CONFIG" ]]; then
TEMP_CONFIG=$(mktemp)
cat > "$TEMP_CONFIG" << SWAY_EOF
exec --no-startup-id dbus-update-activation-environment --systemd HOME XDG_STATE_HOME XDG_DATA_HOME XDG_CACHE_HOME
exec "$QS_CMD; swaymsg exit"
SWAY_EOF
COMPOSITOR_CONFIG="$TEMP_CONFIG"
@@ -376,7 +361,6 @@ SWAY_EOF
cat "$COMPOSITOR_CONFIG" > "$TEMP_CONFIG"
cat >> "$TEMP_CONFIG" << SWAY_EOF
exec --no-startup-id dbus-update-activation-environment --systemd HOME XDG_STATE_HOME XDG_DATA_HOME XDG_CACHE_HOME
exec "$QS_CMD; swaymsg exit"
SWAY_EOF
COMPOSITOR_CONFIG="$TEMP_CONFIG"
@@ -389,7 +373,6 @@ SWAY_EOF
if [[ -z "$COMPOSITOR_CONFIG" ]]; then
TEMP_CONFIG=$(mktemp)
cat > "$TEMP_CONFIG" << SCROLL_EOF
exec --no-startup-id dbus-update-activation-environment --systemd HOME XDG_STATE_HOME XDG_DATA_HOME XDG_CACHE_HOME
exec "$QS_CMD; scrollmsg exit"
SCROLL_EOF
COMPOSITOR_CONFIG="$TEMP_CONFIG"
@@ -398,7 +381,6 @@ SCROLL_EOF
cat "$COMPOSITOR_CONFIG" > "$TEMP_CONFIG"
cat >> "$TEMP_CONFIG" << SCROLL_EOF
exec --no-startup-id dbus-update-activation-environment --systemd HOME XDG_STATE_HOME XDG_DATA_HOME XDG_CACHE_HOME
exec "$QS_CMD; scrollmsg exit"
SCROLL_EOF
COMPOSITOR_CONFIG="$TEMP_CONFIG"

View File

@@ -832,13 +832,21 @@ Column {
spacing: Theme.spacingL
StyledText {
text: textArea.text.length > 0 ? I18n.tr("%1 characters").arg(textArea.text.length) : I18n.tr("Empty")
text: {
const len = textArea.text.length;
if (len === 0) return I18n.tr("Empty");
return len === 1
? I18n.tr("%1 character").arg(len)
: I18n.tr("%1 characters").arg(len);
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
}
StyledText {
text: I18n.tr("Lines: %1").arg(textArea.lineCount)
text: textArea.lineCount === 1
? I18n.tr("Line: %1").arg(textArea.lineCount)
: I18n.tr("Lines: %1").arg(textArea.lineCount)
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
visible: textArea.text.length > 0

View File

@@ -713,7 +713,7 @@ Rectangle {
StyledText {
id: expandedActionText
text: {
const baseText = modelData.text || "Open";
const baseText = modelData.text || I18n.tr("Open");
if (keyboardNavigationActive && (isGroupSelected || selectedNotificationIndex >= 0))
return `${baseText} (${index + 1})`;
return baseText;
@@ -849,7 +849,7 @@ Rectangle {
StyledText {
id: collapsedActionText
text: {
const baseText = modelData.text || "Open";
const baseText = modelData.text || I18n.tr("Open");
if (keyboardNavigationActive && isGroupSelected) {
return `${baseText} (${index + 1})`;
}

View File

@@ -85,7 +85,7 @@ Rectangle {
function getTimeoutText(value) {
if (value === undefined || value === null || isNaN(value)) {
return "5 seconds";
return I18n.tr("5 seconds");
}
for (let i = 0; i < timeoutOptions.length; i++) {
@@ -94,15 +94,15 @@ Rectangle {
}
}
if (value === 0) {
return "Never";
return I18n.tr("Never");
}
if (value < 1000) {
return value + "ms";
}
if (value < 60000) {
return Math.round(value / 1000) + " seconds";
return Math.round(value / 1000) + " " + I18n.tr("seconds");
}
return Math.round(value / 60000) + " minutes";
return Math.round(value / 60000) + " " + I18n.tr("minutes");
}
Column {
@@ -169,7 +169,7 @@ Rectangle {
DankDropdown {
text: I18n.tr("Low Priority")
description: "Timeout for low priority notifications"
description: I18n.tr("Timeout for low priority notifications")
currentValue: getTimeoutText(SettingsData.notificationTimeoutLow)
options: timeoutOptions.map(opt => opt.text)
onValueChanged: value => {
@@ -184,7 +184,7 @@ Rectangle {
DankDropdown {
text: I18n.tr("Normal Priority")
description: "Timeout for normal priority notifications"
description: I18n.tr("Timeout for normal priority notifications")
currentValue: getTimeoutText(SettingsData.notificationTimeoutNormal)
options: timeoutOptions.map(opt => opt.text)
onValueChanged: value => {
@@ -199,7 +199,7 @@ Rectangle {
DankDropdown {
text: I18n.tr("Critical Priority")
description: "Timeout for critical priority notifications"
description: I18n.tr("Timeout for critical priority notifications")
currentValue: getTimeoutText(SettingsData.notificationTimeoutCritical)
options: timeoutOptions.map(opt => opt.text)
onValueChanged: value => {
@@ -225,6 +225,8 @@ Rectangle {
Row {
id: overlayRow
anchors.left: parent.left
anchors.right: overlayToggle.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
@@ -238,17 +240,22 @@ Rectangle {
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
width: overlayRow.width - Theme.iconSizeSmall - Theme.spacingM
StyledText {
width: parent.width
text: I18n.tr("Notification Overlay")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
wrapMode: Text.Wrap
}
StyledText {
width: parent.width
text: I18n.tr("Display all priorities over fullscreen apps")
font.pixelSize: Theme.fontSizeSmall - 1
color: Theme.surfaceVariantText
wrapMode: Text.Wrap
}
}
}
@@ -269,6 +276,8 @@ Rectangle {
Row {
id: privacyRow
anchors.left: parent.left
anchors.right: privacyToggle.left
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
@@ -282,17 +291,22 @@ Rectangle {
Column {
spacing: 2
anchors.verticalCenter: parent.verticalCenter
width: privacyRow.width - Theme.iconSizeSmall - Theme.spacingM
StyledText {
width: parent.width
text: I18n.tr("Privacy Mode")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
wrapMode: Text.Wrap
}
StyledText {
width: parent.width
text: I18n.tr("Hide notification content until expanded")
font.pixelSize: Theme.fontSizeSmall - 1
color: Theme.surfaceVariantText
wrapMode: Text.Wrap
}
}
}

View File

@@ -68,22 +68,22 @@ Item {
property string compositorTooltip: {
if (isHyprland)
return "Hyprland Website";
return I18n.tr("Hyprland Website");
if (isSway)
return "Sway Website";
return I18n.tr("Sway Website");
if (isScroll)
return "Scroll Github";
return I18n.tr("Scroll GitHub");
if (isMiracle)
return "Miracle WM GitHub";
return I18n.tr("Scroll GitHub");
if (isDwl)
return "mangowc GitHub";
return I18n.tr("mangowc GitHub");
if (isLabwc)
return "LabWC Website";
return "niri GitHub";
return I18n.tr("LabWC Website");
return I18n.tr("niri GitHub");
}
property string dmsDiscordUrl: "https://discord.gg/ppWTpKmPgT"
property string dmsDiscordTooltip: "niri/dms Discord"
property string dmsDiscordTooltip: I18n.tr("niri/dms Discord")
property string compositorDiscordUrl: {
if (isHyprland)
@@ -95,17 +95,17 @@ Item {
property string compositorDiscordTooltip: {
if (isHyprland)
return "Hyprland Discord Server";
return I18n.tr("Hyprland Discord Server");
if (isDwl)
return "mangowc Discord Server";
return I18n.tr("mangowc Discord Server");
return "";
}
property string redditUrl: "https://reddit.com/r/niri"
property string redditTooltip: "r/niri Subreddit"
property string redditTooltip: I18n.tr("r/niri Subreddit")
property string ircUrl: "https://web.libera.chat/gamja/?channels=#labwc"
property string ircTooltip: "LabWC IRC Channel"
property string ircTooltip: I18n.tr("LabWC IRC Channel")
property bool showMatrix: isNiri && !isHyprland && !isSway && !isScroll && !isMiracle && !isDwl && !isLabwc
property bool showCompositorDiscord: isHyprland || isDwl
@@ -396,7 +396,7 @@ Item {
visible: showMatrix
property bool hovered: false
property string tooltipText: "niri Matrix Chat"
property string tooltipText: I18n.tr("niri Matrix Chat")
Image {
anchors.fill: parent
@@ -582,9 +582,7 @@ Item {
}
StyledText {
text: I18n.tr(`dms is a highly customizable, modern desktop shell with a <a href="https://m3.material.io/" style="text-decoration:none; color:${Theme.primary};">material 3 inspired</a> design.
<br /><br/>It is built with <a href="https://quickshell.org" style="text-decoration:none; color:${Theme.primary};">Quickshell</a>, a QT6 framework for building desktop shells, and <a href="https://go.dev" style="text-decoration:none; color:${Theme.primary};">Go</a>, a statically typed, compiled programming language.
`)
text: I18n.tr('dms is a highly customizable, modern desktop shell with a <a href="https://m3.material.io/" style="text-decoration:none; color:%1;">material 3 inspired</a> design.<br /><br/>It is built with <a href="https://quickshell.org" style="text-decoration:none; color:%1;">Quickshell</a>, a QT6 framework for building desktop shells, and <a href="https://go.dev" style="text-decoration:none; color:%1;">Go</a>, a statically typed, compiled programming language.').arg(Theme.primary)
textFormat: Text.RichText
font.pixelSize: Theme.fontSizeMedium
linkColor: Theme.primary
@@ -825,7 +823,7 @@ Item {
StyledText {
anchors.horizontalCenter: parent.horizontalCenter
text: `<a href="https://github.com/AvengeMedia/DankMaterialShell/blob/master/LICENSE" style="text-decoration:none; color:${Theme.surfaceVariantText};">MIT License</a>`
text: I18n.tr('<a href="https://github.com/AvengeMedia/DankMaterialShell/blob/master/LICENSE" style="text-decoration:none; color:%1;">MIT License</a>').arg(Theme.surfaceVariantText)
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
textFormat: Text.RichText

View File

@@ -181,11 +181,11 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
model: [
{
"text": "Time",
"text": I18n.tr("Time"),
"icon": "access_time"
},
{
"text": "Location",
"text": I18n.tr("Location"),
"icon": "place"
}
]

View File

@@ -542,7 +542,14 @@ Item {
}
StyledText {
text: KeybindsService.loading ? I18n.tr("Shortcuts") : I18n.tr("Shortcuts (%1)").arg(keybindsTab._filteredBinds.length)
text: {
if (KeybindsService.loading)
return I18n.tr("Shortcuts");
const count = keybindsTab._filteredBinds.length;
return count === 1
? I18n.tr("Shortcut (%1)").arg(count)
: I18n.tr("Shortcuts (%1)").arg(count);
}
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText

View File

@@ -2854,7 +2854,7 @@ Item {
}
StyledText {
text: I18n.tr(`Generate baseline GTK3/4 or QT5/QT6 (requires qt6ct-kde) configurations to follow DMS colors. Only needed once.<br /><br />It is recommended to configure <a href="https://github.com/AvengeMedia/DankMaterialShell/blob/master/README.md#Theming" style="text-decoration:none; color:${Theme.primary};">adw-gtk3</a> prior to applying GTK themes.`)
text: I18n.tr('Generate baseline GTK3/4 or QT5/QT6 (requires qt6ct-kde) configurations to follow DMS colors. Only needed once.<br /><br />It is recommended to configure <a href="https://github.com/AvengeMedia/DankMaterialShell/blob/master/README.md#Theming" style="text-decoration:none; color:%1;">adw-gtk3</a> prior to applying GTK themes.').arg(Theme.primary)
textFormat: Text.RichText
linkColor: Theme.primary
onLinkActivated: url => Qt.openUrlExternally(url)

View File

@@ -138,7 +138,7 @@ Item {
{
"id": "gpuTemp",
"text": I18n.tr("GPU Temperature"),
"description": "",
"description": I18n.tr("GPU temperature display"),
"icon": "auto_awesome_mosaic",
"warning": !DgopService.dgopAvailable ? I18n.tr("Requires 'dgop' tool") : I18n.tr("This widget prevents GPU power off states, which can significantly impact battery life on laptops. It is not recommended to use this on laptops with hybrid graphics."),
"enabled": DgopService.dgopAvailable

View File

@@ -67,12 +67,14 @@ DankPopout {
anchors.verticalCenter: parent.verticalCenter
text: {
if (SystemUpdateService.isChecking)
return "Checking...";
return I18n.tr("Checking...");
if (SystemUpdateService.hasError)
return "Error";
return I18n.tr("Error");
if (SystemUpdateService.updateCount === 0)
return "Up to date";
return SystemUpdateService.updateCount + " updates";
return I18n.tr("Up to date");
return SystemUpdateService.updateCount === 1
? I18n.tr("%1 update").arg(SystemUpdateService.updateCount)
: I18n.tr("%1 updates").arg(SystemUpdateService.updateCount);
}
font.pixelSize: Theme.fontSizeMedium
color: {
@@ -136,18 +138,20 @@ DankPopout {
width: parent.width
text: {
if (SystemUpdateService.hasError) {
return "Failed to check for updates:\n" + SystemUpdateService.errorMessage;
return I18n.tr("Failed to check for updates:\n%1").arg(SystemUpdateService.errorMessage);
}
if (!SystemUpdateService.helperAvailable) {
return "No package manager found. Please install 'paru' or 'yay' on Arch-based systems to check for updates.";
return I18n.tr("No package manager found. Please install 'paru' or 'yay' on Arch-based systems to check for updates.");
}
if (SystemUpdateService.isChecking) {
return "Checking for updates...";
return I18n.tr("Checking for updates...");
}
if (SystemUpdateService.updateCount === 0) {
return "Your system is up to date!";
return I18n.tr("Your system is up to date!");
}
return `Found ${SystemUpdateService.updateCount} packages to update:`;
return SystemUpdateService.updateCount === 1
? I18n.tr("Found %1 package to update:").arg(SystemUpdateService.updateCount)
: I18n.tr("Found %1 packages to update:").arg(SystemUpdateService.updateCount);
}
font.pixelSize: Theme.fontSizeMedium
color: {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff