Add files via upload

This commit is contained in:
FURK4NGG
2026-05-21 12:50:07 +03:00
committed by GitHub
parent 59a77b6573
commit 1845c9661e
2 changed files with 373 additions and 30 deletions
+184 -13
View File
@@ -158,6 +158,11 @@ class ChatCLI:
"ask_for_web_search": True, "ask_for_web_search": True,
"prompt_chooser_blocks": ["copyable"], "prompt_chooser_blocks": ["copyable"],
"response_style": "normal", "response_style": "normal",
"use_image_settings": False,
"image_resolution": "1920x1080",
"image_aspect_ratio": "16:9",
"image_quality": "medium",
"image_style": "",
"show_usage": True, "show_usage": True,
"show_token_value": False, "show_token_value": False,
"token_value": "2.0", "token_value": "2.0",
@@ -217,6 +222,7 @@ class ChatCLI:
bool_keys = [ bool_keys = [
"ask_for_web_search", "ask_for_web_search",
"use_image_settings",
"show_usage", "show_usage",
"show_token_value", "show_token_value",
"force_ui_language", "force_ui_language",
@@ -2687,6 +2693,69 @@ class ChatCLI:
return line.strip() return line.strip()
def _is_image_file_path(self, path: str) -> bool:
ext = Path(str(path or "")).suffix.lower()
return ext in (
".png",
".jpg",
".jpeg",
".webp",
".gif",
".bmp",
".tiff",
".tif",
)
def _message_has_image_file(self, msg: dict) -> bool:
if not isinstance(msg, dict):
return False
if self._is_image_file_path(msg.get("image")):
return True
images = msg.get("images")
if isinstance(images, list):
for p in images:
if self._is_image_file_path(p):
return True
files = msg.get("files")
if isinstance(files, list):
for f in files:
if isinstance(f, dict) and self._is_image_file_path(f.get("path")):
return True
return False
def _build_image_settings_suffix(self) -> str:
cfg = self.load_config()
if not bool(cfg.get("use_image_settings", False)):
return ""
parts = []
resolution = str(cfg.get("image_resolution", "")).strip()
aspect_ratio = str(cfg.get("image_aspect_ratio", "")).strip()
quality = str(cfg.get("image_quality", "")).strip()
style = str(cfg.get("image_style", "")).strip()
if resolution:
parts.append(f"resolution:{resolution}")
if aspect_ratio:
parts.append(f"aspect_ratio:{aspect_ratio}")
if quality:
parts.append(f"quality:{quality}")
if style:
parts.append(f"style:{style}")
if not parts:
return ""
return "\n\nimage{" + ", ".join(parts) + "}"
def append_user_message(self, message: str): def append_user_message(self, message: str):
messages = self.load_chat_messages() messages = self.load_chat_messages()
@@ -2761,6 +2830,29 @@ class ChatCLI:
} }
new_message["files"].append(file_obj) new_message["files"].append(file_obj)
should_add_image_settings = False
# 1) Şu an gönderilen eklerde image varsa
if isinstance(new_message.get("images"), list) and new_message["images"]:
should_add_image_settings = True
# 2) Referans seçildiyse, referans mesajlarında image var mı?
if not should_add_image_settings:
ref_idxs = new_message.get("used_refs") or []
if isinstance(ref_idxs, list):
for ridx in ref_idxs:
if isinstance(ridx, int) and 0 <= ridx < len(messages):
if self._message_has_image_file(messages[ridx]):
should_add_image_settings = True
break
if should_add_image_settings:
image_suffix = self._build_image_settings_suffix()
if image_suffix and image_suffix not in final_message:
final_message = final_message + image_suffix
new_message["content"] = final_message
messages.append(new_message) messages.append(new_message)
self.save_chat_messages(messages) self.save_chat_messages(messages)
@@ -4370,6 +4462,63 @@ class ChatCLI:
elif choice == "4": elif choice == "4":
return return
def menu_image_settings(self):
while True:
self.clear_screen()
cfg = self.load_config()
self.ensure_base_config()
cfg = self.load_config()
use_image_settings = bool(cfg.get("use_image_settings", False))
image_resolution = str(cfg.get("image_resolution", "1920x1080") or "1920x1080")
image_aspect_ratio = str(cfg.get("image_aspect_ratio", "16:9") or "16:9")
image_quality = str(cfg.get("image_quality", "medium") or "medium")
image_style = str(cfg.get("image_style", "") or "")
print(f"\n={self('o_Use_Image_Settings')}=")
print(f"1) {self('o_Use_Image_Settings')}: {'' if use_image_settings else ''}")
print(f"2) {self('o_Image_Resolution')}: {image_resolution}")
print(f"3) {self('o_Image_Aspect_Ratio')}: {image_aspect_ratio}")
print(f"4) {self('o_Image_Quality')}: {image_quality}")
print(f"5) {self('o_Image_Style')}: {image_style}")
print(f"6) {self('o_Go_Back')}")
choice = self._read_input(f"\n{self('o_Selection')}: ").strip()
if choice == "1":
cfg["use_image_settings"] = not use_image_settings
self.save_config(cfg)
elif choice == "2":
val = self._read_input(f"{self('o_New_Value')} ").strip()
if not self._is_escape_input(val):
cfg["image_resolution"] = val or "1920x1080"
self.save_config(cfg)
elif choice == "3":
val = self._read_input(f"{self('o_New_Value')} ").strip()
if not self._is_escape_input(val):
cfg["image_aspect_ratio"] = val or "16:9"
self.save_config(cfg)
elif choice == "4":
val = self._read_input(f"{self('o_New_Value')} ").strip()
if not self._is_escape_input(val):
cfg["image_quality"] = val or "medium"
self.save_config(cfg)
elif choice == "5":
val = self._read_input(f"{self('o_New_Value')} ")
if val == "\x1b" or val.strip().lower() in {"esc", ":q"}:
continue
cfg["image_style"] = val.strip()
self.save_config(cfg)
elif choice == "6":
return
def menu_personalization(self): def menu_personalization(self):
while True: while True:
@@ -4508,21 +4657,43 @@ class ChatCLI:
self._read_input(f"\n{self('o_to_Menu')}") self._read_input(f"\n{self('o_to_Menu')}")
elif choice == "4": elif choice == "4":
cfg = self.load_config() while True:
current = str(cfg.get("response_style", "") or "") self.clear_screen()
print(f"\n{self('o_Response_Style')}:\n{current}\n")
print(f"\n{self('o_Type_ESC')}")
print(f"\n\n{self('o_New_Value')}")
new_style = input(f"\n({self('o_Response_Style_Placeholder')})").strip()
if self._is_escape_input(new_style): cfg = self.load_config()
continue current = str(cfg.get("response_style", "") or "")
use_image_settings = bool(cfg.get("use_image_settings", False))
if new_style: print(f"\n={self('o_Response_Style')}=")
cfg["response_style"] = new_style print(f"1) {self('o_Response_Style')}: {current}")
self.save_config(cfg) print(f"2) {self('o_Use_Image_Settings')}: {'' if use_image_settings else ''}")
print(f"\n{self('o_Saved')}({self('o_Response_Style')})") print(f"3) {self('o_Go_Back')}")
self._read_input(f"\n{self('o_to_Menu')}")
sub = self._read_input(f"\n{self('o_Selection')}: ").strip()
if sub == "1":
print(f"\n{self('o_Response_Style')}:\n{current}\n")
print(f"\n{self('o_Type_ESC')}")
print(f"\n\n{self('o_New_Value')}")
new_style = self._read_input(
f"\n({self('o_Response_Style_Placeholder')}) "
).strip()
if self._is_escape_input(new_style):
continue
if new_style:
cfg["response_style"] = new_style
self.save_config(cfg)
print(f"\n{self('o_Saved')} ({self('o_Response_Style')})")
self._read_input(f"\n{self('o_to_Menu')}")
elif sub == "2":
self.menu_image_settings()
elif sub == "3":
break
elif choice == "5": elif choice == "5":
+189 -17
View File
@@ -83,6 +83,11 @@ def ensure_base_config(config: dict) -> dict:
"ask_for_web_search": True, "ask_for_web_search": True,
"prompt_chooser_blocks": ["copyable"], "prompt_chooser_blocks": ["copyable"],
"response_style": "normal", "response_style": "normal",
"use_image_settings": False,
"image_resolution": "1920x1080",
"image_aspect_ratio": "16:9",
"image_quality": "medium",
"image_style": "",
"show_usage": True, "show_usage": True,
"show_token_value": False, "show_token_value": False,
"token_value": "2.0", "token_value": "2.0",
@@ -148,6 +153,7 @@ def ensure_base_config(config: dict) -> dict:
bool_keys = [ bool_keys = [
"dark_mode", "dark_mode",
"ask_for_web_search", "ask_for_web_search",
"use_image_settings",
"show_usage", "show_usage",
"show_token_value", "show_token_value",
"force_ui_language", "force_ui_language",
@@ -175,6 +181,12 @@ def ensure_base_config(config: dict) -> dict:
continue continue
if isinstance(default, str): if isinstance(default, str):
if key == "image_style":
if not isinstance(value, str):
config[key] = ""
changed = True
continue
if not isinstance(value, str) or not value.strip(): if not isinstance(value, str) or not value.strip():
config[key] = default config[key] = default
changed = True changed = True
@@ -5290,49 +5302,124 @@ class ChatApp(Gtk.Application):
content.set_margin_start(10) content.set_margin_start(10)
content.set_margin_end(10) content.set_margin_end(10)
cfg = ensure_base_config(self.load_config())
label = Gtk.Label() label = Gtk.Label()
self.bind_i18n(label, "label", "o_Response_Style") self.bind_i18n(label, "label", "o_Response_Style")
label.set_xalign(0) label.set_xalign(0)
content.append(label) content.append(label)
entry = Gtk.Entry() entry = Gtk.Entry()
self.add_entry_clipboard_shortcuts(entry, dialog) self.add_entry_clipboard_shortcuts(entry, dialog)
self.bind_i18n(entry, "placeholder", "o_Response_Style_Placeholder") self.bind_i18n(entry, "placeholder", "o_Response_Style_Placeholder")
entry.set_text(str(cfg.get("response_style", "normal")))
cfg = ensure_base_config(self.load_config())
entry.set_text(cfg["response_style"])
content.append(entry) content.append(entry)
# --- Use Image Settings switch ---
image_row = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=8)
image_label = Gtk.Label()
self.bind_i18n(image_label, "label", "o_Use_Image_Settings")
image_label.set_xalign(0)
image_label.set_hexpand(True)
image_switch = Gtk.Switch()
image_switch.set_active(bool(cfg.get("use_image_settings", False)))
image_switch.set_halign(Gtk.Align.END)
image_switch.set_valign(Gtk.Align.CENTER)
image_row.append(image_label)
image_row.append(image_switch)
content.append(image_row)
def add_image_entry(label_key, config_key, default_value):
lab = Gtk.Label()
self.bind_i18n(lab, "label", label_key)
lab.set_xalign(0)
content.append(lab)
ent = Gtk.Entry()
ent.set_text(str(cfg.get(config_key, default_value)))
ent.set_placeholder_text(str(default_value))
self.add_entry_clipboard_shortcuts(ent, dialog)
content.append(ent)
return lab, ent
resolution_label, resolution_entry = add_image_entry(
"o_Image_Resolution",
"image_resolution",
"1920x1080"
)
aspect_label, aspect_entry = add_image_entry(
"o_Image_Aspect_Ratio",
"image_aspect_ratio",
"16:9"
)
quality_label, quality_entry = add_image_entry(
"o_Image_Quality",
"image_quality",
"high"
)
style_label, style_entry = add_image_entry(
"o_Image_Style",
"image_style",
""
)
image_widgets = [
resolution_label,
resolution_entry,
aspect_label,
aspect_entry,
quality_label,
quality_entry,
style_label,
style_entry,
]
def refresh_image_settings_sensitive(*_):
active = bool(image_switch.get_active())
for w in image_widgets:
w.set_sensitive(active)
image_switch.connect("notify::active", refresh_image_settings_sensitive)
refresh_image_settings_sensitive()
dialog.add_button(self("o_Cancel"), Gtk.ResponseType.CANCEL) dialog.add_button(self("o_Cancel"), Gtk.ResponseType.CANCEL)
dialog.add_button(self("o_Save"), Gtk.ResponseType.OK) dialog.add_button(self("o_Save"), Gtk.ResponseType.OK)
# Enter → Kaydet
entry.connect("activate", lambda *_: dialog.response(Gtk.ResponseType.OK)) entry.connect("activate", lambda *_: dialog.response(Gtk.ResponseType.OK))
def on_response(d, resp): def on_response(d, resp):
if resp == Gtk.ResponseType.OK: if resp == Gtk.ResponseType.OK:
cfg2 = self.load_config() cfg2 = ensure_base_config(self.load_config())
cfg2["response_style"] = entry.get_text().strip()
cfg2["response_style"] = entry.get_text().strip() or "normal"
cfg2["use_image_settings"] = bool(image_switch.get_active())
cfg2["image_resolution"] = resolution_entry.get_text().strip() or "1920x1080"
cfg2["image_aspect_ratio"] = aspect_entry.get_text().strip() or "16:9"
cfg2["image_quality"] = quality_entry.get_text().strip() or "high"
cfg2["image_style"] = style_entry.get_text().strip()
self.save_config(cfg2) self.save_config(cfg2)
d.close() d.close()
dialog.connect("response", on_response) dialog.connect("response", on_response)
dialog.show() dialog.show()
# Fokus ver ama seçim yapma
def focus_entry(): def focus_entry():
entry.grab_focus() entry.grab_focus()
entry.set_position(-1) # cursor sona gider entry.set_position(-1)
entry.select_region(0, 0) # seçim yok entry.select_region(0, 0)
return False return False
GLib.idle_add(focus_entry) GLib.idle_add(focus_entry)
def open_key_dialog(self, _button=None): def open_key_dialog(self, _button=None):
@@ -7407,11 +7494,11 @@ class ChatApp(Gtk.Application):
remaining = int(timeout - elapsed) remaining = int(timeout - elapsed)
if remaining <= 0: if remaining <= 0:
self.mic_btn.set_tooltip_text("Timeout: 0") self.mic_btn.set_tooltip_text(f"{self('o_Timeout')}: 0")
GLib.idle_add(self._stop_voice_recording_and_transcribe) GLib.idle_add(self._stop_voice_recording_and_transcribe)
return False return False
self.mic_btn.set_tooltip_text(f"Timeout: {remaining}") self.mic_btn.set_tooltip_text(f"{self('o_Timeout')}: {remaining}")
return True return True
@@ -7564,7 +7651,7 @@ class ChatApp(Gtk.Application):
self._voice_countdown_active = True self._voice_countdown_active = True
if use_timeout: if use_timeout:
self.mic_btn.set_tooltip_text(f"Timeout: {int(timeout_seconds)}") self.mic_btn.set_tooltip_text(f"{self('o_Timeout')}: {int(timeout_seconds)}")
else: else:
self.bind_i18n(self.mic_btn, "tooltip", "o_Stop_Recording") self.bind_i18n(self.mic_btn, "tooltip", "o_Stop_Recording")
@@ -8115,6 +8202,69 @@ class ChatApp(Gtk.Application):
# ---------------- SEND / CALL AI ---------------- # ---------------- SEND / CALL AI ----------------
def _is_image_file_path(self, path: str) -> bool:
ext = Path(str(path or "")).suffix.lower()
return ext in (
".png",
".jpg",
".jpeg",
".webp",
".gif",
".bmp",
".tiff",
".tif",
)
def _message_has_image_file(self, msg: dict) -> bool:
if not isinstance(msg, dict):
return False
if self._is_image_file_path(msg.get("image")):
return True
images = msg.get("images")
if isinstance(images, list):
for p in images:
if self._is_image_file_path(p):
return True
files = msg.get("files")
if isinstance(files, list):
for f in files:
if isinstance(f, dict) and self._is_image_file_path(f.get("path")):
return True
return False
def _build_image_settings_suffix(self) -> str:
cfg = ensure_base_config(self.load_config())
if not bool(cfg.get("use_image_settings", False)):
return ""
parts = []
resolution = str(cfg.get("image_resolution", "")).strip()
aspect_ratio = str(cfg.get("image_aspect_ratio", "")).strip()
quality = str(cfg.get("image_quality", "")).strip()
style = str(cfg.get("image_style", "")).strip()
if resolution:
parts.append(f"resolution:{resolution}")
if aspect_ratio:
parts.append(f"aspect_ratio:{aspect_ratio}")
if quality:
parts.append(f"quality:{quality}")
if style:
parts.append(f"style:{style}")
if not parts:
return ""
return "\n\nimage{" + ", ".join(parts) + "}"
def send_message(self, widget): def send_message(self, widget):
buffer = self.textview.get_buffer() buffer = self.textview.get_buffer()
start, end = buffer.get_bounds() start, end = buffer.get_bounds()
@@ -8195,6 +8345,28 @@ class ChatApp(Gtk.Application):
new_message["refs_groups"] = groups new_message["refs_groups"] = groups
should_add_image_settings = False
# 1) Kullanıcı şu anda png/jpg/webp vs eklediyse
if self.pending_images:
should_add_image_settings = True
# 2) Kullanıcı referans olarak içinde image olan eski mesajı seçtiyse
if not should_add_image_settings:
ref_idxs = new_message.get("used_refs") or []
if isinstance(ref_idxs, list):
for ridx in ref_idxs:
if isinstance(ridx, int) and 0 <= ridx < len(messages):
if self._message_has_image_file(messages[ridx]):
should_add_image_settings = True
break
if should_add_image_settings:
image_suffix = self._build_image_settings_suffix()
if image_suffix and image_suffix not in message:
message = message + image_suffix
new_message["content"] = message
chat_data = self.load_chat_data_ui() chat_data = self.load_chat_data_ui()
messages = chat_data["messages"] messages = chat_data["messages"]