latest ux and readme update
This commit is contained in:
154
gui.py
154
gui.py
@@ -67,27 +67,38 @@ _SUBHDR_COLOR = (220, 200, 120) # sub-section header
|
||||
|
||||
|
||||
|
||||
def _show_text_viewer(title: str, text: str):
|
||||
def _show_text_viewer(title: str, text: str, app_instance=None):
|
||||
if dpg.does_item_exist("win_text_viewer"):
|
||||
dpg.set_value("text_viewer_content", text if text is not None else "")
|
||||
wrap = app_instance.project.get("project", {}).get("word_wrap", False) if app_instance else False
|
||||
dpg.configure_item("win_text_viewer", label=f"Text Viewer - {title}", show=True)
|
||||
if dpg.does_item_exist("text_viewer_content"):
|
||||
dpg.set_value("text_viewer_content", text if text is not None else "")
|
||||
dpg.configure_item("text_viewer_content", show=not wrap)
|
||||
if dpg.does_item_exist("text_viewer_wrap_container"):
|
||||
dpg.set_value("text_viewer_wrap", text if text is not None else "")
|
||||
dpg.configure_item("text_viewer_wrap_container", show=wrap)
|
||||
dpg.focus_item("win_text_viewer")
|
||||
|
||||
|
||||
def _add_text_field(parent: str, label: str, value: str):
|
||||
"""Render a labelled text value; long values get a scrollable box."""
|
||||
wrap = dpg.get_value("project_word_wrap") if dpg.does_item_exist("project_word_wrap") else False
|
||||
with dpg.group(horizontal=False, parent=parent):
|
||||
with dpg.group(horizontal=True):
|
||||
dpg.add_text(f"{label}:", color=_LABEL_COLOR)
|
||||
dpg.add_button(label="[+]", callback=lambda s, a, u: _show_text_viewer(label, u), user_data=value)
|
||||
dpg.add_button(label="[+]", callback=lambda s, a, u: _show_text_viewer(label, u, app_instance=self), user_data=value)
|
||||
if len(value) > COMMS_CLAMP_CHARS:
|
||||
dpg.add_input_text(
|
||||
default_value=value,
|
||||
multiline=True,
|
||||
readonly=True,
|
||||
width=-1,
|
||||
height=80,
|
||||
)
|
||||
if wrap:
|
||||
with dpg.child_window(height=80, border=True):
|
||||
dpg.add_text(value, wrap=0, color=_VALUE_COLOR)
|
||||
else:
|
||||
dpg.add_input_text(
|
||||
default_value=value,
|
||||
multiline=True,
|
||||
readonly=True,
|
||||
width=-1,
|
||||
height=80,
|
||||
)
|
||||
else:
|
||||
dpg.add_text(value if value else "(empty)", wrap=0, color=_VALUE_COLOR)
|
||||
|
||||
@@ -283,7 +294,7 @@ class ConfirmDialog:
|
||||
dpg.add_button(
|
||||
label="[+ Maximize]",
|
||||
user_data=f"{self._tag}_script",
|
||||
callback=lambda s, a, u: _show_text_viewer("Confirm Script", dpg.get_value(u))
|
||||
callback=lambda s, a, u: _show_text_viewer("Confirm Script", dpg.get_value(u, app_instance=self))
|
||||
)
|
||||
dpg.add_input_text(
|
||||
tag=f"{self._tag}_script",
|
||||
@@ -519,6 +530,9 @@ class App:
|
||||
dpg.set_value("project_main_context", proj.get("project", {}).get("main_context", ""))
|
||||
if dpg.does_item_exist("auto_add_history"):
|
||||
dpg.set_value("auto_add_history", proj.get("discussion", {}).get("auto_add", False))
|
||||
if dpg.does_item_exist("project_word_wrap"):
|
||||
dpg.set_value("project_word_wrap", proj.get("project", {}).get("word_wrap", True))
|
||||
self.cb_word_wrap_toggled(app_data=proj.get("project", {}).get("word_wrap", True))
|
||||
|
||||
def _save_active_project(self):
|
||||
"""Write self.project to the active project .toml file."""
|
||||
@@ -708,13 +722,20 @@ class App:
|
||||
|
||||
if dpg.does_item_exist("last_script_text"):
|
||||
dpg.set_value("last_script_text", script)
|
||||
if dpg.does_item_exist("last_script_text_wrap"):
|
||||
dpg.set_value("last_script_text_wrap", script)
|
||||
|
||||
if dpg.does_item_exist("last_script_output"):
|
||||
dpg.set_value("last_script_output", result)
|
||||
if dpg.does_item_exist("last_script_output_wrap"):
|
||||
dpg.set_value("last_script_output_wrap", result)
|
||||
|
||||
self._trigger_script_blink = True
|
||||
|
||||
def _rebuild_tool_log(self):
|
||||
if not dpg.does_item_exist("tool_log_scroll"):
|
||||
return
|
||||
wrap = dpg.get_value("project_word_wrap") if dpg.does_item_exist("project_word_wrap") else False
|
||||
dpg.delete_item("tool_log_scroll", children_only=True)
|
||||
for i, (script, result) in enumerate(self._tool_log, 1):
|
||||
with dpg.group(parent="tool_log_scroll"):
|
||||
@@ -724,20 +745,24 @@ class App:
|
||||
dpg.add_button(
|
||||
label="[+ Script]",
|
||||
user_data=script,
|
||||
callback=lambda s, a, u: _show_text_viewer(f"Call Script", u)
|
||||
callback=lambda s, a, u: _show_text_viewer(f"Call Script", u, app_instance=self)
|
||||
)
|
||||
dpg.add_button(
|
||||
label="[+ Output]",
|
||||
user_data=result,
|
||||
callback=lambda s, a, u: _show_text_viewer(f"Call Output", u)
|
||||
callback=lambda s, a, u: _show_text_viewer(f"Call Output", u, app_instance=self)
|
||||
)
|
||||
if wrap:
|
||||
with dpg.child_window(height=72, border=True):
|
||||
dpg.add_text(result, wrap=0)
|
||||
else:
|
||||
dpg.add_input_text(
|
||||
default_value=result,
|
||||
multiline=True,
|
||||
readonly=True,
|
||||
width=-1,
|
||||
height=72,
|
||||
)
|
||||
dpg.add_input_text(
|
||||
default_value=result,
|
||||
multiline=True,
|
||||
readonly=True,
|
||||
width=-1,
|
||||
height=72,
|
||||
)
|
||||
dpg.add_separator()
|
||||
|
||||
# ---------------------------------------------------------------- helpers
|
||||
@@ -771,6 +796,8 @@ class App:
|
||||
proj["project"]["system_prompt"] = dpg.get_value("project_system_prompt")
|
||||
if dpg.does_item_exist("project_main_context"):
|
||||
proj["project"]["main_context"] = dpg.get_value("project_main_context")
|
||||
if dpg.does_item_exist("project_word_wrap"):
|
||||
proj["project"]["word_wrap"] = dpg.get_value("project_word_wrap")
|
||||
|
||||
# Discussion
|
||||
self._flush_disc_entries_to_project()
|
||||
@@ -811,6 +838,8 @@ class App:
|
||||
self.ai_response = text
|
||||
if dpg.does_item_exist("ai_response"):
|
||||
dpg.set_value("ai_response", text)
|
||||
if dpg.does_item_exist("ai_response_wrap"):
|
||||
dpg.set_value("ai_response_wrap", text)
|
||||
|
||||
def _rebuild_files_list(self):
|
||||
if not dpg.does_item_exist("files_scroll"):
|
||||
@@ -959,6 +988,32 @@ class App:
|
||||
|
||||
# ---------------------------------------------------------------- callbacks
|
||||
|
||||
def cb_word_wrap_toggled(self, sender=None, app_data=None):
|
||||
# This function is now also called by _refresh_project_widgets to set initial state
|
||||
if app_data is None:
|
||||
wrap = dpg.get_value("project_word_wrap") if dpg.does_item_exist("project_word_wrap") else False
|
||||
else:
|
||||
wrap = app_data
|
||||
|
||||
# Persist the setting
|
||||
self.project.setdefault("project", {})["word_wrap"] = wrap
|
||||
|
||||
# Toggle visibility of persistent wrapped/unwrapped container pairs
|
||||
persistent_panels = [
|
||||
"ai_response", "last_script_text", "last_script_output", "text_viewer_content"
|
||||
]
|
||||
for name in persistent_panels:
|
||||
no_wrap_widget = name
|
||||
wrap_container = f"{name}_wrap_container"
|
||||
if dpg.does_item_exist(no_wrap_widget):
|
||||
dpg.configure_item(no_wrap_widget, show=not wrap)
|
||||
if dpg.does_item_exist(wrap_container):
|
||||
dpg.configure_item(wrap_container, show=wrap)
|
||||
|
||||
# Re-render UI components with dynamic content that needs to change widget type
|
||||
self._rebuild_comms_log()
|
||||
self._rebuild_tool_log()
|
||||
|
||||
def cb_browse_output(self):
|
||||
root = hide_tk_root()
|
||||
d = filedialog.askdirectory(title="Select Output Dir")
|
||||
@@ -1325,12 +1380,23 @@ class App:
|
||||
width=36,
|
||||
callback=self._make_disc_insert_cb(i),
|
||||
)
|
||||
dpg.add_button(
|
||||
label="[+ Max]",
|
||||
user_data=f"disc_content_{{i}}",
|
||||
callback=lambda s, a, u, idx=i: _show_text_viewer(f"Entry #{{idx+1}}", dpg.get_value(u, app_instance=self) if dpg.does_item_exist(u) else "", app_instance=self)
|
||||
)
|
||||
dpg.add_button(
|
||||
label="Del",
|
||||
width=36,
|
||||
callback=self._make_disc_insert_cb(i),
|
||||
)
|
||||
dpg.add_button(
|
||||
label="Del",
|
||||
width=36,
|
||||
callback=self._make_disc_remove_cb(i),
|
||||
)
|
||||
dpg.add_text(preview, color=(160, 160, 150))
|
||||
else:
|
||||
with dpg.group(tag=f"disc_body_{i}", show=not collapsed):
|
||||
dpg.add_input_text(
|
||||
tag=f"disc_content_{i}",
|
||||
@@ -1479,7 +1545,7 @@ class App:
|
||||
tag="win_projects",
|
||||
pos=(8, 8),
|
||||
width=400,
|
||||
height=340,
|
||||
height=380,
|
||||
no_close=True,
|
||||
):
|
||||
proj_meta = self.project.get("project", {})
|
||||
@@ -1514,20 +1580,26 @@ class App:
|
||||
dpg.add_button(label="Browse##out", callback=self.cb_browse_output)
|
||||
dpg.add_separator()
|
||||
dpg.add_text("Project Files")
|
||||
with dpg.child_window(tag="projects_scroll", height=-40, border=True):
|
||||
with dpg.child_window(tag="projects_scroll", height=-60, border=True):
|
||||
pass
|
||||
with dpg.group(horizontal=True):
|
||||
dpg.add_button(label="Add Project", callback=self.cb_add_project)
|
||||
dpg.add_button(label="New Project", callback=self.cb_new_project)
|
||||
dpg.add_button(label="Save All", callback=self.cb_save_config)
|
||||
dpg.add_checkbox(
|
||||
tag="project_word_wrap",
|
||||
label="Word-Wrap (Read-only panels)",
|
||||
default_value=self.project.get("project", {}).get("word_wrap", True),
|
||||
callback=self.cb_word_wrap_toggled
|
||||
)
|
||||
|
||||
# ---- Files panel ----
|
||||
with dpg.window(
|
||||
label="Files",
|
||||
tag="win_files",
|
||||
pos=(8, 356),
|
||||
pos=(8, 396),
|
||||
width=400,
|
||||
height=400,
|
||||
height=360,
|
||||
no_close=True,
|
||||
):
|
||||
dpg.add_text("Base Dir")
|
||||
@@ -1687,6 +1759,8 @@ class App:
|
||||
width=-1,
|
||||
height=-48,
|
||||
)
|
||||
with dpg.child_window(tag="ai_response_wrap_container", width=-1, height=-48, border=True, show=False):
|
||||
dpg.add_text("", tag="ai_response_wrap", wrap=0)
|
||||
dpg.add_separator()
|
||||
dpg.add_button(label="-> History", callback=self.cb_append_response_to_history)
|
||||
|
||||
@@ -1776,7 +1850,7 @@ class App:
|
||||
dpg.add_button(
|
||||
label="[+ Maximize]",
|
||||
user_data="last_script_text",
|
||||
callback=lambda s, a, u: _show_text_viewer("Last Script", dpg.get_value(u))
|
||||
callback=lambda s, a, u: _show_text_viewer("Last Script", dpg.get_value(u, app_instance=self))
|
||||
)
|
||||
dpg.add_input_text(
|
||||
tag="last_script_text",
|
||||
@@ -1785,13 +1859,15 @@ class App:
|
||||
width=-1,
|
||||
height=200,
|
||||
)
|
||||
with dpg.child_window(tag="last_script_text_wrap_container", width=-1, height=200, border=True, show=False):
|
||||
dpg.add_text("", tag="last_script_text_wrap", wrap=0)
|
||||
dpg.add_separator()
|
||||
with dpg.group(horizontal=True):
|
||||
dpg.add_text("Output:")
|
||||
dpg.add_button(
|
||||
label="[+ Maximize]",
|
||||
user_data="last_script_output",
|
||||
callback=lambda s, a, u: _show_text_viewer("Last Output", dpg.get_value(u))
|
||||
callback=lambda s, a, u: _show_text_viewer("Last Output", dpg.get_value(u, app_instance=self))
|
||||
)
|
||||
dpg.add_input_text(
|
||||
tag="last_script_output",
|
||||
@@ -1800,6 +1876,8 @@ class App:
|
||||
width=-1,
|
||||
height=-1,
|
||||
)
|
||||
with dpg.child_window(tag="last_script_output_wrap_container", width=-1, height=-1, border=True, show=False):
|
||||
dpg.add_text("", tag="last_script_output_wrap", wrap=0)
|
||||
|
||||
# ---- Global Text Viewer Popup ----
|
||||
with dpg.window(
|
||||
@@ -1818,6 +1896,8 @@ class App:
|
||||
width=-1,
|
||||
height=-1,
|
||||
)
|
||||
with dpg.child_window(tag="text_viewer_wrap_container", width=-1, height=-1, border=False, show=False):
|
||||
dpg.add_text("", tag="text_viewer_wrap", wrap=0)
|
||||
|
||||
def run(self):
|
||||
dpg.create_context()
|
||||
@@ -1876,6 +1956,10 @@ class App:
|
||||
try:
|
||||
dpg.bind_item_theme("last_script_output", 0)
|
||||
dpg.bind_item_theme("last_script_text", 0)
|
||||
if dpg.does_item_exist("last_script_output_wrap_container"):
|
||||
dpg.bind_item_theme("last_script_output_wrap_container", 0)
|
||||
if dpg.does_item_exist("last_script_text_wrap_container"):
|
||||
dpg.bind_item_theme("last_script_text_wrap_container", 0)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
@@ -1884,15 +1968,22 @@ class App:
|
||||
|
||||
if not dpg.does_item_exist("script_blink_theme"):
|
||||
with dpg.theme(tag="script_blink_theme"):
|
||||
with dpg.theme_component(dpg.mvInputText):
|
||||
with dpg.theme_component(dpg.mvAll):
|
||||
dpg.add_theme_color(dpg.mvThemeCol_FrameBg, (0, 100, 255, alpha), tag="script_blink_color")
|
||||
dpg.add_theme_color(dpg.mvThemeCol_ChildBg, (0, 100, 255, alpha), tag="script_blink_color2")
|
||||
else:
|
||||
dpg.set_value("script_blink_color", [0, 100, 255, alpha])
|
||||
if dpg.does_item_exist("script_blink_color2"):
|
||||
dpg.set_value("script_blink_color2", [0, 100, 255, alpha])
|
||||
|
||||
if dpg.does_item_exist("last_script_output"):
|
||||
try:
|
||||
dpg.bind_item_theme("last_script_output", "script_blink_theme")
|
||||
dpg.bind_item_theme("last_script_text", "script_blink_theme")
|
||||
if dpg.does_item_exist("last_script_output_wrap_container"):
|
||||
dpg.bind_item_theme("last_script_output_wrap_container", "script_blink_theme")
|
||||
if dpg.does_item_exist("last_script_text_wrap_container"):
|
||||
dpg.bind_item_theme("last_script_text_wrap_container", "script_blink_theme")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -1910,6 +2001,8 @@ class App:
|
||||
if dpg.does_item_exist("response_blink_theme"):
|
||||
try:
|
||||
dpg.bind_item_theme("ai_response", 0)
|
||||
if dpg.does_item_exist("ai_response_wrap_container"):
|
||||
dpg.bind_item_theme("ai_response_wrap_container", 0)
|
||||
except Exception:
|
||||
pass
|
||||
else:
|
||||
@@ -1919,14 +2012,19 @@ class App:
|
||||
|
||||
if not dpg.does_item_exist("response_blink_theme"):
|
||||
with dpg.theme(tag="response_blink_theme"):
|
||||
with dpg.theme_component(dpg.mvInputText):
|
||||
with dpg.theme_component(dpg.mvAll):
|
||||
dpg.add_theme_color(dpg.mvThemeCol_FrameBg, (0, 255, 0, alpha), tag="response_blink_color")
|
||||
dpg.add_theme_color(dpg.mvThemeCol_ChildBg, (0, 255, 0, alpha), tag="response_blink_color2")
|
||||
else:
|
||||
dpg.set_value("response_blink_color", [0, 255, 0, alpha])
|
||||
if dpg.does_item_exist("response_blink_color2"):
|
||||
dpg.set_value("response_blink_color2", [0, 255, 0, alpha])
|
||||
|
||||
if dpg.does_item_exist("ai_response"):
|
||||
try:
|
||||
dpg.bind_item_theme("ai_response", "response_blink_theme")
|
||||
if dpg.does_item_exist("ai_response_wrap_container"):
|
||||
dpg.bind_item_theme("ai_response_wrap_container", "response_blink_theme")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
Reference in New Issue
Block a user