From 4f4d430db11f4d7be9cb7c6562d611f088cc01cc Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Sat, 13 Sep 2025 10:31:07 +0300 Subject: Simplify UI: remove Days/Tag/What menus; reorder bottom controls to 'Log text', 'Clear', 'Preferences', char count. Simplify Preferences to directory only. Add Magefile with build, run, clean, android, androidcross targets; make run verbose for debugging. --- Magefile.go | 201 ++++++++++++++++++++++++++++++++++++++++++++++++++++ go.mod | 1 + go.sum | 2 + main.go | 171 +++++++++++++------------------------------- ql-250516-000347.md | 1 + 5 files changed, 256 insertions(+), 120 deletions(-) create mode 100644 Magefile.go create mode 100644 ql-250516-000347.md diff --git a/Magefile.go b/Magefile.go new file mode 100644 index 0000000..49a5fb4 --- /dev/null +++ b/Magefile.go @@ -0,0 +1,201 @@ +//go:build mage +// +build mage + +package main + +import ( + "io" + "fmt" + "os" + "path/filepath" + "runtime" + "strconv" + "time" + + "github.com/magefile/mage/sh" +) + +// Build compiles the quicklogger app into ./bin. +func Build() error { + outDir := "bin" + if err := os.MkdirAll(outDir, 0o755); err != nil { + return err + } + + binName := "quicklogger" + if runtime.GOOS == "windows" { + binName += ".exe" + } + + out := filepath.Join(outDir, binName) + fmt.Printf("Building %s\n", out) + return sh.RunV("go", "build", "-o", out, ".") +} + +// Run launches the app with `go run .`. +func Run() error { + fmt.Println("Running quicklogger (verbose build)") + // Show Go version and key env for diagnostics + _ = sh.RunV("go", "version") + _ = sh.RunV("go", "env", "GOVERSION", "GOOS", "GOARCH", "GOMOD", "GOMODCACHE", "GOCACHE") + // Compile+run with verbose and trace flags to show build steps + return sh.RunV("go", "run", "-v", "-x", ".") +} + +// Clean removes build artifacts (bin/ and local APK). +func Clean() error { + fmt.Println("Cleaning build artifacts") + // Remove bin directory + if err := os.RemoveAll("bin"); err != nil { + return err + } + // Remove generated APK if present + _ = os.Remove("quicklogger.apk") + return nil +} + +// Android packages an Android APK via Fyne and copies it to ~/Documents/APKs if present. +func Android() error { + env := map[string]string{} + + // Respect existing ANDROID_NDK_HOME. If not set, try legacy var or a common default path. + ndk := os.Getenv("ANDROID_NDK_HOME") + if ndk == "" { + // Some setups export a misspelled var; honor it if present. + ndk = os.Getenv("ANDORID_NDK_HOME") + } + if ndk == "" { + if home, err := os.UserHomeDir(); err == nil { + guess := filepath.Join(home, "android-ndk", "android-ndk-r26b") + env["ANDROID_NDK_HOME"] = guess + fmt.Printf("ANDROID_NDK_HOME not set, guessing %s\n", guess) + } + } + + fmt.Println("Packaging Android APK via Fyne") + if err := sh.RunWithV(env, "fyne", "package", "-os", "android"); err != nil { + return err + } + + // If ~/Documents/APKs exists, copy the APK there. + home, err := os.UserHomeDir() + if err != nil { + return nil // packaging succeeded; copying is best-effort + } + destDir := filepath.Join(home, "Documents", "APKs") + if st, err := os.Stat(destDir); err == nil && st.IsDir() { + src := "quicklogger.apk" + if _, err := os.Stat(src); err == nil { + dst := filepath.Join(destDir, filepath.Base(src)) + if err := copyFile(src, dst); err != nil { + return err + } + fmt.Printf("Copied %s to %s\n", src, dst) + } + } + return nil +} + +// AndroidCross builds an Android APK using fyne-cross with Docker/Podman. +// Mirrors steps from README using a Podman socket if available. +func AndroidCross() error { + env := map[string]string{} + + // If DOCKER_HOST is not set, try a sensible Podman socket default. + if os.Getenv("DOCKER_HOST") == "" { + uid := os.Geteuid() + sock := filepath.Join("/run/user", strconv.Itoa(uid), "podman", "podman.sock") + if _, err := os.Stat(sock); err == nil { + env["DOCKER_HOST"] = "unix://" + sock + fmt.Printf("Using Podman socket: %s\n", env["DOCKER_HOST"]) + } else { + fmt.Println("DOCKER_HOST is not set and no Podman socket found; relying on default Docker daemon.") + } + } + + // Ensure fyne-cross is available; attempt install if not. + if _, err := sh.Output("which", "fyne-cross"); err != nil { + fmt.Println("Installing fyne-cross (requires network access)...") + if err := sh.RunV("go", "install", "github.com/fyne-io/fyne-cross@latest"); err != nil { + return fmt.Errorf("fyne-cross not found and installation failed: %w", err) + } + } + + // Pull/update builder image then build. + if err := sh.RunWithV(env, "fyne-cross", "android", "--pull"); err != nil { + return err + } + if err := sh.RunWithV(env, "fyne-cross", "android"); err != nil { + return err + } + + // Copy newest APK to ~/Documents/APKs if available. + apk, err := newestAPK("fyne-cross/dist/android") + if err != nil { + // Not fatal; print a note and finish + fmt.Printf("Note: could not locate built APK: %v\n", err) + return nil + } + home, herr := os.UserHomeDir() + if herr != nil { + return nil + } + destDir := filepath.Join(home, "Documents", "APKs") + if st, err := os.Stat(destDir); err == nil && st.IsDir() { + dst := filepath.Join(destDir, filepath.Base(apk)) + if err := copyFile(apk, dst); err != nil { + return err + } + fmt.Printf("Copied %s to %s\n", apk, dst) + } + return nil +} + +func newestAPK(dir string) (string, error) { + entries, err := os.ReadDir(dir) + if err != nil { + return "", err + } + var newest string + var newestMod time.Time + for _, e := range entries { + if e.IsDir() { + continue + } + name := e.Name() + if filepath.Ext(name) != ".apk" { + continue + } + info, err := e.Info() + if err != nil { + continue + } + if info.ModTime().After(newestMod) { + newestMod = info.ModTime() + newest = filepath.Join(dir, name) + } + } + if newest == "" { + return "", fmt.Errorf("no .apk found in %s", dir) + } + return newest, nil +} + +func copyFile(src, dst string) error { + in, err := os.Open(src) + if err != nil { + return err + } + defer in.Close() + + out, err := os.Create(dst) + if err != nil { + return err + } + defer func() { _ = out.Close() }() + + if _, err = io.Copy(out, in); err != nil { + return err + } + return out.Sync() +} diff --git a/go.mod b/go.mod index 278297d..033fb60 100644 --- a/go.mod +++ b/go.mod @@ -19,6 +19,7 @@ require ( github.com/godbus/dbus/v5 v5.1.0 // indirect github.com/gopherjs/gopherjs v1.17.2 // indirect github.com/jsummers/gobmp v0.0.0-20151104160322-e2ba15ffa76e // indirect + github.com/magefile/mage v1.15.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/srwiley/oksvg v0.0.0-20221011165216-be6e8873101c // indirect github.com/srwiley/rasterx v0.0.0-20220730225603-2ab79fcdd4ef // indirect diff --git a/go.sum b/go.sum index d8295d1..f7804c4 100644 --- a/go.sum +++ b/go.sum @@ -207,6 +207,8 @@ github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORN github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/magefile/mage v1.15.0 h1:BvGheCMAsG3bWUDbZ8AyXXpCNwU9u5CB6sM+HNb9HYg= +github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= diff --git a/main.go b/main.go index 5a7d35a..863a40d 100644 --- a/main.go +++ b/main.go @@ -1,16 +1,15 @@ package main import ( - "fmt" - "os" - "strings" - "time" - - "fyne.io/fyne/v2" - "fyne.io/fyne/v2/app" - "fyne.io/fyne/v2/container" - "fyne.io/fyne/v2/dialog" - "fyne.io/fyne/v2/widget" + "fmt" + "os" + "time" + + "fyne.io/fyne/v2" + "fyne.io/fyne/v2/app" + "fyne.io/fyne/v2/container" + "fyne.io/fyne/v2/dialog" + "fyne.io/fyne/v2/widget" ) const ( @@ -20,74 +19,28 @@ const ( ) var ( - defaultDirectory = "." - defaultTagItems = []string{ - "infra", - "log", - "share", - "share:li", - "share:ma", - "share:no", - "track", - "track 10", - "track 15", - "track 20", - "track 25", - "track 30", - "track 5", - "work", - } - defaultWhatItems = []string{ - "Breathing", - "Bulgarian", - "Ema", - "Exercise", - "Meditation", - "Music", - "Reading Articles", - "Reading Books", - "Stretching", - "Tech", - } + defaultDirectory = "." ) var windowSize = fyne.NewSize(400, 100) func createPreferenceWindow(a fyne.App) fyne.Window { - window := a.NewWindow("Preferences") - directoryPreference := widget.NewEntry() - directoryPreference.SetText(a.Preferences().StringWithFallback("Directory", defaultDirectory)) - - tagDropdownPreference := widget.NewEntry() - tagDropdownPreference.SetText(a.Preferences().StringWithFallback("Tags", strings.Join(defaultTagItems, ","))) - - whatDropdownPreference := widget.NewEntry() - whatDropdownPreference.SetText(a.Preferences().StringWithFallback("Whats", strings.Join(defaultWhatItems, ","))) - - window.SetContent(container.NewVBox( - container.NewVBox( - widget.NewLabel("Directory:"), - directoryPreference, - widget.NewLabel("Tags:"), - tagDropdownPreference, - widget.NewLabel("Whats:"), - whatDropdownPreference, - ), - container.NewHBox( - widget.NewButton("Save", func() { - a.Preferences().SetString("Directory", directoryPreference.Text) - a.Preferences().SetString("Tags", tagDropdownPreference.Text) - a.Preferences().SetString("Whats", whatDropdownPreference.Text) - window.Hide() - }), - widget.NewButton("Reset dropdowns", func() { - // directoryPreference.SetText(defaultDirectory) - tagDropdownPreference.SetText(strings.Join(defaultTagItems, ",")) - whatDropdownPreference.SetText(strings.Join(defaultWhatItems, ",")) - }, - ), - ))) - window.Resize(windowSize) + window := a.NewWindow("Preferences") + directoryPreference := widget.NewEntry() + directoryPreference.SetText(a.Preferences().StringWithFallback("Directory", defaultDirectory)) + + window.SetContent(container.NewVBox( + container.NewVBox( + widget.NewLabel("Directory:"), + directoryPreference, + ), + container.NewHBox( + widget.NewButton("Save", func() { + a.Preferences().SetString("Directory", directoryPreference.Text) + window.Hide() + }), + ))) + window.Resize(windowSize) return window } @@ -128,42 +81,20 @@ func createMainWindow(a fyne.App) fyne.Window { } } - // Dropdown with pre-selectable items - daysDropdown := widget.NewSelect([]string{"0", "1", "3", "7", "14", "30", "60", "99"}, func(selected string) { - input.SetText(selected + " ") - window.Canvas().Focus(input) - }) - daysDropdown.PlaceHolder = "Days" - - tagDropdownItems := strings.Split(a.Preferences().StringWithFallback("Tags", strings.Join(defaultTagItems, ",")), ",") - tagDropdown := widget.NewSelect(tagDropdownItems, func(selected string) { - input.Append(selected + " ") - window.Canvas().Focus(input) - }) - tagDropdown.PlaceHolder = "Tag" - - whatDropdownItems := strings.Split(a.Preferences().StringWithFallback("Whats", strings.Join(defaultWhatItems, ",")), ",") - whatDropdown := widget.NewSelect(whatDropdownItems, func(selected string) { - input.Append(selected + " ") - window.Canvas().Focus(input) - input.Cursor() - }) - whatDropdown.PlaceHolder = "What" - - logTextButton := widget.NewButton("Log text", func() { - filename := fmt.Sprintf("%s/ql-%s.md", - a.Preferences().StringWithFallback("Directory", defaultDirectory), - time.Now().Format("060102-150405"), - ) - if err := os.WriteFile(filename, []byte(input.Text), 0644); err != nil { - dialog.ShowError(err, window) - return - } - input.SetText("") - input.SetPlaceHolder(placeholderText) - // Reset character count - charCount.SetText("0 chars") - }) + logTextButton := widget.NewButton("Log text", func() { + filename := fmt.Sprintf("%s/ql-%s.md", + a.Preferences().StringWithFallback("Directory", defaultDirectory), + time.Now().Format("060102-150405"), + ) + if err := os.WriteFile(filename, []byte(input.Text), 0644); err != nil { + dialog.ShowError(err, window) + return + } + input.SetText("") + input.SetPlaceHolder(placeholderText) + // Reset character count + charCount.SetText("0 chars") + }) // Optimization 5: Add clear button for quick text clearing clearButton := widget.NewButton("Clear", func() { @@ -172,17 +103,17 @@ func createMainWindow(a fyne.App) fyne.Window { window.Canvas().Focus(input) }) - window.SetContent(container.NewVBox( - container.NewHBox(daysDropdown, tagDropdown, whatDropdown, logTextButton), - input, - container.NewHBox( - widget.NewButton("Preferences", func() { - createPreferenceWindow(a).Show() - }), - clearButton, - charCount, // Show character count - ), - )) + window.SetContent(container.NewVBox( + input, + container.NewHBox( + logTextButton, + clearButton, + widget.NewButton("Preferences", func() { + createPreferenceWindow(a).Show() + }), + charCount, // Show character count + ), + )) window.Resize(windowSize) window.Canvas().Focus(input) diff --git a/ql-250516-000347.md b/ql-250516-000347.md new file mode 100644 index 0000000..09c37cb --- /dev/null +++ b/ql-250516-000347.md @@ -0,0 +1 @@ +7 share:ma Reading Articles \ No newline at end of file -- cgit v1.2.3