LazyRun v1.0.0: shortcut maker untuk executable dan PWA
- Fungsi A: shortcut dari file executable (ELF/AppImage/script) - Fungsi B: shortcut PWA/URL dengan deteksi browser (native, /opt, Flatpak) - Checkbox Menu Aplikasi / Desktop, desktop-only pakai NoDisplay=true - List + uninstall + deteksi shortcut yang file .desktop-nya hilang - Metadata ~/.config/lazyrun/shortcuts.json, refresh cache XDG/KDE - AppImage + zsync update info via build.sh
This commit is contained in:
1 parent
9cd5a1f82d
commit
da31f7d7d7
31 files changed
+2916
No files matched your search
@@ -0,0 +1,89 @@
|
||||
package shortcut
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
// Entry adalah isi file .desktop yang dihasilkan LazyRun.
|
||||
type Entry struct {
|
||||
Name string
|
||||
ExecArgs []string // argv lengkap; tiap arg di-quote sesuai spec Exec
|
||||
Icon string // path absolut atau nama ikon
|
||||
Categories string // sudah diakhiri ";" atau kosong
|
||||
// NoDisplay menyembunyikan entry dari menu aplikasi. Dipakai kalau user
|
||||
// hanya memilih Desktop: file tetap ada di applications/ sebagai sumber kebenaran.
|
||||
NoDisplay bool
|
||||
}
|
||||
|
||||
// Render menghasilkan isi file .desktop.
|
||||
func (e Entry) Render() string {
|
||||
var b strings.Builder
|
||||
b.WriteString("[Desktop Entry]\n")
|
||||
b.WriteString("Type=Application\n")
|
||||
fmt.Fprintf(&b, "Name=%s\n", escapeValue(e.Name))
|
||||
fmt.Fprintf(&b, "Exec=%s\n", execArgs(e.ExecArgs))
|
||||
if e.Icon != "" {
|
||||
fmt.Fprintf(&b, "Icon=%s\n", escapeValue(e.Icon))
|
||||
}
|
||||
b.WriteString("Terminal=false\n")
|
||||
if e.Categories != "" {
|
||||
fmt.Fprintf(&b, "Categories=%s\n", escapeValue(e.Categories))
|
||||
}
|
||||
if e.NoDisplay {
|
||||
b.WriteString("NoDisplay=true\n")
|
||||
}
|
||||
b.WriteString("Comment=Created by LazyRun\n")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
// WriteDesktop menulis entry .desktop ke path.
|
||||
func WriteDesktop(path string, e Entry) error {
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return err
|
||||
}
|
||||
return os.WriteFile(path, []byte(e.Render()), 0o644)
|
||||
}
|
||||
|
||||
func execArgs(args []string) string {
|
||||
quoted := make([]string, len(args))
|
||||
for i, a := range args {
|
||||
quoted[i] = execQuote(a)
|
||||
}
|
||||
return strings.Join(quoted, " ")
|
||||
}
|
||||
|
||||
// execQuote mengutip satu argumen sesuai aturan field Exec (spec Desktop Entry).
|
||||
func execQuote(s string) string {
|
||||
if s != "" && !strings.ContainsAny(s, " \t\n\"'\\><~|&;$*?#()`") {
|
||||
return s
|
||||
}
|
||||
r := strings.NewReplacer("\\", "\\\\", "\"", "\\\"", "`", "\\`", "$", "\\$", "\n", "\\n")
|
||||
return "\"" + r.Replace(s) + "\""
|
||||
}
|
||||
|
||||
// escapeValue meng-escape nilai string biasa (Name, Icon, Categories).
|
||||
func escapeValue(s string) string {
|
||||
r := strings.NewReplacer("\\", "\\\\", "\n", "\\n", "\r", "\\r", "\t", "\\t")
|
||||
return r.Replace(s)
|
||||
}
|
||||
|
||||
// slugify membuat nama file yang aman dari nama aplikasi.
|
||||
func slugify(name string) string {
|
||||
var b strings.Builder
|
||||
dash := false
|
||||
for _, r := range strings.ToLower(name) {
|
||||
switch {
|
||||
case r >= 'a' && r <= 'z', r >= '0' && r <= '9', unicode.IsLetter(r), unicode.IsDigit(r):
|
||||
b.WriteRune(r)
|
||||
dash = false
|
||||
case b.Len() > 0 && !dash:
|
||||
b.WriteByte('-')
|
||||
dash = true
|
||||
}
|
||||
}
|
||||
return strings.Trim(b.String(), "-")
|
||||
}
|
||||
Reference in new issue
Block a user