Call the tab Agent, pick shortcuts from a list, and stop answering from memory

Four things the first use of it turned up.

The tab was called Claude while the setting inside it chooses between three
providers, two of which are not Claude. It is Agent now, and the messages that
pointed at it by name point at the new one.

The three global shortcuts were the only fields in the window you had to know
the syntax of to fill in, next to a paste key that has been a dropdown all
along. They are editable combo boxes now, sharing one shortlist of combinations
that are usually free. Typing your own still works; it is just no longer the
only way.

The instruction was read as discouraging the tools, which is the opposite of the
point. "Keep it short" is about the answer, and a model that reads it as "answer
off the top of your head" hands back a caveat about its training data instead of
the thing it was asked. Asked who won the 2026 World Cup it answered, twice, that
the tournament had not been played, on the 28th of July. So shortness is now
stated as being about the answer rather than the work, and writing a caveat is
named as the moment to go and look instead. Same question, same model, only the
prompt changed: no tool calls and a wrong answer before, a web search and the
right one after. It also appended its sources, which a reply being pasted into
someone's text field has no use for, so that is ruled out alongside the headings
and the bullet lists.

Clicking the tray icon during a Claude recording did nothing, because the icon
was wired to the dictation toggle and that one leaves the other mode's recording
alone on purpose. The shortcuts should stay tied to their own mode; the icon is
one button and now ends whichever recording is running.
This commit is contained in:
yusufipk
2026-07-28 17:20:41 +07:00
parent 01eea7a363
commit 590c6cbe1a
7 changed files with 74 additions and 30 deletions
+2 -2
View File
@@ -46,7 +46,7 @@ set next to it.
| --- | --- | | --- | --- |
| Start / stop recording | `Ctrl+Space`, or click the tray icon | | Start / stop recording | `Ctrl+Space`, or click the tray icon |
| Cancel a recording | Tray menu → *Cancel recording*, or `dikte cancel` | | Cancel a recording | Tray menu → *Cancel recording*, or `dikte cancel` |
| Speak a command to Claude | Tray menu → *Ask Claude*, or `dikte ask` | | Speak a command to an agent | Tray menu → *Ask Claude*, or `dikte ask` |
| Start / end a meeting | Tray menu → *Record a meeting*, or `dikte meeting` | | Start / end a meeting | Tray menu → *Record a meeting*, or `dikte meeting` |
| Settings | Tray menu → *Settings*, or `dikte settings` | | Settings | Tray menu → *Settings*, or `dikte settings` |
| Reload after an update | Tray menu → *Restart*, or `dikte restart` | | Reload after an update | Tray menu → *Restart*, or `dikte restart` |
@@ -90,7 +90,7 @@ elapsed time, then the stage it is on. It never takes focus. Pressing
a thing you can say to a window that is not Claude. Codex (`codex exec`) runs a thing you can say to a window that is not Claude. Codex (`codex exec`) runs
the same way, and OpenRouter is there as a plain question-and-answer fallback the same way, and OpenRouter is there as a plain question-and-answer fallback
for a machine with neither CLI on it. Provider, model, permissions and working for a machine with neither CLI on it. Provider, model, permissions and working
directory are under Settings → Claude, and commands close together stay in one directory are under Settings → Agent, and commands close together stay in one
conversation. conversation.
- **Meetings** are recorded from the microphone and the speaker output at the - **Meetings** are recorded from the microphone and the speaker output at the
same time, which settles who said what by the channel a voice arrived on same time, which settles who said what by the channel a voice arrived on
+2 -2
View File
@@ -46,7 +46,7 @@ yapıştırılır; modelin yanındaki kutudan düşünme seviyesini de seçebili
| --- | --- | | --- | --- |
| Kaydı başlat / bitir | `Ctrl+Space`, ya da tepsi simgesine tıkla | | Kaydı başlat / bitir | `Ctrl+Space`, ya da tepsi simgesine tıkla |
| Kaydı iptal et | Tepsi menüsü → *Kaydı iptal et*, ya da `dikte cancel` | | Kaydı iptal et | Tepsi menüsü → *Kaydı iptal et*, ya da `dikte cancel` |
| Claude'a sesle komut ver | Tepsi menüsü → *Claude'a sor*, ya da `dikte ask` | | Ajana sesle komut ver | Tepsi menüsü → *Claude'a sor*, ya da `dikte ask` |
| Toplantıyı başlat / bitir | Tepsi menüsü → *Toplantı kaydet*, ya da `dikte meeting` | | Toplantıyı başlat / bitir | Tepsi menüsü → *Toplantı kaydet*, ya da `dikte meeting` |
| Ayarlar | Tepsi menüsü → *Ayarlar*, ya da `dikte settings` | | Ayarlar | Tepsi menüsü → *Ayarlar*, ya da `dikte settings` |
| Güncelleme sonrası yeniden yükle | Tepsi menüsü → *Yeniden başlat*, ya da `dikte restart` | | Güncelleme sonrası yeniden yükle | Tepsi menüsü → *Yeniden başlat*, ya da `dikte restart` |
@@ -88,7 +88,7 @@ süreyi, ardından hangi aşamada olduğunu gösterir. Odak almaz. Dikte çalı
perşembe üçe takvime koy" cümlesini Claude olmayan bir pencerede söyleyebilir perşembe üçe takvime koy" cümlesini Claude olmayan bir pencerede söyleyebilir
olmanı sağlayan da budur. Codex (`codex exec`) da aynı şekilde çalışır; olmanı sağlayan da budur. Codex (`codex exec`) da aynı şekilde çalışır;
OpenRouter ise ikisi de kurulu olmayan bir makinede düz soru cevap için OpenRouter ise ikisi de kurulu olmayan bir makinede düz soru cevap için
duruyor. Sağlayıcı, model, izinler ve çalışma dizini Ayarlar → Claude duruyor. Sağlayıcı, model, izinler ve çalışma dizini Ayarlar → Ajan
sekmesinde; arka arkaya verilen komutlar tek bir konuşmada kalır. sekmesinde; arka arkaya verilen komutlar tek bir konuşmada kalır.
- **Toplantılar** mikrofonla hoparlör çıkışından aynı anda kaydedilir; kimin ne - **Toplantılar** mikrofonla hoparlör çıkışından aynı anda kaydedilir; kimin ne
dediği tahmin edilmez, sesin hangi kanaldan geldiğiyle belli olur. İki taraf dediği tahmin edilmez, sesin hangi kanaldan geldiğiyle belli olur. İki taraf
+1 -1
View File
@@ -178,7 +178,7 @@ def ask(prompt, conf, on_stage=None, should_stop=None):
if not shutil.which(binary): if not shutil.which(binary):
raise AssistantError(t( raise AssistantError(t(
"{binary} not found. Install it, or pick another provider under " "{binary} not found. Install it, or pick another provider under "
"Settings → Claude.", binary=binary, "Settings → Agent.", binary=binary,
)) ))
run = _ask_claude if name == "claude" else _ask_codex run = _ask_claude if name == "claude" else _ask_codex
+21 -2
View File
@@ -232,8 +232,18 @@ follow, and no way to answer a question you ask back.
- Reply in the language you were spoken to in - Reply in the language you were spoken to in
- Keep it short. A sentence or two when that covers it. No preamble, no "here - Keep it short. A sentence or two when that covers it. No preamble, no "here
is what I found", no closing offer of further help is what I found", no closing offer of further help
- Short is the answer, not the work. Being asked for one line is not being asked
to answer off the top of your head: when what was asked turns on something
current, specific or personal, go and look. Search the web, read the file,
open the calendar, run the command. Then answer in one line
- Never hand back a caveat in place of an answer. The moment you are about to
write that something falls after your training data, that you cannot be sure,
or that you have no way to know, is the moment to go and find out instead. You
have the tools. A guess and an apology are both worth less than the ten
seconds that checking costs
- Plain prose. No headings, no bullet lists, no bold, and no code fence unless - Plain prose. No headings, no bullet lists, no bold, and no code fence unless
what was asked for is code what was asked for is code. Nothing appended after the answer either: no list
of sources, no links, no note on how you found it
- When you did something rather than answered something, say what you did in - When you did something rather than answered something, say what you did in
one sentence, carrying the detail that confirms it: the day and time an event one sentence, carrying the detail that confirms it: the day and time an event
was saved for, the name of a file that was written was saved for, the name of a file that was written
@@ -254,8 +264,17 @@ da senin soracağın soruya verilecek bir yanıt yok.
- Sana hangi dilde konuşulduysa o dilde cevap ver - Sana hangi dilde konuşulduysa o dilde cevap ver
- Kısa tut. Yetiyorsa bir iki cümle. Giriş cümlesi kurma, "işte buldukların" - Kısa tut. Yetiyorsa bir iki cümle. Giriş cümlesi kurma, "işte buldukların"
deme, sonunda başka yardım teklif etme deme, sonunda başka yardım teklif etme
- Kısa olması gereken cevap, iş değil. Tek satır istenmesi, aklından cevap ver
demek değildir: sorulan şey güncel, belirli ya da kişisel bir şeye bağlıysa
git bak. İnternette ara, dosyayı oku, takvime bak, komutu çalıştır. Sonra tek
satırla cevapla
- Cevabın yerine asla bir çekince koyma. Bir şeyin eğitim verinden sonrasına
denk geldiğini, emin olamayacağını ya da bilmene imkân olmadığını yazmak
üzereysen, tam o an gidip öğrenmenin zamanıdır. Araçların var. Bir tahmin de
bir özür de, bakmanın alacağı on saniyeden daha az değerlidir
- Düz metin yaz. Başlık, madde işareti, kalın yazı kullanma; istenen şey kodun - Düz metin yaz. Başlık, madde işareti, kalın yazı kullanma; istenen şey kodun
kendisi değilse kod bloğu da açma kendisi değilse kod bloğu da açma. Cevabın arkasına da bir şey ekleme: kaynak
listesi, bağlantı, nasıl bulduğuna dair not olmasın
- Bir şeyi cevaplamak yerine yaptıysan, ne yaptığını tek cümleyle söyle ve onu - Bir şeyi cevaplamak yerine yaptıysan, ne yaptığını tek cümleyle söyle ve onu
doğrulayan ayrıntıyı da yaz: kaydın hangi güne ve saate düştüğü, yazdığın doğrulayan ayrıntıyı da yaz: kaydın hangi güne ve saate düştüğü, yazdığın
dosyanın adı dosyanın adı
+9 -1
View File
@@ -163,7 +163,15 @@ class Dikte:
self._set_icon("audio-input-microphone") self._set_icon("audio-input-microphone")
def _tray_clicked(self, reason): def _tray_clicked(self, reason):
if reason == QSystemTrayIcon.ActivationReason.Trigger: if reason != QSystemTrayIcon.ActivationReason.Trigger:
return
# The icon ends whatever is being recorded rather than only a dictation.
# The two shortcuts are each tied to their own mode, on purpose, but the
# icon is one button: having it refuse to stop a recording it can see is
# just a button that does nothing.
if self.state == RECORDING and self.ask_mode:
self._toggle_ask()
else:
self._toggle() self._toggle()
def _set_icon(self, name): def _set_icon(self, name):
+5 -5
View File
@@ -331,8 +331,8 @@ TR = {
"Using {name}": "{name} kullanıyor…", "Using {name}": "{name} kullanıyor…",
"Thinking…": "Düşünüyor…", "Thinking…": "Düşünüyor…",
"{binary} not found. Install it, or pick another provider under " "{binary} not found. Install it, or pick another provider under "
"Settings → Claude.": "Settings → Agent.":
"{binary} bulunamadı. Kur ya da Ayarlar → Claude sekmesinden başka bir " "{binary} bulunamadı. Kur ya da Ayarlar → Ajan sekmesinden başka bir "
"sağlayıcı seç.", "sağlayıcı seç.",
"Could not run {binary}: {error}": "{binary} çalıştırılamadı: {error}", "Could not run {binary}: {error}": "{binary} çalıştırılamadı: {error}",
"{service} exited with code {code}.": "{service} {code} koduyla çıktı.", "{service} exited with code {code}.": "{service} {code} koduyla çıktı.",
@@ -344,8 +344,8 @@ TR = {
"It was not allowed to use: {tools}": "Şunları kullanmasına izin yoktu: {tools}", "It was not allowed to use: {tools}": "Şunları kullanmasına izin yoktu: {tools}",
"The model returned an empty reply.": "Model boş cevap döndürdü.", "The model returned an empty reply.": "Model boş cevap döndürdü.",
# --- settings: Claude --------------------------------------------------- # --- settings: the agent ------------------------------------------------
"Claude": "Claude", "Agent": "Ajan",
"This shortcut records the same way dictation does, but the transcript is " "This shortcut records the same way dictation does, but the transcript is "
"not what gets pasted. It goes to an agent as a command, and what comes " "not what gets pasted. It goes to an agent as a command, and what comes "
"back is pasted instead: the answer to a question, or a sentence saying " "back is pasted instead: the answer to a question, or a sentence saying "
@@ -386,7 +386,7 @@ TR = {
"The conversation": "Konuşma", "The conversation": "Konuşma",
"The answer": "Cevap", "The answer": "Cevap",
"Found: {path}": "Bulundu: {path}", "Found: {path}": "Bulundu: {path}",
"No KDE shortcut installed. The tray menu asks Claude too.": "No KDE shortcut installed. The tray menu asks it too.":
"Kurulu KDE kısayolu yok. Tepsi menüsünden de sorulabilir.", "Kurulu KDE kısayolu yok. Tepsi menüsünden de sorulabilir.",
"A name like “sonnet” always means the newest model of that line. Opus " "A name like “sonnet” always means the newest model of that line. Opus "
"thinks harder and answers slower, which is felt here more than anywhere " "thinks harder and answers slower, which is felt here more than anywhere "
+34 -17
View File
@@ -90,6 +90,15 @@ REASONING_LEVELS = [
("Very high", "xhigh"), ("Maximum", "max"), ("Very high", "xhigh"), ("Maximum", "max"),
] ]
PASTE_SHORTCUTS = ["ctrl+v", "ctrl+shift+v", "shift+insert"] PASTE_SHORTCUTS = ["ctrl+v", "ctrl+shift+v", "shift+insert"]
# Offered for all three global shortcuts, which keeps them one kind of field
# rather than three. The boxes stay editable: this is a shortlist of
# combinations that are usually free, not the set of ones that work.
SHORTCUTS = [
"Ctrl+Space", "Ctrl+Alt+Space", "Ctrl+Shift+Space", "Meta+Space",
"Ctrl+Alt+A", "Ctrl+Alt+D", "Ctrl+Alt+M", "Ctrl+Alt+Q",
"Meta+A", "Meta+D", "Meta+M",
"Ctrl+Alt+F1", "Ctrl+Alt+F2", "Ctrl+Alt+F3",
]
AUDIO_FILTER = ("*.mp3 *.wav *.m4a *.ogg *.opus *.flac *.aac *.wma " AUDIO_FILTER = ("*.mp3 *.wav *.m4a *.ogg *.opus *.flac *.aac *.wma "
"*.mp4 *.mkv *.webm *.mov *.avi") "*.mp4 *.mkv *.webm *.mov *.avi")
@@ -122,7 +131,7 @@ class SettingsWindow(QDialog):
tabs.addTab(self._general_tab(), t("General")) tabs.addTab(self._general_tab(), t("General"))
tabs.addTab(self._api_tab(), t("API and models")) tabs.addTab(self._api_tab(), t("API and models"))
tabs.addTab(self._prompt_tab(), t("Cleanup rules")) tabs.addTab(self._prompt_tab(), t("Cleanup rules"))
tabs.addTab(self._assistant_tab(), t("Claude")) tabs.addTab(self._assistant_tab(), t("Agent"))
tabs.addTab(self._meeting_tab(), t("Meeting")) tabs.addTab(self._meeting_tab(), t("Meeting"))
tabs.addTab(self._minutes_tab(), t("Minutes")) tabs.addTab(self._minutes_tab(), t("Minutes"))
tabs.addTab(self._file_tab(), t("Audio file")) tabs.addTab(self._file_tab(), t("Audio file"))
@@ -353,8 +362,7 @@ class SettingsWindow(QDialog):
how = QGroupBox(t("How it runs")) how = QGroupBox(t("How it runs"))
how_form = QFormLayout(how) how_form = QFormLayout(how)
self.assistant_shortcut = QLineEdit() self.assistant_shortcut = self._shortcut_box(t("none"))
self.assistant_shortcut.setPlaceholderText(t("none"))
install = QPushButton(t("Install as a KDE shortcut")) install = QPushButton(t("Install as a KDE shortcut"))
install.clicked.connect(self._install_ask_shortcut) install.clicked.connect(self._install_ask_shortcut)
remove = QPushButton(t("Remove")) remove = QPushButton(t("Remove"))
@@ -604,8 +612,7 @@ class SettingsWindow(QDialog):
)) ))
recording_form.addRow("", self.meeting_keep_audio) recording_form.addRow("", self.meeting_keep_audio)
self.meeting_shortcut = QLineEdit() self.meeting_shortcut = self._shortcut_box(t("none"))
self.meeting_shortcut.setPlaceholderText(t("none"))
install = QPushButton(t("Install as a KDE shortcut")) install = QPushButton(t("Install as a KDE shortcut"))
install.clicked.connect(self._install_meeting_shortcut) install.clicked.connect(self._install_meeting_shortcut)
remove = QPushButton(t("Remove")) remove = QPushButton(t("Remove"))
@@ -752,8 +759,7 @@ class SettingsWindow(QDialog):
page = QWidget() page = QWidget()
layout = QVBoxLayout(page) layout = QVBoxLayout(page)
form = QFormLayout() form = QFormLayout()
self.shortcut = QLineEdit() self.shortcut = self._shortcut_box("Ctrl+Space")
self.shortcut.setPlaceholderText("Ctrl+Space")
form.addRow(t("Shortcut"), self.shortcut) form.addRow(t("Shortcut"), self.shortcut)
layout.addLayout(form) layout.addLayout(form)
@@ -837,6 +843,17 @@ class SettingsWindow(QDialog):
layout.addLayout(row) layout.addLayout(row)
return page return page
@staticmethod
def _shortcut_box(placeholder=""):
"""The field a global shortcut is typed or picked in."""
box = QComboBox()
box.setEditable(True)
box.addItems(SHORTCUTS)
box.setCurrentText("")
if placeholder:
box.lineEdit().setPlaceholderText(placeholder)
return box
@staticmethod @staticmethod
def _row(*widgets): def _row(*widgets):
"""Widgets side by side in one form row; the first one takes the space.""" """Widgets side by side in one form row; the first one takes the space."""
@@ -878,7 +895,7 @@ class SettingsWindow(QDialog):
self.cleanup_prompt.setPlainText(conf["cleanup_prompt"] or cfg.default_cleanup_prompt()) self.cleanup_prompt.setPlainText(conf["cleanup_prompt"] or cfg.default_cleanup_prompt())
self.transcribe_prompt.setPlainText(conf["transcribe_prompt"]) self.transcribe_prompt.setPlainText(conf["transcribe_prompt"])
self.assistant_shortcut.setText(conf["assistant_shortcut"]) self.assistant_shortcut.setCurrentText(conf["assistant_shortcut"])
self._select_data(self.assistant_provider, conf["assistant_provider"]) self._select_data(self.assistant_provider, conf["assistant_provider"])
self.assistant_model.setCurrentText(conf["assistant_model"]) self.assistant_model.setCurrentText(conf["assistant_model"])
self._select_data(self.assistant_permission, conf["assistant_permission_mode"]) self._select_data(self.assistant_permission, conf["assistant_permission_mode"])
@@ -906,7 +923,7 @@ class SettingsWindow(QDialog):
self.meeting_cleanup.setChecked(conf["meeting_cleanup"]) self.meeting_cleanup.setChecked(conf["meeting_cleanup"])
self.meeting_max_minutes.setValue(max(5, int(conf["meeting_max_seconds"]) // 60)) self.meeting_max_minutes.setValue(max(5, int(conf["meeting_max_seconds"]) // 60))
self.meeting_keep_audio.setChecked(conf["meeting_keep_audio"]) self.meeting_keep_audio.setChecked(conf["meeting_keep_audio"])
self.meeting_shortcut.setText(conf["meeting_shortcut"]) self.meeting_shortcut.setCurrentText(conf["meeting_shortcut"])
self.meeting_prompt.setPlainText( self.meeting_prompt.setPlainText(
conf["meeting_prompt"] or cfg.default_meeting_prompt() conf["meeting_prompt"] or cfg.default_meeting_prompt()
) )
@@ -915,7 +932,7 @@ class SettingsWindow(QDialog):
self.file_cleanup.setChecked(conf["file_cleanup"]) self.file_cleanup.setChecked(conf["file_cleanup"])
self.file_path = "" self.file_path = ""
self.shortcut.setText(conf["shortcut"]) self.shortcut.setCurrentText(conf["shortcut"])
self.evdev_enabled.setChecked(conf["evdev_hotkey"]) self.evdev_enabled.setChecked(conf["evdev_hotkey"])
self.history_limit.setValue(max(0, int(conf["history_limit"]))) self.history_limit.setValue(max(0, int(conf["history_limit"])))
@@ -962,7 +979,7 @@ class SettingsWindow(QDialog):
conf["cleanup_prompt"] = "" if prompt == cfg.default_cleanup_prompt() else prompt conf["cleanup_prompt"] = "" if prompt == cfg.default_cleanup_prompt() else prompt
conf["transcribe_prompt"] = self.transcribe_prompt.toPlainText().strip() conf["transcribe_prompt"] = self.transcribe_prompt.toPlainText().strip()
conf["assistant_shortcut"] = self.assistant_shortcut.text().strip() conf["assistant_shortcut"] = self.assistant_shortcut.currentText().strip()
conf["assistant_provider"] = self.assistant_provider.currentData() or "claude" conf["assistant_provider"] = self.assistant_provider.currentData() or "claude"
conf["assistant_model"] = (self.assistant_model.currentText().strip() conf["assistant_model"] = (self.assistant_model.currentText().strip()
or cfg.DEFAULTS["assistant_model"]) or cfg.DEFAULTS["assistant_model"])
@@ -1001,7 +1018,7 @@ class SettingsWindow(QDialog):
conf["meeting_cleanup"] = self.meeting_cleanup.isChecked() conf["meeting_cleanup"] = self.meeting_cleanup.isChecked()
conf["meeting_max_seconds"] = self.meeting_max_minutes.value() * 60 conf["meeting_max_seconds"] = self.meeting_max_minutes.value() * 60
conf["meeting_keep_audio"] = self.meeting_keep_audio.isChecked() conf["meeting_keep_audio"] = self.meeting_keep_audio.isChecked()
conf["meeting_shortcut"] = self.meeting_shortcut.text().strip() conf["meeting_shortcut"] = self.meeting_shortcut.currentText().strip()
meeting_prompt = self.meeting_prompt.toPlainText().strip() meeting_prompt = self.meeting_prompt.toPlainText().strip()
conf["meeting_prompt"] = ("" if meeting_prompt == cfg.default_meeting_prompt() conf["meeting_prompt"] = ("" if meeting_prompt == cfg.default_meeting_prompt()
else meeting_prompt) else meeting_prompt)
@@ -1009,7 +1026,7 @@ class SettingsWindow(QDialog):
conf["file_timestamps"] = self.file_timestamps.isChecked() conf["file_timestamps"] = self.file_timestamps.isChecked()
conf["file_cleanup"] = self.file_cleanup.isChecked() conf["file_cleanup"] = self.file_cleanup.isChecked()
conf["shortcut"] = self.shortcut.text().strip() or "Ctrl+Space" conf["shortcut"] = self.shortcut.currentText().strip() or "Ctrl+Space"
conf["evdev_hotkey"] = self.evdev_enabled.isChecked() conf["evdev_hotkey"] = self.evdev_enabled.isChecked()
conf["history_limit"] = self.history_limit.value() conf["history_limit"] = self.history_limit.value()
conf.save() conf.save()
@@ -1217,7 +1234,7 @@ class SettingsWindow(QDialog):
# ---- shortcut -------------------------------------------------------- # ---- shortcut --------------------------------------------------------
def _install_shortcut(self): def _install_shortcut(self):
combo = self.shortcut.text().strip() or "Ctrl+Space" combo = self.shortcut.currentText().strip() or "Ctrl+Space"
clashes = hotkey.conflicting_shortcuts(combo) clashes = hotkey.conflicting_shortcuts(combo)
if clashes: if clashes:
answer = QMessageBox.question( answer = QMessageBox.question(
@@ -1246,7 +1263,7 @@ class SettingsWindow(QDialog):
) )
def _install_meeting_shortcut(self): def _install_meeting_shortcut(self):
combo = self.meeting_shortcut.text().strip() combo = self.meeting_shortcut.currentText().strip()
if not combo: if not combo:
QMessageBox.information(self, t("Shortcut"), QMessageBox.information(self, t("Shortcut"),
t("Type a key combination first.")) t("Type a key combination first."))
@@ -1284,7 +1301,7 @@ class SettingsWindow(QDialog):
# ---- Claude ---------------------------------------------------------- # ---- Claude ----------------------------------------------------------
def _install_ask_shortcut(self): def _install_ask_shortcut(self):
combo = self.assistant_shortcut.text().strip() combo = self.assistant_shortcut.currentText().strip()
if not combo: if not combo:
QMessageBox.information(self, t("Shortcut"), QMessageBox.information(self, t("Shortcut"),
t("Type a key combination first.")) t("Type a key combination first."))
@@ -1316,7 +1333,7 @@ class SettingsWindow(QDialog):
current = hotkey.kde_shortcut_status(hotkey.ASK_DESKTOP_ID) current = hotkey.kde_shortcut_status(hotkey.ASK_DESKTOP_ID)
self.assistant_shortcut_status.setText( self.assistant_shortcut_status.setText(
t("Registered in KDE: {shortcut}", shortcut=current) if current t("Registered in KDE: {shortcut}", shortcut=current) if current
else t("No KDE shortcut installed. The tray menu asks Claude too.") else t("No KDE shortcut installed. The tray menu asks it too.")
) )
def _assistant_provider_changed(self): def _assistant_provider_changed(self):