1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-01-24 13:32:50 -05:00

Compare commits

...

10 Commits

Author SHA1 Message Date
bbedward
3507c6cec3 i18n: RTL fixes in about tab and dank bar settings 2026-01-21 11:57:46 -05:00
purian23
3ff00768ac core: dms chroma notepad updates 2026-01-21 11:48:08 -05:00
bbedward
556d253ea8 launcher v2: fix view mode persistence 2026-01-21 11:43:02 -05:00
bbedward
3922070488 launcher v2: meta improvements
- Allow disabling each plugin from "all" mode
- add IPCs for toggling specific modes
- niri: overview respect size & default to apps mode
- fix unicode icon handling
2026-01-21 11:38:48 -05:00
Eggrror404
eebb4827c4 feat(bar): enlarge bar icons if widget background is off (#1425)
* use iconSizeLarge if noBackground is on

* widgets: pass noBackground to barIconSize in param
2026-01-21 10:44:08 -05:00
Kamil Chmielewski
fd2c6a0784 Feat/niri workspace names (#1396)
* dankbar: show niri workspace names

Keep labels aligned with niri indices and live renames.

* dankbar: prefix named workspaces with index

Use workspace index toggle to show index: name labels.

* workspaces: change size conditions for workspace names

---------

Co-authored-by: bbedward <bbedward@gmail.com>
2026-01-21 10:43:55 -05:00
bbedward
417bf37515 clipboard: fix header GUI and add tooltips 2026-01-21 10:19:52 -05:00
bbedward
132e799265 Revert "settings: fix modal not opening on latest quickshell (#1357)"
This reverts commit bdd01e335d.
2026-01-21 09:19:12 -05:00
dms-ci[bot]
bdc864781b nix: update vendorHash for go.mod changes 2026-01-21 14:18:47 +00:00
purian23
a343bc7562 feat: DMS Core Chroma Syntax Highlighter
- Thanks alecthomas for the project
2026-01-21 09:16:58 -05:00
52 changed files with 1383 additions and 391 deletions

View File

@@ -0,0 +1,294 @@
package main
import (
"bytes"
"fmt"
"io"
"os"
"strings"
"sync"
"github.com/alecthomas/chroma/v2"
"github.com/alecthomas/chroma/v2/formatters/html"
"github.com/alecthomas/chroma/v2/lexers"
"github.com/alecthomas/chroma/v2/styles"
"github.com/spf13/cobra"
"github.com/yuin/goldmark"
highlighting "github.com/yuin/goldmark-highlighting/v2"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
ghtml "github.com/yuin/goldmark/renderer/html"
)
var (
chromaLanguage string
chromaStyle string
chromaInline bool
chromaMarkdown bool
chromaLineNumbers bool
// Caching layer for performance
lexerCache = make(map[string]chroma.Lexer)
styleCache = make(map[string]*chroma.Style)
formatterCache = make(map[string]*html.Formatter)
cacheMutex sync.RWMutex
maxFileSize = int64(5 * 1024 * 1024) // 5MB default
)
var chromaCmd = &cobra.Command{
Use: "chroma [file]",
Short: "Syntax highlight source code",
Long: `Generate syntax-highlighted HTML from source code.
Reads from file or stdin, outputs HTML with syntax highlighting.
Language is auto-detected from filename or can be specified with --language.
Examples:
dms chroma main.go
dms chroma --language python script.py
echo "def foo(): pass" | dms chroma -l python
cat code.rs | dms chroma -l rust --style dracula
dms chroma --markdown README.md
dms chroma --markdown --style github-dark notes.md
dms chroma list-languages
dms chroma list-styles`,
Args: cobra.MaximumNArgs(1),
Run: runChroma,
}
var chromaListLanguagesCmd = &cobra.Command{
Use: "list-languages",
Short: "List all supported languages",
Run: func(cmd *cobra.Command, args []string) {
for _, name := range lexers.Names(true) {
fmt.Println(name)
}
},
}
var chromaListStylesCmd = &cobra.Command{
Use: "list-styles",
Short: "List all available color styles",
Run: func(cmd *cobra.Command, args []string) {
for _, name := range styles.Names() {
fmt.Println(name)
}
},
}
func init() {
chromaCmd.Flags().StringVarP(&chromaLanguage, "language", "l", "", "Language for highlighting (auto-detect if not specified)")
chromaCmd.Flags().StringVarP(&chromaStyle, "style", "s", "monokai", "Color style (monokai, dracula, github, etc.)")
chromaCmd.Flags().BoolVar(&chromaInline, "inline", false, "Output inline styles instead of CSS classes")
chromaCmd.Flags().BoolVar(&chromaLineNumbers, "line-numbers", false, "Show line numbers in output")
chromaCmd.Flags().BoolVarP(&chromaMarkdown, "markdown", "m", false, "Render markdown with syntax-highlighted code blocks")
chromaCmd.Flags().Int64Var(&maxFileSize, "max-size", 5*1024*1024, "Maximum file size to process without warning (bytes)")
chromaCmd.AddCommand(chromaListLanguagesCmd)
chromaCmd.AddCommand(chromaListStylesCmd)
}
func getCachedLexer(key string, fallbackFunc func() chroma.Lexer) chroma.Lexer {
cacheMutex.RLock()
if lexer, ok := lexerCache[key]; ok {
cacheMutex.RUnlock()
return lexer
}
cacheMutex.RUnlock()
lexer := fallbackFunc()
if lexer != nil {
cacheMutex.Lock()
lexerCache[key] = lexer
cacheMutex.Unlock()
}
return lexer
}
func getCachedStyle(name string) *chroma.Style {
cacheMutex.RLock()
if style, ok := styleCache[name]; ok {
cacheMutex.RUnlock()
return style
}
cacheMutex.RUnlock()
style := styles.Get(name)
if style == nil {
fmt.Fprintf(os.Stderr, "Warning: Style '%s' not found, using fallback\n", name)
style = styles.Fallback
}
cacheMutex.Lock()
styleCache[name] = style
cacheMutex.Unlock()
return style
}
func getCachedFormatter(inline bool, lineNumbers bool) *html.Formatter {
key := fmt.Sprintf("inline=%t,lineNumbers=%t", inline, lineNumbers)
cacheMutex.RLock()
if formatter, ok := formatterCache[key]; ok {
cacheMutex.RUnlock()
return formatter
}
cacheMutex.RUnlock()
var opts []html.Option
if inline {
opts = append(opts, html.WithClasses(false))
} else {
opts = append(opts, html.WithClasses(true))
}
opts = append(opts, html.TabWidth(4))
if lineNumbers {
opts = append(opts, html.WithLineNumbers(true))
opts = append(opts, html.LineNumbersInTable(false))
opts = append(opts, html.WithLinkableLineNumbers(false, ""))
}
formatter := html.New(opts...)
cacheMutex.Lock()
formatterCache[key] = formatter
cacheMutex.Unlock()
return formatter
}
func runChroma(cmd *cobra.Command, args []string) {
var source string
var filename string
// Read from file or stdin
if len(args) > 0 {
filename = args[0]
// Check file size before reading
fileInfo, err := os.Stat(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading file info: %v\n", err)
os.Exit(1)
}
if fileInfo.Size() > maxFileSize {
fmt.Fprintf(os.Stderr, "Warning: File size (%d bytes) exceeds recommended limit (%d bytes)\n",
fileInfo.Size(), maxFileSize)
fmt.Fprintf(os.Stderr, "Processing may be slow. Consider using smaller files.\n")
}
content, err := os.ReadFile(filename)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading file: %v\n", err)
os.Exit(1)
}
source = string(content)
} else {
content, err := io.ReadAll(os.Stdin)
if err != nil {
fmt.Fprintf(os.Stderr, "Error reading stdin: %v\n", err)
os.Exit(1)
}
source = string(content)
}
// Handle empty input
if strings.TrimSpace(source) == "" {
return
}
// Handle Markdown rendering
if chromaMarkdown {
md := goldmark.New(
goldmark.WithExtensions(
extension.GFM,
highlighting.NewHighlighting(
highlighting.WithStyle(chromaStyle),
highlighting.WithFormatOptions(
html.WithClasses(!chromaInline),
),
),
),
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
goldmark.WithRendererOptions(
ghtml.WithHardWraps(),
ghtml.WithXHTML(),
),
)
var buf bytes.Buffer
if err := md.Convert([]byte(source), &buf); err != nil {
fmt.Fprintf(os.Stderr, "Markdown rendering error: %v\n", err)
os.Exit(1)
}
fmt.Print(buf.String())
return
}
// Detect or use specified lexer
var lexer chroma.Lexer
if chromaLanguage != "" {
lexer = getCachedLexer(chromaLanguage, func() chroma.Lexer {
l := lexers.Get(chromaLanguage)
if l == nil {
fmt.Fprintf(os.Stderr, "Unknown language: %s\n", chromaLanguage)
os.Exit(1)
}
return l
})
} else if filename != "" {
lexer = getCachedLexer("file:"+filename, func() chroma.Lexer {
return lexers.Match(filename)
})
}
// Try content analysis if no lexer found (limit to first 1KB for performance)
if lexer == nil {
analyzeContent := source
if len(source) > 1024 {
analyzeContent = source[:1024]
}
lexer = lexers.Analyse(analyzeContent)
}
// Fallback to plaintext
if lexer == nil {
lexer = lexers.Fallback
}
lexer = chroma.Coalesce(lexer)
// Get cached style
style := getCachedStyle(chromaStyle)
// Get cached formatter
formatter := getCachedFormatter(chromaInline, chromaLineNumbers)
// Tokenize
iterator, err := lexer.Tokenise(nil, source)
if err != nil {
fmt.Fprintf(os.Stderr, "Tokenization error: %v\n", err)
os.Exit(1)
}
// Format and output
if chromaLineNumbers {
var buf bytes.Buffer
if err := formatter.Format(&buf, style, iterator); err != nil {
fmt.Fprintf(os.Stderr, "Formatting error: %v\n", err)
os.Exit(1)
}
// Add spacing between line numbers
output := buf.String()
output = strings.ReplaceAll(output, "</span><span>", "</span>\u00A0\u00A0<span>")
fmt.Print(output)
} else {
if err := formatter.Format(os.Stdout, style, iterator); err != nil {
fmt.Fprintf(os.Stderr, "Formatting error: %v\n", err)
os.Exit(1)
}
}
}

View File

@@ -515,6 +515,7 @@ func getCommonCommands() []*cobra.Command {
genericNotifyActionCmd,
matugenCmd,
clipboardCmd,
chromaCmd,
doctorCmd,
configCmd,
}

View File

@@ -4,6 +4,7 @@ go 1.24.6
require (
github.com/Wifx/gonetworkmanager/v2 v2.2.0
github.com/alecthomas/chroma/v2 v2.17.2
github.com/charmbracelet/bubbles v0.21.0
github.com/charmbracelet/bubbletea v1.3.10
github.com/charmbracelet/lipgloss v1.1.0
@@ -15,6 +16,8 @@ require (
github.com/sblinch/kdl-go v0.0.0-20251203232544-981d4ecc17c3
github.com/spf13/cobra v1.10.2
github.com/stretchr/testify v1.11.1
github.com/yuin/goldmark v1.7.16
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc
go.etcd.io/bbolt v1.4.3
golang.org/x/exp v0.0.0-20260112195511-716be5621a96
golang.org/x/image v0.35.0
@@ -28,6 +31,7 @@ require (
github.com/clipperhouse/uax29/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.6.2 // indirect
github.com/cyphar/filepath-securejoin v0.6.1 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/go-git/gcfg/v2 v2.0.2 // indirect
github.com/go-git/go-billy/v6 v6.0.0-20260114122816-19306b749ecc // indirect

View File

@@ -4,6 +4,14 @@ github.com/ProtonMail/go-crypto v1.3.0 h1:ILq8+Sf5If5DCpHQp4PbZdS1J7HDFRXz/+xKBi
github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE=
github.com/Wifx/gonetworkmanager/v2 v2.2.0 h1:kPstgsQtY8CmDOOFZd81ytM9Gi3f6ImzPCKF7nNhQ2U=
github.com/Wifx/gonetworkmanager/v2 v2.2.0/go.mod h1:fMDb//SHsKWxyDUAwXvCqurV3npbIyyaQWenGpZ/uXg=
github.com/alecthomas/assert/v2 v2.11.0 h1:2Q9r3ki8+JYXvGsDyBXwH3LcJ+WK5D0gc5E8vS6K3D0=
github.com/alecthomas/assert/v2 v2.11.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
github.com/alecthomas/chroma/v2 v2.2.0/go.mod h1:vf4zrexSH54oEjJ7EdB65tGNHmH3pGZmVkgTP5RHvAs=
github.com/alecthomas/chroma/v2 v2.17.2 h1:Rm81SCZ2mPoH+Q8ZCc/9YvzPUN/E7HgPiPJD8SLV6GI=
github.com/alecthomas/chroma/v2 v2.17.2/go.mod h1:RVX6AvYm4VfYe/zsk7mjHueLDZor3aWCNE14TFlepBk=
github.com/alecthomas/repr v0.0.0-20220113201626-b1b626ac65ae/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8=
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -24,16 +32,12 @@ github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoF
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
github.com/charmbracelet/log v0.4.2 h1:hYt8Qj6a8yLnvR+h7MwsJv/XvmBJXiueUcI3cIxsyig=
github.com/charmbracelet/log v0.4.2/go.mod h1:qifHGX/tc7eluv2R6pWIpyHDDrrb/AG71Pf2ysQu5nw=
github.com/charmbracelet/x/ansi v0.11.3 h1:6DcVaqWI82BBVM/atTyq6yBoRLZFBsnoDoX9GCu2YOI=
github.com/charmbracelet/x/ansi v0.11.3/go.mod h1:yI7Zslym9tCJcedxz5+WBq+eUGMJT0bM06Fqy1/Y4dI=
github.com/charmbracelet/x/ansi v0.11.4 h1:6G65PLu6HjmE858CnTUQY1LXT3ZUWwfvqEROLF8vqHI=
github.com/charmbracelet/x/ansi v0.11.4/go.mod h1:/5AZ+UfWExW3int5H5ugnsG/PWjNcSQcwYsHBlPFQN4=
github.com/charmbracelet/x/cellbuf v0.0.14 h1:iUEMryGyFTelKW3THW4+FfPgi4fkmKnnaLOXuc+/Kj4=
github.com/charmbracelet/x/cellbuf v0.0.14/go.mod h1:P447lJl49ywBbil/KjCk2HexGh4tEY9LH0/1QrZZ9rA=
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
github.com/clipperhouse/displaywidth v0.6.2 h1:ZDpTkFfpHOKte4RG5O/BOyf3ysnvFswpyYrV7z2uAKo=
github.com/clipperhouse/displaywidth v0.6.2/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/displaywidth v0.7.0 h1:QNv1GYsnLX9QBrcWUtMlogpTXuM5FVnBwKWp1O5NwmE=
github.com/clipperhouse/displaywidth v0.7.0/go.mod h1:R+kHuzaYWFkTm7xoMmK1lFydbci4X2CicfbGstSGg0o=
github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs=
@@ -48,6 +52,10 @@ github.com/cyphar/filepath-securejoin v0.6.1/go.mod h1:A8hd4EnAeyujCJRrICiOWqjS1
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
@@ -58,14 +66,10 @@ github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
github.com/go-git/gcfg/v2 v2.0.2 h1:MY5SIIfTGGEMhdA7d7JePuVVxtKL7Hp+ApGDJAJ7dpo=
github.com/go-git/gcfg/v2 v2.0.2/go.mod h1:/lv2NsxvhepuMrldsFilrgct6pxzpGdSRC13ydTLSLs=
github.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd h1:Gd/f9cGi/3h1JOPaa6er+CkKUGyGX2DBJdFbDKVO+R0=
github.com/go-git/go-billy/v6 v6.0.0-20251217170237-e9738f50a3cd/go.mod h1:d3XQcsHu1idnquxt48kAv+h+1MUiYKLH/e7LAzjP+pI=
github.com/go-git/go-billy/v6 v6.0.0-20260114122816-19306b749ecc h1:rhkjrnRkamkRC7woapp425E4CAH6RPcqsS9X8LA93IY=
github.com/go-git/go-billy/v6 v6.0.0-20260114122816-19306b749ecc/go.mod h1:X1oe0Z2qMsa9hkar3AAPuL9hu4Mi3ztXEjdqRhr6fcc=
github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20251229094738-4b14af179146 h1:xYfxAopYyL44ot6dMBIb1Z1njFM0ZBQ99HdIB99KxLs=
github.com/go-git/go-git-fixtures/v5 v5.1.2-0.20251229094738-4b14af179146/go.mod h1:QE/75B8tBSLNGyUUbA9tw3EGHoFtYOtypa2h8YJxsWI=
github.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19 h1:0lz2eJScP8v5YZQsrEw+ggWC5jNySjg4bIZo5BIh6iI=
github.com/go-git/go-git/v6 v6.0.0-20251231065035-29ae690a9f19/go.mod h1:L+Evfcs7EdTqxwv854354cb6+++7TFL3hJn3Wy4g+3w=
github.com/go-git/go-git/v6 v6.0.0-20260114124804-a8db3a6585a6 h1:Yo1MlE8LpvD0pr7mZ04b6hKZKQcPvLrQFgyY1jNMEyU=
github.com/go-git/go-git/v6 v6.0.0-20260114124804-a8db3a6585a6/go.mod h1:enMzPHv+9hL4B7tH7OJGQKNzCkMzXovUoaiXfsLF7Xs=
github.com/go-logfmt/logfmt v0.6.1 h1:4hvbpePJKnIzH1B+8OR/JPbTx37NktoI9LE2QZBBkvE=
@@ -78,6 +82,8 @@ github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUv
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83 h1:B+A58zGFuDrvEZpPN+yS6swJA0nzqgZvDzgl/OPyefU=
github.com/holoplot/go-evdev v0.0.0-20250804134636-ab1d56a1fe83/go.mod h1:iHAf8OIncO2gcQ8XOjS7CMJ2aPbX2Bs0wl5pZyanEqk=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -133,42 +139,35 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/yuin/goldmark v1.4.15/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.16 h1:n+CJdUxaFMiDUNnWC3dMWCIQJSkxH4uz3ZwQBkAlVNE=
github.com/yuin/goldmark v1.7.16/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc h1:+IAOyRda+RLrxa1WC7umKOZRsGq4QrFFMYApOeHzQwQ=
github.com/yuin/goldmark-highlighting/v2 v2.0.0-20230729083705-37449abec8cc/go.mod h1:ovIvrum6DQJA4QsJSovrkC4saKHQVs7TvcaeO8AIl5I=
go.etcd.io/bbolt v1.4.3 h1:dEadXpI6G79deX5prL3QRNP6JB8UxVkqo4UPnHaNXJo=
go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/crypto v0.47.0 h1:V6e3FRj+n4dbpw86FJ8Fv7XVOql7TEwpHapKoMJ/GO8=
golang.org/x/crypto v0.47.0/go.mod h1:ff3Y9VzzKbwSSEzWqJsJVBnWmRwRSHt/6Op5n9bQc4A=
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0=
golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU=
golang.org/x/exp v0.0.0-20260112195511-716be5621a96 h1:Z/6YuSHTLOHfNFdb8zVZomZr7cqNgTJvA8+Qz75D8gU=
golang.org/x/exp v0.0.0-20260112195511-716be5621a96/go.mod h1:nzimsREAkjBCIEFtHiYkrJyT+2uy9YZJB7H1k68CXZU=
golang.org/x/image v0.34.0 h1:33gCkyw9hmwbZJeZkct8XyR11yH889EQt/QH4VmXMn8=
golang.org/x/image v0.34.0/go.mod h1:2RNFBZRB+vnwwFil8GkMdRvrJOFd1AzdZI6vOY+eJVU=
golang.org/x/image v0.35.0 h1:LKjiHdgMtO8z7Fh18nGY6KDcoEtVfsgLDPeLyguqb7I=
golang.org/x/image v0.35.0/go.mod h1:MwPLTVgvxSASsxdLzKrl8BRFuyqMyGhLwmC+TO1Sybk=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/net v0.49.0 h1:eeHFmOGUTtaaPSGNmjBKpbng9MulQsJURQUAfUwY++o=
golang.org/x/net v0.49.0/go.mod h1:/ysNB2EvaqvesRkuLAyjI1ycPZlQHM3q01F02UY/MV8=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/term v0.39.0 h1:RclSuaJf32jOqZz74CkPA9qFuVTX7vhLlpfj/IGWlqY=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/term v0.39.0/go.mod h1:yxzUCTP/U+FzoxfdKmLaA0RV1WgE0VY7hXBwKtY/4ww=
golang.org/x/text v0.33.0 h1:B3njUFyqtHDUI5jMn1YIr5B0IE2U0qck04r6d4KPAxE=
golang.org/x/text v0.33.0/go.mod h1:LuMebE6+rBincTi9+xWTY8TztLzKHc/9C1uBCG27+q8=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -177,5 +176,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -78,7 +78,7 @@
inherit version;
pname = "dms-shell";
src = ./core;
vendorHash = "sha256-lXqOJ0yNlOcXuR3vcuVjFI02Hskmavcasb1Ntf3UlPM=";
vendorHash = "sha256-kWHB/FN6Z2Ydh+VvNrDnbg18RuJSDAle4DHDAP4NpNk=";
subPackages = [ "cmd/dms" ];

View File

@@ -79,6 +79,23 @@ Singleton {
saveSettings();
}
property var launcherPluginVisibility: ({})
function getPluginAllowWithoutTrigger(pluginId) {
if (!launcherPluginVisibility[pluginId])
return true;
return launcherPluginVisibility[pluginId].allowWithoutTrigger !== false;
}
function setPluginAllowWithoutTrigger(pluginId, allow) {
const updated = JSON.parse(JSON.stringify(launcherPluginVisibility));
if (!updated[pluginId])
updated[pluginId] = {};
updated[pluginId].allowWithoutTrigger = allow;
launcherPluginVisibility = updated;
saveSettings();
}
property alias dankBarLeftWidgetsModel: leftWidgetsModel
property alias dankBarCenterWidgetsModel: centerWidgetsModel
property alias dankBarRightWidgetsModel: rightWidgetsModel
@@ -237,6 +254,7 @@ Singleton {
property int appLauncherGridColumns: 4
property bool spotlightCloseNiriOverview: true
property var spotlightSectionViewModes: ({})
onSpotlightSectionViewModesChanged: saveSettings()
property bool niriOverviewOverlayEnabled: true
property string dankLauncherV2Size: "compact"
property bool dankLauncherV2BorderEnabled: false

View File

@@ -752,9 +752,11 @@ Singleton {
return (0.299 * c.r + 0.587 * c.g + 0.114 * c.b) < 0.5;
}
function barIconSize(barThickness, offset) {
function barIconSize(barThickness, offset, noBackground) {
const defaultOffset = offset !== undefined ? offset : -6;
return Math.round((barThickness / 48) * (iconSize + defaultOffset));
const size = (noBackground ?? false) ? iconSizeLarge : iconSize;
return Math.round((barThickness / 48) * (size + defaultOffset));
}
function barTextSize(barThickness, fontScale) {

View File

@@ -415,7 +415,8 @@ var SPEC = {
desktopWidgetGroups: { def: [] },
builtInPluginSettings: { def: {} }
builtInPluginSettings: { def: {} },
launcherPluginVisibility: { def: {} }
};
function getValidKeys() {

View File

@@ -474,15 +474,17 @@ Item {
PopoutService.settingsModalLoader = settingsModalLoader;
}
onActiveChanged: {
if (active && item) {
PopoutService.settingsModal = item;
PopoutService._onSettingsModalLoaded();
}
}
SettingsModal {
id: settingsModal
property bool wasShown: false
Component.onCompleted: {
PopoutService.settingsModal = settingsModal;
PopoutService._onSettingsModalLoaded();
}
onVisibleChanged: {
if (visible) {
wasShown = true;

View File

@@ -1041,6 +1041,20 @@ Item {
return "LAUNCHER_TOGGLE_SUCCESS";
}
function openWith(mode: string): string {
if (!mode)
return "LAUNCHER_OPEN_FAILED: No mode specified";
PopoutService.openSpotlightV2WithMode(mode);
return `LAUNCHER_OPEN_SUCCESS: ${mode}`;
}
function toggleWith(mode: string): string {
if (!mode)
return "LAUNCHER_TOGGLE_FAILED: No mode specified";
PopoutService.toggleSpotlightV2WithMode(mode);
return `LAUNCHER_TOGGLE_SUCCESS: ${mode}`;
}
function openQuery(query: string): string {
PopoutService.openSpotlightV2WithQuery(query);
return "LAUNCHER_OPEN_QUERY_SUCCESS";

View File

@@ -44,26 +44,28 @@ Item {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
DankActionButton {
iconName: "history"
iconSize: Theme.iconSize - 4
iconColor: header.activeTab === "recents" ? Theme.primary : Theme.surfaceText
onClicked: tabChanged("recents")
}
DankActionButton {
iconName: "push_pin"
iconSize: Theme.iconSize - 4
iconColor: header.activeTab === "saved" ? Theme.primary : Theme.surfaceText
opacity: header.pinnedCount > 0 ? 1 : 0
enabled: header.pinnedCount > 0
visible: header.pinnedCount > 0
tooltipText: I18n.tr("Saved")
onClicked: tabChanged("saved")
}
DankActionButton {
iconName: "history"
iconSize: Theme.iconSize - 4
iconColor: header.activeTab === "recents" ? Theme.primary : Theme.surfaceText
tooltipText: I18n.tr("History")
onClicked: tabChanged("recents")
}
DankActionButton {
iconName: "info"
iconSize: Theme.iconSize - 4
iconColor: showKeyboardHints ? Theme.primary : Theme.surfaceText
tooltipText: I18n.tr("Keyboard Shortcuts")
onClicked: keyboardHintsToggled()
}
@@ -71,6 +73,7 @@ Item {
iconName: "delete_sweep"
iconSize: Theme.iconSize
iconColor: Theme.surfaceText
tooltipText: I18n.tr("Clear All")
onClicked: clearAllClicked()
}

View File

@@ -82,14 +82,13 @@ DankModal {
filtered = internalEntries;
} else {
const lowerQuery = query.toLowerCase();
filtered = internalEntries.filter(entry =>
entry.preview.toLowerCase().includes(lowerQuery)
);
filtered = internalEntries.filter(entry => entry.preview.toLowerCase().includes(lowerQuery));
}
// Sort: pinned first, then by ID descending
filtered.sort((a, b) => {
if (a.pinned !== b.pinned) return b.pinned ? 1 : -1;
if (a.pinned !== b.pinned)
return b.pinned ? 1 : -1;
return b.id - a.id;
});
@@ -193,24 +192,19 @@ DankModal {
}
function deletePinnedEntry(entry) {
clearConfirmDialog.show(
I18n.tr("Delete Saved Item?"),
I18n.tr("This will permanently remove this saved clipboard item. This action cannot be undone."),
function () {
DMSService.sendRequest("clipboard.deleteEntry", {
"id": entry.id
}, function (response) {
if (response.error) {
console.warn("ClipboardHistoryModal: Failed to delete entry:", response.error);
return;
}
internalEntries = internalEntries.filter(e => e.id !== entry.id);
updateFilteredModel();
ToastService.showInfo(I18n.tr("Saved item deleted"));
});
},
function () {}
);
clearConfirmDialog.show(I18n.tr("Delete Saved Item?"), I18n.tr("This will permanently remove this saved clipboard item. This action cannot be undone."), function () {
DMSService.sendRequest("clipboard.deleteEntry", {
"id": entry.id
}, function (response) {
if (response.error) {
console.warn("ClipboardHistoryModal: Failed to delete entry:", response.error);
return;
}
internalEntries = internalEntries.filter(e => e.id !== entry.id);
updateFilteredModel();
ToastService.showInfo(I18n.tr("Saved item deleted"));
});
}, function () {});
}
function pinEntry(entry) {
@@ -226,7 +220,9 @@ DankModal {
return;
}
DMSService.sendRequest("clipboard.pinEntry", { "id": entry.id }, function (response) {
DMSService.sendRequest("clipboard.pinEntry", {
"id": entry.id
}, function (response) {
if (response.error) {
ToastService.showError(I18n.tr("Failed to pin entry"));
return;
@@ -238,7 +234,9 @@ DankModal {
}
function unpinEntry(entry) {
DMSService.sendRequest("clipboard.unpinEntry", { "id": entry.id }, function (response) {
DMSService.sendRequest("clipboard.unpinEntry", {
"id": entry.id
}, function (response) {
if (response.error) {
ToastService.showError(I18n.tr("Failed to unpin entry"));
return;
@@ -250,27 +248,20 @@ DankModal {
function clearAll() {
const hasPinned = pinnedCount > 0;
const message = hasPinned
? I18n.tr("This will delete all unpinned entries. %1 pinned entries will be kept.").arg(pinnedCount)
: I18n.tr("This will permanently delete all clipboard history.");
const message = hasPinned ? I18n.tr("This will delete all unpinned entries. %1 pinned entries will be kept.").arg(pinnedCount) : I18n.tr("This will permanently delete all clipboard history.");
clearConfirmDialog.show(
I18n.tr("Clear History?"),
message,
function () {
DMSService.sendRequest("clipboard.clearHistory", null, function (response) {
if (response.error) {
console.warn("ClipboardHistoryModal: Failed to clear history:", response.error);
return;
}
refreshClipboard();
if (hasPinned) {
ToastService.showInfo(I18n.tr("History cleared. %1 pinned entries kept.").arg(pinnedCount));
}
});
},
function () {}
);
clearConfirmDialog.show(I18n.tr("Clear History?"), message, function () {
DMSService.sendRequest("clipboard.clearHistory", null, function (response) {
if (response.error) {
console.warn("ClipboardHistoryModal: Failed to clear history:", response.error);
return;
}
refreshClipboard();
if (hasPinned) {
ToastService.showInfo(I18n.tr("History cleared. %1 pinned entries kept.").arg(pinnedCount));
}
});
}, function () {});
}
function getEntryPreview(entry) {

View File

@@ -431,14 +431,14 @@ Item {
if (searchMode === "all") {
if (searchQuery) {
var allPluginIds = getAllLauncherPluginIds();
var allPluginIds = getVisibleLauncherPluginIds();
for (var i = 0; i < allPluginIds.length; i++) {
var pluginId = allPluginIds[i];
var pItems = getPluginItems(pluginId, searchQuery);
allItems = allItems.concat(pItems);
}
var allBuiltInIds = getAllBuiltInLauncherIds();
var allBuiltInIds = getVisibleBuiltInLauncherIds();
for (var i = 0; i < allBuiltInIds.length; i++) {
var pluginId = allBuiltInIds[i];
var blItems = AppSearchService.getBuiltInLauncherItems(pluginId, searchQuery);
@@ -858,7 +858,10 @@ Item {
}
function getEmptyTriggerPlugins() {
return PluginService.getPluginsWithEmptyTrigger();
var plugins = PluginService.getPluginsWithEmptyTrigger();
return plugins.filter(function (pluginId) {
return SettingsData.getPluginAllowWithoutTrigger(pluginId);
});
}
function getAllLauncherPluginIds() {
@@ -866,11 +869,25 @@ Item {
return Object.keys(launchers);
}
function getVisibleLauncherPluginIds() {
var launchers = PluginService.getLauncherPlugins();
return Object.keys(launchers).filter(function (pluginId) {
return SettingsData.getPluginAllowWithoutTrigger(pluginId);
});
}
function getAllBuiltInLauncherIds() {
var launchers = AppSearchService.getBuiltInLauncherPlugins();
return Object.keys(launchers);
}
function getVisibleBuiltInLauncherIds() {
var launchers = AppSearchService.getBuiltInLauncherPlugins();
return Object.keys(launchers).filter(function (pluginId) {
return SettingsData.getPluginAllowWithoutTrigger(pluginId);
});
}
function getPluginBrowseItems() {
var items = [];
@@ -930,7 +947,10 @@ Item {
}
function getBuiltInEmptyTriggerLaunchers() {
return AppSearchService.getBuiltInLauncherPluginsWithEmptyTrigger();
var plugins = AppSearchService.getBuiltInLauncherPluginsWithEmptyTrigger();
return plugins.filter(function (pluginId) {
return SettingsData.getPluginAllowWithoutTrigger(pluginId);
});
}
function getPluginItems(pluginId, query) {
@@ -947,12 +967,14 @@ Item {
function detectIconType(iconName) {
if (!iconName)
return "material";
if (iconName.indexOf("/") >= 0 || iconName.indexOf(".") >= 0)
return "image";
if (iconName.startsWith("unicode:"))
return "unicode";
if (iconName.startsWith("material:"))
return "material";
if (iconName.startsWith("image:"))
return "image";
if (iconName.indexOf("/") >= 0 || iconName.indexOf(".") >= 0)
return "image";
if (/^[a-z]+-[a-z]/.test(iconName.toLowerCase()))
return "image";
return "material";
@@ -961,6 +983,8 @@ Item {
function stripIconPrefix(iconName) {
if (!iconName)
return "extension";
if (iconName.startsWith("unicode:"))
return iconName.substring(8);
if (iconName.startsWith("material:"))
return iconName.substring(9);
if (iconName.startsWith("image:"))

View File

@@ -53,7 +53,7 @@ Item {
signal dialogClosed
function _initializeAndShow(query) {
function _initializeAndShow(query, mode) {
contentVisible = true;
spotlightContent.searchField.forceActiveFocus();
@@ -61,7 +61,8 @@ Item {
spotlightContent.searchField.text = query;
}
if (spotlightContent.controller) {
spotlightContent.controller.searchMode = "all";
var targetMode = mode || "all";
spotlightContent.controller.searchMode = targetMode;
spotlightContent.controller.activePluginId = "";
spotlightContent.controller.activePluginName = "";
spotlightContent.controller.pluginFilter = "";
@@ -136,6 +137,32 @@ Item {
spotlightOpen ? hide() : show();
}
function showWithMode(mode) {
closeCleanupTimer.stop();
isClosing = false;
openedFromOverview = false;
var focusedScreen = CompositorService.getFocusedScreen();
if (focusedScreen)
launcherWindow.screen = focusedScreen;
spotlightOpen = true;
keyboardActive = true;
ModalManager.openModal(root);
if (useHyprlandFocusGrab)
focusGrab.active = true;
_initializeAndShow("", mode);
}
function toggleWithMode(mode) {
if (spotlightOpen) {
hide();
} else {
showWithMode(mode);
}
}
Timer {
id: closeCleanupTimer
interval: Theme.expressiveDurations.expressiveFastSpatial + 50

View File

@@ -52,6 +52,14 @@ Rectangle {
color: root.isSelected ? Theme.primary : Theme.surfaceText
}
StyledText {
anchors.centerIn: parent
visible: root.item?.iconType === "unicode"
text: root.item?.icon ?? ""
font.pixelSize: parent.iconSize * 0.7
color: root.isSelected ? Theme.primary : Theme.surfaceText
}
Item {
anchors.fill: parent
visible: root.item?.iconType === "composite"

View File

@@ -52,6 +52,14 @@ Rectangle {
color: Theme.surfaceText
}
StyledText {
anchors.centerIn: parent
visible: root.item?.iconType === "unicode"
text: root.item?.icon ?? ""
font.pixelSize: 24
color: Theme.surfaceText
}
Item {
anchors.fill: parent
visible: root.item?.iconType === "composite"

View File

@@ -76,12 +76,20 @@ Rectangle {
DankIcon {
anchors.centerIn: parent
visible: !root.useImage && !root.useIconProvider
visible: !root.useImage && !root.useIconProvider && root.item?.iconType !== "unicode"
name: root.item?.icon ?? "image"
size: Math.min(parent.width, parent.height) * 0.4
color: Theme.surfaceVariantText
}
StyledText {
anchors.centerIn: parent
visible: root.item?.iconType === "unicode"
text: root.item?.icon ?? ""
font.pixelSize: Math.min(parent.width, parent.height) * 0.4
color: Theme.surfaceVariantText
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right

View File

@@ -46,11 +46,24 @@ Item {
function getRealWorkspaces() {
if (CompositorService.isNiri) {
const fallbackWorkspaces = [
{
"id": 1,
"idx": 0,
"name": ""
},
{
"id": 2,
"idx": 1,
"name": ""
}
];
if (!barWindow.screenName || SettingsData.workspaceFollowFocus) {
return NiriService.getCurrentOutputWorkspaceNumbers();
const currentWorkspaces = NiriService.getCurrentOutputWorkspaces();
return currentWorkspaces.length > 0 ? currentWorkspaces : fallbackWorkspaces;
}
const workspaces = NiriService.allWorkspaces.filter(ws => ws.output === barWindow.screenName).map(ws => ws.idx + 1);
return workspaces.length > 0 ? workspaces : [1, 2];
const workspaces = NiriService.allWorkspaces.filter(ws => ws.output === barWindow.screenName);
return workspaces.length > 0 ? workspaces : fallbackWorkspaces;
} else if (CompositorService.isHyprland) {
const workspaces = Hyprland.workspaces?.values || [];
@@ -118,7 +131,7 @@ Item {
return NiriService.getCurrentWorkspaceNumber();
}
const activeWs = NiriService.allWorkspaces.find(ws => ws.output === barWindow.screenName && ws.is_active);
return activeWs ? activeWs.idx + 1 : 1;
return activeWs ? activeWs.idx : 1;
} else if (CompositorService.isHyprland) {
const monitors = Hyprland.monitors?.values || [];
const currentMonitor = monitors.find(monitor => monitor.name === barWindow.screenName);
@@ -151,12 +164,16 @@ Item {
if (CompositorService.isNiri) {
const currentWs = getCurrentWorkspace();
const currentIndex = realWorkspaces.findIndex(ws => ws === currentWs);
const currentIndex = realWorkspaces.findIndex(ws => ws && ws.idx === currentWs);
const validIndex = currentIndex === -1 ? 0 : currentIndex;
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0);
if (nextIndex !== validIndex) {
NiriService.switchToWorkspace(realWorkspaces[nextIndex] - 1);
const nextWorkspace = realWorkspaces[nextIndex];
if (!nextWorkspace || nextWorkspace.idx === undefined) {
return;
}
NiriService.switchToWorkspace(nextWorkspace.idx);
}
} else if (CompositorService.isHyprland) {
const currentWs = getCurrentWorkspace();

View File

@@ -42,7 +42,7 @@ BasePill {
DankIcon {
name: BatteryService.getBatteryIcon()
size: Theme.barIconSize(battery.barThickness)
size: Theme.barIconSize(battery.barThickness, undefined, battery.barConfig?.noBackground)
color: {
if (!BatteryService.batteryAvailable) {
return Theme.widgetIconColor;
@@ -78,7 +78,7 @@ BasePill {
DankIcon {
name: BatteryService.getBatteryIcon()
size: Theme.barIconSize(battery.barThickness, -4)
size: Theme.barIconSize(battery.barThickness, -4, battery.barConfig?.noBackground)
color: {
if (!BatteryService.batteryAvailable) {
return Theme.widgetIconColor;

View File

@@ -45,7 +45,7 @@ BasePill {
DankIcon {
anchors.centerIn: parent
name: "shift_lock"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: Theme.primary
}
}

View File

@@ -17,7 +17,7 @@ BasePill {
DankIcon {
anchors.centerIn: parent
name: "content_paste"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: Theme.widgetIconColor
}
}

View File

@@ -18,7 +18,7 @@ BasePill {
DankIcon {
anchors.centerIn: parent
name: "palette"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: root.isActive ? Theme.primary : Theme.surfaceText
}
}

View File

@@ -29,7 +29,7 @@ BasePill {
property real micAccumulator: 0
property real volumeAccumulator: 0
property real brightnessAccumulator: 0
readonly property real vIconSize: Theme.barIconSize(root.barThickness, -4)
readonly property real vIconSize: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
Loader {
active: root.showPrinterIcon
@@ -459,7 +459,7 @@ BasePill {
DankIcon {
name: "screen_record"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: NiriService.hasActiveCast ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
visible: root.showScreenSharingIcon && NiriService.hasCasts
@@ -468,7 +468,7 @@ BasePill {
DankIcon {
id: networkIcon
name: root.getNetworkIconName()
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: root.getNetworkIconColor()
anchors.verticalCenter: parent.verticalCenter
visible: root.showNetworkIcon && NetworkService.networkAvailable
@@ -477,7 +477,7 @@ BasePill {
DankIcon {
id: vpnIcon
name: "vpn_lock"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: NetworkService.vpnConnected ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
visible: root.showVpnIcon && NetworkService.vpnAvailable && NetworkService.vpnConnected
@@ -486,7 +486,7 @@ BasePill {
DankIcon {
id: bluetoothIcon
name: "bluetooth"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: BluetoothService.connected ? Theme.primary : Theme.surfaceText
anchors.verticalCenter: parent.verticalCenter
visible: root.showBluetoothIcon && BluetoothService.available && BluetoothService.enabled
@@ -502,7 +502,7 @@ BasePill {
DankIcon {
id: audioIcon
name: root.getVolumeIconName()
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: Theme.widgetIconColor
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
@@ -544,7 +544,7 @@ BasePill {
DankIcon {
id: micIcon
name: root.getMicIconName()
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: root.getMicIconColor()
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
@@ -586,7 +586,7 @@ BasePill {
DankIcon {
id: brightnessIcon
name: root.getBrightnessIconName()
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: Theme.widgetIconColor
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
@@ -618,7 +618,7 @@ BasePill {
DankIcon {
id: batteryIcon
name: Theme.getBatteryIcon(BatteryService.batteryLevel, BatteryService.isCharging, BatteryService.batteryAvailable)
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: root.getBatteryIconColor()
anchors.verticalCenter: parent.verticalCenter
visible: root.showBatteryIcon && BatteryService.batteryAvailable
@@ -627,7 +627,7 @@ BasePill {
DankIcon {
id: printerIcon
name: "print"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: Theme.primary
anchors.verticalCenter: parent.verticalCenter
visible: root.showPrinterIcon && CupsService.cupsAvailable && root.hasPrintJobs()
@@ -635,7 +635,7 @@ BasePill {
DankIcon {
name: "settings"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: root.isActive ? Theme.primary : Theme.widgetIconColor
anchors.verticalCenter: parent.verticalCenter
visible: root.hasNoVisibleIcons()

View File

@@ -36,7 +36,7 @@ BasePill {
DankIcon {
name: "memory"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (DgopService.cpuUsage > 80) {
return Theme.tempDanger;
@@ -74,7 +74,7 @@ BasePill {
DankIcon {
id: cpuIcon
name: "memory"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (DgopService.cpuUsage > 80) {
return Theme.tempDanger;

View File

@@ -36,7 +36,7 @@ BasePill {
DankIcon {
name: "device_thermostat"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (DgopService.cpuTemperature > 85) {
return Theme.tempDanger;
@@ -74,7 +74,7 @@ BasePill {
DankIcon {
id: cpuTempIcon
name: "device_thermostat"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (DgopService.cpuTemperature > 85) {
return Theme.tempDanger;

View File

@@ -57,7 +57,7 @@ BasePill {
DankIcon {
name: layout.getLayoutIcon(layout.currentLayoutSymbol)
size: Theme.barIconSize(layout.barThickness)
size: Theme.barIconSize(layout.barThickness, undefined, layout.barConfig?.noBackground)
color: Theme.widgetTextColor
anchors.horizontalCenter: parent.horizontalCenter
}
@@ -78,7 +78,7 @@ BasePill {
DankIcon {
name: layout.getLayoutIcon(layout.currentLayoutSymbol)
size: Theme.barIconSize(layout.barThickness, -4)
size: Theme.barIconSize(layout.barThickness, -4, layout.barConfig?.noBackground)
color: Theme.widgetTextColor
anchors.verticalCenter: parent.verticalCenter
}

View File

@@ -112,7 +112,7 @@ BasePill {
DankIcon {
name: "storage"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (root.diskUsagePercent > 90) {
return Theme.tempDanger;
@@ -146,7 +146,7 @@ BasePill {
DankIcon {
name: "storage"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (root.diskUsagePercent > 90) {
return Theme.tempDanger;

View File

@@ -104,7 +104,7 @@ BasePill {
DankIcon {
name: "auto_awesome_mosaic"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (root.displayTemp > 80) {
return Theme.tempDanger;
@@ -142,7 +142,7 @@ BasePill {
DankIcon {
id: gpuTempIcon
name: "auto_awesome_mosaic"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (root.displayTemp > 80) {
return Theme.tempDanger;

View File

@@ -17,7 +17,7 @@ BasePill {
DankIcon {
anchors.centerIn: parent
name: SessionService.idleInhibited ? "motion_sensor_active" : "motion_sensor_idle"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: Theme.widgetTextColor
}
}

View File

@@ -61,7 +61,7 @@ BasePill {
DankIcon {
name: "keyboard"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: Theme.widgetTextColor
anchors.horizontalCenter: parent.horizontalCenter
}

View File

@@ -21,15 +21,15 @@ BasePill {
visible: SettingsData.launcherLogoMode === "apps"
anchors.centerIn: parent
name: "apps"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: Theme.widgetIconColor
}
SystemLogo {
visible: SettingsData.launcherLogoMode === "os"
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset, root.barConfig?.noBackground)
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset, root.barConfig?.noBackground)
colorOverride: Theme.effectiveLogoColor
brightnessOverride: SettingsData.launcherLogoBrightness
contrastOverride: SettingsData.launcherLogoContrast
@@ -38,8 +38,8 @@ BasePill {
IconImage {
visible: SettingsData.launcherLogoMode === "dank"
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset, root.barConfig?.noBackground)
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset, root.barConfig?.noBackground)
smooth: true
mipmap: true
asynchronous: true
@@ -57,8 +57,8 @@ BasePill {
IconImage {
visible: SettingsData.launcherLogoMode === "compositor" && (CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isSway || CompositorService.isScroll || CompositorService.isLabwc)
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset, root.barConfig?.noBackground)
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset, root.barConfig?.noBackground)
smooth: true
asynchronous: true
source: {
@@ -94,8 +94,8 @@ BasePill {
IconImage {
visible: SettingsData.launcherLogoMode === "custom" && SettingsData.launcherLogoCustomPath !== ""
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset)
width: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset, root.barConfig?.noBackground)
height: Theme.barIconSize(root.barThickness, SettingsData.launcherLogoSizeOffset, root.barConfig?.noBackground)
smooth: true
asynchronous: true
source: SettingsData.launcherLogoCustomPath ? "file://" + SettingsData.launcherLogoCustomPath.replace("file://", "") : ""

View File

@@ -41,7 +41,7 @@ BasePill {
DankIcon {
name: "network_check"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: Theme.widgetTextColor
anchors.horizontalCenter: parent.horizontalCenter
}
@@ -79,7 +79,7 @@ BasePill {
DankIcon {
name: "network_check"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: Theme.widgetTextColor
anchors.verticalCenter: parent.verticalCenter
}

View File

@@ -44,7 +44,7 @@ BasePill {
anchors.centerIn: parent
name: "assignment"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: root.isActive ? Theme.primary : Theme.surfaceText
}
}

View File

@@ -18,7 +18,7 @@ BasePill {
id: notifIcon
anchors.centerIn: parent
name: SessionData.doNotDisturb ? "notifications_off" : "notifications"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: SessionData.doNotDisturb ? Theme.primary : (root.isActive ? Theme.primary : Theme.widgetIconColor)
}

View File

@@ -16,7 +16,7 @@ BasePill {
DankIcon {
anchors.centerIn: parent
name: "power_settings_new"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: Theme.widgetIconColor
}
}

View File

@@ -38,7 +38,7 @@ BasePill {
DankIcon {
name: "developer_board"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (DgopService.memoryUsage > 90) {
return Theme.tempDanger;
@@ -84,7 +84,7 @@ BasePill {
DankIcon {
id: ramIcon
name: "developer_board"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: {
if (DgopService.memoryUsage > 90) {
return Theme.tempDanger;

View File

@@ -366,10 +366,10 @@ Item {
IconImage {
id: iconImg
anchors.left: parent.left
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness)) / 2) : Theme.spacingXS
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)) / 2) : Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
width: Theme.barIconSize(root.barThickness)
height: Theme.barIconSize(root.barThickness)
width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
source: {
root._desktopEntriesUpdateTrigger;
root._appIdSubstitutionsTrigger;
@@ -395,9 +395,9 @@ Item {
DankIcon {
anchors.left: parent.left
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness)) / 2) : Theme.spacingXS
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)) / 2) : Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
name: "sports_esports"
color: Theme.widgetTextColor
visible: !iconImg.visible && Paths.isSteamApp(appId)
@@ -611,10 +611,10 @@ Item {
IconImage {
id: iconImg
anchors.left: parent.left
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness)) / 2) : Theme.spacingXS
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)) / 2) : Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
width: Theme.barIconSize(root.barThickness)
height: Theme.barIconSize(root.barThickness)
width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
source: {
root._desktopEntriesUpdateTrigger;
root._appIdSubstitutionsTrigger;
@@ -640,9 +640,9 @@ Item {
DankIcon {
anchors.left: parent.left
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness)) / 2) : Theme.spacingXS
anchors.leftMargin: (widgetData?.runningAppsCompactMode !== undefined ? widgetData.runningAppsCompactMode : SettingsData.runningAppsCompactMode) ? Math.round((parent.width - Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)) / 2) : Theme.spacingXS
anchors.verticalCenter: parent.verticalCenter
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
name: "sports_esports"
color: Theme.widgetTextColor
visible: !iconImg.visible && Paths.isSteamApp(appId)

View File

@@ -198,8 +198,8 @@ Item {
IconImage {
id: iconImg
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness)
height: Theme.barIconSize(root.barThickness)
width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
source: delegateRoot.iconSource
asynchronous: true
smooth: true
@@ -262,7 +262,7 @@ Item {
DankIcon {
anchors.centerIn: parent
name: root.menuOpen ? "expand_less" : "expand_more"
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: Theme.widgetTextColor
}
@@ -331,8 +331,8 @@ Item {
IconImage {
id: iconImg
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness)
height: Theme.barIconSize(root.barThickness)
width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
source: delegateRoot.iconSource
asynchronous: true
smooth: true
@@ -402,7 +402,7 @@ Item {
return root.menuOpen ? "chevron_right" : "chevron_left";
}
}
size: Theme.barIconSize(root.barThickness)
size: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
color: Theme.widgetTextColor
}
@@ -754,8 +754,8 @@ Item {
IconImage {
id: menuIconImg
anchors.centerIn: parent
width: Theme.barIconSize(root.barThickness)
height: Theme.barIconSize(root.barThickness)
width: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
height: Theme.barIconSize(root.barThickness, undefined, root.barConfig?.noBackground)
source: parent.iconSource
asynchronous: true
smooth: true

View File

@@ -33,7 +33,7 @@ BasePill {
return "system_update_alt";
return "check_circle";
}
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: {
if (SystemUpdateService.hasError)
return Theme.error;
@@ -90,7 +90,7 @@ BasePill {
return "system_update_alt";
return "check_circle";
}
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: {
if (SystemUpdateService.hasError)
return Theme.error;

View File

@@ -41,7 +41,7 @@ BasePill {
id: icon
name: DMSNetworkService.connected ? "vpn_lock" : "vpn_key_off"
size: Theme.barIconSize(root.barThickness, -4)
size: Theme.barIconSize(root.barThickness, -4, root.barConfig?.noBackground)
color: DMSNetworkService.connected ? Theme.primary : Theme.widgetIconColor
opacity: DMSNetworkService.isBusy ? 0.5 : 1.0
anchors.centerIn: parent

View File

@@ -30,7 +30,7 @@ BasePill {
DankIcon {
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
size: Theme.barIconSize(root.barThickness, -6)
size: Theme.barIconSize(root.barThickness, -6, root.barConfig?.noBackground)
color: Theme.widgetIconColor
anchors.horizontalCenter: parent.horizontalCenter
}
@@ -57,7 +57,7 @@ BasePill {
DankIcon {
name: WeatherService.getWeatherIcon(WeatherService.weather.wCode)
size: Theme.barIconSize(root.barThickness, -6)
size: Theme.barIconSize(root.barThickness, -6, root.barConfig?.noBackground)
color: Theme.widgetIconColor
anchors.verticalCenter: parent.verticalCenter
}

View File

@@ -199,15 +199,22 @@ Item {
let targetWorkspaceId;
if (CompositorService.isNiri) {
const wsNumber = typeof ws === "number" ? ws : -1;
if (wsNumber <= 0) {
return [];
if (!ws || typeof ws !== "object") {
const wsNumber = typeof ws === "number" ? ws : -1;
if (wsNumber <= 0) {
return [];
}
const workspace = NiriService.allWorkspaces.find(w => w.idx + 1 === wsNumber && w.output === root.effectiveScreenName);
if (!workspace) {
return [];
}
targetWorkspaceId = workspace.id;
} else {
if (ws.id === undefined || ws.id === -1 || ws.idx === -1) {
return [];
}
targetWorkspaceId = ws.id;
}
const workspace = NiriService.allWorkspaces.find(w => w.idx + 1 === wsNumber && w.output === root.effectiveScreenName);
if (!workspace) {
return [];
}
targetWorkspaceId = workspace.id;
} else if (CompositorService.isHyprland) {
targetWorkspaceId = ws.id !== undefined ? ws.id : ws;
} else if (CompositorService.isDwl) {
@@ -300,6 +307,12 @@ Item {
"active": false,
"hidden": true
};
} else if (CompositorService.isNiri) {
placeholder = {
"id": -1,
"idx": -1,
"name": ""
};
} else if (CompositorService.isHyprland) {
placeholder = {
"id": -1,
@@ -324,28 +337,52 @@ Item {
function getNiriWorkspaces() {
if (NiriService.allWorkspaces.length === 0) {
return [1, 2];
return [
{
"id": 1,
"idx": 0,
"name": ""
},
{
"id": 2,
"idx": 1,
"name": ""
}
];
}
const fallbackWorkspaces = [
{
"id": 1,
"idx": 0,
"name": ""
},
{
"id": 2,
"idx": 1,
"name": ""
}
];
let workspaces;
if (!root.screenName || SettingsData.workspaceFollowFocus) {
workspaces = NiriService.getCurrentOutputWorkspaceNumbers();
const currentWorkspaces = NiriService.getCurrentOutputWorkspaces();
workspaces = currentWorkspaces.length > 0 ? currentWorkspaces : fallbackWorkspaces;
} else {
const displayWorkspaces = NiriService.allWorkspaces.filter(ws => ws.output === root.screenName).map(ws => ws.idx + 1);
workspaces = displayWorkspaces.length > 0 ? displayWorkspaces : [1, 2];
const displayWorkspaces = NiriService.allWorkspaces.filter(ws => ws.output === root.screenName);
workspaces = displayWorkspaces.length > 0 ? displayWorkspaces : fallbackWorkspaces;
}
workspaces = workspaces.slice().sort((a, b) => a.idx - b.idx);
if (!SettingsData.showOccupiedWorkspacesOnly) {
return workspaces;
}
return workspaces.filter(wsNum => {
const workspace = NiriService.allWorkspaces.find(w => w.idx + 1 === wsNum && w.output === root.effectiveScreenName);
if (!workspace)
return false;
if (workspace.is_active)
return workspaces.filter(ws => {
if (ws.is_active)
return true;
return NiriService.windows?.some(win => win.workspace_id === workspace.id) ?? false;
return NiriService.windows?.some(win => win.workspace_id === ws.id) ?? false;
});
}
@@ -359,7 +396,7 @@ Item {
}
const activeWs = NiriService.allWorkspaces.find(ws => ws.output === root.screenName && ws.is_active);
return activeWs ? activeWs.idx + 1 : 1;
return activeWs ? activeWs.idx : 1;
}
function getDwlTags() {
@@ -467,19 +504,22 @@ Item {
readonly property real padding: Math.max(Theme.spacingXS, Theme.spacingS * (widgetHeight / 30))
readonly property real visualWidth: isVertical ? widgetHeight : (workspaceRow.implicitWidth + padding * 2)
readonly property real visualHeight: isVertical ? (workspaceRow.implicitHeight + padding * 2) : widgetHeight
readonly property real appIconSize: Theme.barIconSize(barThickness, -6)
readonly property real appIconSize: Theme.barIconSize(barThickness, -6, root.barConfig?.noBackground)
function getRealWorkspaces() {
return root.workspaceList.filter(ws => {
if (useExtWorkspace)
return ws && (ws.id !== "" || ws.name !== "") && !ws.hidden;
if (CompositorService.isHyprland)
return ws && ws.id !== -1;
if (CompositorService.isDwl)
return ws && ws.tag !== -1;
if (CompositorService.isSway || CompositorService.isScroll)
return ws && ws.num !== -1;
return ws !== -1;
if (useExtWorkspace)
return ws && (ws.id !== "" || ws.name !== "") && !ws.hidden;
if (CompositorService.isNiri)
return ws && ws.idx !== -1;
if (CompositorService.isHyprland)
return ws && ws.id !== -1;
if (CompositorService.isDwl)
return ws && ws.tag !== -1;
if (CompositorService.isSway || CompositorService.isScroll)
return ws && ws.num !== -1;
return ws !== -1;
});
}
@@ -506,7 +546,7 @@ Item {
return;
}
const currentIndex = realWorkspaces.findIndex(ws => ws === root.currentWorkspace);
const currentIndex = realWorkspaces.findIndex(ws => ws && ws.idx === root.currentWorkspace);
const validIndex = currentIndex === -1 ? 0 : currentIndex;
const nextIndex = direction > 0 ? Math.min(validIndex + 1, realWorkspaces.length - 1) : Math.max(validIndex - 1, 0);
@@ -514,7 +554,11 @@ Item {
return;
}
NiriService.switchToWorkspace(realWorkspaces[nextIndex] - 1);
const nextWorkspace = realWorkspaces[nextIndex];
if (!nextWorkspace || nextWorkspace.idx === undefined) {
return;
}
NiriService.switchToWorkspace(nextWorkspace.idx);
} else if (CompositorService.isHyprland) {
const realWorkspaces = getRealWorkspaces();
if (realWorkspaces.length < 2) {
@@ -565,10 +609,26 @@ Item {
}
}
function getWorkspaceIndexFallback(modelData, index) {
if (root.useExtWorkspace)
return index + 1;
if (CompositorService.isNiri)
return (modelData?.idx !== undefined && modelData?.idx !== -1) ? modelData.idx : "";
if (CompositorService.isHyprland)
return modelData?.id || "";
if (CompositorService.isDwl)
return (modelData?.tag !== undefined) ? (modelData.tag + 1) : "";
if (CompositorService.isSway || CompositorService.isScroll)
return modelData?.num || "";
return modelData - 1;
}
function getWorkspaceIndex(modelData, index) {
let isPlaceholder;
if (root.useExtWorkspace) {
isPlaceholder = modelData?.hidden === true;
} else if (CompositorService.isNiri) {
isPlaceholder = modelData?.idx === -1;
} else if (CompositorService.isHyprland) {
isPlaceholder = modelData?.id === -1;
} else if (CompositorService.isDwl) {
@@ -582,26 +642,28 @@ Item {
if (isPlaceholder)
return index + 1;
let workspaceName = "";
if (SettingsData.showWorkspaceName) {
let workspaceName = modelData?.name;
workspaceName = modelData?.name ?? "";
if (workspaceName && workspaceName !== "") {
if (root.isVertical) {
return workspaceName.charAt(0);
workspaceName = workspaceName.charAt(0);
}
return workspaceName;
} else {
workspaceName = "";
}
}
if (root.useExtWorkspace)
return index + 1;
if (CompositorService.isHyprland)
return modelData?.id || "";
if (CompositorService.isDwl)
return (modelData?.tag !== undefined) ? (modelData.tag + 1) : "";
if (CompositorService.isSway || CompositorService.isScroll)
return modelData?.num || "";
return modelData - 1;
if (workspaceName) {
if (SettingsData.showWorkspaceIndex) {
const indexLabel = getWorkspaceIndexFallback(modelData, index);
return indexLabel ? `${indexLabel}: ${workspaceName}` : workspaceName;
}
return workspaceName;
}
return getWorkspaceIndexFallback(modelData, index);
}
readonly property bool hasNativeWorkspaceSupport: CompositorService.isNiri || CompositorService.isHyprland || CompositorService.isDwl || CompositorService.isSway || CompositorService.isScroll
@@ -747,6 +809,8 @@ Item {
property bool isActive: {
if (root.useExtWorkspace)
return (modelData?.id || modelData?.name) === root.currentWorkspace;
if (CompositorService.isNiri)
return !!(modelData && modelData.idx === root.currentWorkspace);
if (CompositorService.isHyprland)
return !!(modelData && modelData.id === root.currentWorkspace);
if (CompositorService.isDwl)
@@ -769,6 +833,8 @@ Item {
property bool isPlaceholder: {
if (root.useExtWorkspace)
return !!(modelData && modelData.hidden);
if (CompositorService.isNiri)
return !!(modelData && modelData.idx === -1);
if (CompositorService.isHyprland)
return !!(modelData && modelData.id === -1);
if (CompositorService.isDwl)
@@ -800,6 +866,10 @@ Item {
readonly property real baseWidth: root.isVertical ? (SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5) : (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7)
readonly property real baseHeight: root.isVertical ? (isActive ? root.widgetHeight * 1.05 : root.widgetHeight * 0.7) : (SettingsData.showWorkspaceApps ? widgetHeight * 0.7 : widgetHeight * 0.5)
readonly property bool hasWorkspaceName: SettingsData.showWorkspaceName && modelData?.name && modelData.name !== ""
readonly property bool workspaceNamesEnabled: SettingsData.showWorkspaceName && CompositorService.isNiri
readonly property real contentImplicitWidth: (hasWorkspaceName || loadedHasIcon) ? (appIconsLoader.item?.contentWidth ?? 0) : 0
readonly property real contentImplicitHeight: (workspaceNamesEnabled || loadedHasIcon) ? (appIconsLoader.item?.contentHeight ?? 0) : 0
readonly property real iconsExtraWidth: {
if (!root.isVertical && SettingsData.showWorkspaceApps && loadedIcons.length > 0) {
@@ -816,8 +886,16 @@ Item {
return 0;
}
readonly property real visualWidth: baseWidth + iconsExtraWidth
readonly property real visualHeight: baseHeight + iconsExtraHeight
readonly property real visualWidth: {
if (contentImplicitWidth <= 0) return baseWidth + iconsExtraWidth;
const padding = root.isVertical ? Theme.spacingXS : Theme.spacingS;
return Math.max(baseWidth + iconsExtraWidth, contentImplicitWidth + padding);
}
readonly property real visualHeight: {
if (contentImplicitHeight <= 0) return baseHeight + iconsExtraHeight;
const padding = root.isVertical ? Theme.spacingS : Theme.spacingXS;
return Math.max(baseHeight + iconsExtraHeight, contentImplicitHeight + padding);
}
readonly property color unfocusedColor: {
switch (SettingsData.workspaceUnfocusedColorMode) {
@@ -915,8 +993,8 @@ Item {
} else if (CompositorService.isNiri) {
if (isRightClick) {
NiriService.toggleOverview();
} else {
NiriService.switchToWorkspace(modelData - 1);
} else if (modelData && modelData.idx !== undefined) {
NiriService.switchToWorkspace(modelData.idx);
}
} else if (CompositorService.isHyprland && modelData?.id) {
if (isRightClick && root.hyprlandOverviewLoader?.item) {
@@ -958,7 +1036,7 @@ Item {
if (root.useExtWorkspace) {
wsData = modelData;
} else if (CompositorService.isNiri) {
wsData = NiriService.allWorkspaces.find(ws => ws.idx + 1 === modelData && ws.output === root.effectiveScreenName) || null;
wsData = modelData || null;
} else if (CompositorService.isHyprland) {
wsData = modelData;
} else if (CompositorService.isDwl) {
@@ -984,6 +1062,8 @@ Item {
if (SettingsData.showWorkspaceApps) {
if (CompositorService.isDwl || CompositorService.isSway || CompositorService.isScroll) {
delegateRoot.loadedIcons = root.getWorkspaceIcons(modelData);
} else if (CompositorService.isNiri) {
delegateRoot.loadedIcons = root.getWorkspaceIcons(isPlaceholder ? null : modelData);
} else {
delegateRoot.loadedIcons = root.getWorkspaceIcons(CompositorService.isHyprland ? modelData : (modelData === -1 ? null : modelData));
}
@@ -1096,8 +1176,12 @@ Item {
Loader {
id: appIconsLoader
anchors.fill: parent
active: SettingsData.showWorkspaceApps
active: SettingsData.showWorkspaceApps || SettingsData.showWorkspaceIndex || SettingsData.showWorkspaceName || loadedHasIcon
sourceComponent: Item {
id: contentRoot
readonly property real contentWidth: contentRow.item?.implicitWidth ?? 0
readonly property real contentHeight: contentRow.item?.implicitHeight ?? 0
Loader {
id: contentRow
anchors.centerIn: parent

View File

@@ -23,6 +23,7 @@ Item {
property string pendingSaveContent: ""
signal hideRequested
signal previewRequested(string content)
Ref {
service: NotepadStorageService
@@ -198,6 +199,10 @@ Item {
createNewTab();
}
onPreviewRequested: {
textEditor.togglePreview()
}
onEscapePressed: {
root.hideRequested();
}

View File

@@ -11,6 +11,12 @@ pragma ComponentBehavior: Bound
Column {
id: root
Component.onCompleted: {
if (PluginService.isPluginLoaded("dankNotepadModule")) {
pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", "")
}
}
property alias text: textArea.text
property alias textArea: textArea
property bool contentLoaded: false
@@ -21,10 +27,15 @@ Column {
property var searchMatches: []
property int currentMatchIndex: -1
property int matchCount: 0
property bool inlinePreviewVisible: false
property string previewMode: "split" // split | full
property string pluginHighlightedHtml: ""
property string lastPluginContent: ""
signal saveRequested()
signal openRequested()
signal newRequested()
signal previewRequested()
signal escapePressed()
signal contentChanged()
signal settingsRequested()
@@ -50,6 +61,7 @@ Column {
lastSavedContent = content
textArea.text = content
contentLoaded = true
syncContentToPlugin()
}
)
}
@@ -164,6 +176,47 @@ Column {
})
}
function togglePreview() {
if (!inlinePreviewVisible) {
inlinePreviewVisible = true
previewMode = "split"
} else if (previewMode === "split") {
previewMode = "full"
} else {
inlinePreviewVisible = false
previewMode = "split"
}
syncContentToPlugin()
}
function renderPreviewHtml() {
if (!inlinePreviewVisible) return ""
return pluginHighlightedHtml.length > 0 ? pluginHighlightedHtml : "<p><i>Rendering preview…</i></p>"
}
function syncContentToPlugin() {
if (!PluginService.isPluginLoaded("dankNotepadModule"))
return
if (!currentTab)
return
const filePath = currentTab?.filePath || ""
const ext = filePath.split('.').pop().toLowerCase()
const content = textArea.text
if (content === lastPluginContent && SettingsData.getBuiltInPluginSetting("dankNotepadModule", "previewActive", false) === inlinePreviewVisible) {
return
}
lastPluginContent = content
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "previewActive", inlinePreviewVisible)
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFilePath", filePath)
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "currentFileExtension", ext)
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "sourceContent", content)
SettingsData.setBuiltInPluginSetting("dankNotepadModule", "updatedAt", Date.now())
}
function hideSearch() {
searchVisible = false
searchQuery = ""
@@ -174,6 +227,57 @@ Column {
textArea.forceActiveFocus()
}
function copyPlainTextToClipboard() {
if (!inlinePreviewVisible || !textArea.text) return
const content = textArea.text
if (content.length > 0) {
const proc = Qt.createQmlObject(`
import QtQuick
import Quickshell.Io
Process {
property string content: ""
command: ["sh", "-c", "printf '%s' \\"$CONTENT\\" | dms clipboard copy"]
environment: { "CONTENT": content }
running: false
}`,
root,
"copyProc"
)
proc.content = content
proc.running = true
proc.exited.connect(() => {
ToastService.showInfo(I18n.tr("Copied to clipboard"))
proc.destroy()
})
}
}
function copyHtmlToClipboard() {
if (!inlinePreviewVisible || !pluginHighlightedHtml) return
if (pluginHighlightedHtml.length > 0) {
const proc = Qt.createQmlObject(`
import QtQuick
import Quickshell.Io
Process {
property string content: ""
command: ["sh", "-c", "printf '%s' \\"$CONTENT\\" | dms clipboard copy"]
environment: { "CONTENT": content }
running: false
}`,
root,
"copyProcHtml"
)
proc.content = pluginHighlightedHtml
proc.running = true
proc.exited.connect(() => {
ToastService.showInfo(I18n.tr("HTML copied to clipboard"))
proc.destroy()
})
}
}
spacing: Theme.spacingM
StyledRect {
@@ -303,7 +407,6 @@ Column {
onClicked: root.findNext()
}
// Close button
DankActionButton {
id: closeSearchButton
Layout.alignment: Qt.AlignVCenter
@@ -323,192 +426,311 @@ Column {
border.width: 1
radius: Theme.cornerRadius
DankFlickable {
id: flickable
RowLayout {
id: editorPreviewRow
anchors.fill: parent
anchors.margins: 1
clip: true
contentWidth: width - 11
spacing: Theme.spacingM
Item {
id: editorPane
visible: !inlinePreviewVisible || previewMode === "split"
Layout.fillHeight: true
Layout.fillWidth: !inlinePreviewVisible || previewMode === "split"
Layout.preferredWidth: inlinePreviewVisible ? parent.width * 0.55 : parent.width
clip: true
DankFlickable {
id: flickable
anchors.fill: parent
clip: true
contentWidth: width - 11
Rectangle {
id: lineNumberArea
anchors.left: parent.left
anchors.top: parent.top
width: SettingsData.notepadShowLineNumbers ? Math.max(30, 32 + Theme.spacingXS) : 0
height: textArea.contentHeight + textArea.topPadding + textArea.bottomPadding
color: "transparent"
visible: SettingsData.notepadShowLineNumbers
ListView {
id: lineNumberList
anchors.top: parent.top
anchors.topMargin: textArea.topPadding
anchors.right: parent.right
anchors.rightMargin: 2
width: 32
height: textArea.contentHeight
model: SettingsData.notepadShowLineNumbers ? root.lineModel : []
interactive: false
spacing: 0
delegate: Item {
id: lineDelegate
required property int index
required property string modelData
width: 32
height: measuringText.contentHeight
Text {
id: measuringText
width: textArea.width - textArea.leftPadding - textArea.rightPadding
text: modelData || " "
font: textArea.font
wrapMode: Text.Wrap
visible: false
}
StyledText {
anchors.right: parent.right
anchors.rightMargin: 4
anchors.top: parent.top
text: index + 1
font.family: textArea.font.family
font.pixelSize: textArea.font.pixelSize
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4)
horizontalAlignment: Text.AlignRight
}
}
}
}
TextArea.flickable: TextArea {
id: textArea
placeholderText: ""
placeholderTextColor: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
font.family: SettingsData.notepadUseMonospace ? SettingsData.monoFontFamily : (SettingsData.notepadFontFamily || SettingsData.fontFamily)
font.pixelSize: SettingsData.notepadFontSize * SettingsData.fontScale
font.letterSpacing: 0
color: Theme.surfaceText
selectedTextColor: Theme.background
selectionColor: Theme.primary
selectByMouse: true
selectByKeyboard: true
wrapMode: TextArea.Wrap
focus: true
activeFocusOnTab: true
textFormat: TextEdit.PlainText
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
persistentSelection: true
tabStopDistance: 40
leftPadding: (SettingsData.notepadShowLineNumbers ? lineNumberArea.width + Theme.spacingXS : Theme.spacingM)
topPadding: Theme.spacingM
rightPadding: Theme.spacingM
bottomPadding: Theme.spacingM
cursorDelegate: Rectangle {
width: 1.5
radius: 1
color: Theme.surfaceText
x: textArea.cursorRectangle.x
y: textArea.cursorRectangle.y
height: textArea.cursorRectangle.height
opacity: 1.0
SequentialAnimation on opacity {
running: textArea.activeFocus
loops: Animation.Infinite
PropertyAnimation { from: 1.0; to: 0.0; duration: 650; easing.type: Easing.InOutQuad }
PropertyAnimation { from: 0.0; to: 1.0; duration: 650; easing.type: Easing.InOutQuad }
}
}
Component.onCompleted: {
loadCurrentTabContent()
setTextDocumentLineHeight()
root.updateLineModel()
Qt.callLater(() => {
textArea.forceActiveFocus()
})
}
Connections {
target: NotepadStorageService
function onCurrentTabIndexChanged() {
loadCurrentTabContent()
Qt.callLater(() => {
textArea.forceActiveFocus()
})
}
function onTabsChanged() {
if (NotepadStorageService.tabs.length > 0 && !contentLoaded) {/* Lines 444-445 omitted */}
}
}
Connections {
target: SettingsData
function onNotepadShowLineNumbersChanged() {
root.updateLineModel()
}
}
onTextChanged: {
if (contentLoaded && text !== lastSavedContent) {
autoSaveTimer.restart()
}
root.contentChanged()
root.updateLineModel()
pluginSyncTimer.restart()
}
Keys.onEscapePressed: (event) => {
root.escapePressed()
event.accepted = true
}
Keys.onPressed: (event) => {
if (event.modifiers & Qt.ControlModifier) {
switch (event.key) {
case Qt.Key_S:
event.accepted = true
root.saveRequested()
break
case Qt.Key_O:
event.accepted = true
root.openRequested()
break
case Qt.Key_N:
event.accepted = true
root.newRequested()
break
case Qt.Key_A:
event.accepted = true
textArea.selectAll()
break
case Qt.Key_F:
event.accepted = true
root.showSearch()
break
case Qt.Key_P:
if (PluginService.isPluginLoaded("dankNotepadModule")) {
event.accepted = true
root.previewRequested()
}
break
}
}
}
background: Rectangle {
color: "transparent"
}
}
StyledText {
id: placeholderOverlay
text: I18n.tr("Start typing your notes here...")
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
font.family: textArea.font.family
font.pixelSize: textArea.font.pixelSize
visible: textArea.text.length === 0
anchors.left: textArea.left
anchors.top: textArea.top
anchors.leftMargin: textArea.leftPadding
anchors.topMargin: textArea.topPadding
z: textArea.z + 1
}
}
}
Rectangle {
id: lineNumberArea
anchors.left: parent.left
anchors.top: parent.top
width: SettingsData.notepadShowLineNumbers ? Math.max(30, 32 + Theme.spacingXS) : 0
height: textArea.contentHeight + textArea.topPadding + textArea.bottomPadding
color: "transparent"
visible: SettingsData.notepadShowLineNumbers
id: previewDivider
visible: inlinePreviewVisible && previewMode === "split"
Layout.fillHeight: true
Layout.preferredWidth: 1
color: Theme.outlineMedium
}
ListView {
id: lineNumberList
Item {
id: previewPane
visible: inlinePreviewVisible
Layout.fillHeight: true
Layout.fillWidth: previewMode === "full"
Layout.preferredWidth: previewMode === "full" ? parent.width : parent.width * 0.45
clip: true
// Preview header with copy buttons
Rectangle {
id: previewHeader
anchors.top: parent.top
anchors.topMargin: textArea.topPadding
anchors.left: parent.left
anchors.right: parent.right
anchors.rightMargin: 2
width: 32
height: textArea.contentHeight
model: SettingsData.notepadShowLineNumbers ? root.lineModel : []
interactive: false
spacing: 0
height: 36
color: Qt.rgba(Theme.surface.r, Theme.surface.g, Theme.surface.b, Theme.notepadTransparency)
z: 2
delegate: Item {
id: lineDelegate
required property int index
required property string modelData
width: 32
height: measuringText.contentHeight
Row {
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
anchors.rightMargin: Theme.spacingM
spacing: Theme.spacingS
Text {
id: measuringText
width: textArea.width - textArea.leftPadding - textArea.rightPadding
text: modelData || " "
font: textArea.font
wrapMode: Text.Wrap
visible: false
// Copy plain text button
DankActionButton {
iconName: "content_copy"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceTextMedium
onClicked: copyPlainTextToClipboard()
}
StyledText {
anchors.right: parent.right
anchors.rightMargin: 4
anchors.top: parent.top
text: index + 1
font.family: textArea.font.family
font.pixelSize: textArea.font.pixelSize
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.4)
horizontalAlignment: Text.AlignRight
anchors.verticalCenter: parent.verticalCenter
text: I18n.tr("Copy Text")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
}
}
}
}
TextArea.flickable: TextArea {
id: textArea
placeholderText: ""
placeholderTextColor: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
font.family: SettingsData.notepadUseMonospace ? SettingsData.monoFontFamily : (SettingsData.notepadFontFamily || SettingsData.fontFamily)
font.pixelSize: SettingsData.notepadFontSize * SettingsData.fontScale
font.letterSpacing: 0
color: Theme.surfaceText
selectedTextColor: Theme.background
selectionColor: Theme.primary
selectByMouse: true
selectByKeyboard: true
wrapMode: TextArea.Wrap
focus: true
activeFocusOnTab: true
textFormat: TextEdit.PlainText
inputMethodHints: Qt.ImhNoPredictiveText | Qt.ImhNoAutoUppercase
persistentSelection: true
tabStopDistance: 40
leftPadding: (SettingsData.notepadShowLineNumbers ? lineNumberArea.width + Theme.spacingXS : Theme.spacingM)
topPadding: Theme.spacingM
rightPadding: Theme.spacingM
bottomPadding: Theme.spacingM
cursorDelegate: Rectangle {
width: 1.5
radius: 1
color: Theme.surfaceText
x: textArea.cursorRectangle.x
y: textArea.cursorRectangle.y
height: textArea.cursorRectangle.height
opacity: 1.0
Rectangle {
width: 1
height: 20
color: Theme.outlineVariant
anchors.verticalCenter: parent.verticalCenter
}
SequentialAnimation on opacity {
running: textArea.activeFocus
loops: Animation.Infinite
PropertyAnimation { from: 1.0; to: 0.0; duration: 650; easing.type: Easing.InOutQuad }
PropertyAnimation { from: 0.0; to: 1.0; duration: 650; easing.type: Easing.InOutQuad }
}
}
// Copy HTML button
DankActionButton {
iconName: "code"
iconSize: Theme.iconSize - 4
iconColor: Theme.surfaceTextMedium
onClicked: copyHtmlToClipboard()
}
Component.onCompleted: {
loadCurrentTabContent()
setTextDocumentLineHeight()
root.updateLineModel()
Qt.callLater(() => {
textArea.forceActiveFocus()
})
}
Connections {
target: NotepadStorageService
function onCurrentTabIndexChanged() {
loadCurrentTabContent()
Qt.callLater(() => {
textArea.forceActiveFocus()
})
}
function onTabsChanged() {
if (NotepadStorageService.tabs.length > 0 && !contentLoaded) {
loadCurrentTabContent()
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: I18n.tr("Copy HTML")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
}
}
}
Connections {
target: SettingsData
function onNotepadShowLineNumbersChanged() {
root.updateLineModel()
DankFlickable {
id: previewFlickable
anchors.top: previewHeader.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.topMargin: Theme.spacingS
clip: true
contentWidth: width - 11
contentHeight: previewText.paintedHeight + Theme.spacingM * 2
Text {
id: previewText
width: parent.width - Theme.spacingM
padding: Theme.spacingM
wrapMode: Text.WordWrap
textFormat: Text.RichText
text: inlinePreviewVisible ? renderPreviewHtml() : ""
color: Theme.surfaceText
font.family: SettingsData.notepadFontFamily || SettingsData.fontFamily
font.pixelSize: Theme.fontSizeMedium
linkColor: Theme.primary
onLinkActivated: url => Qt.openUrlExternally(url)
}
}
onTextChanged: {
if (contentLoaded && text !== lastSavedContent) {
autoSaveTimer.restart()
}
root.contentChanged()
root.updateLineModel()
}
Keys.onEscapePressed: (event) => {
root.escapePressed()
event.accepted = true
}
Keys.onPressed: (event) => {
if (event.modifiers & Qt.ControlModifier) {
switch (event.key) {
case Qt.Key_S:
event.accepted = true
root.saveRequested()
break
case Qt.Key_O:
event.accepted = true
root.openRequested()
break
case Qt.Key_N:
event.accepted = true
root.newRequested()
break
case Qt.Key_A:
event.accepted = true
selectAll()
break
case Qt.Key_F:
event.accepted = true
root.showSearch()
break
}
}
}
background: Rectangle {
color: "transparent"
}
}
StyledText {
id: placeholderOverlay
text: I18n.tr("Start typing your notes here...")
color: Qt.rgba(Theme.surfaceText.r, Theme.surfaceText.g, Theme.surfaceText.b, 0.5)
font.family: textArea.font.family
font.pixelSize: textArea.font.pixelSize
visible: textArea.text.length === 0
anchors.left: textArea.left
anchors.top: textArea.top
anchors.leftMargin: textArea.leftPadding
anchors.topMargin: textArea.topPadding
z: textArea.z + 1
}
}
}
@@ -575,6 +797,24 @@ Column {
color: Theme.surfaceTextMedium
}
}
Row {
spacing: Theme.spacingS
visible: PluginService.isPluginLoaded("dankNotepadModule")
DankActionButton {
iconName: inlinePreviewVisible ? "visibility" : "visibility_off"
iconSize: Theme.iconSize - 2
iconColor: Theme.surfaceText
enabled: textArea.text.length > 0
onClicked: root.previewRequested()
}
StyledText {
anchors.verticalCenter: parent.verticalCenter
text: I18n.tr("Preview")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceTextMedium
}
}
}
DankActionButton {
@@ -646,4 +886,20 @@ Column {
autoSaveToSession()
}
}
Timer {
id: pluginSyncTimer
interval: 350
repeat: false
onTriggered: syncContentToPlugin()
}
Connections {
target: SettingsData
function onBuiltInPluginSettingsChanged() {
if (PluginService.isPluginLoaded("dankNotepadModule")) {
pluginHighlightedHtml = SettingsData.getBuiltInPluginSetting("dankNotepadModule", "highlightedHtml", "")
}
}
}
}

View File

@@ -59,8 +59,8 @@ Item {
readonly property bool hasVerticalPill: verticalBarPill !== null
readonly property bool hasPopout: popoutContent !== null
readonly property int iconSize: Theme.barIconSize(barThickness, -4)
readonly property int iconSizeLarge: Theme.barIconSize(barThickness)
readonly property int iconSize: Theme.barIconSize(barThickness, -4, root.barConfig?.noBackground)
readonly property int iconSizeLarge: Theme.barIconSize(barThickness, undefined, root.barConfig?.noBackground)
Component.onCompleted: {
loadPluginData();

View File

@@ -7,6 +7,9 @@ import qs.Widgets
Item {
id: aboutTab
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property bool isHyprland: CompositorService.isHyprland
property bool isNiri: CompositorService.isNiri
property bool isSway: CompositorService.isSway
@@ -255,7 +258,7 @@ Item {
anchors.horizontalCenter: parent.horizontalCenter
spacing: Theme.spacingS
property bool compactMode: parent.width < 400
property bool compactMode: parent.width < 450
DankButton {
id: docsButton
@@ -628,6 +631,7 @@ Item {
}
Row {
anchors.left: parent.left
spacing: Theme.spacingL
Column {
@@ -637,6 +641,7 @@ Item {
text: I18n.tr("Version")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignLeft
}
StyledText {
@@ -644,6 +649,7 @@ Item {
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
horizontalAlignment: Text.AlignLeft
}
}
@@ -660,6 +666,7 @@ Item {
text: I18n.tr("API")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignLeft
}
StyledText {
@@ -667,6 +674,7 @@ Item {
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
horizontalAlignment: Text.AlignLeft
}
}
@@ -683,6 +691,7 @@ Item {
text: I18n.tr("Status")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignLeft
}
Row {
@@ -701,6 +710,7 @@ Item {
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
horizontalAlignment: Text.AlignLeft
}
}
}
@@ -715,6 +725,8 @@ Item {
text: I18n.tr("Capabilities")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
width: parent.width
horizontalAlignment: Text.AlignLeft
}
Flow {
@@ -780,6 +792,7 @@ Item {
}
Row {
anchors.left: parent.left
spacing: Theme.spacingS
DankButton {

View File

@@ -9,6 +9,9 @@ import qs.Modules.Settings.Widgets
Item {
id: dankBarTab
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property var parentModal: null
property string selectedBarId: "default"
@@ -366,9 +369,12 @@ Item {
font.pixelSize: Theme.fontSizeMedium
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width
horizontalAlignment: Text.AlignLeft
}
Row {
width: parent.width
spacing: Theme.spacingS
StyledText {
@@ -390,12 +396,14 @@ Item {
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignLeft
}
StyledText {
text: "•"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignLeft
}
StyledText {
@@ -409,12 +417,14 @@ Item {
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignLeft
}
StyledText {
text: "•"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignLeft
}
StyledText {
@@ -428,12 +438,14 @@ Item {
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignLeft
}
StyledText {
text: "•"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignLeft
visible: {
SettingsData.barConfigs;
const cfg = SettingsData.getBarConfig(barCard.modelData.id);
@@ -445,6 +457,7 @@ Item {
text: I18n.tr("Disabled")
font.pixelSize: Theme.fontSizeSmall
color: Theme.error
horizontalAlignment: Text.AlignLeft
visible: {
SettingsData.barConfigs;
const cfg = SettingsData.getBarConfig(barCard.modelData.id);
@@ -520,6 +533,7 @@ Item {
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
horizontalAlignment: Text.AlignLeft
}
Column {
@@ -1126,7 +1140,9 @@ Item {
text: I18n.tr("Color")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
x: Theme.spacingM
horizontalAlignment: Text.AlignLeft
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
}
Item {

View File

@@ -572,6 +572,160 @@ Item {
}
}
SettingsCard {
id: pluginVisibilityCard
width: parent.width
iconName: "filter_list"
title: I18n.tr("Plugin Visibility")
settingKey: "pluginVisibility"
property var allLauncherPlugins: {
SettingsData.launcherPluginVisibility;
var plugins = [];
var builtIn = AppSearchService.getBuiltInLauncherPlugins() || {};
for (var pluginId in builtIn) {
var plugin = builtIn[pluginId];
plugins.push({
id: pluginId,
name: plugin.name || pluginId,
icon: plugin.cornerIcon || "extension",
iconType: "material",
isBuiltIn: true,
trigger: AppSearchService.getBuiltInPluginTrigger(pluginId) || ""
});
}
var thirdParty = PluginService.getLauncherPlugins() || {};
for (var pluginId in thirdParty) {
var plugin = thirdParty[pluginId];
var rawIcon = plugin.icon || "extension";
plugins.push({
id: pluginId,
name: plugin.name || pluginId,
icon: rawIcon.startsWith("material:") ? rawIcon.substring(9) : rawIcon.startsWith("unicode:") ? rawIcon.substring(8) : rawIcon,
iconType: rawIcon.startsWith("unicode:") ? "unicode" : "material",
isBuiltIn: false,
trigger: PluginService.getPluginTrigger(pluginId) || ""
});
}
return plugins.sort((a, b) => a.name.localeCompare(b.name));
}
StyledText {
width: parent.width
text: I18n.tr("Control which plugins appear in 'All' mode without requiring a trigger prefix. Disabled plugins will only show when using their trigger.")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
wrapMode: Text.WordWrap
}
Column {
width: parent.width
spacing: Theme.spacingS
Repeater {
model: pluginVisibilityCard.allLauncherPlugins
delegate: Rectangle {
id: visibilityDelegate
required property var modelData
required property int index
width: parent.width
height: 52
radius: Theme.cornerRadius
color: Qt.rgba(Theme.surfaceContainer.r, Theme.surfaceContainer.g, Theme.surfaceContainer.b, 0.3)
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
Item {
width: Theme.iconSize
height: Theme.iconSize
anchors.verticalCenter: parent.verticalCenter
DankIcon {
anchors.centerIn: parent
visible: visibilityDelegate.modelData.iconType !== "unicode"
name: visibilityDelegate.modelData.icon
size: Theme.iconSize
color: Theme.primary
}
StyledText {
anchors.centerIn: parent
visible: visibilityDelegate.modelData.iconType === "unicode"
text: visibilityDelegate.modelData.icon
font.pixelSize: Theme.iconSize
color: Theme.primary
}
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: 2
Row {
spacing: Theme.spacingS
StyledText {
text: visibilityDelegate.modelData.name
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
}
Rectangle {
visible: visibilityDelegate.modelData.isBuiltIn
width: dmsLabel.implicitWidth + Theme.spacingS
height: 16
radius: 8
color: Theme.primaryContainer
anchors.verticalCenter: parent.verticalCenter
StyledText {
id: dmsLabel
anchors.centerIn: parent
text: "DMS"
font.pixelSize: Theme.fontSizeSmall - 2
color: Theme.primaryText
}
}
}
StyledText {
text: visibilityDelegate.modelData.trigger ? I18n.tr("Trigger: %1").arg(visibilityDelegate.modelData.trigger) : I18n.tr("No trigger")
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
}
DankToggle {
id: visibilityToggle
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
checked: SettingsData.getPluginAllowWithoutTrigger(visibilityDelegate.modelData.id)
onToggled: function (isChecked) {
SettingsData.setPluginAllowWithoutTrigger(visibilityDelegate.modelData.id, isChecked);
}
}
}
}
StyledText {
width: parent.width
text: I18n.tr("No launcher plugins installed.")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter
visible: pluginVisibilityCard.allLauncherPlugins.length === 0
}
}
}
SettingsCard {
width: parent.width
iconName: "search"

View File

@@ -61,6 +61,8 @@ StyledRect {
font.pixelSize: Theme.fontSizeLarge
font.weight: Font.Medium
color: Theme.surfaceText
width: parent.width
horizontalAlignment: Text.AlignLeft
visible: root.title !== ""
}

View File

@@ -122,8 +122,10 @@ Scope {
onShouldShowSpotlightChanged: {
if (shouldShowSpotlight) {
if (launcherContent?.controller)
if (launcherContent?.controller) {
launcherContent.controller.searchMode = "apps";
launcherContent.controller.performSearch();
}
return;
}
if (!isActiveScreen)
@@ -198,8 +200,11 @@ Scope {
id: spotlightContainer
x: Theme.snap((parent.width - width) / 2, overlayWindow.dpr)
y: Theme.snap((parent.height - height) / 2, overlayWindow.dpr)
width: Theme.px(500, overlayWindow.dpr)
height: Theme.px(600, overlayWindow.dpr)
readonly property int baseWidth: SettingsData.dankLauncherV2Size === "medium" ? 720 : SettingsData.dankLauncherV2Size === "large" ? 860 : 620
readonly property int baseHeight: SettingsData.dankLauncherV2Size === "medium" ? 720 : SettingsData.dankLauncherV2Size === "large" ? 860 : 600
width: Math.min(baseWidth, overlayWindow.screen.width - 100)
height: Math.min(baseHeight, overlayWindow.screen.height - 100)
readonly property bool animatingOut: niriOverviewScope.isClosing && overlayWindow.isSpotlightScreen
@@ -252,7 +257,7 @@ Scope {
property bool isClosing: niriOverviewScope.isClosing
function hide() {
if (niriOverviewScope.searchActive) {
niriOverviewScope.hideAndReleaseKeyboard();
niriOverviewScope.hideSpotlight();
return;
}
NiriService.toggleOverview();

View File

@@ -49,7 +49,7 @@ Singleton {
function setWorkspaces(newMap) {
root.workspaces = newMap;
allWorkspaces = Object.values(newMap).sort((a, b) => a.idx - b.idx);
root.allWorkspaces = Object.values(newMap).sort((a, b) => a.idx - b.idx);
}
Component.onCompleted: fetchOutputs()
@@ -863,9 +863,13 @@ Singleton {
return currentOutputWorkspaces.map(w => w.idx + 1);
}
function getCurrentOutputWorkspaces() {
return currentOutputWorkspaces.slice();
}
function getCurrentWorkspaceNumber() {
if (focusedWorkspaceIndex >= 0 && focusedWorkspaceIndex < allWorkspaces.length) {
return allWorkspaces[focusedWorkspaceIndex].idx + 1;
return allWorkspaces[focusedWorkspaceIndex].idx;
}
return 1;
}

View File

@@ -366,6 +366,7 @@ Singleton {
property bool _spotlightV2WantsOpen: false
property bool _spotlightV2WantsToggle: false
property string _spotlightV2PendingQuery: ""
property string _spotlightV2PendingMode: ""
function openSpotlightV2() {
if (spotlightV2Modal) {
@@ -388,6 +389,17 @@ Singleton {
}
}
function openSpotlightV2WithMode(mode: string) {
if (spotlightV2Modal) {
spotlightV2Modal.showWithMode(mode);
} else if (spotlightV2ModalLoader) {
_spotlightV2PendingMode = mode;
_spotlightV2WantsOpen = true;
_spotlightV2WantsToggle = false;
spotlightV2ModalLoader.active = true;
}
}
function closeSpotlightV2() {
spotlightV2Modal?.hide();
}
@@ -402,12 +414,26 @@ Singleton {
}
}
function toggleSpotlightV2WithMode(mode: string) {
if (spotlightV2Modal) {
spotlightV2Modal.toggleWithMode(mode);
} else if (spotlightV2ModalLoader) {
_spotlightV2PendingMode = mode;
_spotlightV2WantsToggle = true;
_spotlightV2WantsOpen = false;
spotlightV2ModalLoader.active = true;
}
}
function _onSpotlightV2ModalLoaded() {
if (_spotlightV2WantsOpen) {
_spotlightV2WantsOpen = false;
if (_spotlightV2PendingQuery) {
spotlightV2Modal?.showWithQuery(_spotlightV2PendingQuery);
_spotlightV2PendingQuery = "";
} else if (_spotlightV2PendingMode) {
spotlightV2Modal?.showWithMode(_spotlightV2PendingMode);
_spotlightV2PendingMode = "";
} else {
spotlightV2Modal?.show();
}
@@ -415,7 +441,12 @@ Singleton {
}
if (_spotlightV2WantsToggle) {
_spotlightV2WantsToggle = false;
spotlightV2Modal?.toggle();
if (_spotlightV2PendingMode) {
spotlightV2Modal?.toggleWithMode(_spotlightV2PendingMode);
_spotlightV2PendingMode = "";
} else {
spotlightV2Modal?.toggle();
}
}
}