Updated wormhole.

This commit is contained in:
ak47 committed 2026-10-13 21:47:55 -04:00
1 parent e56771aa22
commit 48f7d5262c
4 files changed
+84 -4

No files matched your search

+10 -2
View File
@@ -1,9 +1,17 @@
# Wormhole # Wormhole
An experimental Magic Wormhole Tentacle for Cthulhu-Go. Suggested shortcut: 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. 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. Install the `magic-wormhole` CLI using your distribution's package manager.
Clipboard operations require `xclip` on Xorg. Missing `wormhole` does not stop Clipboard operations require `xclip` on Xorg. Missing `wormhole` does not stop
reader startup: the shortcut speaks and brailles **Magic wormhole is not 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 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. 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. “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 Native application focus must stay on the original field. Send it to a
second Wormhole endpoint; the recipient must get exactly the current line. second Wormhole endpoint; the recipient must get exactly the current line.
+22 -1
View File
@@ -90,6 +90,25 @@ func (w *wormhole) preferencesForm() tentacle.Interaction {
{ID: "save", Label: "Save", Kind: tentacle.Button}, {ID: "save", Label: "Save", Kind: tentacle.Button},
}}, Next: "preferences"} }}, 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) { func (w *wormhole) savePreferences(ctx context.Context, raw json.RawMessage) (any, error) {
value, err := submission(raw) value, err := submission(raw)
if err != nil { if err != nil {
@@ -146,11 +165,13 @@ func (w *wormhole) commands() []tentacle.Command {
{"wormhole", "Wormhole menu", w.open}, {"menu", "Wormhole menu selection", w.choose}, {"wormhole", "Wormhole menu", w.open}, {"menu", "Wormhole menu selection", w.choose},
{"send", "Send Wormhole item", w.send}, {"receive", "Receive Wormhole item", w.receive}, {"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}, {"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} command := tentacle.Command{ID: entry.id, Title: entry.title, Description: entry.title, Presentation: true, Interaction: true, Handle: entry.handler}
if entry.id == "wormhole" { 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) commands = append(commands, command)
} }
return commands return commands
+51
View File
@@ -89,6 +89,57 @@ func TestMenuPreferencesAndMissingDependency(t *testing.T) {
t.Fatal(result, err) 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) { func TestSendPreservesClipboardTextAndCodePreference(t *testing.T) {
for _, copyCode := range []bool{false, true} { for _, copyCode := range []bool{false, true} {
t.Run(map[bool]string{false: "read-only", true: "copy-code"}[copyCode], func(t *testing.T) { t.Run(map[bool]string{false: "read-only", true: "copy-code"}[copyCode], func(t *testing.T) {
+1 -1
View File
@@ -18,6 +18,6 @@ major = 1
minor = 0 minor = 0
[requirements] [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" runtime = "Magic Wormhole CLI (wormhole); xclip for clipboard operations"
build = "Go 1.24 or newer and the adjacent cthulhu-go source checkout" build = "Go 1.24 or newer and the adjacent cthulhu-go source checkout"