Put the modules in a package and the scripts in a folder

Twenty-two files at the top of the tree was the first thing anybody saw of
this repository. They are one package now, imported relatively, and the three
scripts that are not the front door moved under scripts/. install.sh stays
where the README has always said it is.

What starts the application is dikte/__main__.py: python3 -m dikte runs it,
and so does naming the file, which is what the launcher symlink, both .desktop
files, the macOS bundle and every registered shortcut do. Run by path there is
no package around it, so it puts the checkout on sys.path itself.

The installers now keep the keys you chose when they are given none, which is
what an update is: update.sh no longer has to read them out and pass them back.
An updater from before this commit cannot read them at all, so the one thing it
can say, the default key with an empty discard key, is read as "nothing was
asked for" rather than obeyed. That guard can go once nobody is updating across
this commit.
This commit is contained in:
2026-08-16 14:01:01 +03:00
parent 8444b6822d
commit 2f59029261
50 changed files with 316 additions and 215 deletions
+3 -3
View File
@@ -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
View File
@@ -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,
+1 -1
View File
@@ -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
View File
@@ -21,7 +21,7 @@ import unittest
import wave
from unittest import mock
import audio
from dikte import audio
from tests.support import (
DikteTest,
FakeCompleted,
+3 -3
View File
@@ -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
View File
@@ -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
+6 -6
View File
@@ -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
+2 -2
View File
@@ -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
View File
@@ -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)
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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):
+3 -3
View File
@@ -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
View File
@@ -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
View File
@@ -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):
+2 -2
View File
@@ -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
View File
@@ -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
View File
@@ -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
+5 -5
View File
@@ -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