diff --git a/wormhole/README.md b/wormhole/README.md index faadfc2..7c17dea 100644 --- a/wormhole/README.md +++ b/wormhole/README.md @@ -1,9 +1,17 @@ # Wormhole An experimental Magic Wormhole Tentacle for Cthulhu-Go. Suggested shortcut: -**Cthulhu+Alt+Shift+W**, on desktop and laptop layouts. It opens the reader's +**Cthulhu+Ctrl+Shift+W**, on desktop and laptop layouts. It opens the reader's internal **Wormhole menu**, preserving the underlying application's focus. +With a reader supporting Tentacle settings registration, global Preferences → +Tentacles → select Wormhole → Settings opens **Wormhole preferences** directly. +Save persists the two checkboxes and returns to reader preferences; Escape +discards the form's edits. This entry point works without the Wormhole CLI. +Rebuild both the reader and Tentacle to obtain settings registration and the +Ctrl+Shift shortcut; the published 0.1.1 binary uses Alt+Shift and has no +registered settings entry point. + Install the `magic-wormhole` CLI using your distribution's package manager. Clipboard operations require `xclip` on Xorg. Missing `wormhole` does not stop reader startup: the shortcut speaks and brailles **Magic wormhole is not @@ -117,7 +125,7 @@ After configuring the local entry: 1. Quit Cthulhu with Cthulhu+Q. Press Ctrl+Escape, then Shift+G. These are separate key presses. Shift+G rebuilds the reader and braille helper. -2. Focus an ordinary multiline text field. Open Cthulhu+Alt+Shift+W. Expect +2. Focus an ordinary multiline text field. Open Cthulhu+Ctrl+Shift+W. Expect “Wormhole menu. Send focused item” in speech and the menu option on braille. Native application focus must stay on the original field. Send it to a second Wormhole endpoint; the recipient must get exactly the current line. diff --git a/wormhole/src/main.go b/wormhole/src/main.go index 8fe958b..33afe18 100644 --- a/wormhole/src/main.go +++ b/wormhole/src/main.go @@ -90,6 +90,25 @@ func (w *wormhole) preferencesForm() tentacle.Interaction { {ID: "save", Label: "Save", Kind: tentacle.Button}, }}, Next: "preferences"} } +func (w *wormhole) openPreferences(ctx context.Context, _ json.RawMessage) (any, error) { + w.mutex.Lock() + defer w.mutex.Unlock() + if err := ctx.Err(); err != nil { + return nil, err + } + result := w.preferencesForm() + result.Next = "save-settings" + return result, nil +} +func (w *wormhole) saveSettings(ctx context.Context, raw json.RawMessage) (any, error) { + value, err := w.savePreferences(ctx, raw) + if err != nil { + return nil, err + } + result := value.(tentacle.Interaction) + result.Form, result.Next, result.Done = nil, "", true + return result, nil +} func (w *wormhole) savePreferences(ctx context.Context, raw json.RawMessage) (any, error) { value, err := submission(raw) if err != nil { @@ -146,11 +165,13 @@ func (w *wormhole) commands() []tentacle.Command { {"wormhole", "Wormhole menu", w.open}, {"menu", "Wormhole menu selection", w.choose}, {"send", "Send Wormhole item", w.send}, {"receive", "Receive Wormhole item", w.receive}, {"preferences", "Save Wormhole preferences", w.savePreferences}, {"control", "Wormhole transfer controls", w.control}, {"poll", "Wormhole transfer status", w.poll}, + {"settings", "Wormhole preferences", w.openPreferences}, {"save-settings", "Save Wormhole settings", w.saveSettings}, } { command := tentacle.Command{ID: entry.id, Title: entry.title, Description: entry.title, Presentation: true, Interaction: true, Handle: entry.handler} if entry.id == "wormhole" { - command.Bindings = []tentacle.Binding{{Layout: "desktop", Scope: "global", Key: "Cthulhu+Alt+Shift+W"}, {Layout: "laptop", Scope: "global", Key: "Cthulhu+Alt+Shift+W"}} + command.Bindings = []tentacle.Binding{{Layout: "desktop", Scope: "global", Key: "Cthulhu+Ctrl+Shift+W"}, {Layout: "laptop", Scope: "global", Key: "Cthulhu+Ctrl+Shift+W"}} } + command.Settings = entry.id == "settings" commands = append(commands, command) } return commands diff --git a/wormhole/src/wormhole_test.go b/wormhole/src/wormhole_test.go index 0fa6eda..5c6bcf6 100644 --- a/wormhole/src/wormhole_test.go +++ b/wormhole/src/wormhole_test.go @@ -89,6 +89,57 @@ func TestMenuPreferencesAndMissingDependency(t *testing.T) { t.Fatal(result, err) } } + +func TestRegisteredSettingsAndDefaultShortcut(t *testing.T) { + w := testWormhole(t, "exit 0\n") + w.program = filepath.Join(t.TempDir(), "missing") + settings := 0 + for _, command := range w.commands() { + if command.ID == "wormhole" { + if len(command.Bindings) != 2 { + t.Fatal(command.Bindings) + } + for index, layout := range []string{"desktop", "laptop"} { + binding := command.Bindings[index] + if binding.Layout != layout || binding.Scope != "global" || binding.Key != "Cthulhu+Ctrl+Shift+W" { + t.Fatal(binding) + } + } + } + if !command.Settings { + continue + } + settings++ + value, err := command.Handle(context.Background(), nil) + if err != nil { + t.Fatal(err) + } + form := value.(tentacle.Interaction) + if !command.Interaction || !command.Presentation || form.Form.Title != "Wormhole preferences" || form.Next != "save-settings" { + t.Fatal(command, form) + } + } + if settings != 1 { + t.Fatalf("settings entry points: %d", settings) + } + value, err := w.saveSettings(context.Background(), arguments(tentacle.Submission{Activated: "save", Values: map[string]tentacle.Value{"copy-code": {Checked: true}}})) + if err != nil { + t.Fatal(err) + } + result := value.(tentacle.Interaction) + if !result.Done || result.Form != nil || result.Next != "" || !w.preferences.CopySendCode { + t.Fatal(result) + } + data, err := os.ReadFile(w.settingsPath) + var saved preferences + if err != nil || json.Unmarshal(data, &saved) != nil || !saved.CopySendCode { + t.Fatal("settings did not persist", err) + } + _, err = w.saveSettings(context.Background(), arguments(tentacle.Submission{Cancelled: true})) + if err != nil || !w.preferences.CopySendCode { + t.Fatal("Cancel changed saved settings", err) + } +} func TestSendPreservesClipboardTextAndCodePreference(t *testing.T) { for _, copyCode := range []bool{false, true} { t.Run(map[bool]string{false: "read-only", true: "copy-code"}[copyCode], func(t *testing.T) { diff --git a/wormhole/tentacle.toml b/wormhole/tentacle.toml index 55801da..1c6e306 100644 --- a/wormhole/tentacle.toml +++ b/wormhole/tentacle.toml @@ -18,6 +18,6 @@ major = 1 minor = 0 [requirements] -reader = "Cthulhu-Go with interactive Tentacle support" +reader = "Cthulhu-Go with interactive Tentacle settings support" runtime = "Magic Wormhole CLI (wormhole); xclip for clipboard operations" build = "Go 1.24 or newer and the adjacent cthulhu-go source checkout"