Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
5157732
Add custom titlebar with custom window controls
bennyscripts Jan 20, 2026
dfe219c
Move console into home page and remove discord and ghost details cards
bennyscripts Jan 20, 2026
1f2c55f
Make dark colours darker and remove dedicated quit button in the sidebar
bennyscripts Jan 20, 2026
c4a44f4
Fix padding issues in scripts page
bennyscripts Jan 20, 2026
589f231
Disable maximise button
bennyscripts Jan 20, 2026
aa2b9ca
Add resizing - VERY BUGGY
bennyscripts Jan 20, 2026
cd4da36
Change ttkbootstrap version to 1.14.2
bennyscripts Jan 20, 2026
ef4a39e
Fix transparency on Windows
Jan 20, 2026
52bdf37
Fix padding, fonts and added icon to titlebar for windows
Jan 20, 2026
491765d
Fix transparency on Linux
bennyscripts Jan 20, 2026
3351c4f
Fixed visual bugs when resizing, resize grips are now made once and o…
bennyscripts Jan 20, 2026
f26879c
Hide window until fully ready
bennyscripts Jan 20, 2026
1e919cc
Add rounded corners
bennyscripts Jan 20, 2026
dd047ed
Fix problems when minimising window
bennyscripts Jan 20, 2026
af2b0b9
Change header font to match title in home page
bennyscripts Jan 20, 2026
807306e
Add global Style enum for globally shared colours that are not part o…
bennyscripts Jan 20, 2026
bc9ca05
Fix minimising window on Windows
bennyscripts Jan 20, 2026
83d97c0
Change font sizes
bennyscripts Jan 20, 2026
3edf99d
Add dedicated close function and make mac os close button withdraw to…
bennyscripts Jan 20, 2026
4254f50
Custom dropdown menu widget
bennyscripts Jan 21, 2026
a31f7b9
Add resize grips to tools pages
bennyscripts Jan 21, 2026
e69bba9
Disable create theme button when name entry is empty
bennyscripts Jan 21, 2026
cf2dcc3
Add switch for toggling RPC and session spoofing
bennyscripts Jan 21, 2026
77aff60
Add state to rounded button
bennyscripts Jan 21, 2026
ba4bd52
Completely redesigned settings page
bennyscripts Jan 21, 2026
019dd3e
Change button radius and colour
bennyscripts Jan 21, 2026
41d27c3
Add right-chevron-tiny
bennyscripts Jan 21, 2026
cb7469d
Change size of inactive window controls on mac os
bennyscripts Jan 21, 2026
906c1e8
Add lift to rounded frame
bennyscripts Jan 21, 2026
e1ae991
Add version to Mac OS Info.plist
bennyscripts Jan 21, 2026
1db6f74
Add page title to scripts page
bennyscripts Jan 22, 2026
ee201ff
Move scripts page to bottom of sidebar
bennyscripts Jan 22, 2026
d88e732
Redesign tools to be a grid
bennyscripts Jan 22, 2026
9c6cdb2
Remove .lower from embeds
bennyscripts Jan 22, 2026
263320b
Add Key to SerpAPI label
bennyscripts Jan 22, 2026
85a31b2
Add load_image_from_url method
bennyscripts Jan 22, 2026
8568608
Add rich presence preview to gui
bennyscripts Jan 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions bot/helpers/cmdhelper.py
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ async def send_message(ctx, embed_obj: dict, extra_title="", extra_message="", d
msg_style = "codeblock"

if msg_style == "codeblock":
description = re.sub(r"[*_~`]", "", codeblock_desc).lower()
description = re.sub(r"[*_~`]", "", codeblock_desc)
if title == theme.title:
title = f"{theme.emoji} {title}"

Expand All @@ -159,17 +159,17 @@ async def send_message(ctx, embed_obj: dict, extra_title="", extra_message="", d

msg = await ctx.send(
str(codeblock.Codeblock(
title=title.lower(),
description=description.lower(),
extra_title=extra_title.lower(),
# footer=footer.lower()
title=title,
description=description,
extra_title=extra_title,
# footer=footer
)),
delete_after=delete_after
)

elif msg_style == "image":
title = remove_emojis(title.replace(theme.emoji, "").lstrip())
embed2 = imgembed.Embed(title=title.lower(), description=description.lower(), colour=colour)
embed2 = imgembed.Embed(title=title, description=description, colour=colour)
embed2.set_footer(text=footer)
embed2.set_thumbnail(url=thumbnail)
embed_file = embed2.save()
Expand All @@ -181,8 +181,8 @@ async def send_message(ctx, embed_obj: dict, extra_title="", extra_message="", d
if title == theme.title:
title = f"{theme.emoji} {title}"
embed = discord.Embed(
title=title.lower(),
description=description.lower(),
title=title,
description=description,
colour=discord.Color.from_str(colour)
)
embed.set_footer(text=footer)
Expand Down
54 changes: 36 additions & 18 deletions compile.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,40 @@
import os
import sys
import platform
import subprocess
import plistlib

from utils.config import VERSION


def patch_macos_plist(app_name):
plist_path = os.path.join("dist", f"{app_name}.app", "Contents", "Info.plist")

if not os.path.exists(plist_path):
print("Info.plist not found, skipping version patch")
return

with open(plist_path, "rb") as f:
plist = plistlib.load(f)

plist["CFBundleShortVersionString"] = VERSION # About menu
plist["CFBundleVersion"] = VERSION # build number

with open(plist_path, "wb") as f:
plistlib.dump(plist, f)

print(f"Patched Info.plist with version {VERSION}")


def build():
system = platform.system()

name = "Ghost"
entry_script = "ghost.py"
icon = "data/icon-win.png" if system == "Windows" else "data/icon.png"

common_args = [
args = [
"pyinstaller",
"--name=" + name,
f"--name={name}",
"--onefile",
"--windowed",
"--noconsole",
Expand All @@ -25,32 +47,28 @@ def build():
entry_script
]

# Add paths to site-packages if needed
if system == "Windows":
site_packages = ".venv\\Lib\\site-packages"
add_data = [
args += [
"--paths=.venv\\Lib\\site-packages",
"--add-data=data\\*;data",
"--add-data=data\\fonts\\*;data/fonts",
"--add-data=data\\icons\\*;data/icons"
]
else:
site_packages = ".venv/lib/python3.10/site-packages"
add_data = [
args += [
"--paths=.venv/lib/python3.10/site-packages",
"--add-data=data/*:data",
"--add-data=data/fonts/*:data/fonts",
"--add-data=data/icons/*:data/icons"
"--add-data=data/icons/*:data/icons",
"--osx-bundle-identifier=fun.benny.ghost"
]

common_args.append(f"--paths={site_packages}")
common_args += add_data

# macOS-specific option
print(f"🔨 Building Ghost {VERSION} for {system}...")
subprocess.run(args, check=True)

if system == "Darwin":
common_args.append("--osx-bundle-identifier=fun.benny.ghost")
patch_macos_plist(name)

# Run the command
print(f"Building for {system}...")
subprocess.run(common_args)

if __name__ == "__main__":
build()
4 changes: 2 additions & 2 deletions data/gui_theme.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@
"type": "dark",
"colors": {
"primary": "#433dfb",
"secondary": "#222324",
"secondary": "#1e1d1d",
"success": "#0abf34",
"info": "#2b6eff",
"warning": "#f39c12",
"danger": "#ff341f",
"light": "#ADB5BD",
"dark": "#1a1c1c",
"dark": "#171616",
"bg": "#121111",
"fg": "#ffffff",
"selectbg": "#555555",
Expand Down
3 changes: 2 additions & 1 deletion gui/components/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
from .settings_frame import SettingsFrame
from .settings_panel import SettingsPanel
from .titlebar import Titlebar
from .tool_page import ToolPage
from .tool_page import ToolPage
from .dropdown_menu import DropdownMenu
111 changes: 111 additions & 0 deletions gui/components/dropdown_menu.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import ttkbootstrap as ttk
from gui.components import RoundedFrame

class DropdownMenu:
def __init__(self, parent, options, command=None):
self.parent = parent
self.options = options
self.selected_option = ttk.StringVar(value=options[0] if options else "")
self.command = command

def _hover_enter(self, wrapper, label):
wrapper.set_background("#282828")
label.configure(background="#282828")

def _hover_leave(self, wrapper, label):
wrapper.set_background(self.parent.style.colors.get("secondary"))
label.configure(background=self.parent.style.colors.get("secondary"))

def _rearrange_options(self):
selected = self.selected_option.get()
self.options.remove(selected)
self.options.insert(0, selected)

def _open_menu(self, event):
if not self._alive():
return

for widget in self.frame.winfo_children():
widget.destroy()

self._rearrange_options()
index = 0
for option in self.options:
index += 1

wrapper = RoundedFrame(self.frame, radius=8, background=self.parent.style.colors.get("secondary"))
wrapper.pack(fill=ttk.X, padx=5, pady=(4, 5 if index == len(self.options) else 0))
wrapper.bind("<Button-1>", lambda e, opt=option: self._on_option_selected(opt))

label = ttk.Label(wrapper, text=option, background=self.parent.style.colors.get("secondary"), anchor="w")
label.pack(fill=ttk.X, padx=5, pady=2)
label.bind("<Button-1>", lambda e, opt=option: self._on_option_selected(opt))

label.bind("<Enter>", lambda e, w=wrapper, l=label: self._hover_enter(w, l))
label.bind("<Leave>", lambda e, w=wrapper, l=label: self._hover_leave(w, l))
wrapper.bind("<Enter>", lambda e, w=wrapper, l=label: self._hover_enter(w, l))
wrapper.bind("<Leave>", lambda e, w=wrapper, l=label: self._hover_leave(w, l))

def _close_menu(self):
if not self._alive():
return

for widget in self.frame.winfo_children():
widget.destroy()

label = ttk.Label(self.frame, textvariable=self.selected_option, anchor="w", background=self.parent.style.colors.get("secondary"))
label.pack(fill=ttk.X, padx=10, pady=5)
label.bind("<Button-1>", self._open_menu)
self.frame.bind("<Button-1>", self._open_menu)

self.down_arrow = ttk.Label(self.frame, text="▼", background=self.parent.style.colors.get("secondary"), font=("Host Grotesk", 10))
self.down_arrow.place(relx=1.0, rely=0.5, x=-10, y=0, anchor="e")
self.down_arrow.bind("<Button-1>", self._open_menu)

def _on_option_selected(self, option):
if not self._alive():
return

self.selected_option.set(option)
if self.command:
self.command(option)

if self._alive():
self._close_menu()

def _outside_click(self, event):
if not self._alive():
return
if not self.frame.winfo_containing(event.x_root, event.y_root):
self._close_menu()

def draw(self):
self.parent.bind("<Button-1>", self._outside_click, add="+")
self.frame = RoundedFrame(self.parent, radius=5, bootstyle="secondary.TFrame")

label = ttk.Label(self.frame, textvariable=self.selected_option, anchor="w", background=self.parent.style.colors.get("secondary"))
label.pack(fill=ttk.X, padx=10, pady=5)
label.bind("<Button-1>", self._open_menu)
self.frame.bind("<Button-1>", self._open_menu)

self.down_arrow = ttk.Label(self.frame, text="▼", background=self.parent.style.colors.get("secondary"), font=("Host Grotesk", 10))
self.down_arrow.place(relx=1.0, rely=0.5, x=-10, y=0, anchor="e")
self.down_arrow.bind("<Button-1>", self._open_menu)

return self.frame

def value(self):
return self.selected_option.get()

def set_selected(self, option):
if option in self.options:
self.selected_option.set(option)
if self._alive():
self._close_menu()

def destroy(self):
if self._alive():
self.frame.destroy()

def _alive(self):
return hasattr(self, "frame") and self.frame.winfo_exists()
27 changes: 27 additions & 0 deletions gui/components/rounded_button.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ def __init__(self, parent, radius=(8, 8, 8, 8), text=None, image=None, command=N
self.style = self.root.style
self.padx = kwargs.get("padx", 2)
self.pady = kwargs.get("pady", 0 if sys.platform != "darwin" else 1)
self.state = "normal"
self.command = command

self.configure(background=self._get_parent_background())

Expand Down Expand Up @@ -66,11 +68,36 @@ def _darken_color(self, hex_color, factor=0.9):

def _hover_enter(self, event=None):
""" Apply hover effect """
if self.state == "disabled":
return
hover_color = self._darken_color(self.original_bg, 0.9)
self.frame.set_background(hover_color)
self.button.configure(background=hover_color)

def _hover_leave(self, event=None):
""" Reset to original color """
if self.state == "disabled":
return
self.frame.set_background(self.original_bg)
self.button.configure(background=self.original_bg)

def set_state(self, state):
if state == "disabled":
self.state = "disabled"
self.button.state(["disabled"])
self.frame.set_background(self.style.colors.get("secondary"))
self.button.configure(background=self.style.colors.get("secondary"))
if self.command:
self.button.unbind("<Button-1>")
self.frame.unbind("<Button-1>")
else:
self.state = "normal"
self.button.state(["!disabled"])
self.frame.set_background(self.original_bg)
self.button.configure(background=self.original_bg)
if self.command:
self.button.bind("<Button-1>", self.command)
self.frame.bind("<Button-1>", self.command)

def set_text(self, text):
self.button.configure(text=text)
5 changes: 4 additions & 1 deletion gui/components/rounded_frame.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,7 @@ def set_width(self, width):
def bind(self, sequence=None, func=None, add=None):
if sequence == "<Configure>":
return super().bind(sequence, self.on_resize, add=add)
return super().bind(sequence, func, add=add)
return super().bind(sequence, func, add=add)

def lift(self):
self.master.tk.call("raise", self._w)
6 changes: 3 additions & 3 deletions gui/components/settings/apis.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
from gui.components import SettingsPanel

class APIsPanel(SettingsPanel):
def __init__(self, root, parent, images, config):
super().__init__(root, parent, "APIs", images.get("apis"))
def __init__(self, root, parent, images, config, width=None):
super().__init__(root, parent, "APIs", images.get("apis"), width=width, collapsed=False)
self.cfg = config
self.api_keys_tk_entries = {}
self.api_keys_entries = {
"serpapi": "SerpAPI"
"serpapi": "SerpAPI Key"
}

def _save_api_keys(self):
Expand Down
Loading