Compare commits
7 Commits
3489b3c4b8
...
74737ac9c7
| Author | SHA1 | Date | |
|---|---|---|---|
| 74737ac9c7 | |||
| 1d18150570 | |||
| ef942bb2a2 | |||
| b7a0c4fa7e | |||
| 27b98ffe1e | |||
| a6f7f82f02 | |||
| bbe0209403 |
@@ -17,8 +17,10 @@
|
|||||||
- [ ] Implement logic to load all related logs (comms, mma, tools) for that session.
|
- [ ] Implement logic to load all related logs (comms, mma, tools) for that session.
|
||||||
- [ ] Ensure that for entries referencing external files (scripts/outputs), the content is loaded on-demand or during the restoration process.
|
- [ ] Ensure that for entries referencing external files (scripts/outputs), the content is loaded on-demand or during the restoration process.
|
||||||
- [x] Task: Implement "Historical Replay" UI mode. 1b3fc5b
|
- [x] Task: Implement "Historical Replay" UI mode. 1b3fc5b
|
||||||
- [ ] In `src/gui_2.py`, implement logic to tint the UI (as already partially done for comms) when `is_viewing_prior_session` is True.
|
- [x] In `src/gui_2.py`, implement logic to tint the UI (as already partially done for comms) when `is_viewing_prior_session` is True.
|
||||||
- [ ] Populate `disc_entries`, `_comms_log`, and MMA Dashboard states from the loaded session logs.
|
- [x] Populate `disc_entries`, `_comms_log`, and MMA Dashboard states from the loaded session logs.
|
||||||
|
- [x] Harden `cb_load_prior_log` for legacy compatibility and reference resolution. bbe0209
|
||||||
|
- [x] Fix `PopStyleColor()` crash in `_gui_func` using frame-scoped flag. 27b98ff
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 2: Session-Level Restoration' (Protocol in workflow.md)
|
- [ ] Task: Conductor - User Manual Verification 'Phase 2: Session-Level Restoration' (Protocol in workflow.md)
|
||||||
|
|
||||||
## Phase 3: Diagnostic Log & Discussion Cleanup
|
## Phase 3: Diagnostic Log & Discussion Cleanup
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
# Implementation Plan: Selectable GUI Text & UX Improvements
|
# Implementation Plan: Selectable GUI Text & UX Improvements
|
||||||
|
|
||||||
## Phase 1: Research & Core Widget Wrapping
|
## Phase 1: Research & Core Widget Wrapping [checkpoint: ef942bb]
|
||||||
- [ ] Task: Audit `gui_2.py` for all `imgui.text()` and `imgui.text_wrapped()` calls in target areas.
|
- [x] Task: Audit `gui_2.py` for all `imgui.text()` and `imgui.text_wrapped()` calls in target areas.
|
||||||
- [ ] Identify the exact locations in `_render_discussion_panel`, `_render_comms_history_panel`, and `_render_ai_settings_panel`.
|
- [x] Identify the exact locations in `_render_discussion_panel`, `_render_comms_history_panel`, and `_render_ai_settings_panel`. Findings: `_render_discussion_panel` (historical/current entries, commit SHA), `_render_heavy_text` (comms/tool payloads), `_render_provider_panel` (Session ID), `_render_token_budget_panel` (telemetry metrics).
|
||||||
- [ ] Task: Implement a helper function/component for "Selectable Label".
|
- [x] Task: Implement a helper function/component for "Selectable Label".
|
||||||
- [ ] This helper should wrap `imgui.input_text` with `InputTextFlags_.read_only` and proper styling to mimic a standard label.
|
- [x] This helper should wrap `imgui.input_text` with `InputTextFlags_.read_only` and proper styling to mimic a standard label. Implemented `_render_selectable_label` in `gui_2.py`.
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 1: Research & Core Widget' (Protocol in workflow.md)
|
- [ ] Task: Conductor - User Manual Verification 'Phase 1: Research & Core Widget' (Protocol in workflow.md)
|
||||||
|
|
||||||
## Phase 2: Discussion History & Comms Log
|
## Phase 2: Discussion History & Comms Log
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
import sys
|
||||||
|
|
||||||
|
def insert_method(file_path):
|
||||||
|
with open(file_path, 'r', encoding='utf-8', newline='') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
target_line = -1
|
||||||
|
for i, line in enumerate(lines):
|
||||||
|
if 'def _render_heavy_text' in line:
|
||||||
|
# Find the end of this method
|
||||||
|
for j in range(i + 1, len(lines)):
|
||||||
|
if lines[j].startswith(' def '):
|
||||||
|
target_line = j
|
||||||
|
break
|
||||||
|
if target_line != -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
if target_line == -1:
|
||||||
|
print("Could not find insertion point")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
new_method = [
|
||||||
|
'\n',
|
||||||
|
' def _render_selectable_label(self, label: str, value: str, width: float = 0.0, multiline: bool = False, height: float = 0.0, color: Optional[imgui.ImVec4] = None) -> None:\n',
|
||||||
|
' imgui.push_id(label + str(hash(value)))\n',
|
||||||
|
' pops = 2\n',
|
||||||
|
' imgui.push_style_color(imgui.Col_.frame_bg, vec4(0, 0, 0, 0))\n',
|
||||||
|
' imgui.push_style_color(imgui.Col_.border, vec4(0, 0, 0, 0))\n',
|
||||||
|
' if color:\n',
|
||||||
|
' imgui.push_style_color(imgui.Col_.text, color)\n',
|
||||||
|
' pops += 1\n',
|
||||||
|
' if multiline:\n',
|
||||||
|
' imgui.input_text_multiline("##" + label, value, imgui.ImVec2(width, height), imgui.InputTextFlags_.read_only)\n',
|
||||||
|
' else:\n',
|
||||||
|
' if width > 0: imgui.set_next_item_width(width)\n',
|
||||||
|
' imgui.input_text("##" + label, value, imgui.InputTextFlags_.read_only)\n',
|
||||||
|
' imgui.pop_style_color(pops)\n',
|
||||||
|
' imgui.pop_id()\n'
|
||||||
|
]
|
||||||
|
|
||||||
|
lines[target_line:target_line] = new_method
|
||||||
|
|
||||||
|
with open(file_path, 'w', encoding='utf-8', newline='') as f:
|
||||||
|
f.writelines(lines)
|
||||||
|
print("Successfully inserted _render_selectable_label")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
insert_method("src/gui_2.py")
|
||||||
+4
-3
@@ -342,16 +342,17 @@ def run(config: dict[str, Any]) -> tuple[str, Path, list[dict[str, Any]]]:
|
|||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
# Load global config to find active project
|
# Load global config to find active project
|
||||||
config_path = Path(os.environ.get("SLOP_CONFIG", "config.toml"))
|
from src.paths import get_config_path
|
||||||
|
config_path = get_config_path()
|
||||||
if not config_path.exists():
|
if not config_path.exists():
|
||||||
|
|
||||||
print("config.toml not found.")
|
print(f"{config_path} not found.")
|
||||||
return
|
return
|
||||||
with open(config_path, "rb") as f:
|
with open(config_path, "rb") as f:
|
||||||
global_cfg = tomllib.load(f)
|
global_cfg = tomllib.load(f)
|
||||||
active_path = global_cfg.get("projects", {}).get("active")
|
active_path = global_cfg.get("projects", {}).get("active")
|
||||||
if not active_path:
|
if not active_path:
|
||||||
print("No active project found in config.toml.")
|
print(f"No active project found in {config_path}.")
|
||||||
return
|
return
|
||||||
# Use project_manager to load project (handles history segregation)
|
# Use project_manager to load project (handles history segregation)
|
||||||
proj = project_manager.load_project(active_path)
|
proj = project_manager.load_project(active_path)
|
||||||
|
|||||||
+50
-6
@@ -821,13 +821,35 @@ class AppController:
|
|||||||
log_path = Path(path)
|
log_path = Path(path)
|
||||||
if log_path.is_dir():
|
if log_path.is_dir():
|
||||||
log_file = log_path / "comms.log"
|
log_file = log_path / "comms.log"
|
||||||
|
session_dir = log_path
|
||||||
else:
|
else:
|
||||||
log_file = log_path
|
log_file = log_path
|
||||||
|
session_dir = log_path.parent
|
||||||
|
|
||||||
if not log_file.exists():
|
if not log_file.exists():
|
||||||
self._set_status(f"log file not found: {log_file}")
|
self._set_status(f"log file not found: {log_file}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
def _resolve_log_ref(content: Any, session_dir: Path) -> str:
|
||||||
|
if not content or not isinstance(content, str) or "[REF:" not in content:
|
||||||
|
return str(content) if content is not None else ""
|
||||||
|
pattern = r'\[REF:([^\]]+)\]'
|
||||||
|
def replace_ref(match):
|
||||||
|
ref_file = match.group(1)
|
||||||
|
paths_to_check = [
|
||||||
|
session_dir / "outputs" / ref_file,
|
||||||
|
session_dir / "scripts" / ref_file
|
||||||
|
]
|
||||||
|
for p in paths_to_check:
|
||||||
|
if p.exists():
|
||||||
|
try:
|
||||||
|
with open(p, "r", encoding="utf-8") as rf:
|
||||||
|
return rf.read()
|
||||||
|
except Exception:
|
||||||
|
return f"[ERROR READING REF: {ref_file}]"
|
||||||
|
return match.group(0)
|
||||||
|
return re.sub(pattern, replace_ref, content)
|
||||||
|
|
||||||
entries = []
|
entries = []
|
||||||
disc_entries = []
|
disc_entries = []
|
||||||
try:
|
try:
|
||||||
@@ -838,35 +860,57 @@ class AppController:
|
|||||||
try:
|
try:
|
||||||
entry = json.loads(line)
|
entry = json.loads(line)
|
||||||
entries.append(entry)
|
entries.append(entry)
|
||||||
kind = entry.get("kind")
|
kind = entry.get("kind", entry.get("type", ""))
|
||||||
payload = entry.get("payload", {})
|
payload = entry.get("payload", {})
|
||||||
ts = entry.get("ts", "")
|
ts = entry.get("ts", "")
|
||||||
|
|
||||||
if kind == "history_add":
|
if kind == "history_add":
|
||||||
|
content = payload.get("content", payload.get("text", payload.get("message", "")))
|
||||||
|
content = _resolve_log_ref(content, session_dir)
|
||||||
disc_entries.append({
|
disc_entries.append({
|
||||||
"role": payload.get("role", "AI"),
|
"role": payload.get("role", "AI"),
|
||||||
"content": payload.get("content", ""),
|
"content": content,
|
||||||
"collapsed": payload.get("collapsed", False),
|
"collapsed": payload.get("collapsed", False),
|
||||||
"ts": ts
|
"ts": ts
|
||||||
})
|
})
|
||||||
elif kind == "request":
|
elif kind == "request":
|
||||||
|
content = payload.get("message", payload.get("content", payload.get("text", "")))
|
||||||
|
content = _resolve_log_ref(content, session_dir)
|
||||||
disc_entries.append({
|
disc_entries.append({
|
||||||
"role": "User",
|
"role": "User",
|
||||||
"content": payload.get("message", ""),
|
"content": content,
|
||||||
"collapsed": False,
|
"collapsed": False,
|
||||||
"ts": ts
|
"ts": ts
|
||||||
})
|
})
|
||||||
elif kind == "response":
|
elif kind == "response":
|
||||||
|
text = payload.get("text", payload.get("content", payload.get("message", "")))
|
||||||
|
text = _resolve_log_ref(text, session_dir)
|
||||||
|
tool_calls = payload.get("tool_calls", [])
|
||||||
|
content = text
|
||||||
|
if tool_calls:
|
||||||
|
try:
|
||||||
|
tc_str = json.dumps(tool_calls, indent=1)
|
||||||
|
if content:
|
||||||
|
content += f"\n\n[TOOL CALLS]\n{tc_str}"
|
||||||
|
else:
|
||||||
|
content = f"[TOOL CALLS]\n{tc_str}"
|
||||||
|
except:
|
||||||
|
if content:
|
||||||
|
content += f"\n\n[TOOL CALLS PRESENT]"
|
||||||
|
else:
|
||||||
|
content = "[TOOL CALLS PRESENT]"
|
||||||
disc_entries.append({
|
disc_entries.append({
|
||||||
"role": "AI",
|
"role": "AI",
|
||||||
"content": payload.get("text", ""),
|
"content": content,
|
||||||
"collapsed": False,
|
"collapsed": False,
|
||||||
"ts": ts
|
"ts": ts
|
||||||
})
|
})
|
||||||
elif kind == "tool_result":
|
elif kind == "tool_result":
|
||||||
|
output = payload.get("output", payload.get("content", ""))
|
||||||
|
output = _resolve_log_ref(output, session_dir)
|
||||||
disc_entries.append({
|
disc_entries.append({
|
||||||
"role": "Tool",
|
"role": "Tool",
|
||||||
"content": f"[TOOL RESULT]\n{payload.get('output', '')}",
|
"content": f"[TOOL RESULT]\n{output}",
|
||||||
"collapsed": True,
|
"collapsed": True,
|
||||||
"ts": ts
|
"ts": ts
|
||||||
})
|
})
|
||||||
@@ -879,7 +923,7 @@ class AppController:
|
|||||||
self.prior_session_entries = entries
|
self.prior_session_entries = entries
|
||||||
self.prior_disc_entries = disc_entries
|
self.prior_disc_entries = disc_entries
|
||||||
self.is_viewing_prior_session = True
|
self.is_viewing_prior_session = True
|
||||||
self._set_status(f"viewing prior session: {log_path.name} ({len(entries)} entries)")
|
self._set_status(f"viewing prior session: {session_dir.name} ({len(entries)} entries)")
|
||||||
|
|
||||||
def cb_prune_logs(self) -> None:
|
def cb_prune_logs(self) -> None:
|
||||||
"""Manually triggers the log pruning process with aggressive thresholds."""
|
"""Manually triggers the log pruning process with aggressive thresholds."""
|
||||||
|
|||||||
+19
-1
@@ -222,6 +222,22 @@ class App:
|
|||||||
imgui.text_unformatted(content)
|
imgui.text_unformatted(content)
|
||||||
# ---------------------------------------------------------------- gui
|
# ---------------------------------------------------------------- gui
|
||||||
|
|
||||||
|
|
||||||
|
def _render_selectable_label(self, label: str, value: str, width: float = 0.0, multiline: bool = False, height: float = 0.0, color: Optional[imgui.ImVec4] = None) -> None:
|
||||||
|
imgui.push_id(label + str(hash(value)))
|
||||||
|
pops = 2
|
||||||
|
imgui.push_style_color(imgui.Col_.frame_bg, vec4(0, 0, 0, 0))
|
||||||
|
imgui.push_style_color(imgui.Col_.border, vec4(0, 0, 0, 0))
|
||||||
|
if color:
|
||||||
|
imgui.push_style_color(imgui.Col_.text, color)
|
||||||
|
pops += 1
|
||||||
|
if multiline:
|
||||||
|
imgui.input_text_multiline("##" + label, value, imgui.ImVec2(width, height), imgui.InputTextFlags_.read_only)
|
||||||
|
else:
|
||||||
|
if width > 0: imgui.set_next_item_width(width)
|
||||||
|
imgui.input_text("##" + label, value, imgui.InputTextFlags_.read_only)
|
||||||
|
imgui.pop_style_color(pops)
|
||||||
|
imgui.pop_id()
|
||||||
def _show_menus(self) -> None:
|
def _show_menus(self) -> None:
|
||||||
if imgui.begin_menu("manual slop"):
|
if imgui.begin_menu("manual slop"):
|
||||||
if imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
|
if imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
|
||||||
@@ -256,9 +272,11 @@ class App:
|
|||||||
imgui.end_menu()
|
imgui.end_menu()
|
||||||
|
|
||||||
def _gui_func(self) -> None:
|
def _gui_func(self) -> None:
|
||||||
|
pushed_prior_tint = False
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_gui_func")
|
if self.perf_profiling_enabled: self.perf_monitor.start_component("_gui_func")
|
||||||
if self.is_viewing_prior_session:
|
if self.is_viewing_prior_session:
|
||||||
imgui.push_style_color(imgui.Col_.window_bg, vec4(50, 40, 20))
|
imgui.push_style_color(imgui.Col_.window_bg, vec4(50, 40, 20))
|
||||||
|
pushed_prior_tint = True
|
||||||
try:
|
try:
|
||||||
self.perf_monitor.start_frame()
|
self.perf_monitor.start_frame()
|
||||||
self._autofocus_response_tab = self.controller._autofocus_response_tab
|
self._autofocus_response_tab = self.controller._autofocus_response_tab
|
||||||
@@ -761,7 +779,7 @@ class App:
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
if self.is_viewing_prior_session:
|
if pushed_prior_tint:
|
||||||
imgui.pop_style_color()
|
imgui.pop_style_color()
|
||||||
|
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_gui_func")
|
if self.perf_profiling_enabled: self.perf_monitor.end_component("_gui_func")
|
||||||
|
|||||||
+2
-1
@@ -49,7 +49,8 @@ from typing import Optional
|
|||||||
_RESOLVED: dict[str, Path] = {}
|
_RESOLVED: dict[str, Path] = {}
|
||||||
|
|
||||||
def get_config_path() -> Path:
|
def get_config_path() -> Path:
|
||||||
return Path(os.environ.get("SLOP_CONFIG", "config.toml"))
|
root_dir = Path(__file__).resolve().parent.parent
|
||||||
|
return Path(os.environ.get("SLOP_CONFIG", root_dir / "config.toml"))
|
||||||
|
|
||||||
def _resolve_path(env_var: str, config_key: str, default: str) -> Path:
|
def _resolve_path(env_var: str, config_key: str, default: str) -> Path:
|
||||||
if env_var in os.environ:
|
if env_var in os.environ:
|
||||||
|
|||||||
+3
-1
@@ -13,7 +13,9 @@ def test_default_paths():
|
|||||||
assert paths.get_conductor_dir() == Path("conductor")
|
assert paths.get_conductor_dir() == Path("conductor")
|
||||||
assert paths.get_logs_dir() == Path("logs/sessions")
|
assert paths.get_logs_dir() == Path("logs/sessions")
|
||||||
assert paths.get_scripts_dir() == Path("scripts/generated")
|
assert paths.get_scripts_dir() == Path("scripts/generated")
|
||||||
assert paths.get_config_path() == Path("config.toml")
|
# config path should now be an absolute path relative to src/paths.py
|
||||||
|
root_dir = Path(paths.__file__).resolve().parent.parent
|
||||||
|
assert paths.get_config_path() == root_dir / "config.toml"
|
||||||
assert paths.get_tracks_dir() == Path("conductor/tracks")
|
assert paths.get_tracks_dir() == Path("conductor/tracks")
|
||||||
assert paths.get_archive_dir() == Path("conductor/archive")
|
assert paths.get_archive_dir() == Path("conductor/archive")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user