Commit Graph
200 Commits
Author SHA1 Message Date
yusufipek 08fc2e4a9d Group the model lists and say which row this machine should take
The two local model boxes handed over a flat list sorted by size and left
every choice in it to the reader. For whisper that interleaved the models:
large-v3-turbo-q5_0 landed between the two medium quantisations, half a
screen from the turbo model it is a copy of. For cleanup it was forty
repository ids, half of which answer with nothing at all because what they
publish is split across files or larger than the cap, and an empty box read
as though the click had not registered.

Now each box says what the machine is, groups the list by model, and marks
the row to take:

- whisper rows are grouped by model, with the quantisations and the
  English-only files under the model they are a copy of, and every row says
  its bit depth rather than leaving q5_1 and Q4_K_M and BF16 to be decoded.
- the recommendation follows the machine. Under 4 GB it is small-q5_1;
  with a graphics interface and 15 GB it is large-v3-q5_0, which is worth
  about two and a half points of word error in the languages that are not
  English; in between it is turbo, and a processor build where the Vulkan
  one belongs is not counted as a card.
- a row larger than half the memory less a gigabyte says it is too big.
- the publisher box holds the five suggestions until the switch beside it
  is turned on, and a line under it says in words what the chosen one is.
- a publisher that answers with nothing says why instead of going blank.
- the draft heads (dflash, dspark, eagle3) are no longer offered as models,
  and neither are the base models that sit beside their tuned twin.
2026-09-05 11:12:42 +03:00
Yusuf İpek 4d0c0e29f1 Merge pull request #70 from nomoreshow/feat/managed-vulkan-whisper
Ship a managed Vulkan whisper-server for Linux x64
whisper.cpp-v1.9.3
2026-09-05 10:27:20 +03:00
yusufipek 4ae44720c8 Merge master, and let one function pick the archive
master grew _pick_asset() while this branch grew a second answer to the
same question inside install_program(). Two functions deciding which
archive this machine wants is one too many, so the managed build is
asked for at the top of the picker: Dikte's own Vulkan whisper-server
first where the machine is one it is built for, then upstream's newest,
then the nightly pointer, then the newest release that carries a build.

install_program() is back to master's three lines and reads which of the
two landed off the asset name.
2026-09-05 10:06:38 +03:00
Yusuf İpek 74c37c0112 Merge pull request #73 from yusufipk/llama-nightly-and-model-box
Find the llama.cpp nightly builds, and keep the model box honest
2026-09-05 09:51:24 +03:00
yusufipek fb80d85332 Stop two tests failing on what is around them
The macOS path test set XDG_CONFIG_HOME to "/c" and asserted that "/c" was nowhere in the answer, but the home these run under is a mkdtemp path, so any TMPDIR with a "/c" in it failed the test. macOS is exactly where that happens: its temporary directories are /var/folders/<two letters>/, and one letter in thirty-six starts with c. The needle is now a word no directory can be called.

The other is the `ask` verb reading what was piped into it. Nothing was, but the runner's own stdin is not nothing either, so the verb read whatever the runner left there. It now reads an empty stream, whichever runner is in use.
2026-09-05 09:47:50 +03:00
yusufipek fff9cd1c55 Keep the publisher and the model boxes saying the same thing
Changing the publisher left the model box untouched: the old selection was carried over, added back as "not downloaded" and selected again, so a model the new repository does not publish could be saved against it. The selection is now only carried within the publisher it was made in, every keystroke in the publisher box no longer starts its own request, and a list that comes back for a publisher that is no longer chosen is dropped rather than answering the wrong one.

The status line grew two things it could not say before. A row rebuilt from a name alone carries no file to fetch, and the Download button stayed lit over it doing nothing; those rows now say the publisher does not offer the model, and the button is out. A model that is here while the program above it is not no longer reads "Ready", which is what had people asking why nothing transcribed.
2026-09-05 09:47:42 +03:00
yusufipek cfeed2af8c Find the llama.cpp builds where they are actually published
"latest" for llama.cpp is a version marker carrying one file, nightly-tag.txt, and the archives it names hang off a prerelease that "latest" never points at. Reading only the latest release meant Dikte offered no llama-server for any machine, so _pick_asset now follows that pointer, and when there is none it walks the recent releases and takes the newest one that does carry a build for this machine. hub grows releases() for the listing and text() for the pointer file; the pointer is read rather than cached, because what it carries is a few bytes on the way to a download that is checksummed in full.

The extra lookups are best effort: whatever goes wrong in them leaves the caller's own message standing, but a first release that could not be fetched at all is kept and re-raised when nothing else turns up, so an unreachable GitHub still reads as an unreachable GitHub rather than as a machine nobody publishes for.
2026-09-05 09:47:22 +03:00
yusufipek 156d8bf8e8 Write the release order down where it will be read
Enabling immutable releases, configuring the dependency-release
environment, which order the two dispatch runs go in, and which five
constants have to move together were all in the pull request
description, which is not a place anybody looks a year later.

The same file says what this costs: the pinned digest means a backend
update is a Dikte release, and the build is deterministic between two
runs of one builder rather than across time, because the apt metadata
behind the pinned packages is not pinned and LunarG drops superseded
ones. Both are worth knowing before the next version bump, not during
it.

The README keeps a clause instead of the paragraph it had grown.
2026-09-05 09:46:08 +03:00
yusufipek 5f6e4ad782 Make a version bump possible without editing the workflow first
The digest was a literal in the workflow while the version was an input,
so a dispatch for anything but 1.9.3 built the archive and then failed
its own gate. The digest of a version nobody has reviewed cannot be
known before it is built, which made the inputs unusable for the one job
they exist for.

An empty expected_sha256 now reports the digest of what was built and
refuses to publish; a digest handed in is checked the way the literal
was. The input shapes are also checked before the checkout that uses
them rather than after it.
2026-09-05 09:45:48 +03:00
yusufipek 10ef4e62a9 Smoke-test the bundle the way Dikte starts it
The CPU run passed -ng, which tells whisper.cpp not to look for a
backend at all, so the claim the bundle rests on, that it starts and
transcribes where Vulkan cannot, was never tested. Dikte passes -ng only
when its GPU setting is off; every other run goes through the backend
registry.

Both runs lose the flag, and a third one is added between them: the
loader installed, nothing behind it. That is a real machine, libvulkan
pulled in by something else with no driver to go with it, and it is the
one whose answer nobody knew.
2026-09-05 09:45:19 +03:00
yusufipek 00a5283adb Let a downloaded program be downloaded again
The button disappeared the moment anything landed, and nothing else on
the window asks for that download. So a machine that got the processor
build before its graphics driver was installed can never be given the
Vulkan one, and nobody can pick up a newer whisper.cpp either: both of
those are the same missing control.

It reads "Download again" once a copy is here, and stays hidden while a
system one is on the PATH, because that is the copy that would run.
2026-09-05 09:45:00 +03:00
yusufipek c3bf328eef Build the bundle only for what the bundle is built from
The trigger listed dikte/ggml.py, both test files and the two READMEs,
which are among the files that change most often. A typo fix in the
README started a run that builds a container image, compiles every
Vulkan shader and starts two more containers, with a 45 minute timeout
on it because that is roughly what it costs.

Nothing is lost. What ties ggml.py to the release, the tag, version,
commit and reviewed digest, is asserted in tests/test_packaging.py, and
that file already runs on every pull request in milliseconds.
2026-09-05 09:37:08 +03:00
Yusuf İpek 3ad41d84f9 Merge pull request #69 from yusufipk/openrouter-subtitle-model
Let OpenRouter audio files use a chosen model instead of whisper-1
2026-09-05 09:25:57 +03:00
yusufipek 59180b8eff Say when the processor build landed instead of the Vulkan one
The install falls back to upstream's CPU archive whenever Dikte's own
release, the file in it, or its reviewed digest is not there, and until
the package is published by hand that is every download. It happened
without a word: the window said "Downloaded, version v1.9.3." either
way, and a graphics card sitting idle looks exactly like one being used.

The install record now carries which of the two builds landed, written
only where both were on offer, and the settings window says so on the
line that already reports the version.
2026-09-05 09:16:59 +03:00
yusufipek 956c3eaf3c Leave the tar extraction fallback where it was
Refusing the install on a Python without the extraction filters is a
change to how every archive on every platform is unpacked, and it has
nothing to do with shipping a Vulkan whisper-server. On those Pythons
the download stops working altogether, which is a worse answer than the
one that was there.

Worth doing on its own terms, in its own change, where the versions it
turns away can be argued about without a backend release riding on it.
2026-09-05 09:16:49 +03:00
nomoreshow 1f57455a34 Fix cross-platform test assumptions 2026-09-04 01:04:36 +03:00
nomoreshow e0eae4d8fe Ship a managed Vulkan whisper-server for Linux x64 2026-09-04 00:34:55 +03:00
yusufipek eda1398a2b Call the OpenRouter subtitle model the audio file model
Timestamps are a file transcription option, so the model box is named
after the file rather than the format it ends up in.
2026-09-02 12:43:07 +03:00
yusufipek 6e307bd8d0 Let OpenRouter subtitles use a chosen model instead of whisper-1
A timestamped run on OpenRouter always asked openai/whisper-1 for the
segments, whatever model was picked for plain transcription. Not every
model there returns segment times, so the one to use is now its own
setting, openrouter_subtitle_model, shown in the speech-to-text box only
when OpenRouter is the provider. Empty keeps the old whisper-1 fallback.

Target carries the choice as subtitle_model and timestamp_model() reads
it; the other providers are unchanged.
2026-09-02 12:41:43 +03:00
yusufipek 310ef8d7cf Dikte 1.1.0 v1.1.0 2026-08-27 16:38:59 +03:00
Yusuf İpek 4f304e3d94 Merge pull request #62 from yusufipk/claude/remove-tooltip-texts-356de5
Drop three explanatory texts from the settings window
2026-08-27 16:36:39 +03:00
yusufipek c1554c092e Drop three explanatory texts from the settings window
The agent and meeting tabs opened with an intro paragraph, and the
cleanup provider box showed a long tooltip comparing the providers.
None of them earned the space: the intro texts were unclear and the
tooltip restated what picking a provider already shows. The orphaned
Turkish translations go with them.
2026-08-27 16:35:01 +03:00
Yusuf İpek 632804e922 Merge pull request #56 from sudoeren/feat/opencode-go-provider
Add OpenCode Go as a cleanup and agent provider
2026-08-27 16:31:47 +03:00
Yusuf İpek ec57d154fa Merge pull request #61 from yusufipk/bundled-libxkbcommon
Leave both halves of libxkbcommon to the system
2026-08-27 16:31:03 +03:00
yusufipek 22a73bd939 Merge master into the OpenCode Go branch
Master grew Google AI Studio and Antigravity as providers, a doctor that
names each provider's own key, and settings that fetch every hosted
model list as the window opens. OpenCode Go is folded into each: its
name joins SERVICES and the doctor's key table, its key row sits beside
Google's, its Fetch button follows the per-provider pattern Google's
uses, and _load_hosted_models fetches its catalog at open when a key is
on file, filling the cleanup and agent boxes alike.
2026-08-27 16:27:57 +03:00
yusufipek c503d12e62 Leave both halves of libxkbcommon to the system
Qt's xcb plugin links libxkbcommon and libxkbcommon-x11, and the x11 half
hands keymap objects to the core half to free, so the pair must come from
one build. The build machine has only the core half installed, so
PyInstaller bundled that one alone and the other kept loading from the
user's system; an Ubuntu 22.04 core freeing what a current x11 half
allocated is the segfault of issue #57, which a Turkish layout happened
to move to startup. Every desktop that can show a window carries both
libraries from one build, so the bundle now carries neither.

Fixes #57
2026-08-27 16:25:59 +03:00
Yusuf İpek 5fd32d105f Merge pull request #59 from Oztturk/gemini-and-agy-cleanup
Clean up on Google AI Studio, or on Antigravity
2026-08-27 16:14:34 +03:00
yusufipekandClaude Fable 5 d331ccb877 Fetch every model list at open, without being asked
Codex's list already arrived on its own, because reading a cache on disk
costs nothing. The other three lists were behind a Fetch button, which
meant the built-in ones aged in front of anyone who never pressed it. Now
OpenRouter's and Google's lists are fetched when the settings window
opens, and Antigravity's comes off `agy models`, which prints one
id-tab-name line per model and answers over the network in a couple of
seconds; all three run off the interface thread.

Nobody asked, so nothing is reported: a failure changes nothing on
screen, the built-in lists stay, and the Fetch buttons remain both the
retry and the place an error is worth explaining. A provider whose key
has not been given is not called at all, so opening Settings is not by
itself a request to two vendors.

Also drops an import the merge had left in twice.

Co-Authored-By: Claude Fable 5 <[email protected]>
2026-08-27 16:12:18 +03:00
yusufipek fa3d72f5a7 Fetch OpenCode Go's catalog as the settings window opens
Codex already refreshes its boxes from the source at open, so the
built-in list is never the whole truth for longer than a window takes to
build. OpenCode Go now gets the same courtesy: one request to /models on
a background thread, both of its boxes refilled with the picked model
kept, skipped entirely when there is no key to send.
2026-08-27 16:04:30 +03:00
yusufipek 1ff18e46ab Call OpenRouter the quickest again
Nothing was measured that put OpenCode Go beside it, so the tooltip
claims only what is known: OpenRouter is the quickest, and OpenCode Go
merely needs nothing installed either. The translation follows word for
word.
2026-08-27 16:04:09 +03:00
yusufipek 664a6f53a4 Offer only models the OpenCode Go catalog actually lists
ox-alpha-free is not in the /models answer, and the comment claimed the
chat endpoint hides Grok, Luna, MiniMax and Qwen when its own catalog
lists them. The built-in list is a starting set; the Fetch button asks
the endpoint for the full catalog of the day.
2026-08-27 15:55:32 +03:00
yusufipek 0ecd4d251e Translate the cleanup provider tooltip again
The tooltip gained OpenCode Go but its translation was added without the
llama.cpp sentence the tooltip actually carries, so a Turkish window
showed the English text. The full entry now matches the tooltip word for
word, and the two shorter variants nothing looks up any more are gone.
2026-08-27 15:54:54 +03:00
Yusuf İpek 7b7df1df62 Merge pull request #60 from AydoganCan60/feature/display-selection
Add display selection for recording indicator
2026-08-27 15:54:30 +03:00
yusufipek 65055eeb56 Give OpenCode Go a reachable Fetch model list button
The one button lived in the OpenRouter model row, which leaves the
screen whenever another provider is chosen, so the OpenCode fetch path
could never be clicked. The OpenCode row now carries its own button into
the same handler, the row as a whole is what hides, and the fetched list
lands in the box it belongs to without touching the meeting box.
2026-08-27 15:53:44 +03:00
yusufipek 23243c5171 Trim the README and polish the display selection
The settings-storage paragraph goes back to the one sentence it was. The screen list now shows native resolutions rather than the scaled ones, the Turkish tab is named Ekran, and the repositioning comment says what a named screen changes.
2026-08-27 15:52:02 +03:00
yusufipek ccc8ba596e Merge master into the OpenCode Go branch
Codex now asks itself for its model list, doctor grew ready flags and a
per-provider line, and the Groq key joined the masked ones; the OpenCode
additions are folded into each. The no-CLI cleanup test follows the
fake_run to fake_cli rename.
2026-08-27 15:51:49 +03:00
yusufipek 1df2d03056 Merge master into the Gemini and Antigravity branch
Both sides rewrote the doctor: master rebuilt it around ready flags so a
fully local setup stops reading as broken, while this branch taught it
that Google has a key of its own and that the local model has neither a
key nor a program. Kept master's structure and folded the branch in: the
two hosted providers answer for their keys, a CLI for its program, and
the JSON keeps both the provider-aware "key" and master's "ready".

SECRET_KEYS gained groq on master and gemini here; the resolution keeps
all four. _conclude was also changed by both: master stopped matching
stderr wording and asks _API_TROUBLE instead, this branch renamed its
last parameter to the provider's short name so a session is stored under
the name it is read back by; the rename now rides on master's body.
codex_models() and the settings loaders landed beside the new Gemini
ones, so both stay. The new cleanup tests called the CLI fake by its old
name, fake_run, which master had renamed to fake_cli.
2026-08-27 15:50:48 +03:00
Yusuf İpek 4f0d320ff1 Merge pull request #55 from yusufipk/transcript-queue
Let the next dictation start while the last one is still working
2026-08-27 15:40:12 +03:00
yusufipek f6c6122253 Merge master into the transcript queue branch 2026-08-27 15:35:08 +03:00
Yusuf İpek 9736bd0ecd Merge pull request #54 from yusufipk/codex-model-list
Ask Codex itself which models it offers
2026-08-27 15:31:24 +03:00
Yusuf İpek d896680a4f Merge pull request #50 from huseyin-emre-tigci/single-instance
Survive every way a dictation was being lost
2026-08-27 15:28:05 +03:00
Yusuf İpek 7c135b1063 Merge pull request #46 from senolsun/fix-live-language-switch
Rebuild the settings window when the language changes
2026-08-27 15:25:34 +03:00
yusufipek 17a55e1efc Merge master into the reliability branch
The paste block was rewritten by both sides: master taught press() to
put the remembered application back in front (focus), this branch moved
the history write ahead of the paste and stopped restoring the old
clipboard over a transcript the key press refused. Kept this branch's
order and error handling, and handed press() the focus it now takes.
2026-08-27 15:23:41 +03:00
yusufipek a3bb5ed29b Keep the rebuild away from a window a thread is still writing to
deleteLater destroyed a window whose model boxes a daemon thread was
still reporting into: a running download died with a RuntimeError, the
.part stayed on disk and the UI said nothing. Dropping the reference is
how the ordinary close path lets a window go, and the closures a thread
holds keep the object alive until it is done, so the rebuild now does
the same. While the transcriber or either model box is working the
rebuild is skipped altogether; _built_language keeps the old language,
so the next quiet save asks for it by itself.

The new window is also placed and turned to the old tab before it is
shown, so it no longer comes up at the default size and jumps. And
tests/test_ui.py now says that a save with a changed language emits
language_changed, and one without does not.
2026-08-27 15:13:54 +03:00
yusufipek e681accc90 Merge master into the live language switch branch 2026-08-27 15:10:57 +03:00
Yusuf İpek 66cf3f3923 Merge pull request #43 from ademtfkc/mac-keeps-the-front
Keep the front where the dictation started on macOS
2026-08-27 15:08:26 +03:00
Aydogan e6208418f9 Add display selection for recording indicator 2026-08-27 07:46:34 +03:00
oztturkandClaude Opus 5 7ebc3bf825 Ask Google for the lowest rung it has rather than for none
Google's compatibility layer has no word for off. Sending
reasoning_effort "none" is refused outright, so choosing Thinking → Off
made every cleanup fail and paste the raw transcript instead:

  HTTP 400: Request contains an invalid argument. (INVALID_ARGUMENT)

Measured against gemini-3.5-flash-lite, asking it to reply "ok":

  nothing sent               50.59s
  reasoning_effort "none"    400
  reasoning_effort "minimal" 15.40s
  reasoning_effort "low"     53.35s
  reasoning_effort "high"    63.83s

So "none" lands on "minimal", which is both accepted and the quickest of
them, and quickest is what cleanup wants. The thinking_config route the
documentation offers is an SDK wrapper and is not a field this endpoint
knows: sending it is "Unknown name \"google\"".

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 16:40:27 +03:00
oztturkandClaude Opus 5 1ffc3cff9d Let an error body that is an array still be read
Google answers some failures with a JSON array holding the object every
other provider sends on its own. _extract_error called .get() on it and
raised AttributeError, which is not the ApiError every caller is holding,
so a 503 from Google took the whole dictation down instead of pasting the
raw transcript with the failure shown beside it.

Found by dictating against a Google AI Studio outage:

  HTTP Error 503: Service Unavailable
  AttributeError: 'list' object has no attribute 'get'

It runs while an exception is being raised, so it now ends in a string
whatever arrives.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 16:29:41 +03:00
oztturkandClaude Opus 5 1812461612 Clean up on Google AI Studio, or on Antigravity
OpenRouter's free tier rate-limits and carries no free Gemini model, and
cleaning up through Claude Code costs a fixed few seconds because it opens
a whole CLI session to drop three "uh"s. Google's own free tier suits a
short, frequent request, and its OpenAI-compatible endpoint answers
/chat/completions, so cleanup there is one request and the same code path
OpenRouter already takes.

The one thing that is not shared is the thinking level. Google reads
OpenAI's flat reasoning_effort rather than OpenRouter's object, and "none"
is how thinking is turned off, so it is sent rather than skipped: a Flash
model left to think spends exactly the second this provider was chosen to
save. Its top two rungs land on "high", which is as far as Google goes.

Speech to text stays where it was. That endpoint has no
/audio/transcriptions behind it, audio only goes in as base64 inside a
chat message, and what comes back has none of the segment times a subtitle
file or a meeting transcript is built out of.

Antigravity joins as well, on cleanup and as an agent. It is a CLI like the
other two and costs the same session, so it is here for people who already
pay for it rather than as an answer to the speed. It takes neither an empty
tool list nor a read-only sandbox, and cleanup.py now says so plainly
instead of implying parity; what it gets is a project of its own, the home
directory, and its slash commands off.

Three things were already wrong and are fixed on the way past, because the
new providers walk the same paths: doctor raised KeyError on the local
model, whose executable is ""; the history recorded Claude's model whoever
answered; and every agent row read "asked Claude".

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-08-26 16:23:04 +03:00