mirror of
https://github.com/yusufipk/dikte.git
synced 2026-09-11 10:56:10 +00:00
Merge pull request #37 from yusufipk/package-layout
Put the modules in a package and the scripts in a folder
This commit is contained in:
@@ -71,6 +71,6 @@ jobs:
|
||||
- name: Check the installer parses
|
||||
run: |
|
||||
bash -n install.sh
|
||||
bash -n install-mac.sh
|
||||
bash -n update.sh
|
||||
bash -n uninstall.sh
|
||||
bash -n scripts/install-mac.sh
|
||||
bash -n scripts/update.sh
|
||||
bash -n scripts/uninstall.sh
|
||||
|
||||
@@ -56,7 +56,7 @@ tools instead:
|
||||
sudo apt install pulseaudio-utils xclip xdotool ffmpeg
|
||||
```
|
||||
|
||||
On macOS the same `./install.sh` runs and hands over to `install-mac.sh`, which
|
||||
On macOS the same `./install.sh` runs and hands over to `scripts/install-mac.sh`, which
|
||||
puts down a `Dikte.app` in `~/Applications`, the `dikte` command and a
|
||||
LaunchAgent:
|
||||
|
||||
@@ -84,9 +84,10 @@ transcribe in the cloud. A meeting needs BlackHole or Loopback
|
||||
(`brew install blackhole-2ch`); dictation does not.
|
||||
|
||||
`install.sh` adds the `dikte` command, a menu entry, an autostart entry and the
|
||||
two global shortcuts, whose keys are its two arguments. `./update.sh` pulls and
|
||||
puts all of that back, keeping the keys you chose; `./uninstall.sh` takes it away
|
||||
again and leaves your settings and dictations alone unless you pass `--purge`.
|
||||
two global shortcuts, whose keys are its two arguments, or the ones already in
|
||||
your settings when it is given none. `./scripts/update.sh` pulls and puts all of
|
||||
that back; `./scripts/uninstall.sh` takes it away again and leaves your settings
|
||||
and dictations alone unless you pass `--purge`.
|
||||
|
||||
Speech to text and cleanup each pick a provider in the settings window, and both
|
||||
run here by default, on models of your own. The cloud is the other option:
|
||||
@@ -201,8 +202,13 @@ keys.
|
||||
|
||||
## Layout
|
||||
|
||||
Everything below is in the `dikte` package, which is what `python3 -m dikte`
|
||||
runs and what the `__main__.py` in it hands to every launcher and shortcut.
|
||||
`scripts/` holds install-mac.sh, update.sh and uninstall.sh; install.sh stays at
|
||||
the top, and `tests/` has a file per module.
|
||||
|
||||
```
|
||||
dikte.py entry point, tray icon, state machine
|
||||
app.py entry point, tray icon, state machine
|
||||
cli.py the command line: every verb, and what it answers with
|
||||
ipc.py one request and one reply over the local socket
|
||||
audio.py PCM capture: pw-record for dictation, ffmpeg for a meeting
|
||||
@@ -224,7 +230,7 @@ i18n.py the string table
|
||||
```
|
||||
|
||||
The indicator is drawn through XWayland, because a Wayland client cannot place a
|
||||
window in a screen corner; `dikte.py` sets `QT_QPA_PLATFORM=xcb` for that.
|
||||
window in a screen corner; `app.py` sets `QT_QPA_PLATFORM=xcb` for that.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
+12
-7
@@ -55,7 +55,7 @@ araçlarıyla çalışır:
|
||||
sudo apt install pulseaudio-utils xclip xdotool ffmpeg
|
||||
```
|
||||
|
||||
macOS'ta da aynı `./install.sh` çalışır, işi `install-mac.sh`'a devreder;
|
||||
macOS'ta da aynı `./install.sh` çalışır, işi `scripts/install-mac.sh`'a devreder;
|
||||
`~/Applications` içine bir `Dikte.app`, `dikte` komutunu ve bir LaunchAgent
|
||||
kurar:
|
||||
|
||||
@@ -83,10 +83,10 @@ BlackHole veya Loopback gerekiyor (`brew install blackhole-2ch`); dikte için
|
||||
gerekmiyor.
|
||||
|
||||
`install.sh` `dikte` komutunu, menü girdisini, oturum açılışında otomatik
|
||||
başlatmayı ve iki global kısayolu kurar; tuşları da iki argümanı. `./update.sh`
|
||||
son sürümü çeker ve bunları senin seçtiğin tuşlarla yerine koyar;
|
||||
`./uninstall.sh` hepsini geri alır, `--purge` demedikçe ayarlarına ve
|
||||
diktelerine dokunmaz.
|
||||
başlatmayı ve iki global kısayolu kurar; tuşları iki argümanı, argüman
|
||||
verilmezse ayarlarında duranlar. `./scripts/update.sh` son sürümü çeker ve
|
||||
bunları yerine koyar; `./scripts/uninstall.sh` hepsini geri alır, `--purge`
|
||||
demedikçe ayarlarına ve diktelerine dokunmaz.
|
||||
|
||||
Sesi yazıya çevirme ve temizleme, ayarlar penceresinde ayrı ayrı sağlayıcı
|
||||
seçer; ikisi de varsayılan olarak burada, kendi modellerinle çalışır. Bulutu
|
||||
@@ -198,8 +198,13 @@ Kısayollar sekmesi bağlanacak komutu gösterir.
|
||||
|
||||
## Dosyalar
|
||||
|
||||
Aşağıdakilerin hepsi `dikte` paketinin içinde: `python3 -m dikte` bunu çalıştırır,
|
||||
içindeki `__main__.py` de her başlatıcının ve kısayolun adlandırdığı dosyadır.
|
||||
`scripts/` altında install-mac.sh, update.sh ve uninstall.sh var; install.sh en
|
||||
üstte kalır, `tests/` içinde de her modülün bir dosyası.
|
||||
|
||||
```
|
||||
dikte.py giriş noktası, tepsi simgesi, durum makinesi
|
||||
app.py giriş noktası, tepsi simgesi, durum makinesi
|
||||
cli.py komut satırı: bütün fiiller ve verdikleri cevap
|
||||
ipc.py yerel sokette bir istek, bir cevap
|
||||
audio.py PCM kaydı: diktede pw-record, toplantıda ffmpeg
|
||||
@@ -221,7 +226,7 @@ i18n.py metin tablosu
|
||||
```
|
||||
|
||||
Gösterge XWayland üzerinden çizilir; Wayland'da bir pencereyi belirli bir köşeye
|
||||
yerleştirmenin yolu yok, `dikte.py` bu yüzden `QT_QPA_PLATFORM=xcb` ayarlar.
|
||||
yerleştirmenin yolu yok, `app.py` bu yüzden `QT_QPA_PLATFORM=xcb` ayarlar.
|
||||
|
||||
## Lisans
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
"""Dikte: press a key, talk, press again to transcribe, clean up and paste.
|
||||
|
||||
The package is the application. Nothing is imported here on purpose: `dikte
|
||||
config get` runs through the same package as the tray icon does, and it has no
|
||||
business loading Qt to answer one question.
|
||||
"""
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env python3
|
||||
"""What `python3 -m dikte` and the installed `dikte` command both run.
|
||||
|
||||
The file is also executed by path, because that is what a desktop shortcut and
|
||||
the launcher in ~/.local/bin do: neither knows a working directory to be in.
|
||||
Run that way there is no package around it, so the checkout has to be put on
|
||||
the import path here, before the first line of the application is imported.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
if not __package__:
|
||||
# realpath, because the launcher is a symlink into the checkout: what has to
|
||||
# end up on the path is the checkout, not ~/.local/bin. The directory this
|
||||
# file is in comes off the path in exchange, so that a module beside it is
|
||||
# only ever reachable as part of the package.
|
||||
here = os.path.dirname(os.path.realpath(__file__))
|
||||
sys.path[:] = [p for p in sys.path if os.path.realpath(p or ".") != here]
|
||||
sys.path.insert(0, os.path.dirname(here))
|
||||
|
||||
from dikte.app import main # noqa: E402
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -22,8 +22,8 @@ import threading
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
import ggml
|
||||
from i18n import t
|
||||
from . import ggml
|
||||
from .i18n import t
|
||||
|
||||
APP_URL = "https://github.com/yusufipk/dikte"
|
||||
USER_AGENT = f"dikte/1.0 (+{APP_URL})"
|
||||
Executable → Regular
+18
-21
@@ -1,10 +1,11 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Dikte: press Ctrl+Space, talk, press again to transcribe, clean up and paste.
|
||||
|
||||
This is the application: the tray icon, the state machine, and the socket the
|
||||
terminal talks to. Every verb it answers is in cli.py, which is also what runs
|
||||
`dikte.py --help`; the only argument handled here is --gui, which is how the
|
||||
`dikte --help`; the only argument handled here is --gui, which is how the
|
||||
command line says "there is no instance to talk to, so be one".
|
||||
|
||||
Nothing runs this file directly; __main__.py is what the launchers start.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
@@ -36,21 +37,21 @@ from PyQt6.QtGui import QAction, QIcon # noqa: E402
|
||||
from PyQt6.QtNetwork import QLocalServer, QLocalSocket # noqa: E402
|
||||
from PyQt6.QtWidgets import QApplication, QMenu, QSystemTrayIcon # noqa: E402
|
||||
|
||||
import assistant # noqa: E402
|
||||
import audio # noqa: E402
|
||||
import cli # noqa: E402
|
||||
import config as cfg # noqa: E402
|
||||
import ggml # noqa: E402
|
||||
import hotkey # noqa: E402
|
||||
import i18n # noqa: E402
|
||||
import ipc # noqa: E402
|
||||
import meeting # noqa: E402
|
||||
import trayicon # noqa: E402
|
||||
from i18n import t # noqa: E402
|
||||
from meeting import MeetingPipeline # noqa: E402
|
||||
from overlay import Overlay # noqa: E402
|
||||
from settings_ui import SettingsWindow # noqa: E402
|
||||
from worker import Pipeline # noqa: E402
|
||||
from . import assistant # noqa: E402
|
||||
from . import audio # noqa: E402
|
||||
from . import cli # noqa: E402
|
||||
from . import config as cfg # noqa: E402
|
||||
from . import ggml # noqa: E402
|
||||
from . import hotkey # noqa: E402
|
||||
from . import i18n # noqa: E402
|
||||
from . import ipc # noqa: E402
|
||||
from . import meeting # noqa: E402
|
||||
from . import trayicon # noqa: E402
|
||||
from .i18n import t # noqa: E402
|
||||
from .meeting import MeetingPipeline # noqa: E402
|
||||
from .overlay import Overlay # noqa: E402
|
||||
from .settings_ui import SettingsWindow # noqa: E402
|
||||
from .worker import Pipeline # noqa: E402
|
||||
|
||||
SERVER_NAME = ipc.SERVER_NAME
|
||||
IDLE, RECORDING, BUSY = "idle", "recording", "busy"
|
||||
@@ -1168,7 +1169,3 @@ def run_app(args):
|
||||
QTimer.singleShot(0, dikte.toggle_meeting)
|
||||
|
||||
return app.exec()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -29,9 +29,9 @@ import subprocess
|
||||
import threading
|
||||
import time
|
||||
|
||||
import api
|
||||
import config as cfg
|
||||
from i18n import t
|
||||
from . import api
|
||||
from . import config as cfg
|
||||
from .i18n import t
|
||||
|
||||
SESSION_FILE = cfg.DATA_DIR / "assistant.json"
|
||||
PROVIDERS = ("claude", "codex", "openrouter")
|
||||
@@ -31,7 +31,7 @@ import wave
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtSignal
|
||||
|
||||
from i18n import t
|
||||
from .i18n import t
|
||||
|
||||
RATE = 16000
|
||||
CHANNELS = 1
|
||||
@@ -18,10 +18,10 @@ import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
import api
|
||||
import assistant
|
||||
import ggml
|
||||
from i18n import t
|
||||
from . import api
|
||||
from . import assistant
|
||||
from . import ggml
|
||||
from .i18n import t
|
||||
|
||||
PROVIDERS = ("openrouter", "local", "claude", "codex")
|
||||
|
||||
+10
-10
@@ -22,16 +22,16 @@ import time
|
||||
|
||||
from PyQt6.QtCore import QCoreApplication, QTimer
|
||||
|
||||
import api
|
||||
import assistant
|
||||
import audio
|
||||
import cleanup
|
||||
import config as cfg
|
||||
import filetranscribe
|
||||
import hotkey
|
||||
import ipc
|
||||
import meeting
|
||||
import paste
|
||||
from . import api
|
||||
from . import assistant
|
||||
from . import audio
|
||||
from . import cleanup
|
||||
from . import config as cfg
|
||||
from . import filetranscribe
|
||||
from . import hotkey
|
||||
from . import ipc
|
||||
from . import meeting
|
||||
from . import paste
|
||||
|
||||
NOT_RUNNING = 3
|
||||
|
||||
@@ -6,12 +6,12 @@ import json
|
||||
import os
|
||||
import sys
|
||||
|
||||
import api
|
||||
import ggml
|
||||
import i18n
|
||||
import paste
|
||||
import paths
|
||||
from i18n import t
|
||||
from . import api
|
||||
from . import ggml
|
||||
from . import i18n
|
||||
from . import paste
|
||||
from . import paths
|
||||
from .i18n import t
|
||||
|
||||
|
||||
_MACOS = sys.platform == "darwin"
|
||||
@@ -23,10 +23,10 @@ import wave
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtSignal
|
||||
|
||||
import api
|
||||
import cleanup
|
||||
import ggml
|
||||
from i18n import t
|
||||
from . import api
|
||||
from . import cleanup
|
||||
from . import ggml
|
||||
from .i18n import t
|
||||
|
||||
UPLOAD_LIMIT = 24 * 1024 * 1024 # the APIs take 25 MB; leave the form its room
|
||||
MP3_BITRATE = "48k" # mono speech at 16 kHz: whisper hears nothing less
|
||||
@@ -44,9 +44,9 @@ import time
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
import hub
|
||||
import paths
|
||||
from i18n import t
|
||||
from . import hub
|
||||
from . import paths
|
||||
from .i18n import t
|
||||
|
||||
HOST = "127.0.0.1"
|
||||
# The path api.py asks for, so its URL and the server's line up.
|
||||
@@ -30,7 +30,7 @@ import threading
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtSignal
|
||||
|
||||
from i18n import t
|
||||
from .i18n import t
|
||||
|
||||
DESKTOP_ID = "dikte-toggle.desktop"
|
||||
CANCEL_DESKTOP_ID = "dikte-cancel.desktop"
|
||||
@@ -25,7 +25,7 @@ import urllib.error
|
||||
import urllib.parse
|
||||
import urllib.request
|
||||
|
||||
from i18n import t
|
||||
from .i18n import t
|
||||
|
||||
GITHUB_API = "https://api.github.com"
|
||||
HF_API = "https://huggingface.co/api"
|
||||
@@ -22,8 +22,13 @@ CONNECT_MS = 800
|
||||
|
||||
|
||||
def script_path():
|
||||
"""The package entry point, as a path.
|
||||
|
||||
A shortcut and a relaunch both start a second process, and neither has a
|
||||
working directory to run `-m dikte` from, so the file is named outright.
|
||||
"""
|
||||
return os.path.realpath(
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), "dikte.py")
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), "__main__.py")
|
||||
)
|
||||
|
||||
|
||||
@@ -24,13 +24,13 @@ import wave
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtSignal
|
||||
|
||||
import api
|
||||
import cleanup
|
||||
import config as cfg
|
||||
import filetranscribe
|
||||
import vad
|
||||
from filetranscribe import Cancelled, format_timestamp
|
||||
from i18n import t
|
||||
from . import api
|
||||
from . import cleanup
|
||||
from . import config as cfg
|
||||
from . import filetranscribe
|
||||
from . import vad
|
||||
from .filetranscribe import Cancelled, format_timestamp
|
||||
from .i18n import t
|
||||
|
||||
# Where the document stops being prose and starts being the transcript. It is a
|
||||
# comment, so it never shows up in a rendered document, and it is what a retry
|
||||
@@ -19,7 +19,7 @@ import sys
|
||||
import tempfile
|
||||
import time
|
||||
|
||||
from i18n import t
|
||||
from .i18n import t
|
||||
|
||||
# Linux input event codes (linux/input-event-codes.h), which is what ydotool
|
||||
# takes. They are also the list of keys a paste shortcut may be built from, so
|
||||
@@ -13,19 +13,19 @@ from PyQt6.QtWidgets import (
|
||||
QPushButton, QScrollArea, QSpinBox, QTabWidget, QVBoxLayout, QWidget,
|
||||
)
|
||||
|
||||
import api
|
||||
import assistant
|
||||
import audio
|
||||
import cleanup
|
||||
import config as cfg
|
||||
import filetranscribe
|
||||
import ggml
|
||||
import hotkey
|
||||
import ipc
|
||||
import meeting
|
||||
import paste
|
||||
from filetranscribe import FileTranscriber
|
||||
from i18n import t
|
||||
from . import api
|
||||
from . import assistant
|
||||
from . import audio
|
||||
from . import cleanup
|
||||
from . import config as cfg
|
||||
from . import filetranscribe
|
||||
from . import ggml
|
||||
from . import hotkey
|
||||
from . import ipc
|
||||
from . import meeting
|
||||
from . import paste
|
||||
from .filetranscribe import FileTranscriber
|
||||
from .i18n import t
|
||||
|
||||
UI_LANGUAGES = [("Automatic (system)", "auto"), ("Turkish", "tr"), ("English", "en")]
|
||||
LANGUAGES = [
|
||||
@@ -118,7 +118,7 @@ def _working(painter, size, ink):
|
||||
painter.drawPath(head)
|
||||
|
||||
|
||||
# The names Linux themes use, which are what dikte.py asks for either way.
|
||||
# The names Linux themes use, which are what app.py asks for either way.
|
||||
SHAPES = {
|
||||
"audio-input-microphone": _microphone,
|
||||
"media-record": _record,
|
||||
@@ -15,15 +15,15 @@ import traceback
|
||||
|
||||
from PyQt6.QtCore import QObject, pyqtSignal
|
||||
|
||||
import api
|
||||
import assistant
|
||||
import audio
|
||||
import cleanup
|
||||
import config as cfg
|
||||
import i18n
|
||||
import paste
|
||||
import vad
|
||||
from i18n import t
|
||||
from . import api
|
||||
from . import assistant
|
||||
from . import audio
|
||||
from . import cleanup
|
||||
from . import config as cfg
|
||||
from . import i18n
|
||||
from . import paste
|
||||
from . import vad
|
||||
from .i18n import t
|
||||
|
||||
CHUNK_SECONDS = audio.CHUNK_FRAMES / audio.RATE
|
||||
|
||||
+43
-14
@@ -9,18 +9,35 @@ DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# path. That is a second script rather than a branch through this one, and
|
||||
# update.sh reaches it through here without having to know which it is on.
|
||||
if [[ "$(uname -s)" == "Darwin" ]]; then
|
||||
exec "$DIR/install-mac.sh" "$@"
|
||||
exec "$DIR/scripts/install-mac.sh" "$@"
|
||||
fi
|
||||
|
||||
PY="$(command -v python3)"
|
||||
# The one file that starts the application, whoever is asking: the launcher
|
||||
# below, both .desktop files, and every shortcut Dikte registers.
|
||||
ENTRY="$DIR/dikte/__main__.py"
|
||||
BIN_DIR="$HOME/.local/bin"
|
||||
APP_DIR="$HOME/.local/share/applications"
|
||||
AUTOSTART_DIR="$HOME/.config/autostart"
|
||||
ICON_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/icons"
|
||||
SHORTCUT="${1:-Ctrl+Space}"
|
||||
# Without the colon, so that a second argument given as "" stays empty. That is
|
||||
# how update.sh says "this one was turned off", as against not saying anything.
|
||||
CANCEL_SHORTCUT="${2-Ctrl+Alt+Space}"
|
||||
# Only the one: the discard key's default is the settings' own, read back below.
|
||||
DEFAULT_SHORTCUT="Ctrl+Space"
|
||||
# Given as arguments, or asked of the settings further down. An installer run
|
||||
# again, which is what every update does, must not undo a key you chose in
|
||||
# Settings, so silence here means "keep whatever is there".
|
||||
SHORTCUT="${1:-}"
|
||||
CANCEL_SHORTCUT="${2-}"
|
||||
# Passed as "" means the discard key is off, which is not the same answer as
|
||||
# not being passed at all.
|
||||
CANCEL_GIVEN=$(( $# >= 2 ))
|
||||
# One caller says both without meaning either: an updater from before Dikte
|
||||
# became a package looks for the settings at a path that no longer exists, and
|
||||
# so passes the default key and an empty discard key rather than yours. This
|
||||
# can go once nobody is updating across that commit any more.
|
||||
if [[ "$SHORTCUT" == "$DEFAULT_SHORTCUT" && $CANCEL_GIVEN == 1 && -z "$CANCEL_SHORTCUT" ]]; then
|
||||
SHORTCUT=""
|
||||
CANCEL_GIVEN=0
|
||||
fi
|
||||
|
||||
say() { printf ' %s\n' "$1"; }
|
||||
ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; }
|
||||
@@ -83,8 +100,8 @@ fi
|
||||
|
||||
# 3. Launchers -------------------------------------------------------------
|
||||
mkdir -p "$BIN_DIR" "$APP_DIR" "$AUTOSTART_DIR" "$ICON_DIR"
|
||||
ln -sf "$DIR/dikte.py" "$BIN_DIR/dikte"
|
||||
chmod +x "$DIR/dikte.py"
|
||||
ln -sf "$ENTRY" "$BIN_DIR/dikte"
|
||||
chmod +x "$ENTRY"
|
||||
ok "Command installed: $BIN_DIR/dikte"
|
||||
case ":$PATH:" in
|
||||
*":$BIN_DIR:"*) ;;
|
||||
@@ -97,7 +114,7 @@ esac
|
||||
# bare X11 login Qt is left with hicolor, which has no such name, and the entry
|
||||
# comes out blank. hicolor is also where this goes, since it is the theme every
|
||||
# desktop must fall back to.
|
||||
if "$PY" "$DIR/trayicon.py" --hicolor "$ICON_DIR" >/dev/null 2>&1; then
|
||||
if PYTHONPATH="$DIR" "$PY" -m dikte.trayicon --hicolor "$ICON_DIR" >/dev/null 2>&1; then
|
||||
ICON=dikte
|
||||
# Only GTK reads a cache, and only if one is already there; a stale cache
|
||||
# would otherwise hide the file we just wrote.
|
||||
@@ -116,7 +133,7 @@ cat > "$APP_DIR/dikte.desktop" <<EOF
|
||||
Type=Application
|
||||
Name=Dikte
|
||||
Comment=Voice dictation: record, transcribe, clean up, paste
|
||||
Exec=$PY $DIR/dikte.py
|
||||
Exec=$PY $ENTRY
|
||||
Icon=$ICON
|
||||
Categories=Utility;AudioVideo;
|
||||
StartupNotify=false
|
||||
@@ -127,7 +144,7 @@ cat > "$AUTOSTART_DIR/dikte.desktop" <<EOF
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Dikte
|
||||
Exec=$PY $DIR/dikte.py
|
||||
Exec=$PY $ENTRY
|
||||
Icon=$ICON
|
||||
X-GNOME-Autostart-enabled=true
|
||||
StartupNotify=false
|
||||
@@ -143,14 +160,26 @@ ok "Will start automatically on login"
|
||||
# knows which desktop it is on, and it stores the combination in the settings
|
||||
# as well, which is where the built-in listener reads it from. A key written
|
||||
# to only one of the two places is a key that half works.
|
||||
if [[ "$SHORTCUT" == "$CANCEL_SHORTCUT" ]]; then
|
||||
warn "Both arguments are $SHORTCUT, so the discard key was left out."
|
||||
# What was not asked for is read back out of the settings, which is where Dikte
|
||||
# keeps the keys and so what a second run of this script has to leave alone.
|
||||
stored() { PYTHONPATH="$DIR" "$PY" -m dikte config get "$1" 2>/dev/null || true; }
|
||||
if [[ -z "$SHORTCUT" ]]; then
|
||||
SHORTCUT="$(stored shortcut)"
|
||||
SHORTCUT="${SHORTCUT:-$DEFAULT_SHORTCUT}"
|
||||
fi
|
||||
if [[ $CANCEL_GIVEN == 0 ]]; then
|
||||
# An empty answer here is a discard key that was turned off, and it stays off.
|
||||
CANCEL_SHORTCUT="$(stored cancel_shortcut)"
|
||||
fi
|
||||
|
||||
if [[ -n "$CANCEL_SHORTCUT" && "$SHORTCUT" == "$CANCEL_SHORTCUT" ]]; then
|
||||
warn "Both keys are $SHORTCUT, so the discard key was left out."
|
||||
say "Pass two different combinations, or set it in Settings → Shortcuts."
|
||||
CANCEL_SHORTCUT=""
|
||||
fi
|
||||
|
||||
register() { # which combination label
|
||||
if out="$("$PY" "$DIR/dikte.py" shortcut install "$1" --combo "$2" 2>&1)"; then
|
||||
if out="$("$PY" "$ENTRY" shortcut install "$1" --combo "$2" 2>&1)"; then
|
||||
ok "$3: $2"
|
||||
else
|
||||
# One line: the rest of what it has to say about KWin is printed below.
|
||||
@@ -167,7 +196,7 @@ if python3 -c 'import PyQt6.QtWidgets' 2>/dev/null; then
|
||||
# not this script's. Guessing from XDG_CURRENT_DESKTOP here is how every
|
||||
# session that was neither GNOME nor KDE used to be promised a KWin that was
|
||||
# never running.
|
||||
case "$("$PY" -c 'import sys; sys.path.insert(0, sys.argv[1]); import hotkey; print(hotkey.backend())' "$DIR" 2>/dev/null)" in
|
||||
case "$(PYTHONPATH="$DIR" "$PY" -c 'from dikte import hotkey; print(hotkey.backend())' 2>/dev/null)" in
|
||||
kde)
|
||||
warn "KWin only reads these at startup, so they go live after your next"
|
||||
say "login. Until then open Settings → Shortcuts and turn on the"
|
||||
|
||||
@@ -11,19 +11,38 @@
|
||||
# Homebrew moves that copy.
|
||||
set -euo pipefail
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# The checkout, one level up: this script lives in scripts/, everything it
|
||||
# touches is at the top of the tree.
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
# The one file that starts the application, whoever is asking: the wrapper in
|
||||
# ~/.local/bin, the bundle, and every shortcut Dikte registers.
|
||||
ENTRY="$DIR/dikte/__main__.py"
|
||||
APP_DIR="$HOME/Applications"
|
||||
APP="$APP_DIR/Dikte.app"
|
||||
BIN_DIR="$HOME/.local/bin"
|
||||
AGENT_DIR="$HOME/Library/LaunchAgents"
|
||||
AGENT_ID="io.github.yusufipk.dikte"
|
||||
AGENT="$AGENT_DIR/$AGENT_ID.plist"
|
||||
SHORTCUT="${1:-Ctrl+Option+Space}"
|
||||
# Without the colon, so that a second argument given as "" stays empty. That is
|
||||
# how update.sh says "this one was turned off", as against not saying anything.
|
||||
CANCEL_SHORTCUT="${2-Ctrl+Option+D}"
|
||||
# Only the one: the discard key's default is the settings' own, read back below.
|
||||
DEFAULT_SHORTCUT="Ctrl+Option+Space"
|
||||
# Given as arguments, or asked of the settings further down. An installer run
|
||||
# again, which is what every update does, must not undo a key you chose in
|
||||
# Settings, so silence here means "keep whatever is there".
|
||||
SHORTCUT="${1:-}"
|
||||
CANCEL_SHORTCUT="${2-}"
|
||||
# Passed as "" means the discard key is off, which is not the same answer as
|
||||
# not being passed at all.
|
||||
CANCEL_GIVEN=$(( $# >= 2 ))
|
||||
# One caller says both without meaning either: an updater from before Dikte
|
||||
# became a package looks for the settings at a path that no longer exists, and
|
||||
# so passes the default key and an empty discard key rather than yours. This
|
||||
# can go once nobody is updating across that commit any more.
|
||||
if [[ "$SHORTCUT" == "$DEFAULT_SHORTCUT" && $CANCEL_GIVEN == 1 && -z "$CANCEL_SHORTCUT" ]]; then
|
||||
SHORTCUT=""
|
||||
CANCEL_GIVEN=0
|
||||
fi
|
||||
|
||||
# The two places Homebrew installs to, in front, for the same reason dikte.py
|
||||
# The two places Homebrew installs to, in front, for the same reason the app
|
||||
# puts them there: a shell that has not been logged into since Homebrew was
|
||||
# installed does not have them, and this script would then report ffmpeg as
|
||||
# missing while the application finds it perfectly well.
|
||||
@@ -147,7 +166,7 @@ if [ ! -d "\$PYTHONHOME" ]; then
|
||||
osascript -e 'display alert "Dikte" message "The Python this was installed against is gone, most likely after a brew upgrade. Run ./install.sh again."' >/dev/null 2>&1
|
||||
exit 1
|
||||
fi
|
||||
exec "\$HERE/python3" "$DIR/dikte.py" --gui "\$@"
|
||||
exec "\$HERE/python3" "$ENTRY" --gui "\$@"
|
||||
EOF
|
||||
chmod +x "$APP/Contents/MacOS/Dikte"
|
||||
|
||||
@@ -186,7 +205,7 @@ printf 'APPL????' > "$APP/Contents/PkgInfo"
|
||||
# and no second place to change what Dikte looks like. Failing to draw it is
|
||||
# not worth stopping for: a bundle with no icon gets the generic one.
|
||||
iconset="$(mktemp -d)/Dikte.iconset"
|
||||
if "$PY" "$DIR/trayicon.py" "$iconset" >/dev/null 2>&1 \
|
||||
if PYTHONPATH="$DIR" "$PY" -m dikte.trayicon "$iconset" >/dev/null 2>&1 \
|
||||
&& iconutil -c icns "$iconset" -o "$APP/Contents/Resources/Dikte.icns" 2>/dev/null; then
|
||||
ok "Icon drawn"
|
||||
else
|
||||
@@ -212,7 +231,7 @@ fi
|
||||
-f "$APP" >/dev/null 2>&1 || true
|
||||
|
||||
# 4. The command ------------------------------------------------------------
|
||||
# A wrapper, where Linux gets a symlink to dikte.py. The shebang there is
|
||||
# A wrapper, where Linux gets a symlink to the entry point. The shebang there is
|
||||
# `env python3`, and on a Mac that is Apple's 3.9: the symlink would resolve to
|
||||
# the one interpreter that cannot run this. Naming the interpreter here also
|
||||
# gives update.sh and uninstall.sh somewhere to read it from, so that the three
|
||||
@@ -221,9 +240,9 @@ mkdir -p "$BIN_DIR"
|
||||
cat > "$BIN_DIR/dikte" <<EOF
|
||||
#!/bin/sh
|
||||
# Written by install-mac.sh. Edit that, not this.
|
||||
exec "$PY" "$DIR/dikte.py" "\$@"
|
||||
exec "$PY" "$ENTRY" "\$@"
|
||||
EOF
|
||||
chmod +x "$BIN_DIR/dikte" "$DIR/dikte.py"
|
||||
chmod +x "$BIN_DIR/dikte" "$ENTRY"
|
||||
ok "Command installed: $BIN_DIR/dikte"
|
||||
case ":$PATH:" in
|
||||
*":$BIN_DIR:"*) ;;
|
||||
@@ -271,14 +290,27 @@ fi
|
||||
# shortcut registry at all, so "installed" means the running application is
|
||||
# holding the combination, and the settings file is where it reads it from
|
||||
# after a restart. Nothing here needs a logout, unlike KDE.
|
||||
if [[ "$SHORTCUT" == "$CANCEL_SHORTCUT" ]]; then
|
||||
warn "Both arguments are $SHORTCUT, so the discard key was left out."
|
||||
#
|
||||
# What was not asked for is read back out of the settings, which is what makes
|
||||
# a second run of this script leave the keys you chose alone.
|
||||
stored() { PYTHONPATH="$DIR" "$PY" -m dikte config get "$1" 2>/dev/null || true; }
|
||||
if [[ -z "$SHORTCUT" ]]; then
|
||||
SHORTCUT="$(stored shortcut)"
|
||||
SHORTCUT="${SHORTCUT:-$DEFAULT_SHORTCUT}"
|
||||
fi
|
||||
if [[ $CANCEL_GIVEN == 0 ]]; then
|
||||
# An empty answer here is a discard key that was turned off, and it stays off.
|
||||
CANCEL_SHORTCUT="$(stored cancel_shortcut)"
|
||||
fi
|
||||
|
||||
if [[ -n "$CANCEL_SHORTCUT" && "$SHORTCUT" == "$CANCEL_SHORTCUT" ]]; then
|
||||
warn "Both keys are $SHORTCUT, so the discard key was left out."
|
||||
say "Pass two different combinations, or set it in Settings → Shortcuts."
|
||||
CANCEL_SHORTCUT=""
|
||||
fi
|
||||
|
||||
register() { # which combination label
|
||||
if out="$("$PY" "$DIR/dikte.py" shortcut install "$1" --combo "$2" 2>&1)"; then
|
||||
if out="$("$PY" "$ENTRY" shortcut install "$1" --combo "$2" 2>&1)"; then
|
||||
ok "$3: $2"
|
||||
else
|
||||
warn "${out%%$'\n'*}"
|
||||
@@ -4,7 +4,10 @@
|
||||
# is the word that deletes them.
|
||||
set -euo pipefail
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# The checkout, one level up: this script lives in scripts/, everything it
|
||||
# touches is at the top of the tree.
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ENTRY="$DIR/dikte/__main__.py"
|
||||
USER_NAME="$(id -un)"
|
||||
BIN_DIR="$HOME/.local/bin"
|
||||
|
||||
@@ -49,7 +52,7 @@ count() {
|
||||
|
||||
usage() {
|
||||
cat <<EOF
|
||||
Usage: ./uninstall.sh [--purge] [--yes]
|
||||
Usage: ./scripts/uninstall.sh [--purge] [--yes]
|
||||
|
||||
--purge also delete the settings ($CONFIG_DIR)
|
||||
and the dictations, meetings and recordings ($DATA_DIR)
|
||||
@@ -93,9 +96,9 @@ if ((MACOS)); then
|
||||
say "Nothing to unregister: macOS shortcuts live only while Dikte runs."
|
||||
elif [[ -n "$PY" ]] && "$PY" -c 'import PyQt6.QtWidgets' 2>/dev/null; then
|
||||
for which in toggle pause cancel ask meeting; do
|
||||
"$PY" "$DIR/dikte.py" shortcut remove "$which" >/dev/null 2>&1 || true
|
||||
"$PY" "$ENTRY" shortcut remove "$which" >/dev/null 2>&1 || true
|
||||
done
|
||||
case "$("$PY" -c 'import sys; sys.path.insert(0, sys.argv[1]); import hotkey; print(hotkey.backend())' "$DIR" 2>/dev/null)" in
|
||||
case "$(PYTHONPATH="$DIR" "$PY" -c 'from dikte import hotkey; print(hotkey.backend())' 2>/dev/null)" in
|
||||
kde)
|
||||
ok "Global shortcuts unregistered"
|
||||
say "KWin reads that file at startup, so the keys are free after your next login."
|
||||
@@ -111,10 +114,12 @@ fi
|
||||
# 2. The running instance --------------------------------------------------
|
||||
# It holds a tray icon and a socket; asking it to quit is tidier than pulling
|
||||
# its launchers out from under it.
|
||||
if pgrep -u "$USER_NAME" -f 'dikte\.py' >/dev/null 2>&1; then
|
||||
[[ -n "$PY" ]] && "$PY" "$DIR/dikte.py" quit >/dev/null 2>&1 || true
|
||||
# The pattern matches an instance started before Dikte became a package as well,
|
||||
# which is what an uninstall run straight after an update finds running.
|
||||
if pgrep -u "$USER_NAME" -f 'dikte(/__main__|)\.py' >/dev/null 2>&1; then
|
||||
[[ -n "$PY" ]] && "$PY" "$ENTRY" quit >/dev/null 2>&1 || true
|
||||
sleep 0.5
|
||||
if pgrep -u "$USER_NAME" -f 'dikte\.py' >/dev/null 2>&1; then
|
||||
if pgrep -u "$USER_NAME" -f 'dikte(/__main__|)\.py' >/dev/null 2>&1; then
|
||||
warn "Dikte is still running; close it from the tray icon"
|
||||
else
|
||||
ok "Stopped the running instance"
|
||||
@@ -216,11 +221,11 @@ elif [[ "$CONFIG_DIR" == "$DATA_DIR" ]]; then
|
||||
# macOS keeps both in the one directory a Mac user's backup already knows
|
||||
# about, so naming it twice would only look like two things were kept.
|
||||
say "Settings and dictations kept: $CONFIG_DIR"
|
||||
say "Delete them too with: ./uninstall.sh --purge"
|
||||
say "Delete them too with: ./scripts/uninstall.sh --purge"
|
||||
else
|
||||
say "Settings kept: $CONFIG_DIR"
|
||||
say "Dictations kept: $DATA_DIR"
|
||||
say "Delete them too with: ./uninstall.sh --purge"
|
||||
say "Delete them too with: ./scripts/uninstall.sh --purge"
|
||||
fi
|
||||
|
||||
echo
|
||||
@@ -2,7 +2,10 @@
|
||||
# Dikte updater: pull, put the launchers back, restart what was running.
|
||||
set -euo pipefail
|
||||
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
# The checkout, one level up: this script lives in scripts/, everything it
|
||||
# touches is at the top of the tree.
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
ENTRY="$DIR/dikte/__main__.py"
|
||||
USER_NAME="$(id -un)"
|
||||
|
||||
if [[ "$(uname -s)" == "Darwin" ]]; then
|
||||
@@ -13,10 +16,8 @@ if [[ "$(uname -s)" == "Darwin" ]]; then
|
||||
# make a missing file the end of the script rather than a question answered no.
|
||||
PY="$(sed -n 's/^exec "\([^"]*\)".*/\1/p' "$HOME/.local/bin/dikte" 2>/dev/null | head -1 || true)"
|
||||
[[ -x "$PY" ]] || PY="$(command -v python3 || true)"
|
||||
DEFAULT_SHORTCUT="Ctrl+Option+Space"
|
||||
else
|
||||
PY="$(command -v python3 || true)"
|
||||
DEFAULT_SHORTCUT="Ctrl+Space"
|
||||
fi
|
||||
|
||||
say() { printf ' %s\n' "$1"; }
|
||||
@@ -24,13 +25,6 @@ ok() { printf ' \033[32m✓\033[0m %s\n' "$1"; }
|
||||
warn() { printf ' \033[33m!\033[0m %s\n' "$1"; }
|
||||
die() { printf ' \033[31m✗\033[0m %s\n' "$1"; echo; exit 1; }
|
||||
|
||||
# The combination stored in the settings, which is where Dikte itself reads it
|
||||
# from and the one place that is the same whichever mechanism the session has.
|
||||
setting() {
|
||||
[[ -n "$PY" ]] || return 0
|
||||
"$PY" "$DIR/dikte.py" config get "$1" 2>/dev/null || true
|
||||
}
|
||||
|
||||
echo
|
||||
echo "Updating Dikte"
|
||||
echo "──────────────"
|
||||
@@ -90,18 +84,15 @@ echo
|
||||
|
||||
# 4. Launchers --------------------------------------------------------------
|
||||
# An update can add a dependency or move a file, so the installer runs again.
|
||||
# It would otherwise register its own defaults over the keys you chose, so it
|
||||
# is told what those are. Read before the installer runs, since it is the one
|
||||
# writing them.
|
||||
shortcut="$(setting shortcut)"
|
||||
cancel_shortcut="$(setting cancel_shortcut)"
|
||||
# Positional, so a chosen discard key cannot be passed without the other one.
|
||||
"$DIR/install.sh" "${shortcut:-$DEFAULT_SHORTCUT}" "${cancel_shortcut:-}"
|
||||
# With no keys named it keeps the ones stored in the settings, which are the
|
||||
# ones you chose.
|
||||
"$DIR/install.sh"
|
||||
|
||||
# 5. The running instance ---------------------------------------------------
|
||||
# It is still running the code from before the pull.
|
||||
if pgrep -u "$USER_NAME" -f 'dikte\.py' >/dev/null 2>&1; then
|
||||
if [[ -n "$PY" ]] && "$PY" "$DIR/dikte.py" restart >/dev/null 2>&1; then
|
||||
# It is still running the code from before the pull, which on an update across
|
||||
# the move into a package is a process still named after the old entry point.
|
||||
if pgrep -u "$USER_NAME" -f 'dikte(/__main__|)\.py' >/dev/null 2>&1; then
|
||||
if [[ -n "$PY" ]] && "$PY" "$ENTRY" restart >/dev/null 2>&1; then
|
||||
ok "Restarted, so the new version is the one running"
|
||||
else
|
||||
warn "Could not restart it; use the tray menu → Restart"
|
||||
+3
-3
@@ -22,9 +22,9 @@ import urllib.request
|
||||
import wave
|
||||
from unittest import mock
|
||||
|
||||
import assistant
|
||||
import config as cfg
|
||||
import i18n
|
||||
from dikte import assistant
|
||||
from dikte import config as cfg
|
||||
from dikte import i18n
|
||||
|
||||
# What the application is, rather than what it does: PipeWire, wl-clipboard,
|
||||
# ydotool, KDE's shortcut file, /dev/input. A port to another desktop replaces
|
||||
|
||||
+2
-2
@@ -16,8 +16,8 @@ import threading
|
||||
import time
|
||||
import unittest
|
||||
|
||||
import api
|
||||
import ggml
|
||||
from dikte import api
|
||||
from dikte import ggml
|
||||
from tests.support import (
|
||||
DikteTest,
|
||||
fake_urlopen,
|
||||
|
||||
@@ -14,7 +14,7 @@ import time
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import assistant
|
||||
from dikte import assistant
|
||||
from tests.support import DikteTest, fake_urlopen, only_these_tools
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ import unittest
|
||||
import wave
|
||||
from unittest import mock
|
||||
|
||||
import audio
|
||||
from dikte import audio
|
||||
from tests.support import (
|
||||
DikteTest,
|
||||
FakeCompleted,
|
||||
|
||||
@@ -11,9 +11,9 @@ import subprocess
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import api
|
||||
import cleanup
|
||||
import ggml
|
||||
from dikte import api
|
||||
from dikte import cleanup
|
||||
from dikte import ggml
|
||||
from tests.support import DikteTest, fake_urlopen, sent_json, url_error
|
||||
from tests.test_api import FakeServer, chat_reply
|
||||
|
||||
|
||||
+4
-4
@@ -12,10 +12,10 @@ import json
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import cli
|
||||
import config as cfg
|
||||
import hotkey
|
||||
import ipc
|
||||
from dikte import cli
|
||||
from dikte import config as cfg
|
||||
from dikte import hotkey
|
||||
from dikte import ipc
|
||||
from tests.support import DikteTest, fake_urlopen
|
||||
|
||||
|
||||
|
||||
@@ -11,12 +11,12 @@ import os
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import api
|
||||
import cleanup
|
||||
import config as cfg
|
||||
import ggml
|
||||
import i18n
|
||||
import paste
|
||||
from dikte import api
|
||||
from dikte import cleanup
|
||||
from dikte import config as cfg
|
||||
from dikte import ggml
|
||||
from dikte import i18n
|
||||
from dikte import paste
|
||||
from tests.support import DikteTest
|
||||
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ import unittest
|
||||
import wave
|
||||
from unittest import mock
|
||||
|
||||
import api
|
||||
import filetranscribe as ft
|
||||
from dikte import api
|
||||
from dikte import filetranscribe as ft
|
||||
from tests.support import DikteTest, make_wav, silence, tone
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -17,8 +17,8 @@ import threading
|
||||
import time
|
||||
from unittest import mock
|
||||
|
||||
import ggml
|
||||
import hub
|
||||
from dikte import ggml
|
||||
from dikte import hub
|
||||
from tests.support import (DikteTest, fake_urlopen, http_error, json_body,
|
||||
linux_only, url_error)
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ import subprocess
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import config as cfg
|
||||
import hotkey
|
||||
from dikte import config as cfg
|
||||
from dikte import hotkey
|
||||
from tests.support import DikteTest, FakeCompleted, linux_only
|
||||
|
||||
SHORTCUTS_RC = """[services][dikte-toggle.desktop]
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
import json
|
||||
|
||||
import hub
|
||||
from dikte import hub
|
||||
from tests.support import DikteTest, fake_urlopen, http_error, url_error
|
||||
|
||||
RELEASE = {
|
||||
|
||||
+1
-1
@@ -9,7 +9,7 @@ import string
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import i18n
|
||||
from dikte import i18n
|
||||
from tests.support import DikteTest
|
||||
|
||||
|
||||
|
||||
+2
-2
@@ -11,7 +11,7 @@ import sys
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import ipc
|
||||
from dikte import ipc
|
||||
|
||||
|
||||
class FakeSocket:
|
||||
@@ -57,7 +57,7 @@ class FakeSocket:
|
||||
|
||||
class Paths(unittest.TestCase):
|
||||
def test_script_path_points_at_dikte(self):
|
||||
self.assertTrue(ipc.script_path().endswith("dikte.py"))
|
||||
self.assertTrue(ipc.script_path().endswith("dikte/__main__.py"))
|
||||
self.assertTrue(os.path.exists(ipc.script_path()))
|
||||
|
||||
def test_the_shortcut_command_runs_it_with_this_interpreter(self):
|
||||
|
||||
@@ -11,9 +11,9 @@ import unittest
|
||||
import wave
|
||||
from unittest import mock
|
||||
|
||||
import api
|
||||
import config as cfg
|
||||
import meeting
|
||||
from dikte import api
|
||||
from dikte import config as cfg
|
||||
from dikte import meeting
|
||||
from tests.support import DikteTest, make_wav, silence, speech, stereo, tone
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -22,7 +22,7 @@ import unittest
|
||||
from typing import ClassVar
|
||||
from unittest import mock
|
||||
|
||||
import paste
|
||||
from dikte import paste
|
||||
from tests.support import DikteTest, FakeCompleted, only_these_tools
|
||||
|
||||
|
||||
|
||||
+3
-3
@@ -9,9 +9,9 @@ import os
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import config as cfg
|
||||
import ggml
|
||||
import paths
|
||||
from dikte import config as cfg
|
||||
from dikte import ggml
|
||||
from dikte import paths
|
||||
|
||||
|
||||
class Directories(unittest.TestCase):
|
||||
|
||||
@@ -15,7 +15,7 @@ from unittest import mock
|
||||
from PyQt6.QtGui import QImage
|
||||
from PyQt6.QtWidgets import QApplication
|
||||
|
||||
import trayicon
|
||||
from dikte import trayicon
|
||||
from tests.support import DikteTest
|
||||
|
||||
# One application for the whole run; Qt allows no second one.
|
||||
@@ -66,7 +66,7 @@ class Tray(DikteTest):
|
||||
self.patch_attr(trayicon, "_cache", {})
|
||||
|
||||
def test_a_name_we_do_not_draw_is_a_null_icon(self):
|
||||
# dikte.py asks the theme first and falls through to here, so anything
|
||||
# app.py asks the theme first and falls through to here, so anything
|
||||
# answered with a picture would be one the theme should have given.
|
||||
self.assertTrue(trayicon.icon("emblem-important").isNull())
|
||||
|
||||
|
||||
+9
-9
@@ -16,12 +16,12 @@ from PyQt6.QtCore import QPoint, QPointF, Qt
|
||||
from PyQt6.QtGui import QWheelEvent
|
||||
from PyQt6.QtWidgets import QApplication, QMessageBox
|
||||
|
||||
import cleanup
|
||||
import config as cfg
|
||||
import hotkey
|
||||
import overlay as overlay_module
|
||||
import paste
|
||||
import settings_ui
|
||||
from dikte import cleanup
|
||||
from dikte import config as cfg
|
||||
from dikte import hotkey
|
||||
from dikte import overlay as overlay_module
|
||||
from dikte import paste
|
||||
from dikte import settings_ui
|
||||
from tests.support import DikteTest, only_these_tools
|
||||
|
||||
# One application for the whole run; Qt allows no second one.
|
||||
@@ -281,7 +281,7 @@ class Settings(DikteTest):
|
||||
text = self.shortcut_tab_text(window)
|
||||
self.assertIn("i3 keeps no shortcut registry", text)
|
||||
self.assertNotIn("KWin", text)
|
||||
self.assertIn("dikte.py toggle", text)
|
||||
self.assertIn("__main__.py toggle", text)
|
||||
# Not a choice to offer where it is the only mechanism there is.
|
||||
self.assertTrue(window.evdev_enabled.isHidden())
|
||||
self.assertFalse([button for button in
|
||||
@@ -458,7 +458,7 @@ class MacSettings(Settings):
|
||||
text = self.shortcut_tab_text(window)
|
||||
self.assertIn("Dikte asks macOS for these combinations", text)
|
||||
self.assertNotIn("KWin", text)
|
||||
self.assertNotIn("dikte.py toggle", text)
|
||||
self.assertNotIn("__main__.py toggle", text)
|
||||
|
||||
def test_the_paste_keys_on_offer_are_the_ones_a_mac_uses(self):
|
||||
window = self.window(cfg.Config())
|
||||
@@ -482,7 +482,7 @@ class KdeSettings(Settings):
|
||||
self.assertIn("KWin only reads shortcut settings at startup", text)
|
||||
self.assertIn("Install as a KDE shortcut", text)
|
||||
self.assertNotIn("keeps no shortcut registry", text)
|
||||
self.assertNotIn("dikte.py toggle", text)
|
||||
self.assertNotIn("__main__.py toggle", text)
|
||||
# Here it is a choice: the wait for the next login, or the key press
|
||||
# reaching the focused application as well.
|
||||
self.assertFalse(window.evdev_enabled.isHidden())
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
|
||||
import unittest
|
||||
|
||||
import vad
|
||||
from dikte import vad
|
||||
from tests.support import DikteTest
|
||||
|
||||
CHUNK = 1024 / 16000 # what worker.py feeds it: one chunk of the level meter
|
||||
|
||||
@@ -11,11 +11,11 @@ import os
|
||||
import unittest
|
||||
from unittest import mock
|
||||
|
||||
import api
|
||||
import assistant
|
||||
import config as cfg
|
||||
import paste
|
||||
import worker
|
||||
from dikte import api
|
||||
from dikte import assistant
|
||||
from dikte import config as cfg
|
||||
from dikte import paste
|
||||
from dikte import worker
|
||||
from tests.support import DikteTest, make_wav, speech
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user