1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-01 19:18:28 -04:00

feat: add QR Generator launcher (#2941)

* feat: add QR Generator modal with auto-generate on input debounce

- QRGeneratorModal with dual-buffer sequential fade animation
- Auto-generate QR code on typing stop (200ms debounce)
- Clear text button on input field
- Escape key closes modal
- Register modal in PopoutService and AppSearchService
- Go backend for text QR code generation

* fix: add QR Generator toggle to Launcher DMS settings

* qr: also add qrg built in launcher plugin

---------

Co-authored-by: bbedward <bbedward@gmail.com>
This commit is contained in:
Huỳnh Thiện Lộc
2026-07-31 03:33:36 +07:00
committed by GitHub
parent 2d3706321a
commit 0033e3f0e0
8 changed files with 440 additions and 11 deletions
+18
View File
@@ -43,6 +43,8 @@ func HandleRequest(conn *models.Conn, req models.Request, manager *Manager) {
handleGetNetworkQRCode(conn, req, manager)
case "network.qrcode-content":
handleGetNetworkQRCodeContent(conn, req, manager)
case "network.generate-qrcode":
handleGenerateQRCode(conn, req)
case "network.delete-qrcode":
handleDeleteQRCode(conn, req, manager)
case "network.ethernet.info":
@@ -365,6 +367,22 @@ func handleGetNetworkQRCodeContent(conn *models.Conn, req models.Request, manage
models.Respond(conn, req.ID, content)
}
func handleGenerateQRCode(conn *models.Conn, req models.Request) {
text, err := params.String(req.Params, "text")
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
paths, err := generateTextQRCode(text)
if err != nil {
models.RespondError(conn, req.ID, err.Error())
return
}
models.Respond(conn, req.ID, paths)
}
func handleDeleteQRCode(conn *models.Conn, req models.Request, _ *Manager) {
path, err := params.String(req.Params, "path")
if err != nil {
@@ -0,0 +1,64 @@
package network
import (
"crypto/sha256"
"fmt"
"os"
"time"
"github.com/yeqown/go-qrcode/v2"
"github.com/yeqown/go-qrcode/writer/standard"
)
const textQRCodeTmpPrefix = "/tmp/dank-text-qrcode-"
func generateTextQRCode(text string) ([2]string, error) {
qrc, err := qrcode.New(text)
if err != nil {
return [2]string{}, fmt.Errorf("failed to create QR code for text: %w", err)
}
pathThemed, pathNormal := textQRCodePaths(text)
if err := saveQRCodePNG(qrc, pathThemed, standard.WithBgTransparent(), standard.WithFgColorRGBHex("#ffffff")); err != nil {
return [2]string{}, err
}
if err := saveQRCodePNG(qrc, pathNormal); err != nil {
return [2]string{}, err
}
return [2]string{pathThemed, pathNormal}, nil
}
// Write to a temp file and rename into place so the shell's Image never
// observes a partially written PNG.
func saveQRCodePNG(qrc *qrcode.QRCode, path string, opts ...standard.ImageOption) error {
tmpPath := path + ".tmp"
opts = append(opts, standard.WithBuiltinImageEncoder(standard.PNG_FORMAT))
w, err := standard.New(tmpPath, opts...)
if err != nil {
return fmt.Errorf("failed to create QR code writer: %w", err)
}
if err := qrc.Save(w); err != nil {
os.Remove(tmpPath)
return fmt.Errorf("failed to save QR code: %w", err)
}
if err := os.Rename(tmpPath, path); err != nil {
os.Remove(tmpPath)
return fmt.Errorf("failed to move QR code into place: %w", err)
}
return nil
}
// Paths are unique per generation, not per text: the library's mask selection
// is non-deterministic, so regenerating the same text produces different
// bytes, and reusing a path lets the shell's URL-keyed pixmap cache serve a
// stale pattern over the new file.
func textQRCodePaths(text string) (themed, normal string) {
hash := fmt.Sprintf("%x", sha256.Sum256([]byte(text)))[:8]
nonce := time.Now().UnixNano()
themed = fmt.Sprintf("%s%s-%d-themed.png", textQRCodeTmpPrefix, hash, nonce)
normal = fmt.Sprintf("%s%s-%d-normal.png", textQRCodeTmpPrefix, hash, nonce)
return
}
+1 -1
View File
@@ -24,7 +24,7 @@ func qrCodePaths(ssid string) (themed, normal string) {
func isValidQRCodePath(path string) bool {
clean := filepath.Clean(path)
return strings.HasPrefix(clean, qrCodeTmpPrefix) && strings.HasSuffix(clean, ".png")
return (strings.HasPrefix(clean, qrCodeTmpPrefix) || strings.HasPrefix(clean, textQRCodeTmpPrefix)) && strings.HasSuffix(clean, ".png")
}
var safePathChar = regexp.MustCompile(`[^a-zA-Z0-9_-]`)