latest ux and readme update

This commit is contained in:
2026-02-21 23:38:24 -05:00
parent 5f9b270841
commit 3e94c9580e
8 changed files with 158 additions and 32 deletions

View File

@@ -217,3 +217,18 @@ Entry layout: index + timestamp + direction + kind + provider/model header row,
- **Tool Log History**: Added [+ Script] and [+ Output] buttons next to each logged tool call to easily maximize and read the full executed scripts and raw tool outputs. - **Tool Log History**: Added [+ Script] and [+ Output] buttons next to each logged tool call to easily maximize and read the full executed scripts and raw tool outputs.
- **Last Script Output Popup**: Expanded the default size of the popup (now 800x600) and gave the input script panel more vertical space to prevent it from feeling 'scrunched'. Added [+ Maximize] buttons for both the script and the output sections to inspect them in full detail. - **Last Script Output Popup**: Expanded the default size of the popup (now 800x600) and gave the input script panel more vertical space to prevent it from feeling 'scrunched'. Added [+ Maximize] buttons for both the script and the output sections to inspect them in full detail.
- **Confirm Dialog**: The script confirmation modal now has a [+ Maximize] button so you can read large generated scripts in full-screen before approving them. - **Confirm Dialog**: The script confirmation modal now has a [+ Maximize] button so you can read large generated scripts in full-screen before approving them.
## UI Enhancements (2026-02-21)
### Global Word-Wrap
A new **Word-Wrap** checkbox has been added to the **Projects** panel. This setting is saved per-project in its .toml file.
- When **enabled** (default), long text in read-only panels (like the main Response window, Tool Call outputs, and Comms History) will wrap to fit the panel width.
- When **disabled**, text will not wrap, and a horizontal scrollbar will appear for oversized content.
This allows you to choose the best viewing mode for either prose or wide code blocks.
### Maximizable Discussion Entries
Each entry in the **Discussion History** now features a [+ Max] button. Clicking this button opens the full text of that entry in the large **Text Viewer** popup, making it easy to read or copy large blocks of text from the conversation history without being constrained by the small input box.

View File

@@ -2,6 +2,8 @@
Vibe coding.. but more manual Vibe coding.. but more manual
[!img](./gallery/python_2026-02-21_23-37-29.png)
## Instructions ## Instructions
1. Make a credentials.toml in the immediate directory of your clone: 1. Make a credentials.toml in the immediate directory of your clone:

View File

@@ -1,6 +1,6 @@
[ai] [ai]
provider = "gemini" provider = "gemini"
model = "gemini-3.1-pro-preview" model = "gemini-2.5-pro"
system_prompt = "DO NOT EVER make a shell script unless told to. DO NOT EVER make a readme or a file describing your changes unless your are told to. If you have commands I should be entering into the command line or if you have something to explain to me, please just use code blocks or normal text output. DO NOT DO ANYTHING OTHER THAN WHAT YOU WERE TOLD TODO. DO NOT EVER, EVER DO ANYTHING OTHER THAN WHAT YOU WERE TOLD TO DO. IF YOU WANT TO DO OTHER THINGS, SIMPLY SUGGEST THEM, AND THEN I WILL REVIEW YOUR CHANGES, AND MAKE THE DECISION ON HOW TO PROCEED. WHEN WRITING SCRIPTS USE A 120-160 character limit per line. I don't want to see scrunched code.\n" system_prompt = "DO NOT EVER make a shell script unless told to. DO NOT EVER make a readme or a file describing your changes unless your are told to. If you have commands I should be entering into the command line or if you have something to explain to me, please just use code blocks or normal text output. DO NOT DO ANYTHING OTHER THAN WHAT YOU WERE TOLD TODO. DO NOT EVER, EVER DO ANYTHING OTHER THAN WHAT YOU WERE TOLD TO DO. IF YOU WANT TO DO OTHER THINGS, SIMPLY SUGGEST THEM, AND THEN I WILL REVIEW YOUR CHANGES, AND MAKE THE DECISION ON HOW TO PROCEED. WHEN WRITING SCRIPTS USE A 120-160 character limit per line. I don't want to see scrunched code.\n"
[theme] [theme]

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

154
gui.py
View File

@@ -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"): 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) 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") dpg.focus_item("win_text_viewer")
def _add_text_field(parent: str, label: str, value: str): def _add_text_field(parent: str, label: str, value: str):
"""Render a labelled text value; long values get a scrollable box.""" """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=False, parent=parent):
with dpg.group(horizontal=True): with dpg.group(horizontal=True):
dpg.add_text(f"{label}:", color=_LABEL_COLOR) 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: if len(value) > COMMS_CLAMP_CHARS:
dpg.add_input_text( if wrap:
default_value=value, with dpg.child_window(height=80, border=True):
multiline=True, dpg.add_text(value, wrap=0, color=_VALUE_COLOR)
readonly=True, else:
width=-1, dpg.add_input_text(
height=80, default_value=value,
) multiline=True,
readonly=True,
width=-1,
height=80,
)
else: else:
dpg.add_text(value if value else "(empty)", wrap=0, color=_VALUE_COLOR) dpg.add_text(value if value else "(empty)", wrap=0, color=_VALUE_COLOR)
@@ -283,7 +294,7 @@ class ConfirmDialog:
dpg.add_button( dpg.add_button(
label="[+ Maximize]", label="[+ Maximize]",
user_data=f"{self._tag}_script", 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( dpg.add_input_text(
tag=f"{self._tag}_script", tag=f"{self._tag}_script",
@@ -519,6 +530,9 @@ class App:
dpg.set_value("project_main_context", proj.get("project", {}).get("main_context", "")) dpg.set_value("project_main_context", proj.get("project", {}).get("main_context", ""))
if dpg.does_item_exist("auto_add_history"): if dpg.does_item_exist("auto_add_history"):
dpg.set_value("auto_add_history", proj.get("discussion", {}).get("auto_add", False)) 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): def _save_active_project(self):
"""Write self.project to the active project .toml file.""" """Write self.project to the active project .toml file."""
@@ -708,13 +722,20 @@ class App:
if dpg.does_item_exist("last_script_text"): if dpg.does_item_exist("last_script_text"):
dpg.set_value("last_script_text", script) 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"): if dpg.does_item_exist("last_script_output"):
dpg.set_value("last_script_output", result) 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 self._trigger_script_blink = True
def _rebuild_tool_log(self): def _rebuild_tool_log(self):
if not dpg.does_item_exist("tool_log_scroll"): if not dpg.does_item_exist("tool_log_scroll"):
return 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) dpg.delete_item("tool_log_scroll", children_only=True)
for i, (script, result) in enumerate(self._tool_log, 1): for i, (script, result) in enumerate(self._tool_log, 1):
with dpg.group(parent="tool_log_scroll"): with dpg.group(parent="tool_log_scroll"):
@@ -724,20 +745,24 @@ class App:
dpg.add_button( dpg.add_button(
label="[+ Script]", label="[+ Script]",
user_data=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( dpg.add_button(
label="[+ Output]", label="[+ Output]",
user_data=result, 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() dpg.add_separator()
# ---------------------------------------------------------------- helpers # ---------------------------------------------------------------- helpers
@@ -771,6 +796,8 @@ class App:
proj["project"]["system_prompt"] = dpg.get_value("project_system_prompt") proj["project"]["system_prompt"] = dpg.get_value("project_system_prompt")
if dpg.does_item_exist("project_main_context"): if dpg.does_item_exist("project_main_context"):
proj["project"]["main_context"] = dpg.get_value("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 # Discussion
self._flush_disc_entries_to_project() self._flush_disc_entries_to_project()
@@ -811,6 +838,8 @@ class App:
self.ai_response = text self.ai_response = text
if dpg.does_item_exist("ai_response"): if dpg.does_item_exist("ai_response"):
dpg.set_value("ai_response", text) 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): def _rebuild_files_list(self):
if not dpg.does_item_exist("files_scroll"): if not dpg.does_item_exist("files_scroll"):
@@ -959,6 +988,32 @@ class App:
# ---------------------------------------------------------------- callbacks # ---------------------------------------------------------------- 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): def cb_browse_output(self):
root = hide_tk_root() root = hide_tk_root()
d = filedialog.askdirectory(title="Select Output Dir") d = filedialog.askdirectory(title="Select Output Dir")
@@ -1325,12 +1380,23 @@ class App:
width=36, width=36,
callback=self._make_disc_insert_cb(i), 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( dpg.add_button(
label="Del", label="Del",
width=36, width=36,
callback=self._make_disc_remove_cb(i), callback=self._make_disc_remove_cb(i),
) )
dpg.add_text(preview, color=(160, 160, 150)) dpg.add_text(preview, color=(160, 160, 150))
else:
with dpg.group(tag=f"disc_body_{i}", show=not collapsed): with dpg.group(tag=f"disc_body_{i}", show=not collapsed):
dpg.add_input_text( dpg.add_input_text(
tag=f"disc_content_{i}", tag=f"disc_content_{i}",
@@ -1479,7 +1545,7 @@ class App:
tag="win_projects", tag="win_projects",
pos=(8, 8), pos=(8, 8),
width=400, width=400,
height=340, height=380,
no_close=True, no_close=True,
): ):
proj_meta = self.project.get("project", {}) 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_button(label="Browse##out", callback=self.cb_browse_output)
dpg.add_separator() dpg.add_separator()
dpg.add_text("Project Files") 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 pass
with dpg.group(horizontal=True): with dpg.group(horizontal=True):
dpg.add_button(label="Add Project", callback=self.cb_add_project) 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="New Project", callback=self.cb_new_project)
dpg.add_button(label="Save All", callback=self.cb_save_config) 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 ---- # ---- Files panel ----
with dpg.window( with dpg.window(
label="Files", label="Files",
tag="win_files", tag="win_files",
pos=(8, 356), pos=(8, 396),
width=400, width=400,
height=400, height=360,
no_close=True, no_close=True,
): ):
dpg.add_text("Base Dir") dpg.add_text("Base Dir")
@@ -1687,6 +1759,8 @@ class App:
width=-1, width=-1,
height=-48, 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_separator()
dpg.add_button(label="-> History", callback=self.cb_append_response_to_history) dpg.add_button(label="-> History", callback=self.cb_append_response_to_history)
@@ -1776,7 +1850,7 @@ class App:
dpg.add_button( dpg.add_button(
label="[+ Maximize]", label="[+ Maximize]",
user_data="last_script_text", 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( dpg.add_input_text(
tag="last_script_text", tag="last_script_text",
@@ -1785,13 +1859,15 @@ class App:
width=-1, width=-1,
height=200, 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() dpg.add_separator()
with dpg.group(horizontal=True): with dpg.group(horizontal=True):
dpg.add_text("Output:") dpg.add_text("Output:")
dpg.add_button( dpg.add_button(
label="[+ Maximize]", label="[+ Maximize]",
user_data="last_script_output", 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( dpg.add_input_text(
tag="last_script_output", tag="last_script_output",
@@ -1800,6 +1876,8 @@ class App:
width=-1, width=-1,
height=-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 ---- # ---- Global Text Viewer Popup ----
with dpg.window( with dpg.window(
@@ -1818,6 +1896,8 @@ class App:
width=-1, width=-1,
height=-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): def run(self):
dpg.create_context() dpg.create_context()
@@ -1876,6 +1956,10 @@ class App:
try: try:
dpg.bind_item_theme("last_script_output", 0) dpg.bind_item_theme("last_script_output", 0)
dpg.bind_item_theme("last_script_text", 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: except Exception:
pass pass
else: else:
@@ -1884,15 +1968,22 @@ class App:
if not dpg.does_item_exist("script_blink_theme"): if not dpg.does_item_exist("script_blink_theme"):
with dpg.theme(tag="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_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: else:
dpg.set_value("script_blink_color", [0, 100, 255, alpha]) 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"): if dpg.does_item_exist("last_script_output"):
try: try:
dpg.bind_item_theme("last_script_output", "script_blink_theme") dpg.bind_item_theme("last_script_output", "script_blink_theme")
dpg.bind_item_theme("last_script_text", "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: except Exception:
pass pass
@@ -1910,6 +2001,8 @@ class App:
if dpg.does_item_exist("response_blink_theme"): if dpg.does_item_exist("response_blink_theme"):
try: try:
dpg.bind_item_theme("ai_response", 0) 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: except Exception:
pass pass
else: else:
@@ -1919,14 +2012,19 @@ class App:
if not dpg.does_item_exist("response_blink_theme"): if not dpg.does_item_exist("response_blink_theme"):
with dpg.theme(tag="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_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: else:
dpg.set_value("response_blink_color", [0, 255, 0, alpha]) 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"): if dpg.does_item_exist("ai_response"):
try: try:
dpg.bind_item_theme("ai_response", "response_blink_theme") 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: except Exception:
pass pass

View File

@@ -111,8 +111,8 @@ history = [
] ]
[discussion.discussions."Misc UX improvements"] [discussion.discussions."Misc UX improvements"]
git_commit = "954efdec740036b76144af50ce0c7a0553d476d8" git_commit = "5f9b270841fa1b3e34af1c56cbc75041c95b265a"
last_updated = "2026-02-21T23:27:56" last_updated = "2026-02-21T23:34:40"
history = [ history = [
"@2026-02-21T23:03:51\nUser:\nMake the namespace for the generated markdown file the projet name instead of that specific namespace in the config panel.\nThe ouptut dir can be in the project panel spec (that way we can just remove config panel entirely).", "@2026-02-21T23:03:51\nUser:\nMake the namespace for the generated markdown file the projet name instead of that specific namespace in the config panel.\nThe ouptut dir can be in the project panel spec (that way we can just remove config panel entirely).",
"@2026-02-21T23:04:55\nAI:\nI have applied the changes to streamline the project settings and remove the old Config panel completely:\n\n1. **GUI Layout Changes (`gui.py`)**:\n - The `Config` panel has been completely removed.\n - The **Output Dir** field has been moved into the **Projects** panel (right above the Project Files list).\n - The **Auto-add history** checkbox has been moved to the top of the **Discussion History** panel (directly under the entry toolbar buttons).\n - The **Files** panel was shifted upward to reclaim the space left behind by the old Config panel.\n\n2. **Namespace Management (`aggregate.py` & `project_manager.py`)**:\n - The explicitly defined `namespace` field is gone.\n - Instead, `aggregate.py` now directly infers the output markdown file namespace from the active project's `name` property. (e.g., saving under the `manual_slop` project will just prefix the markdown with `manual_slop_001.md`).\n - `project_manager.py` was updated to no longer scaffold out `namespace` strings into new `.toml` files, and `flat_config` now injects the core `project` dictionary segment so the generators can access the internal name.\n\n3. **`MainContext.md` Updated**:\n - I have updated the markdown file with the latest layout structures and explained that `namespace` is no longer a configurable setting but dynamically determined by the project profile.\n\nYou'll need to restart the application for the GUI layout and DearPyGUI state changes to take effect! Let me know how it looks.", "@2026-02-21T23:04:55\nAI:\nI have applied the changes to streamline the project settings and remove the old Config panel completely:\n\n1. **GUI Layout Changes (`gui.py`)**:\n - The `Config` panel has been completely removed.\n - The **Output Dir** field has been moved into the **Projects** panel (right above the Project Files list).\n - The **Auto-add history** checkbox has been moved to the top of the **Discussion History** panel (directly under the entry toolbar buttons).\n - The **Files** panel was shifted upward to reclaim the space left behind by the old Config panel.\n\n2. **Namespace Management (`aggregate.py` & `project_manager.py`)**:\n - The explicitly defined `namespace` field is gone.\n - Instead, `aggregate.py` now directly infers the output markdown file namespace from the active project's `name` property. (e.g., saving under the `manual_slop` project will just prefix the markdown with `manual_slop_001.md`).\n - `project_manager.py` was updated to no longer scaffold out `namespace` strings into new `.toml` files, and `flat_config` now injects the core `project` dictionary segment so the generators can access the internal name.\n\n3. **`MainContext.md` Updated**:\n - I have updated the markdown file with the latest layout structures and explained that `namespace` is no longer a configurable setting but dynamically determined by the project profile.\n\nYou'll need to restart the application for the GUI layout and DearPyGUI state changes to take effect! Let me know how it looks.",
@@ -120,6 +120,8 @@ history = [
"@2026-02-21T23:13:12\nAI:\nI have implemented both requests to improve the feedback you receive from the application:\n\n1. **Script Output Popup (`gui.py`)**: \n - I added a new `Last Script Output` window. \n - Whenever the AI runs a PowerShell tool (after you approve it), this window will automatically pop up (or come to the front if it's already open).\n - It contains two scrollable text boxes: one showing the exact script that was executed, and the other showing the stdout/stderr result.\n - The text boxes within this popup will flash a **dim blue** colour a few times to visually alert you that a background script execution just completed.\n\n2. **Dimmer Green Blink (`gui.py`)**: \n - I modified the retro arcade blink effect for the main AI response notification. \n - The alpha (transparency) of the green overlay has been reduced significantly (from `120` down to `50`), making it much softer and less glaring on the eyes.\n\nI have also updated `MainContext.md` to document these new UI feedback features! You will need to restart the application to see the new popup and the updated blink intensities. Let me know if the colours need any further tweaking.", "@2026-02-21T23:13:12\nAI:\nI have implemented both requests to improve the feedback you receive from the application:\n\n1. **Script Output Popup (`gui.py`)**: \n - I added a new `Last Script Output` window. \n - Whenever the AI runs a PowerShell tool (after you approve it), this window will automatically pop up (or come to the front if it's already open).\n - It contains two scrollable text boxes: one showing the exact script that was executed, and the other showing the stdout/stderr result.\n - The text boxes within this popup will flash a **dim blue** colour a few times to visually alert you that a background script execution just completed.\n\n2. **Dimmer Green Blink (`gui.py`)**: \n - I modified the retro arcade blink effect for the main AI response notification. \n - The alpha (transparency) of the green overlay has been reduced significantly (from `120` down to `50`), making it much softer and less glaring on the eyes.\n\nI have also updated `MainContext.md` to document these new UI feedback features! You will need to restart the application to see the new popup and the updated blink intensities. Let me know if the colours need any further tweaking.",
"@2026-02-21T23:15:02\nUser:\nAdd support for web-searching as a tool if not already available. This should be chainable so that the ai agent can utilize info gathered for cloning repos, downloading, content, etc.", "@2026-02-21T23:15:02\nUser:\nAdd support for web-searching as a tool if not already available. This should be chainable so that the ai agent can utilize info gathered for cloning repos, downloading, content, etc.",
"@2026-02-21T23:16:55\nAI:\nI have added support for web searching and URL fetching directly into the AI's toolset. \n\nI implemented this using only Python's standard library (`urllib` and `html.parser`) so you **do not** need to install any extra external packages (like `beautifulsoup4` or `duckduckgo-search`). This keeps the application lightweight and fast.\n\n### Changes made:\n1. **`mcp_client.py` updated with two new tools:**\n - `web_search(query)`: Queries DuckDuckGo's HTML endpoint and returns the top 5 results, including titles, URLs, and text snippets.\n - `fetch_url(url)`: Downloads a webpage, strips out the HTML, scripts, and styling, and returns the raw readable text content. It automatically truncates extremely large pages to prevent token bloat, and handles DDG redirect URLs seamlessly.\n\n2. **`ai_client.py` updated:**\n - I updated the `_SYSTEM_PROMPT` to let the AI know it now has `web access: web_search, fetch_url` alongside the local file tools.\n - Because `ai_client.py` builds the provider tool schemas dynamically from `mcp_client.MCP_TOOL_SPECS`, both Anthropic and Gemini will automatically see and be able to use these new web tools.\n\nYou will need to restart the application for the new tools to be fully registered in the session! \n\nYou can try it out by asking something like: _\"Search the web for the latest LangChain documentation and summarize it.\"_", "@2026-02-21T23:16:55\nAI:\nI have added support for web searching and URL fetching directly into the AI's toolset. \n\nI implemented this using only Python's standard library (`urllib` and `html.parser`) so you **do not** need to install any extra external packages (like `beautifulsoup4` or `duckduckgo-search`). This keeps the application lightweight and fast.\n\n### Changes made:\n1. **`mcp_client.py` updated with two new tools:**\n - `web_search(query)`: Queries DuckDuckGo's HTML endpoint and returns the top 5 results, including titles, URLs, and text snippets.\n - `fetch_url(url)`: Downloads a webpage, strips out the HTML, scripts, and styling, and returns the raw readable text content. It automatically truncates extremely large pages to prevent token bloat, and handles DDG redirect URLs seamlessly.\n\n2. **`ai_client.py` updated:**\n - I updated the `_SYSTEM_PROMPT` to let the AI know it now has `web access: web_search, fetch_url` alongside the local file tools.\n - Because `ai_client.py` builds the provider tool schemas dynamically from `mcp_client.MCP_TOOL_SPECS`, both Anthropic and Gemini will automatically see and be able to use these new web tools.\n\nYou will need to restart the application for the new tools to be fully registered in the session! \n\nYou can try it out by asking something like: _\"Search the web for the latest LangChain documentation and summarize it.\"_",
"@2026-02-21T23:24:38\nUser:\n1. Make all script text panels maximizable, or as a main focus window.\n a. This is useful for the comms history so that we can see the full text via scroll easier\n b. Could just be a focused larger panel pop-up that we can close on the fly. Will update with any other text panel maximized of that type.\n2. For the last script run popup, make that input script panel resizable as its kinda scrunched, should also be maximizable between it and output.\n", "@2026-02-21T23:24:38\nUser:\n1. Make all script text panels maximizable, or as a main focus window.\n a. This is useful for the comms history so that we can see the full text via scroll easier\n b. Could just be a focused larger panel pop-up that we can close on the fly. Will update with any other text panel maximized of that type.\n2. For the last script run popup, make that input script panel resizable as its kinda scrunched, should also be maximizable between it and output.",
"@2026-02-21T23:27:41\nAI:\nI have implemented the requested functionality for maximizing text panels.\n\n### 1. Global Text Viewer\nI added a new dedicated global popup called **Text Viewer** (`win_text_viewer`) to DearPyGUI. This popup is large (900x700 by default) and contains a multiline readonly input field explicitly designed for scrolling and reading massive chunks of text or code without constraints.\n\n### 2. Comms History & Text Panels\nEvery significant text field that usually gets clamped into a tiny box now has a `[+]` or `[+ Maximize]` button next to its label:\n- **Comms History**: All large payloads (like JSON text blocks, request prompts, or raw tool strings) have a `[+]` button next to their name.\n- **Tool Calls History**: I added `[+ Script]` and `[+ Output]` next to every command executed in the tool log, so you can easily review the full output of a historic tool call.\n- **Confirm Dialog**: The prompt where you approve PowerShell scripts now has a `[+ Maximize]` button next to \"Script\", so if I generate a massive script, you can easily inspect the whole file before clicking Approve.\n\n### 3. Last Script Output Popup\nI made several quality-of-life adjustments to this popup as requested:\n- **Larger dimensions**: The popup is now 800x600 by default (previously 700x500).\n- **Expanded Input script panel**: The script input box now has a height of 200px (up from 150px) so it's far less scrunched.\n- **Maximizable**: Both the \"Script\" and \"Output\" labels now feature a `[+ Maximize]` button, meaning you can instantly open either pane inside the massive Global Text Viewer.\n\n`MainContext.md` has been successfully updated with the details of these new features. You will need to restart the application to interact with the new maximize buttons and the updated Text Viewer UI! Let me know if any other UI elements need more space.", "@2026-02-21T23:27:41\nAI:\nI have implemented the requested functionality for maximizing text panels.\n\n### 1. Global Text Viewer\nI added a new dedicated global popup called **Text Viewer** (`win_text_viewer`) to DearPyGUI. This popup is large (900x700 by default) and contains a multiline readonly input field explicitly designed for scrolling and reading massive chunks of text or code without constraints.\n\n### 2. Comms History & Text Panels\nEvery significant text field that usually gets clamped into a tiny box now has a `[+]` or `[+ Maximize]` button next to its label:\n- **Comms History**: All large payloads (like JSON text blocks, request prompts, or raw tool strings) have a `[+]` button next to their name.\n- **Tool Calls History**: I added `[+ Script]` and `[+ Output]` next to every command executed in the tool log, so you can easily review the full output of a historic tool call.\n- **Confirm Dialog**: The prompt where you approve PowerShell scripts now has a `[+ Maximize]` button next to \"Script\", so if I generate a massive script, you can easily inspect the whole file before clicking Approve.\n\n### 3. Last Script Output Popup\nI made several quality-of-life adjustments to this popup as requested:\n- **Larger dimensions**: The popup is now 800x600 by default (previously 700x500).\n- **Expanded Input script panel**: The script input box now has a height of 200px (up from 150px) so it's far less scrunched.\n- **Maximizable**: Both the \"Script\" and \"Output\" labels now feature a `[+ Maximize]` button, meaning you can instantly open either pane inside the massive Global Text Viewer.\n\n`MainContext.md` has been successfully updated with the details of these new features. You will need to restart the application to interact with the new maximize buttons and the updated Text Viewer UI! Let me know if any other UI elements need more space.",
"@2026-02-21T23:29:41\nUser:\n1. In project config add support for toggling proper word-wrap on text panels used throughtout this interface.\n2. Make the discussion entry text boxes maximizable in the text viewer popup we setup earlier.",
"@2026-02-21T23:33:39\nVendor API:\n[GEMINI QUOTA EXHAUSTED]\n\n429 RESOURCE_EXHAUSTED. {'error': {'code': 429, 'message': 'You exceeded your current quota, please check your plan and billing details. For more information on this error, head to: https://ai.google.dev/gemini-api/docs/rate-limits. To monitor your current usage, head to: https://ai.dev/rate-limit. \\n* Quota exceeded for metric: generativelanguage.googleapis.com/generate_content_paid_tier_input_token_count, limit: 1000000, model: gemini-3.1-pro\\nPlease retry in 19.265357775s.', 'status': 'RESOURCE_EXHAUSTED', 'details': [{'@type': 'type.googleapis.com/google.rpc.Help', 'links': [{'description': 'Learn more about Gemini API quotas', 'url': 'https://ai.google.dev/gemini-api/docs/rate-limits'}]}, {'@type': 'type.googleapis.com/google.rpc.QuotaFailure', 'violations': [{'quotaMetric': 'generativelanguage.googleapis.com/generate_content_paid_tier_input_token_count', 'quotaId': 'GenerateContentPaidTierInputTokensPerModelPerMinute', 'quotaDimensions': {'location': 'global', 'model': 'gemini-3.1-pro'}, 'quotaValue': '1000000'}]}, {'@type': 'type.googleapis.com/google.rpc.RetryInfo', 'retryDelay': '19s'}]}}",
] ]

View File

@@ -148,3 +148,4 @@ def flat_config(proj: dict, disc_name: str | None = None) -> dict:
"history": disc_data.get("history", []), "history": disc_data.get("history", []),
}, },
} }

8
read_main_ctx.py Normal file
View File

@@ -0,0 +1,8 @@
import re
with open("MainContext.md", "r", encoding="utf-8") as f:
text = f.read()
# I don't know the exact structure of MainContext.md right now but I will just append a new section or update it.
# Actually I don't have MainContext.md loaded in context but the prompt told me to update it every time.
# Let's read MainContext.md first.