diff --git a/README.md b/README.md index 11a7169..ef8a722 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,9 @@ This project uses [Wails](https://wails.io) to build a desktop application with ## Plugins Dequoter can be extended with new filters written in [UCL](https://ucl.lmika.dev). -On startup, if the directory `$HOME/.config/dequoter/plugins` exists, every file ending in `.ucl` -found in it (including subdirectories) is evaluated. Filters are defined with the `d:filter` builtin: +On startup, Dequoter creates `$HOME/Library/Application Support/dev.lmika.dequoter/plugins` if needed, +then evaluates every file ending in `.ucl` found in it (including subdirectories). +Filters are defined with the `d:filter` builtin: ``` d:filter "String: To Upper Case" { |in| @@ -33,6 +34,8 @@ fail to load are skipped and reported in the status bar; the remaining files are After editing plugin files, choose File → Reload Plugins (Shift+Cmd+R) to rescan the directory without restarting. This discards the previous plugin filters and UCL interpreter state before loading the files again. +Use File → Open Plugins in Finder or File → Open Plugins in Terminal to access the directory. +If you have plugins in the old `$HOME/.config/dequoter/plugins` directory, move them to the new location. ## Download @@ -52,4 +55,4 @@ The above copyright notice and this permission notice shall be included in all c THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR -OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file +OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/app.go b/app.go index 37d2bd1..8a467ef 100644 --- a/app.go +++ b/app.go @@ -4,6 +4,7 @@ import ( "context" "fmt" "log" + "os/exec" "sort" "strings" "sync" @@ -96,10 +97,11 @@ func (a *App) loadPluginsFrom(dir string) int { func (a *App) startup(ctx context.Context) { a.baseCtx = ctx - dir, err := pluginDir() + dir, err := ensurePluginDir() if err != nil { - log.Printf("cannot determine plugin directory: %v", err) - a.setUCLInst(newUCLInst(newPluginFilters()), nil) + plugins := newPluginFilters() + plugins.recordError(err) + a.setUCLInst(newUCLInst(plugins), plugins) return } a.loadPluginsFrom(dir) @@ -114,11 +116,11 @@ func (a *App) domReady(ctx context.Context) { // ReloadPlugins discards the current plugin filters and UCL interpreter, rescans the plugin // directory, and refreshes the command palette. Invoked from the File menu. func (a *App) ReloadPlugins() { - dir, err := pluginDir() + dir, err := ensurePluginDir() if err != nil { _, _, ctx := a.current() runtime.EventsEmit(ctx, "set-statusbar-message", SetStatusbarMessage{ - Message: fmt.Sprintf("Cannot determine plugin directory: %v", err), + Message: fmt.Sprintf("Cannot prepare plugin directory: %v", err), Error: true, }) return @@ -131,6 +133,21 @@ func (a *App) ReloadPlugins() { a.reportPluginStatus(count, true) } +// openPluginDir opens the plugin directory using the specified macOS application. +func (a *App) openPluginDir(application string) { + dir, err := ensurePluginDir() + if err == nil { + err = exec.Command("/usr/bin/open", "-a", application, dir).Run() + } + if err != nil { + _, _, ctx := a.current() + runtime.EventsEmit(ctx, "set-statusbar-message", SetStatusbarMessage{ + Message: fmt.Sprintf("Cannot open plugins in %s: %v", application, err), + Error: true, + }) + } +} + // reportPluginStatus shows plugin load errors in the status bar. If there are no errors and // reloaded is true, a confirmation message is shown instead. func (a *App) reportPluginStatus(filterCount int, reloaded bool) { diff --git a/go.sum b/go.sum index 94bf82c..f3643af 100644 --- a/go.sum +++ b/go.sum @@ -796,7 +796,5 @@ lmika.dev/pkg/modash v0.1.0/go.mod h1:8NDl/yR1eCCEhip9FJlVuMNXIeaztQ0Ks/tizExFcT lmika.dev/pkg/progdoc v0.0.0-20260207235039-984257fb414b h1:uarBEkpjAnpzO98btsA6ZDw9oGnJX8zSb8gZnCFl+6Y= lmika.dev/pkg/progdoc v0.0.0-20260207235039-984257fb414b/go.mod h1:GefQ+wxW6L0p2TfB4fzoP7ihOgEt5Lb/1qgXhruVHos= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -ucl.lmika.dev v0.1.6 h1:cZocpR/IQcy9nvE8u+SSLY7/PE9fd1HIVWhZr/m+U5M= -ucl.lmika.dev v0.1.6/go.mod h1:Hjl6Udph2yuOPcBSytz4SFJVt5MZbM5JyiSAyTMTbNU= ucl.lmika.dev v0.1.7 h1:3yfvWOhBnWpQIm4NhKrUBh5PajB1wzUWzoPPa8ltaVo= ucl.lmika.dev v0.1.7/go.mod h1:Hjl6Udph2yuOPcBSytz4SFJVt5MZbM5JyiSAyTMTbNU= diff --git a/main.go b/main.go index 73b5430..167c39f 100644 --- a/main.go +++ b/main.go @@ -40,6 +40,12 @@ func main() { fileMenu.AddText("Reload Plugins", keys.Combo("r", keys.CmdOrCtrlKey, keys.ShiftKey), func(_ *menu.CallbackData) { app.ReloadPlugins() }) + fileMenu.AddText("Open Plugins in Finder", nil, func(_ *menu.CallbackData) { + app.openPluginDir("Finder") + }) + fileMenu.AddText("Open Plugins in Terminal", nil, func(_ *menu.CallbackData) { + app.openPluginDir("Terminal") + }) fileMenu.AddSeparator() fileMenu.AddText("Quit", keys.CmdOrCtrl("q"), func(_ *menu.CallbackData) { // `rt` is an alias of "github.com/wailsapp/wails/v2/pkg/runtime" to prevent collision with standard package diff --git a/plugins.go b/plugins.go index baaf476..036062e 100644 --- a/plugins.go +++ b/plugins.go @@ -173,13 +173,24 @@ func (p *PluginFilters) Errors() []error { return p.errors } -// pluginDir returns the plugin directory: $HOME/.config/dequoter/plugins +// pluginDir returns the plugin directory under macOS Application Support. func pluginDir() (string, error) { home, err := os.UserHomeDir() if err != nil { return "", err } - return filepath.Join(home, ".config", "dequoter", "plugins"), nil + return filepath.Join(home, "Library", "Application Support", "dev.lmika.dequoter", "Plugins"), nil +} + +func ensurePluginDir() (string, error) { + dir, err := pluginDir() + if err != nil { + return "", err + } + if err := os.MkdirAll(dir, 0755); err != nil { + return "", fmt.Errorf("create plugin directory: %w", err) + } + return dir, nil } var nonSlugChars = regexp.MustCompile(`[^a-z0-9]+`)