Skip to content

Commit 6f8bd2f

Browse files
authored
Merge pull request #9 from ruscher/main
Include ChatAI
2 parents 0828f05 + a70f065 commit 6f8bd2f

3 files changed

Lines changed: 338 additions & 0 deletions

File tree

Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
#!/bin/bash
2+
3+
# ChatAI Plasmoid Installation Script
4+
# This script manages the installation and removal of the ChatAI Plasmoid for KDE Plasma
5+
6+
ACTION="${1:-install}"
7+
8+
install_chatai() {
9+
echo "Downloading ChatAI Plasmoid..."
10+
11+
# Download the plasmoid
12+
if ! wget -O /tmp/chatai.zip https://github.com/DenysMb/ChatAI-Plasmoid/archive/refs/heads/main.zip; then
13+
echo "ERROR: Failed to download ChatAI Plasmoid"
14+
exit 1
15+
fi
16+
17+
echo "Installing ChatAI Plasmoid..."
18+
19+
# Try to install, if it fails, try to upgrade
20+
if ! kpackagetool6 -t Plasma/Applet -i /tmp/chatai.zip; then
21+
echo "Package already exists, attempting upgrade..."
22+
if ! kpackagetool6 -t Plasma/Applet -u /tmp/chatai.zip; then
23+
echo "ERROR: Failed to install or upgrade ChatAI Plasmoid"
24+
exit 1
25+
fi
26+
fi
27+
28+
echo "Adding ChatAI widget to panel..."
29+
30+
# Unlock desktop editing mode
31+
qdbus6 org.kde.plasmashell /PlasmaShell evaluateScript "lockCorona(false)"
32+
33+
# Add widget to panel
34+
qdbus6 org.kde.plasmashell /PlasmaShell org.kde.PlasmaShell.evaluateScript "
35+
var allPanels = panels();
36+
for (var i = 0; i < allPanels.length; i++) {
37+
allPanels[i].addWidget('ChatAI-Plasmoid');
38+
}
39+
"
40+
41+
# Lock desktop editing mode again
42+
qdbus6 org.kde.plasmashell /PlasmaShell evaluateScript "lockCorona(true)"
43+
44+
# Clean up
45+
rm -f /tmp/chatai.zip
46+
47+
echo "ChatAI Plasmoid installed successfully!"
48+
}
49+
50+
remove_chatai() {
51+
echo "Removing ChatAI Plasmoid from configuration..."
52+
53+
# Unlock desktop editing mode
54+
qdbus6 org.kde.plasmashell /PlasmaShell evaluateScript "lockCorona(false)"
55+
56+
# Create temporary Python script for removal
57+
cat > /tmp/remove_chatai.py << 'PYTHON_SCRIPT'
58+
import os
59+
import sys
60+
61+
# Caminho do arquivo de configuração do Plasma
62+
config_path = os.path.expanduser("~/.config/plasma-org.kde.plasma.desktop-appletsrc")
63+
64+
# Verifica se existe
65+
if not os.path.exists(config_path):
66+
print("Arquivo de configuração não encontrado.")
67+
sys.exit(1)
68+
69+
print(f"Lendo: {config_path}")
70+
71+
with open(config_path, "r") as f:
72+
lines = f.readlines()
73+
74+
new_lines = []
75+
current_buffer = []
76+
found_target = False
77+
78+
# O ID que queremos eliminar
79+
TARGET_PLUGIN = "plugin=ChatAI-Plasmoid"
80+
81+
for line in lines:
82+
# O arquivo INI do KDE separa seções com colchetes, ex: [Containments][2][Applets][61]
83+
if line.strip().startswith("["):
84+
# Se tínhamos uma seção anterior salva no buffer
85+
if current_buffer:
86+
# Só salvamos no arquivo final se NÃO achamos o plugin alvo nela
87+
if not found_target:
88+
new_lines.extend(current_buffer)
89+
else:
90+
print(f"Removendo bloco infectado: {current_buffer[0].strip()}")
91+
92+
# Reinicia o buffer para a nova seção
93+
current_buffer = [line]
94+
found_target = False
95+
else:
96+
current_buffer.append(line)
97+
# Verifica se essa linha identifica o plugin inimigo
98+
if TARGET_PLUGIN in line:
99+
found_target = True
100+
101+
# Processa o último bloco que ficou no buffer
102+
if current_buffer and not found_target:
103+
new_lines.extend(current_buffer)
104+
elif current_buffer and found_target:
105+
print(f"Removendo bloco infectado final: {current_buffer[0].strip()}")
106+
107+
# Reescreve o arquivo
108+
with open(config_path, "w") as f:
109+
f.writelines(new_lines)
110+
111+
print("Limpeza concluída no arquivo.")
112+
PYTHON_SCRIPT
113+
114+
# Execute the Python removal script
115+
python3 /tmp/remove_chatai.py
116+
117+
# Clean up
118+
rm -f /tmp/remove_chatai.py
119+
120+
echo "Uninstalling ChatAI Plasmoid package..."
121+
122+
# Remove the plasmoid package
123+
kpackagetool6 -t Plasma/Applet -r ChatAI-Plasmoid
124+
125+
# Lock desktop editing mode again
126+
qdbus6 org.kde.plasmashell /PlasmaShell evaluateScript "lockCorona(true)"
127+
128+
echo "ChatAI Plasmoid removed successfully!"
129+
echo "You may need to restart Plasma Shell for changes to take full effect."
130+
}
131+
132+
check_chatai() {
133+
if kpackagetool6 -t Plasma/Applet -l 2>/dev/null | grep -q "ChatAI-Plasmoid"; then
134+
echo "true"
135+
else
136+
echo "false"
137+
fi
138+
}
139+
140+
case "$ACTION" in
141+
install)
142+
install_chatai
143+
;;
144+
remove)
145+
remove_chatai
146+
;;
147+
check)
148+
check_chatai
149+
;;
150+
*)
151+
echo "Usage: $0 {install|remove|check}"
152+
exit 1
153+
;;
154+
esac
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
2+
import gi
3+
gi.require_version('Gtk', '4.0')
4+
gi.require_version('Adw', '1')
5+
from gi.repository import Gtk, Adw, GLib
6+
import subprocess
7+
import gettext
8+
import threading
9+
10+
DOMAIN = 'biglinux-settings'
11+
_ = gettext.gettext
12+
13+
class ChatAIDialog(Adw.Window):
14+
def __init__(self, parent_window, script_path, on_close_callback=None):
15+
super().__init__(modal=True, transient_for=parent_window)
16+
self.set_default_size(500, 300)
17+
self.set_title(_("ChatAI Installation"))
18+
19+
self.script_path = script_path
20+
self.on_close_callback = on_close_callback
21+
self.is_running = False
22+
23+
# Main content box
24+
self.main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=20, margin_top=20, margin_bottom=20, margin_start=20, margin_end=20)
25+
self.set_content(self.main_box)
26+
27+
# Header
28+
self.status_label = Gtk.Label(label=_("Installing ChatAI..."))
29+
self.status_label.add_css_class("title-3")
30+
self.main_box.append(self.status_label)
31+
32+
# Progress Bar
33+
self.progress_bar = Gtk.ProgressBar()
34+
self.progress_bar.set_hexpand(True)
35+
self.progress_bar.set_show_text(True)
36+
self.progress_bar.pulse() # Indeterminate since we don't have exact progress
37+
self.main_box.append(self.progress_bar)
38+
39+
# Detail Log (Expander)
40+
self.log_expander = Gtk.Expander(label=_("Show Details"))
41+
self.log_view = Gtk.TextView()
42+
self.log_view.set_editable(False)
43+
self.log_view.set_wrap_mode(Gtk.WrapMode.WORD)
44+
self.log_view.set_monospace(True)
45+
scrolled = Gtk.ScrolledWindow()
46+
scrolled.set_child(self.log_view)
47+
scrolled.set_min_content_height(150)
48+
self.log_expander.set_child(scrolled)
49+
self.main_box.append(self.log_expander)
50+
51+
# Buttons
52+
self.button_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10, halign=Gtk.Align.CENTER)
53+
self.main_box.append(self.button_box)
54+
55+
self.close_button = Gtk.Button(label=_("Close"))
56+
self.close_button.connect("clicked", self.on_close)
57+
self.close_button.set_visible(False)
58+
self.close_button.add_css_class("suggested-action")
59+
self.button_box.append(self.close_button)
60+
61+
# Start process
62+
self.start_install()
63+
64+
def append_log(self, text):
65+
buffer = self.log_view.get_buffer()
66+
end_iter = buffer.get_end_iter()
67+
buffer.insert(end_iter, text + "\n")
68+
# Scroll to bottom
69+
adj = self.log_expander.get_child().get_vadjustment()
70+
adj.set_value(adj.get_upper())
71+
72+
def update_status(self, text):
73+
self.status_label.set_label(text)
74+
75+
def start_install(self):
76+
self.is_running = True
77+
threading.Thread(target=self.install_thread).start()
78+
79+
def install_thread(self):
80+
cmd = [self.script_path, "install"]
81+
82+
# We need to read stdout line by line
83+
process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True, bufsize=1)
84+
85+
while True:
86+
line = process.stdout.readline()
87+
if not line and process.poll() is not None:
88+
break
89+
if line:
90+
text = line.strip()
91+
GLib.idle_add(self.append_log, text)
92+
GLib.idle_add(self.progress_bar.pulse)
93+
94+
rc = process.poll()
95+
GLib.idle_add(self.on_install_finished, rc)
96+
97+
def on_install_finished(self, return_code):
98+
self.is_running = False
99+
if return_code == 0:
100+
self.update_status(_("Installation Complete!"))
101+
self.progress_bar.set_fraction(1.0)
102+
self.close_button.set_visible(True)
103+
if self.on_close_callback:
104+
self.on_close_callback(True)
105+
else:
106+
self.update_status(_("Installation Failed."))
107+
self.close_button.set_visible(True)
108+
if self.on_close_callback:
109+
self.on_close_callback(False)
110+
111+
def on_close(self, btn):
112+
self.close()

usr/share/biglinux/biglinux-settings/ai_page.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
sys.path.append(os.path.join(current_dir, 'ai'))
1919

2020
from krita_ai_dialog import KritaAIDialog
21+
from chatai_dialog import ChatAIDialog
2122

2223
class AIPage(BaseSettingsPage):
2324
def populate_content(self, content_box):
@@ -39,7 +40,72 @@ def ai_group(self, parent):
3940
icon_name="krita"
4041
)
4142

43+
# ChatAI
44+
self.chatai_switch = self.create_row_with_clickable_link(
45+
group,
46+
_("ChatAI"),
47+
_("A variety of chats like Plasmoid for your KDE Plasma desktop."),
48+
"chatai",
49+
icon_name="utilities-terminal" # Placeholder icon, user didn't specify
50+
)
51+
4252
def on_switch_changed(self, switch, state):
53+
if switch == self.chatai_switch:
54+
script_path = self.switch_scripts.get(switch)
55+
if state:
56+
# Show Disclaimer Dialog
57+
dialog = Adw.MessageDialog(
58+
transient_for=self.main_window,
59+
heading=_("ChatAI Requirements"),
60+
body=_("This resource requires at least 16 GB of RAM and is not recommended for legacy computers.")
61+
)
62+
dialog.add_response("cancel", _("Cancel"))
63+
dialog.add_response("accept", _("Accept"))
64+
65+
dialog.set_default_response("accept")
66+
dialog.set_close_response("cancel")
67+
68+
def on_disclaimer_response(dlg, response):
69+
if response == "accept":
70+
# Proceed to install
71+
install_dialog = ChatAIDialog(self.main_window, script_path, on_close_callback=lambda success: self.on_chatai_dialog_closed(switch, success))
72+
install_dialog.present()
73+
else:
74+
# Cancelled - Toggle switch back to OFF
75+
switch.handler_block_by_func(self.on_switch_changed)
76+
switch.set_active(False)
77+
switch.handler_unblock_by_func(self.on_switch_changed)
78+
79+
dialog.connect("response", on_disclaimer_response)
80+
dialog.present()
81+
return False
82+
83+
else:
84+
# Remove process
85+
subprocess.run([script_path, "remove"])
86+
87+
# Ask to restart Plasma Shell
88+
dialog = Adw.MessageDialog(
89+
transient_for=self.main_window,
90+
heading=_("Restart Plasma Shell"),
91+
body=_("To apply changes, it is recommended to restart Plasma Shell. Do you want to restart it now?")
92+
)
93+
dialog.add_response("cancel", _("No"))
94+
dialog.add_response("restart", _("Yes"))
95+
96+
dialog.set_default_response("restart")
97+
dialog.set_close_response("cancel")
98+
99+
def on_restart_response(dlg, response):
100+
if response == "restart":
101+
# Execute restart command
102+
subprocess.run("killall plasmashell && plasmashell &", shell=True)
103+
104+
dialog.connect("response", on_restart_response)
105+
dialog.present()
106+
107+
return False
108+
43109
if switch == self.krita_ai_switch:
44110
if state:
45111
# Install process
@@ -98,3 +164,9 @@ def on_krita_dialog_closed(self, switch, success):
98164
else:
99165
# Sync state?
100166
pass
167+
168+
def on_chatai_dialog_closed(self, switch, success):
169+
if not success:
170+
switch.handler_block_by_func(self.on_switch_changed)
171+
switch.set_active(False)
172+
switch.handler_unblock_by_func(self.on_switch_changed)

0 commit comments

Comments
 (0)