Fenrir can now create aliases for application overrides and put them in your clipboard.

This commit is contained in:
Storm Dragon committed 2026-10-28 22:59:13 -04:00
1 parent 7df1fa3ecb
commit 7d82105d1d
15 files changed
+594 -114

No files matched your search

+8
View File
@@ -235,12 +235,20 @@ using the standard socket:
```bash
echo "command say Hello" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-daemon.sock
echo "command interrupt" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-daemon.sock
echo "command application ninjam" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-daemon.sock
echo "command application default" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-daemon.sock
echo "setting set reading#mode=highlight" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-daemon.sock
```
Set `remote.enabled = false` to disable remote control. Command and settings
requests can also be enabled separately in the `[remote]` section.
`command application ID` selects an override for the reader's active terminal,
like Fenrir+A. IDs are `default`, `networkmanager`, `ninjam`, `rogue`, and `codex`.
In Fenrir+A, select an application and Tab to **Copy alias to clipboard** to
copy a Bash alias that selects its override before launching the program.
The program still launches if Fenrir or `socat` is unavailable.
## Command-line help
Run `./fenrir --help` for all options. Useful options include `--config`,
+26 -2
View File
@@ -12,8 +12,9 @@ For build requirements and setup, see the [README](../README.md).
Press **Fenrir+A** to open **Applications**. Up and Down select an item; Enter
activates it and closes the menu. Escape or the Cancel button closes the menu
without changing the selection. Tab and Shift+Tab move between the list, Cancel,
and the tab selector. The active override is initially selected.
without changing the selection. Tab and Shift+Tab move between the list,
Copy alias to clipboard, Cancel, and the tab selector. The active override is
initially selected.
Type the start of a name to jump to it: **n** selects NetworkManager, then **i**
selects Ninjam. Searching ignores letter case and does not activate the item.
@@ -26,6 +27,22 @@ launch programs. Choose **Default** to restore the settings and reading regions
that were in effect before the override. Overrides are built into Fenrir and
are never selected automatically or remembered across restarts.
To launch a program with its override, select its name in this list, then Tab
to **Copy alias to clipboard** and press Enter. This copies a Bash alias into
Fenrir's clipboard without selecting the override or closing the menu. Paste it
into your Bash configuration, such as `~/.bashrc`, and load that configuration
in a new shell. The aliases use the executable names `cninjam`, `nmtui`, `rogue`,
and `codex`; Default has no alias. Existing aliases with those names are replaced
when you load the definition.
The alias requests the override through `socat`, then launches the real program
with the arguments you supplied. Errors remain visible, and the program still
launches if the request fails, remote control is disabled, or Fenrir or `socat`
is missing. Socket inactivity is limited to two seconds. It uses the standard
socket, or your configured `remote.socket_file`, rather than a process-specific
socket that changes at restart. Paste with Fenrir's clipboard command; desktop
clipboard synchronization follows your normal clipboard settings.
While an override is active, **Fenrir+F** opens its application settings,
with the application name in the title. **OK**, **Fenrir+Control+S**, and remote
`SETTING SAVE` save that application's preferences. Quick and remote changes
@@ -559,6 +576,13 @@ Other supported operations include:
- `command tempdisablespeech`: temporarily silence speech.
- `command clipboard TEXT`: add text to the clipboard.
- `command exportclipboard`: export the selected clipboard.
- `command application ID`: select `default`, `networkmanager`, `ninjam`,
`rogue`, or `codex` for this reader's active terminal, like Fenrir+A.
This requires command remote control. An empty reply means selection
succeeded; invalid IDs, busy settings, and restoration failures return an
error. Application recognition and profile loading happen later, when the
application appears; this command does not launch it. `default` restores
ordinary settings. Use a private socket when targeting a particular reader.
- `command window X1 Y1 X2 Y2`: set a reading window, using zero-based,
inclusive coordinates.
- `command resetwindow`: clear the window.
+35
View File
@@ -0,0 +1,35 @@
// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"strings"
"git.stormux.org/storm/fenrir/internal/control"
)
func shell_quote(text string) string {
return "'" + strings.ReplaceAll(text, "'", "'\"'\"'") + "'"
}
// The last simple command receives the arguments following the alias. Keep the
// optional reader request in an OR list so errexit cannot prevent the launch.
func application_alias(id, socketPath string) string {
program := ""
switch id {
case "networkmanager":
program = "nmtui"
case "ninjam":
program = "cninjam"
case "rogue", "codex":
program = id
default:
return ""
}
if socketPath == "" {
socketPath = control.MainSocket
}
body := "(echo " + shell_quote("command application "+id) + " | socat -T 2 - " + shell_quote("UNIX-CLIENT:"+socketPath) + ") || :; command " + program
return "alias " + program + "=" + shell_quote(body)
}
+153
View File
@@ -0,0 +1,153 @@
// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"context"
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
"time"
"git.stormux.org/storm/fenrir/internal/control"
"git.stormux.org/storm/fenrir/internal/interaction"
)
func TestApplicationAliasesShellCheck(t *testing.T) {
path, err := exec.LookPath("shellcheck")
if err != nil {
t.Skip("shellcheck unavailable")
}
for _, id := range []string{"networkmanager", "ninjam", "rogue", "codex"} {
process := exec.Command(path, "-s", "bash", "-")
process.Stdin = strings.NewReader(application_alias(id, "") + "\n")
if output, err := process.CombinedOutput(); err != nil {
t.Fatalf("%s: %v: %s", id, err, output)
}
}
}
func TestApplicationAliasThroughPrivateSocket(t *testing.T) {
socatPath, err := exec.LookPath("socat")
if err != nil {
t.Skip("socat unavailable")
}
directory := t.TempDir()
socketPath := filepath.Join(directory, "reader.sock")
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
server, err := control.Open(ctx, control.Options{SocketFile: socketPath, RegistryDirectory: directory})
if err != nil {
t.Fatal(err)
}
defer server.Close()
if err := os.Symlink(socatPath, filepath.Join(directory, "socat")); err != nil {
t.Fatal(err)
}
if err := os.WriteFile(filepath.Join(directory, "cninjam"), []byte("#!/bin/sh\necho application-launched\n"), 0700); err != nil {
t.Fatal(err)
}
a, _ := persistence_application(t, true)
override_read(t, a, override_screen(1, "[Username@host directory] $"))
process := exec.CommandContext(ctx, "/bin/bash", "--noprofile", "--norc", "-c", "shopt -s expand_aliases\n"+application_alias("ninjam", socketPath)+"\ncninjam\n")
process.Env = append(os.Environ(), "PATH="+directory)
completed := make(chan struct{})
var output []byte
var processError error
go func() {
output, processError = process.CombinedOutput()
close(completed)
}()
select {
case request := <-server.Requests():
if request.Text != "command application ninjam" {
t.Fatal(request.Text)
}
a.remote_request(request)
case <-ctx.Done():
t.Fatal("alias did not send selection request")
}
<-completed
if processError != nil || string(output) != "application-launched\n" || a.overrides[1] == nil {
t.Fatalf("launch failed: %v, %s", processError, output)
}
override_read(t, a, override_screen(1, "ninjam"))
if a.overrideCurrent == nil || a.overrideCurrent.id != "ninjam" {
t.Fatal("pre-launch selection did not activate when application appeared")
}
}
func TestApplicationAliasLaunchesWithArgumentsDespiteReaderFailure(t *testing.T) {
for _, scenario := range []string{"success", "rejected", "failed", "missing"} {
t.Run(scenario, func(t *testing.T) {
directory := t.TempDir()
requestPath := filepath.Join(directory, "request")
argsPath := filepath.Join(directory, "args")
program := "#!/bin/sh\nprintf '%s\\n' \"$@\" > \"$ALIAS_ARGS\"\nexit 7\n"
if err := os.WriteFile(filepath.Join(directory, "cninjam"), []byte(program), 0700); err != nil {
t.Fatal(err)
}
if scenario != "missing" {
client := "#!/bin/sh\nprintf '%s\\n' \"$@\" > \"$ALIAS_REQUEST\"\n/bin/cat >> \"$ALIAS_REQUEST\"\n"
if scenario == "rejected" {
client += "echo 'Command remote control is disabled'\n"
}
if scenario == "failed" {
client += "exit 1\n"
}
if err := os.WriteFile(filepath.Join(directory, "socat"), []byte(client), 0700); err != nil {
t.Fatal(err)
}
}
socketPath := "/tmp/example socket'$(false).sock"
script := "set -euo pipefail\nshopt -s expand_aliases\n" + application_alias("ninjam", socketPath) + "\ncninjam 'argument with spaces' '*.wav'\n"
process := exec.Command("/bin/bash", "--noprofile", "--norc", "-c", script)
process.Env = append(os.Environ(), "PATH="+directory, "ALIAS_ARGS="+argsPath, "ALIAS_REQUEST="+requestPath)
output, err := process.CombinedOutput()
if failure, ok := err.(*exec.ExitError); !ok || failure.ExitCode() != 7 {
t.Fatalf("application status lost: %v, %s", err, output)
}
args, err := os.ReadFile(argsPath)
if err != nil || string(args) != "argument with spaces\n*.wav\n" {
t.Fatal("arguments lost", string(args), err)
}
if scenario != "missing" {
request, err := os.ReadFile(requestPath)
if err != nil || string(request) != "-T\n2\n-\nUNIX-CLIENT:"+socketPath+"\ncommand application ninjam\n" {
t.Fatal("socket or request changed by shell", string(request), err)
}
}
if scenario == "rejected" && !strings.Contains(string(output), "Command remote control is disabled") {
t.Fatal("reader error hidden", string(output))
}
})
}
}
func TestApplicationsMenuCopiesAliasWithoutSelecting(t *testing.T) {
a, voice := persistence_application(t, true)
override_read(t, a, override_screen(1, "ninjam"))
a.open_applications()
a.ui.handle(interaction.Key{Name: "tab"})
a.ui.handle(interaction.Key{Name: "enter"})
if got := voice.messages[len(voice.messages)-1]; got != "Default has no application alias" {
t.Fatal(got)
}
a.ui.handle(interaction.Key{Name: "tab", Shift: true})
// Use the list's prefix search, then Tab to the copy button.
a.ui.handle(interaction.Key{Text: "n"})
a.ui.handle(interaction.Key{Text: "i"})
a.ui.handle(interaction.Key{Name: "tab"})
a.ui.handle(interaction.Key{Name: "enter"})
if !a.ui.active || len(a.overrides) != 0 {
t.Fatal("copy closed menu or selected an override")
}
text, ok := a.clipboards.history.Current()
if !ok || text != application_alias("ninjam", "") {
t.Fatal("wrong clipboard contents", text)
}
a.ui.handle(interaction.Key{Name: "escape"})
}
+40 -3
View File
@@ -16,6 +16,7 @@ import (
"git.stormux.org/storm/fenrir/internal/config"
"git.stormux.org/storm/fenrir/internal/i18n"
"git.stormux.org/storm/fenrir/internal/interaction"
"git.stormux.org/storm/fenrir/internal/presentation"
"git.stormux.org/storm/fenrir/internal/prompt"
"git.stormux.org/storm/fenrir/internal/reader"
"git.stormux.org/storm/fenrir/internal/screen"
@@ -119,12 +120,20 @@ func (a *application) install_override(saved overrideSnapshot, terminal int) {
// A synthesizer selection can require asynchronous restoration. Until it
// completes, no new context is installed and all settings mutations are gated.
func (a *application) restore_override(done func()) bool {
return a.restore_override_result(func(err error) {
if err == nil {
done()
}
})
}
func (a *application) restore_override_result(done func(error)) bool {
current := a.overrideCurrent
if current == nil {
current = a.overrideFailed
}
if current == nil {
done()
done(nil)
return true
}
if a.speechPending || a.saver.busy || a.overrideTransition || a.overrideRestoreFailed {
@@ -141,7 +150,7 @@ func (a *application) restore_override(done func()) bool {
a.overrideCurrent = nil
a.overrideFailed = nil
current.adapter.Reset()
done()
done(nil)
}
if same_speech_selection(a.settings.live.Presentation.Speech, current.underlying.settings.live.Presentation.Speech) {
restore()
@@ -161,6 +170,7 @@ func (a *application) restore_override(done func()) bool {
a.overrideCurrent, a.overrideFailed = nil, current
current.adapter.Reset()
a.voice.Say(i18n.Format("Could not restore application settings: {0}", err.Error()))
done(err)
return
}
restore()
@@ -177,8 +187,21 @@ func (a *application) select_override(id string) {
if terminal == 0 || !a.lastActive {
return
}
a.select_override_result(id, func(string) {})
}
// Completion means selected, not recognized or profile-loaded: launchers must
// be able to select an override before starting the application. Callers check
// the ID and busy/terminal gates before entering this path.
func (a *application) select_override_result(id string, done func(string)) {
terminal := a.lastTerminal
a.overrideRestoreFailed = false // An explicit selection retries restoration.
a.restore_override(func() {
a.restore_override_result(func(err error) {
if err != nil {
done("Could not restore application settings: " + err.Error())
return
}
a.ui.close()
a.overrideDirty = true
if old := a.overrides[terminal]; old != nil {
old.adapter.Close()
@@ -186,6 +209,7 @@ func (a *application) select_override(id string) {
}
if id == "default" {
a.voice.Say(i18n.T("Default settings restored"))
done("")
return
}
var adapter applicationoverrides.Adapter
@@ -211,6 +235,7 @@ func (a *application) select_override(id string) {
selected.shell.Observe(a.lastSnapshot)
a.overrides[terminal] = selected
a.voice.Say(i18n.Format("{0} application override selected", i18n.T(adapter.Name())))
done("")
})
}
@@ -329,6 +354,7 @@ func (a *application) open_applications() {
}
session, err := interaction.New_session(interaction.Form{Type: interaction.TabForm, Title: "Applications", Tabs: []interaction.Tab{{ID: "applications", Label: "Applications", Controls: []interaction.Control{
{ID: "application", Label: "Application", Kind: interaction.List, Items: items, Selected: position, Action: "activate"},
{ID: "copy_alias", Label: "Copy alias to clipboard", Kind: interaction.Button, Action: "copy_alias"},
{ID: "cancel", Label: "Cancel", Kind: interaction.Button, Action: "cancel"},
}}}})
if err != nil {
@@ -351,6 +377,17 @@ func (a *application) open_applications() {
a.ui.close()
a.select_override(ids[selected])
}
if action == "copy_alias" {
selected := session.Values()["applications.application"].Selected
text := application_alias(ids[selected], a.settings.live.Remote.SocketFile)
if text == "" {
a.voice.Say(i18n.T("Default has no application alias"))
return
}
if a.clipboards.add(text, true) {
a.present(i18n.T("Application alias copied to clipboard"), presentation.CopyToClipboard)
}
}
}
a.voice.Say(session.Open())
}
@@ -213,6 +213,7 @@ func TestApplicationsMenuActivatesOnlyOnEnterAndReleasesCapture(t *testing.T) {
t.Fatal("active item not selected")
}
a.ui.handle(interaction.Key{Name: "tab"})
a.ui.handle(interaction.Key{Name: "tab"}) // Skip Copy alias to reach Cancel.
a.ui.handle(interaction.Key{Name: "enter"})
if a.ui.active || a.overrides[1] == nil {
t.Fatal("Cancel button changed selection")
+30
View File
@@ -0,0 +1,30 @@
// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"strings"
"git.stormux.org/storm/fenrir/internal/control"
)
// Runs after the command authorization and enablement gates.
func (a *application) remote_application(parameter string, request control.Request) {
id := strings.ToLower(strings.TrimSpace(parameter))
switch id {
case "default", "networkmanager", "ninjam", "rogue", "codex":
default:
remote_reply(request.Reply, "APPLICATION requires one ID: default, networkmanager, ninjam, rogue, codex")
return
}
if a.saver.busy || a.speechPending || a.overrideTransition {
remote_reply(request.Reply, "Settings are busy; try again after the current operation")
return
}
if a.lastTerminal == 0 || !a.lastActive {
remote_reply(request.Reply, "No active terminal for application selection")
return
}
a.select_override_result(id, func(response string) { remote_reply(request.Reply, response) })
}
+117
View File
@@ -0,0 +1,117 @@
// Copyright (c) 2026 Storm Dragon
// SPDX-License-Identifier: GPL-3.0-or-later
package app
import (
"errors"
"os"
"testing"
"git.stormux.org/storm/fenrir/internal/config"
"git.stormux.org/storm/fenrir/internal/control"
"git.stormux.org/storm/fenrir/internal/speech"
)
func TestRemoteApplicationSelectionAndDefault(t *testing.T) {
for _, terminalMode := range []bool{true, false} {
a, _ := persistence_application(t, terminalMode)
override_read(t, a, override_screen(1, "ninjam"))
for _, id := range []string{"ninjam", "networkmanager", "rogue", "codex", "default"} {
if got := runtime_request(a, "COMMAND APPLICATION "+id); got != "" {
t.Fatal(id, got)
}
selected := a.overrides[1]
if id == "default" {
if selected != nil {
t.Fatal("Default retained selection")
}
} else if selected == nil || selected.id != id {
t.Fatal("wrong application selected", id)
}
}
if got := runtime_request(a, "command application NINJAM"); got != "" {
t.Fatal(got)
}
override_read(t, a, override_screen(1, "ninjam"))
if a.overrideCurrent == nil || a.engine.Tracking_mode() != "highlight" {
t.Fatal("remote selection did not activate Ninjam")
}
if got := runtime_request(a, "command application default"); got != "" || a.overrideCurrent != nil {
t.Fatal("Default did not restore settings", got)
}
}
}
func TestRemoteApplicationRejectsInvalidAndUnavailableRequests(t *testing.T) {
a, _ := persistence_application(t, true)
override_read(t, a, override_screen(1, "ninjam"))
a.select_override("ninjam")
selected := a.overrides[1]
for _, parameter := range []string{"", "unknown", "ninjam extra", "default extra"} {
if got := runtime_request(a, "COMMAND APPLICATION "+parameter); got == "" || a.overrides[1] != selected {
t.Fatal("invalid request changed selection", parameter, got)
}
}
for _, gate := range []string{"disabled", "save", "speech", "transition", "inactive", "no-terminal", "unauthorized"} {
t.Run(gate, func(t *testing.T) {
uid := os.Geteuid()
switch gate {
case "disabled":
a.settings.live.Remote.EnableCommandRemote = false
case "save":
a.saver.busy = true
case "speech":
a.speechPending = true
case "transition":
a.overrideTransition = true
case "inactive":
a.lastActive = false
case "no-terminal":
a.lastTerminal = 0
case "unauthorized":
uid++
}
reply := make(chan string, 1)
a.remote_request(control.Request{UID: uid, Text: "COMMAND APPLICATION default", Reply: reply})
if got := <-reply; got == "" || a.overrides[1] != selected {
t.Fatal("gated request changed selection", got)
}
a.settings.live.Remote.EnableCommandRemote = true
a.saver.busy, a.speechPending, a.overrideTransition = false, false, false
a.lastActive, a.lastTerminal = true, 1
})
}
}
func TestRemoteApplicationWaitsForVoiceRestoration(t *testing.T) {
for _, fail := range []bool{false, true} {
a, _, _ := selection_application(t, true)
a.prepareSpeech = func(config.Speech) (speech.Transport, error) { return &preparedVoice{}, nil }
override_read(t, a, override_screen(1, "ninjam"))
a.select_override("ninjam")
override_read(t, a, override_screen(1, "ninjam"))
if got := request_selection(t, a, "SET speech#module=fixture;speech#voice=Temporary"); got != "" {
t.Fatal(got)
}
if fail {
a.prepareSpeech = func(config.Speech) (speech.Transport, error) { return nil, errors.New("fixture unavailable") }
}
reply := make(chan string, 1)
a.remote_request(control.Request{UID: os.Geteuid(), Text: "COMMAND APPLICATION rogue", Reply: reply})
select {
case got := <-reply:
t.Fatal("selection replied before restoration", got)
default:
}
finish_selection(t, a)
got := <-reply
if fail {
if got == "" || a.overrides[1].id != "ninjam" {
t.Fatal("restoration failure lost", got)
}
} else if got != "" || a.overrides[1].id != "rogue" {
t.Fatal("selection failed after restoration", got)
}
}
}
+3
View File
@@ -226,6 +226,9 @@ func (a *application) remote_request(request control.Request) {
a.clipboards.add(parameter, true)
case "EXPORTCLIPBOARD":
a.clipboards.command(command.ExportClipboard)
case "APPLICATION":
a.remote_application(parameter, request)
deferred = true
default:
response = "Unsupported remote command: " + verb
}
+31 -19
View File
@@ -71,11 +71,15 @@ msgstr "Wütend"
msgid "Apple"
msgstr "Apfel"
#: internal/app/application_overrides.go:331
#: internal/app/application_overrides.go:356
msgid "Application"
msgstr "Anwendung"
#: internal/app/application_overrides.go:330
#: internal/app/application_overrides.go:388
msgid "Application alias copied to clipboard"
msgstr "Anwendungsalias in die Zwischenablage kopiert"
#: internal/app/application_overrides.go:355
msgid "Applications"
msgstr "Anwendungen"
@@ -172,7 +176,7 @@ msgstr "Schmetterling"
msgid "Canada"
msgstr "Kanada"
#: internal/app/application_overrides.go:332
#: internal/app/application_overrides.go:358
#: internal/preferences/settings.go:93
msgid "Cancel"
msgstr "Abbrechen"
@@ -274,7 +278,7 @@ msgstr "Import aus der Zwischenablage fehlgeschlagen"
msgid "Clipboard sync"
msgstr "Zwischenablage synchronisieren"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Codex"
msgstr "Codex"
@@ -330,17 +334,21 @@ msgstr "Verbindung, Tempo und Intervall"
msgid "Cool"
msgstr "Cool"
#: internal/app/application_overrides.go:357
msgid "Copy alias to clipboard"
msgstr "Alias in die Zwischenablage kopieren"
#: internal/app/spellcheck_menu.go:175
msgid "Could not check spelling: {0}"
msgstr "Rechtschreibung konnte nicht geprüft werden: {0}"
#: internal/app/application_overrides.go:284
#: internal/app/application_overrides.go:85
#: internal/app/application_overrides.go:309
#: internal/app/application_overrides.go:86
msgid "Could not load application settings: {0}"
msgstr "Anwendungseinstellungen konnten nicht geladen werden: {0}"
#: internal/app/application_overrides.go:335
#: internal/app/application_overrides.go:339
#: internal/app/application_overrides.go:361
#: internal/app/application_overrides.go:365
msgid "Could not open Applications: {0}"
msgstr "Anwendungen konnten nicht geöffnet werden: {0}"
@@ -357,8 +365,8 @@ msgstr "Fenrir-Menü konnte nicht geöffnet werden: {0}"
msgid "Could not open Spellcheck Menu: {0}"
msgstr "Rechtschreibmenü konnte nicht geöffnet werden: {0}"
#: internal/app/application_overrides.go:163
#: internal/app/application_overrides.go:302
#: internal/app/application_overrides.go:172
#: internal/app/application_overrides.go:327
msgid "Could not restore application settings: {0}"
msgstr "Anwendungseinstellungen konnten nicht wiederhergestellt werden: {0}"
@@ -387,12 +395,16 @@ msgstr "Dolch"
msgid "Date format"
msgstr "Datumsformat"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Default"
msgstr "Standard"
#: internal/app/application_overrides.go:188
#: internal/app/application_overrides.go:248
#: internal/app/application_overrides.go:384
msgid "Default has no application alias"
msgstr "Standard hat keinen Anwendungsalias"
#: internal/app/application_overrides.go:211
#: internal/app/application_overrides.go:273
msgid "Default settings restored"
msgstr "Standardeinstellungen wiederhergestellt"
@@ -817,7 +829,7 @@ msgstr "Übelkeit"
msgid "Netherlands"
msgstr "Niederlande"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "NetworkManager"
msgstr "NetworkManager"
@@ -825,7 +837,7 @@ msgstr "NetworkManager"
msgid "New channel"
msgstr "Neuer Kanal"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Ninjam"
msgstr "Ninjam"
@@ -1101,7 +1113,7 @@ msgstr "Ring"
msgid "Rock on"
msgstr "Rock-Geste"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Rogue"
msgstr "Rogue"
@@ -1201,8 +1213,8 @@ msgstr "Live-Bereich aus einer oder zwei Markierungen festlegen"
msgid "Set window begin and end marks"
msgstr "Setze Fensteranfangs- und -endmarken"
#: internal/app/application_overrides.go:173
#: internal/app/application_overrides.go:321 internal/app/commands.go:84
#: internal/app/application_overrides.go:183
#: internal/app/application_overrides.go:346 internal/app/commands.go:84
msgid "Settings are busy"
msgstr "Einstellungen sind beschäftigt"
@@ -2469,7 +2481,7 @@ msgstr "Zwinkern"
msgid "{0} added to dictionary"
msgstr "{0} zum Wörterbuch hinzugefügt"
#: internal/app/application_overrides.go:213
#: internal/app/application_overrides.go:237
msgid "{0} application override selected"
msgstr "Anwendungseinstellungen für {0} ausgewählt"
+31 -19
View File
@@ -71,11 +71,15 @@ msgstr "Enfadado"
msgid "Apple"
msgstr "Manzana"
#: internal/app/application_overrides.go:331
#: internal/app/application_overrides.go:356
msgid "Application"
msgstr "Aplicación"
#: internal/app/application_overrides.go:330
#: internal/app/application_overrides.go:388
msgid "Application alias copied to clipboard"
msgstr "Alias de la aplicación copiado al portapapeles"
#: internal/app/application_overrides.go:355
msgid "Applications"
msgstr "Aplicaciones"
@@ -172,7 +176,7 @@ msgstr "Mariposa"
msgid "Canada"
msgstr "Canadá"
#: internal/app/application_overrides.go:332
#: internal/app/application_overrides.go:358
#: internal/preferences/settings.go:93
msgid "Cancel"
msgstr "Cancelar"
@@ -274,7 +278,7 @@ msgstr "Error al importar el portapapeles"
msgid "Clipboard sync"
msgstr "Sincronización del portapapeles"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Codex"
msgstr "Codex"
@@ -332,17 +336,21 @@ msgstr "Conexión, tempo e intervalo"
msgid "Cool"
msgstr "Genial"
#: internal/app/application_overrides.go:357
msgid "Copy alias to clipboard"
msgstr "Copiar alias al portapapeles"
#: internal/app/spellcheck_menu.go:175
msgid "Could not check spelling: {0}"
msgstr "No se pudo revisar la ortografía: {0}"
#: internal/app/application_overrides.go:284
#: internal/app/application_overrides.go:85
#: internal/app/application_overrides.go:309
#: internal/app/application_overrides.go:86
msgid "Could not load application settings: {0}"
msgstr "No se pudo cargar la configuración de la aplicación: {0}"
#: internal/app/application_overrides.go:335
#: internal/app/application_overrides.go:339
#: internal/app/application_overrides.go:361
#: internal/app/application_overrides.go:365
msgid "Could not open Applications: {0}"
msgstr "No se pudo abrir Aplicaciones: {0}"
@@ -359,8 +367,8 @@ msgstr "No se pudo abrir el menú de Fenrir: {0}"
msgid "Could not open Spellcheck Menu: {0}"
msgstr "No se pudo abrir el menú de ortografía: {0}"
#: internal/app/application_overrides.go:163
#: internal/app/application_overrides.go:302
#: internal/app/application_overrides.go:172
#: internal/app/application_overrides.go:327
msgid "Could not restore application settings: {0}"
msgstr "No se pudo restaurar la configuración de la aplicación: {0}"
@@ -389,12 +397,16 @@ msgstr "Daga"
msgid "Date format"
msgstr "Formato de fecha"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Default"
msgstr "Predeterminado"
#: internal/app/application_overrides.go:188
#: internal/app/application_overrides.go:248
#: internal/app/application_overrides.go:384
msgid "Default has no application alias"
msgstr "Predeterminado no tiene un alias de aplicación"
#: internal/app/application_overrides.go:211
#: internal/app/application_overrides.go:273
msgid "Default settings restored"
msgstr "Configuración predeterminada restaurada"
@@ -827,7 +839,7 @@ msgstr "Náuseas"
msgid "Netherlands"
msgstr "Países Bajos"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "NetworkManager"
msgstr "NetworkManager"
@@ -835,7 +847,7 @@ msgstr "NetworkManager"
msgid "New channel"
msgstr "Nuevo canal"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Ninjam"
msgstr "Ninjam"
@@ -1110,7 +1122,7 @@ msgstr "Anillo"
msgid "Rock on"
msgstr "Gesto de rock"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Rogue"
msgstr "Rogue"
@@ -1210,8 +1222,8 @@ msgstr "Definir una región dinámica a partir de una o dos marcas"
msgid "Set window begin and end marks"
msgstr "Establece una marca de principio y otra para el final de la ventana"
#: internal/app/application_overrides.go:173
#: internal/app/application_overrides.go:321 internal/app/commands.go:84
#: internal/app/application_overrides.go:183
#: internal/app/application_overrides.go:346 internal/app/commands.go:84
msgid "Settings are busy"
msgstr "La configuración está ocupada"
@@ -2477,7 +2489,7 @@ msgstr "guiño"
msgid "{0} added to dictionary"
msgstr "{0} añadido al diccionario"
#: internal/app/application_overrides.go:213
#: internal/app/application_overrides.go:237
msgid "{0} application override selected"
msgstr "Ajustes de aplicación seleccionados para {0}"
+26 -14
View File
@@ -61,11 +61,15 @@ msgstr ""
msgid "Apple"
msgstr ""
#: internal/app/application_overrides.go:331
#: internal/app/application_overrides.go:356
msgid "Application"
msgstr ""
#: internal/app/application_overrides.go:330
#: internal/app/application_overrides.go:388
msgid "Application alias copied to clipboard"
msgstr ""
#: internal/app/application_overrides.go:355
msgid "Applications"
msgstr ""
@@ -161,7 +165,7 @@ msgstr ""
msgid "Canada"
msgstr ""
#: internal/app/application_overrides.go:332 internal/preferences/settings.go:93
#: internal/app/application_overrides.go:358 internal/preferences/settings.go:93
msgid "Cancel"
msgstr ""
@@ -261,7 +265,7 @@ msgstr ""
msgid "Clipboard sync"
msgstr ""
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Codex"
msgstr ""
@@ -317,15 +321,19 @@ msgstr ""
msgid "Cool"
msgstr ""
#: internal/app/application_overrides.go:357
msgid "Copy alias to clipboard"
msgstr ""
#: internal/app/spellcheck_menu.go:175
msgid "Could not check spelling: {0}"
msgstr ""
#: internal/app/application_overrides.go:284 internal/app/application_overrides.go:85
#: internal/app/application_overrides.go:309 internal/app/application_overrides.go:86
msgid "Could not load application settings: {0}"
msgstr ""
#: internal/app/application_overrides.go:335 internal/app/application_overrides.go:339
#: internal/app/application_overrides.go:361 internal/app/application_overrides.go:365
msgid "Could not open Applications: {0}"
msgstr ""
@@ -341,7 +349,7 @@ msgstr ""
msgid "Could not open Spellcheck Menu: {0}"
msgstr ""
#: internal/app/application_overrides.go:163 internal/app/application_overrides.go:302
#: internal/app/application_overrides.go:172 internal/app/application_overrides.go:327
msgid "Could not restore application settings: {0}"
msgstr ""
@@ -369,11 +377,15 @@ msgstr ""
msgid "Date format"
msgstr ""
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Default"
msgstr ""
#: internal/app/application_overrides.go:188 internal/app/application_overrides.go:248
#: internal/app/application_overrides.go:384
msgid "Default has no application alias"
msgstr ""
#: internal/app/application_overrides.go:211 internal/app/application_overrides.go:273
msgid "Default settings restored"
msgstr ""
@@ -785,7 +797,7 @@ msgstr ""
msgid "Netherlands"
msgstr ""
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "NetworkManager"
msgstr ""
@@ -793,7 +805,7 @@ msgstr ""
msgid "New channel"
msgstr ""
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Ninjam"
msgstr ""
@@ -1061,7 +1073,7 @@ msgstr ""
msgid "Rock on"
msgstr ""
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Rogue"
msgstr ""
@@ -1161,7 +1173,7 @@ msgstr ""
msgid "Set window begin and end marks"
msgstr ""
#: internal/app/application_overrides.go:173 internal/app/application_overrides.go:321 internal/app/commands.go:84
#: internal/app/application_overrides.go:183 internal/app/application_overrides.go:346 internal/app/commands.go:84
msgid "Settings are busy"
msgstr ""
@@ -2361,7 +2373,7 @@ msgstr ""
msgid "{0} added to dictionary"
msgstr ""
#: internal/app/application_overrides.go:213
#: internal/app/application_overrides.go:237
msgid "{0} application override selected"
msgstr ""
+31 -19
View File
@@ -72,11 +72,15 @@ msgstr "Złość"
msgid "Apple"
msgstr "Jabłko"
#: internal/app/application_overrides.go:331
#: internal/app/application_overrides.go:356
msgid "Application"
msgstr "Aplikacja"
#: internal/app/application_overrides.go:330
#: internal/app/application_overrides.go:388
msgid "Application alias copied to clipboard"
msgstr "Alias aplikacji skopiowany do schowka"
#: internal/app/application_overrides.go:355
msgid "Applications"
msgstr "Aplikacje"
@@ -173,7 +177,7 @@ msgstr "Motyl"
msgid "Canada"
msgstr "Kanada"
#: internal/app/application_overrides.go:332
#: internal/app/application_overrides.go:358
#: internal/preferences/settings.go:93
msgid "Cancel"
msgstr "Anuluj"
@@ -275,7 +279,7 @@ msgstr "Importowanie schowka nie powiodło się"
msgid "Clipboard sync"
msgstr "Synchronizacja schowka"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Codex"
msgstr "Codex"
@@ -332,17 +336,21 @@ msgstr "Połączenie, tempo i interwał"
msgid "Cool"
msgstr "Wyluzowanie"
#: internal/app/application_overrides.go:357
msgid "Copy alias to clipboard"
msgstr "Kopiuj alias do schowka"
#: internal/app/spellcheck_menu.go:175
msgid "Could not check spelling: {0}"
msgstr "Nie można sprawdzić pisowni: {0}"
#: internal/app/application_overrides.go:284
#: internal/app/application_overrides.go:85
#: internal/app/application_overrides.go:309
#: internal/app/application_overrides.go:86
msgid "Could not load application settings: {0}"
msgstr "Nie można wczytać ustawień aplikacji: {0}"
#: internal/app/application_overrides.go:335
#: internal/app/application_overrides.go:339
#: internal/app/application_overrides.go:361
#: internal/app/application_overrides.go:365
msgid "Could not open Applications: {0}"
msgstr "Nie można otworzyć menu aplikacji: {0}"
@@ -359,8 +367,8 @@ msgstr "Nie można otworzyć menu Fenrira: {0}"
msgid "Could not open Spellcheck Menu: {0}"
msgstr "Nie można otworzyć menu sprawdzania pisowni: {0}"
#: internal/app/application_overrides.go:163
#: internal/app/application_overrides.go:302
#: internal/app/application_overrides.go:172
#: internal/app/application_overrides.go:327
msgid "Could not restore application settings: {0}"
msgstr "Nie można przywrócić ustawień aplikacji: {0}"
@@ -389,12 +397,16 @@ msgstr "Sztylet"
msgid "Date format"
msgstr "Format daty"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Default"
msgstr "Domyślne"
#: internal/app/application_overrides.go:188
#: internal/app/application_overrides.go:248
#: internal/app/application_overrides.go:384
msgid "Default has no application alias"
msgstr "Ustawienia domyślne nie mają aliasu aplikacji"
#: internal/app/application_overrides.go:211
#: internal/app/application_overrides.go:273
msgid "Default settings restored"
msgstr "Przywrócono ustawienia domyślne"
@@ -822,7 +834,7 @@ msgstr "Nudności"
msgid "Netherlands"
msgstr "Holandia"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "NetworkManager"
msgstr "NetworkManager"
@@ -830,7 +842,7 @@ msgstr "NetworkManager"
msgid "New channel"
msgstr "Nowy kanał"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Ninjam"
msgstr "Ninjam"
@@ -1104,7 +1116,7 @@ msgstr "Pierścień"
msgid "Rock on"
msgstr "Gest rockowy"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Rogue"
msgstr "Rogue"
@@ -1204,8 +1216,8 @@ msgstr "Ustaw obszar dynamiczny za pomocą jednego lub dwóch znaczników"
msgid "Set window begin and end marks"
msgstr "Ustaw początek i koniec okna."
#: internal/app/application_overrides.go:173
#: internal/app/application_overrides.go:321 internal/app/commands.go:84
#: internal/app/application_overrides.go:183
#: internal/app/application_overrides.go:346 internal/app/commands.go:84
msgid "Settings are busy"
msgstr "Ustawienia są zajęte"
@@ -2468,7 +2480,7 @@ msgstr "mrugnięcie"
msgid "{0} added to dictionary"
msgstr "Dodano {0} do słownika"
#: internal/app/application_overrides.go:213
#: internal/app/application_overrides.go:237
msgid "{0} application override selected"
msgstr "Wybrano ustawienia aplikacji {0}"
+31 -19
View File
@@ -70,11 +70,15 @@ msgstr "Zangado"
msgid "Apple"
msgstr "Maçã"
#: internal/app/application_overrides.go:331
#: internal/app/application_overrides.go:356
msgid "Application"
msgstr "Aplicativo"
#: internal/app/application_overrides.go:330
#: internal/app/application_overrides.go:388
msgid "Application alias copied to clipboard"
msgstr "Alias da aplicação copiado para a área de transferência"
#: internal/app/application_overrides.go:355
msgid "Applications"
msgstr "Aplicativos"
@@ -171,7 +175,7 @@ msgstr "Borboleta"
msgid "Canada"
msgstr "Canadá"
#: internal/app/application_overrides.go:332
#: internal/app/application_overrides.go:358
#: internal/preferences/settings.go:93
msgid "Cancel"
msgstr "Cancelar"
@@ -273,7 +277,7 @@ msgstr "Falha ao importar a área de transferência"
msgid "Clipboard sync"
msgstr "Sincronização da área de transferência"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Codex"
msgstr "Codex"
@@ -330,17 +334,21 @@ msgstr "Conexão, andamento e intervalo"
msgid "Cool"
msgstr "Legal"
#: internal/app/application_overrides.go:357
msgid "Copy alias to clipboard"
msgstr "Copiar alias para a área de transferência"
#: internal/app/spellcheck_menu.go:175
msgid "Could not check spelling: {0}"
msgstr "Não foi possível verificar a ortografia: {0}"
#: internal/app/application_overrides.go:284
#: internal/app/application_overrides.go:85
#: internal/app/application_overrides.go:309
#: internal/app/application_overrides.go:86
msgid "Could not load application settings: {0}"
msgstr "Não foi possível carregar as configurações do aplicativo: {0}"
#: internal/app/application_overrides.go:335
#: internal/app/application_overrides.go:339
#: internal/app/application_overrides.go:361
#: internal/app/application_overrides.go:365
msgid "Could not open Applications: {0}"
msgstr "Não foi possível abrir Aplicativos: {0}"
@@ -357,8 +365,8 @@ msgstr "Não foi possível abrir o menu do Fenrir: {0}"
msgid "Could not open Spellcheck Menu: {0}"
msgstr "Não foi possível abrir o menu de ortografia: {0}"
#: internal/app/application_overrides.go:163
#: internal/app/application_overrides.go:302
#: internal/app/application_overrides.go:172
#: internal/app/application_overrides.go:327
msgid "Could not restore application settings: {0}"
msgstr "Não foi possível restaurar as configurações do aplicativo: {0}"
@@ -387,12 +395,16 @@ msgstr "Adaga"
msgid "Date format"
msgstr "Formato da data"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Default"
msgstr "Padrão"
#: internal/app/application_overrides.go:188
#: internal/app/application_overrides.go:248
#: internal/app/application_overrides.go:384
msgid "Default has no application alias"
msgstr "Padrão não tem um alias de aplicação"
#: internal/app/application_overrides.go:211
#: internal/app/application_overrides.go:273
msgid "Default settings restored"
msgstr "Configurações padrão restauradas"
@@ -824,7 +836,7 @@ msgstr "Enjoado"
msgid "Netherlands"
msgstr "Países Baixos"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "NetworkManager"
msgstr "NetworkManager"
@@ -832,7 +844,7 @@ msgstr "NetworkManager"
msgid "New channel"
msgstr "Novo canal"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Ninjam"
msgstr "Ninjam"
@@ -1108,7 +1120,7 @@ msgstr "Anel"
msgid "Rock on"
msgstr "Gesto de rock"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Rogue"
msgstr "Rogue"
@@ -1208,8 +1220,8 @@ msgstr "Definir uma região dinâmica a partir de uma ou duas marcas"
msgid "Set window begin and end marks"
msgstr "Definir marcas de início e fim da janela"
#: internal/app/application_overrides.go:173
#: internal/app/application_overrides.go:321 internal/app/commands.go:84
#: internal/app/application_overrides.go:183
#: internal/app/application_overrides.go:346 internal/app/commands.go:84
msgid "Settings are busy"
msgstr "As configurações estão ocupadas"
@@ -2481,7 +2493,7 @@ msgstr "piscadela"
msgid "{0} added to dictionary"
msgstr "{0} adicionado ao dicionário"
#: internal/app/application_overrides.go:213
#: internal/app/application_overrides.go:237
msgid "{0} application override selected"
msgstr "Configurações do aplicativo {0} selecionadas"
+31 -19
View File
@@ -69,11 +69,15 @@ msgstr "Злость"
msgid "Apple"
msgstr "Яблоко"
#: internal/app/application_overrides.go:331
#: internal/app/application_overrides.go:356
msgid "Application"
msgstr "Приложение"
#: internal/app/application_overrides.go:330
#: internal/app/application_overrides.go:388
msgid "Application alias copied to clipboard"
msgstr "Псевдоним приложения скопирован в буфер обмена"
#: internal/app/application_overrides.go:355
msgid "Applications"
msgstr "Приложения"
@@ -170,7 +174,7 @@ msgstr "Бабочка"
msgid "Canada"
msgstr "Канада"
#: internal/app/application_overrides.go:332
#: internal/app/application_overrides.go:358
#: internal/preferences/settings.go:93
msgid "Cancel"
msgstr "Отмена"
@@ -272,7 +276,7 @@ msgstr "Не удалось импортировать буфер обмена"
msgid "Clipboard sync"
msgstr "Синхронизация буфера обмена"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Codex"
msgstr "Codex"
@@ -329,17 +333,21 @@ msgstr "Соединение, темп и интервал"
msgid "Cool"
msgstr "Круто"
#: internal/app/application_overrides.go:357
msgid "Copy alias to clipboard"
msgstr "Копировать псевдоним в буфер обмена"
#: internal/app/spellcheck_menu.go:175
msgid "Could not check spelling: {0}"
msgstr "Не удалось проверить правописание: {0}"
#: internal/app/application_overrides.go:284
#: internal/app/application_overrides.go:85
#: internal/app/application_overrides.go:309
#: internal/app/application_overrides.go:86
msgid "Could not load application settings: {0}"
msgstr "Не удалось загрузить настройки приложения: {0}"
#: internal/app/application_overrides.go:335
#: internal/app/application_overrides.go:339
#: internal/app/application_overrides.go:361
#: internal/app/application_overrides.go:365
msgid "Could not open Applications: {0}"
msgstr "Не удалось открыть меню приложений: {0}"
@@ -356,8 +364,8 @@ msgstr "Не удалось открыть меню Fenrir: {0}"
msgid "Could not open Spellcheck Menu: {0}"
msgstr "Не удалось открыть меню проверки правописания: {0}"
#: internal/app/application_overrides.go:163
#: internal/app/application_overrides.go:302
#: internal/app/application_overrides.go:172
#: internal/app/application_overrides.go:327
msgid "Could not restore application settings: {0}"
msgstr "Не удалось восстановить настройки приложения: {0}"
@@ -386,12 +394,16 @@ msgstr "Кинжал"
msgid "Date format"
msgstr "Формат даты"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Default"
msgstr "По умолчанию"
#: internal/app/application_overrides.go:188
#: internal/app/application_overrides.go:248
#: internal/app/application_overrides.go:384
msgid "Default has no application alias"
msgstr "Для настроек по умолчанию нет псевдонима приложения"
#: internal/app/application_overrides.go:211
#: internal/app/application_overrides.go:273
msgid "Default settings restored"
msgstr "Настройки по умолчанию восстановлены"
@@ -817,7 +829,7 @@ msgstr "Тошнота"
msgid "Netherlands"
msgstr "Нидерланды"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "NetworkManager"
msgstr "NetworkManager"
@@ -825,7 +837,7 @@ msgstr "NetworkManager"
msgid "New channel"
msgstr "Новый канал"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Ninjam"
msgstr "Ninjam"
@@ -1100,7 +1112,7 @@ msgstr "Кольцо"
msgid "Rock on"
msgstr "Рокерский жест"
#: internal/app/application_overrides.go:324
#: internal/app/application_overrides.go:349
msgid "Rogue"
msgstr "Rogue"
@@ -1200,8 +1212,8 @@ msgstr "Задать динамическую область по одной и
msgid "Set window begin and end marks"
msgstr "Установка начала и конца окна"
#: internal/app/application_overrides.go:173
#: internal/app/application_overrides.go:321 internal/app/commands.go:84
#: internal/app/application_overrides.go:183
#: internal/app/application_overrides.go:346 internal/app/commands.go:84
msgid "Settings are busy"
msgstr "Настройки заняты"
@@ -2465,7 +2477,7 @@ msgstr "подмигивание"
msgid "{0} added to dictionary"
msgstr "{0} добавлено в словарь"
#: internal/app/application_overrides.go:213
#: internal/app/application_overrides.go:237
msgid "{0} application override selected"
msgstr "Выбраны настройки приложения {0}"