18 Commits

Author SHA1 Message Date
ed 70bfcc61c2 update tracks 2026-03-13 18:32:52 -04:00
ed 976a3b63e0 checkpoint: ai went retarted 2026-03-13 18:31:02 -04:00
ed 0e0b185290 checkpoint: progressing on frosted glass panels 2026-03-13 16:09:52 -04:00
ed 5b196dccf0 checkpoint frosted glass 2026-03-13 15:33:40 -04:00
ed 1771c32006 docs(conductor): Synchronize docs for track 'Frosted Glass Background Effect' 2026-03-13 15:07:47 -04:00
ed 6a39f440d9 chore(conductor): Mark track 'Frosted Glass Background Effect' as complete 2026-03-13 15:05:26 -04:00
ed cecbe2245a feat(gui): Implement frosted glass UI controls and config persistence 2026-03-13 15:04:49 -04:00
ed ad9ffb68f5 conductor(plan): Mark Phase 2 as complete 2026-03-13 14:50:02 -04:00
ed e9b7875de5 conductor(checkpoint): Checkpoint end of Phase 2 2026-03-13 14:49:38 -04:00
ed 3d5c768598 conductor(plan): Mark Phase 2 capture pipeline as complete 2026-03-13 14:49:06 -04:00
ed f297e7a3bd feat(shaders): Implement FBO capture lifecycle in ShaderManager 2026-03-13 14:48:43 -04:00
ed 4a705a8060 conductor(plan): Mark Phase 1 as complete 2026-03-13 14:46:26 -04:00
ed 55f3bd87e2 conductor(checkpoint): Checkpoint end of Phase 1 2026-03-13 14:46:02 -04:00
ed e56d4af097 conductor(plan): Mark Phase 1 shader implementation as complete 2026-03-13 14:45:40 -04:00
ed 1328bc159b feat(shaders): Implement FrostedGlassShader compilation in ShaderManager 2026-03-13 14:45:16 -04:00
ed 3a0d388502 adjust tracks.md 2026-03-13 14:41:08 -04:00
ed 879e0991c9 chore(conductor): Add new track 'Frosted Glass Background Effect' 2026-03-13 14:40:43 -04:00
ed d96adca67c update track ordering 2026-03-13 14:40:37 -04:00
16 changed files with 832 additions and 190 deletions
+1 -1
View File
@@ -66,7 +66,7 @@ For deep implementation details when planning or implementing tracks, consult `d
- **Clean Project Root:** Enforces a "Cruft-Free Root" policy by organizing core implementation into a `src/` directory and redirecting all temporary test data, configurations, and AI-generated artifacts to `tests/artifacts/`.
- **Performance Diagnostics:** Comprehensive, conditional per-component profiling across the entire application. Features a dedicated **Diagnostics Panel** providing real-time telemetry for FPS, Frame Time, CPU usage, and **Detailed Component Timings** for all GUI panels and background threads, including automated threshold-based latency alerts.
- **Automated UX Verification:** A robust IPC mechanism via API hooks and a modular simulation suite allows for human-like simulation walkthroughs and automated regression testing of the full GUI lifecycle across multiple specialized scenarios.
- **Professional UI Theme & Typography:** Implements a high-fidelity visual system featuring **Inter** and **Maple Mono** fonts for optimal readability. Employs a cohesive "Subtle Rounding" aesthetic across all standard widgets, supported by custom **soft shadow shaders** for modals and popups to provide depth and professional polish. Includes a selectable **NERV UI theme** featuring a "Black Void" palette, zero-rounding geometry, and CRT-style visual effects (scanlines, status flickering).
- **Professional UI Theme & Typography:** Implements a high-fidelity visual system featuring **Inter** and **Maple Mono** fonts for optimal readability. Employs a cohesive \"Subtle Rounding\" aesthetic across all standard widgets, supported by custom **soft shadow shaders** for modals and popups, and a high-fidelity **frosted glass (acrylic) background effect** for panels to provide depth and professional polish. Includes a selectable **NERV UI theme** featuring a \"Black Void\" palette, zero-rounding geometry, and CRT-style visual effects (scanlines, status flickering).
- **Rich Text & Syntax Highlighting:** Provides advanced rendering for messages, logs, and tool outputs using a hybrid Markdown system. Supports GitHub-Flavored Markdown (GFM) via `imgui_markdown` and integrates `ImGuiColorTextEdit` for high-performance syntax highlighting of code blocks (Python, JSON, C++, etc.). Includes automated language detection and clickable URL support.
- **Multi-Viewport & Layout Management:** Full support for ImGui Multi-Viewport, allowing users to detach panels into standalone OS windows for complex multi-monitor workflows. Includes a comprehensive **Layout Presets system**, enabling developers to save, name, and instantly restore custom window arrangements, including their Multi-Viewport state.
- **Headless Backend Service & Hook API:** Optional headless mode allowing the core AI and tool execution logic to run as a decoupled service. Features a comprehensive Hook API and WebSocket event streaming for remote orchestration, deep state inspection, and manual worker lifecycle management.
+1 -1
View File
@@ -70,6 +70,6 @@
- **Synchronous IPC Approval Flow:** A specialized bridge mechanism that allows headless AI providers (like Gemini CLI) to synchronously request and receive human approval for tool calls via the GUI's REST API hooks.
- **High-Fidelity Selectable Labels:** Implements a pattern for making read-only UI text selectable by wrapping `imgui.input_text` with `imgui.InputTextFlags_.read_only`. Includes a specialized `_render_selectable_label` helper that resets frame backgrounds, borders, and padding to mimic standard labels while enabling OS-level clipboard support (Ctrl+C).
- **Hybrid Markdown Rendering:** Employs a custom `MarkdownRenderer` that orchestrates `imgui_markdown` for standard text and headers while intercepting code blocks to render them via cached `ImGuiColorTextEdit` instances. This ensures high-performance rich text rendering with robust syntax highlighting and stateful text selection.
- **Hybrid Shader Pipeline:** Utilizes an optimized `ImDrawList`-based batching technique to simulate UI effects such as soft shadows and acrylic glass overlays without the overhead of heavy GPU-resident shaders. Supplemented by a true GPU shader pipeline using `PyOpenGL` and Framebuffer Objects (FBOs) for complex post-processing (CRT scanlines, bloom) and dynamic backgrounds.
- **Hybrid Shader Pipeline:** Utilizes an optimized `ImDrawList`-based batching technique to simulate UI effects such as soft shadows without the overhead of heavy GPU-resident shaders. Supplemented by a true GPU shader pipeline using `PyOpenGL` and Framebuffer Objects (FBOs) for complex post-processing (CRT scanlines, bloom), dynamic backgrounds, and high-fidelity **frosted glass (acrylic) blurring** of the GUI panels via multi-pass Gaussian/Kawase filtering.
- **Interface-Driven Development (IDD):** Enforces a "Stub-and-Resolve" pattern where cross-module dependencies are resolved by generating signatures/contracts before implementation.
+10 -10
View File
@@ -33,16 +33,10 @@ This file tracks all major tracks for the project. Each track has its own detail
*Goal: Integrate Beads (git-backed graph issue tracker) as an alternative backend for MMA implementation tracks and tickets.*
7. [ ] **Track: Optimization pass for Data-Oriented Python heuristics**
*Link: [./tracks/data_oriented_optimization_20260312/](./tracks/data_oriented_optimization_20260312/)*
---
*Link: [./tracks/data_oriented_optimization_20260312/](./tracks/data_oriented_optimization_20260312/)*
- [ ] **Track: Rich Thinking Trace Handling**
*Link: [./tracks/thinking_trace_handling_20260313/](./tracks/thinking_trace_handling_20260313/)*
---
- [ ] **Track: Advanced Text Viewer with Syntax Highlighting**
*Link: [./tracks/text_viewer_rich_rendering_20260313/](./tracks/text_viewer_rich_rendering_20260313/)*
8. [ ] **Track: Rich Thinking Trace Handling**
*Link: [./tracks/thinking_trace_handling_20260313/](./tracks/thinking_trace_handling_20260313/)*
---
@@ -66,7 +60,7 @@ This file tracks all major tracks for the project. Each track has its own detail
5. [x] **Track: NERV UI Theme Integration** (Archived 2026-03-09)
6. [ ] **Track: Custom Shader and Window Frame Support**
6. [x] **Track: Custom Shader and Window Frame Support**
*Link: [./tracks/custom_shaders_20260309/](./tracks/custom_shaders_20260309/)*
7. [x] **Track: UI/UX Improvements - Presets and AI Settings**
@@ -85,6 +79,12 @@ This file tracks all major tracks for the project. Each track has its own detail
*Link: [./tracks/undo_redo_history_20260311/](./tracks/undo_redo_history_20260311/)*
*Goal: Robust, non-provider based undo/redo for text inputs, UI controls, discussion mutations, and context management. Includes hotkey support and a history list view.*
11. [ ] **Track: Advanced Text Viewer with Syntax Highlighting**
*Link: [./tracks/text_viewer_rich_rendering_20260313/](./tracks/text_viewer_rich_rendering_20260313/)*
12. [x] **Track: Frosted Glass Background Effect**
*Link: [./tracks/frosted_glass_20260313/](./tracks/frosted_glass_20260313/)*
---
### Additional Language Support
@@ -0,0 +1,5 @@
# Track frosted_glass_20260313 Context
- [Specification](./spec.md)
- [Implementation Plan](./plan.md)
- [Metadata](./metadata.json)
@@ -0,0 +1,8 @@
{
"track_id": "frosted_glass_20260313",
"type": "feature",
"status": "new",
"created_at": "2026-03-13T14:39:00Z",
"updated_at": "2026-03-13T14:39:00Z",
"description": "Add 'frosted glass' bg for transparency on panels and popups. This blurring effect will allow drop downs and other elements of these panels to not get hard to discern from background text or elements behind the panel."
}
@@ -0,0 +1,26 @@
# Implementation Plan: Frosted Glass Background Effect
## Phase 1: Shader Development & Integration [checkpoint: 55f3bd8]
- [x] Task: Audit `src/shader_manager.py` to identify existing background/post-process integration points. [1328bc1]
- [x] Task: Write Tests: Verify `ShaderManager` can compile and bind a multi-pass blur shader. [1328bc1]
- [x] Task: Implement: Add `FrostedGlassShader` (GLSL) to `src/shader_manager.py`. [1328bc1]
- [x] Task: Implement: Integrate the blur shader into the `ShaderManager` lifecycle. [1328bc1]
- [x] Task: Conductor - User Manual Verification 'Phase 1: Shader Development & Integration' (Protocol in workflow.md) [55f3bd8]
## Phase 2: Framebuffer Capture Pipeline [checkpoint: e9b7875]
- [x] Task: Write Tests: Verify the FBO capture mechanism correctly samples the back buffer and stores it in a texture. [f297e7a]
- [x] Task: Implement: Update `src/shader_manager.py` or `src/gui_2.py` to handle "pre-rendering" of the background into a texture for blurring. [f297e7a]
- [x] Task: Implement: Ensure the blurred texture is updated every frame or on window move events. [f297e7a]
- [x] Task: Conductor - User Manual Verification 'Phase 2: Framebuffer Capture Pipeline' (Protocol in workflow.md) [e9b7875]
## Phase 3: GUI Integration & Rendering [checkpoint: cecbe22]
- [x] Task: Write Tests: Verify that a mocked ImGui window successfully calls the frosted glass rendering logic. [cecbe22]
- [x] Task: Implement: Create a `_render_frosted_background(self, pos, size)` helper in `src/gui_2.py`. [cecbe22]
- [x] Task: Implement: Update panel rendering loops (e.g. `_gui_func`) to inject the frosted background before calling `imgui.begin()` for major panels. [cecbe22]
- [x] Task: Conductor - User Manual Verification 'Phase 3: GUI Integration & Rendering' (Protocol in workflow.md) [cecbe22]
## Phase 4: UI Controls & Configuration [checkpoint: cecbe22]
- [x] Task: Write Tests: Verify that modifying blur uniforms via the Live Editor updates the shader state. [cecbe22]
- [x] Task: Implement: Add "Frosted Glass" sliders (Blur, Tint, Opacity) to the **Shader Editor** in `src/gui_2.py`. [cecbe22]
- [x] Task: Implement: Update `src/theme.py` to parse and store frosted glass settings from `config.toml`. [cecbe22]
- [x] Task: Conductor - User Manual Verification 'Phase 4: UI Controls & Configuration' (Protocol in workflow.md) [cecbe22]
@@ -0,0 +1,34 @@
# Specification: Frosted Glass Background Effect
## Overview
Implement a high-fidelity "frosted glass" (acrylic) background effect for all GUI panels and popups within the Manual Slop interface. This effect will use a GPU-resident shader to blur the content behind active windows, improving readability and visual depth while preventing background text from clashing with foreground UI elements.
## Functional Requirements
- **GPU-Accelerated Blur:**
- Implement a GLSL fragment shader (e.g., Gaussian or Kawase blur) within the existing `ShaderManager` pipeline.
- The shader must sample the current frame buffer background and render a blurred version behind the active window's background.
- **Global Integration:**
- The effect must automatically apply to all standard ImGui panels and popups.
- Integrate with `imgui.begin()` and `imgui.begin_popup()` (or via a reusable wrapper helper).
- **Real-Time Tuning:**
- Add controls to the **Live Shader Editor** to adjust the following parameters:
- **Blur Radius:** Control the intensity of the Gaussian blur.
- **Tint Intensity:** Control the strength of the "frost" overlay color.
- **Base Opacity:** Control the overall transparency of the frosted layer.
- **Persistence:**
- Save frosted glass parameters to `config.toml` under the `theme` or `shader` section.
## Technical Implementation
- **Shader Pipeline:** Use `PyOpenGL` to manage a dedicated background texture/FBO for sampling.
- **Coordinate Mapping:** Ensure the blur shader correctly maps screen coordinates to the region behind the current ImGui window.
- **State Integration:** Store tuning parameters in `App.shader_uniforms` and ensure they are updated every frame.
## Acceptance Criteria
- [ ] Panels and popups have a distinct, blurred background that clearly separates them from the content behind them.
- [ ] Changing the "Blur Radius" slider in the Shader Editor immediately updates the visual frostiness.
- [ ] The effect remains stable during window dragging and resizing.
- [ ] No significant performance degradation (maintaining target FPS).
## Out of Scope
- Implementing different blur types (e.g., motion blur, radial blur).
- Per-panel unique blur settings (initially global only).
+13 -9
View File
@@ -23,15 +23,15 @@ active = "C:/projects/gencpp/gencpp_sloppy.toml"
separate_message_panel = false
separate_response_panel = false
separate_tool_calls_panel = false
bg_shader_enabled = true
bg_shader_enabled = false
crt_filter_enabled = false
separate_task_dag = false
separate_usage_analytics = false
separate_usage_analytics = true
separate_tier1 = false
separate_tier2 = false
separate_tier3 = false
separate_tier4 = false
separate_external_tools = false
separate_external_tools = true
[gui.show_windows]
"Context Hub" = true
@@ -39,7 +39,7 @@ separate_external_tools = false
"AI Settings" = true
"MMA Dashboard" = true
"Task DAG" = false
"Usage Analytics" = false
"Usage Analytics" = true
"Tier 1" = false
"Tier 2" = false
"Tier 3" = false
@@ -52,20 +52,24 @@ separate_external_tools = false
"Operations Hub" = true
Message = false
Response = true
"Tool Calls" = false
"Tool Calls" = true
Theme = true
"Log Management" = true
Diagnostics = false
"External Tools" = false
"Shader Editor" = false
"Shader Editor" = true
[theme]
palette = "Nord Dark"
font_path = "C:/projects/manual_slop/assets/fonts/MapleMono-Regular.ttf"
font_size = 18.0
font_size = 16.0
scale = 1.0
transparency = 0.5400000214576721
child_transparency = 0.5899999737739563
transparency = 0.699999988079071
child_transparency = 0.6899999976158142
frosted_blur_radius = 29.68400001525879
frosted_tint_intensity = 0.5659999847412109
frosted_opacity = 0.5389999747276306
frosted_glass_enabled = true
[mma]
max_workers = 4
+57 -58
View File
@@ -12,7 +12,7 @@ ViewportPos=43,95
ViewportId=0x78C57832
Size=897,649
Collapsed=0
DockId=0x00000001,0
DockId=0x00000005,0
[Window][Files]
ViewportPos=3125,170
@@ -33,7 +33,7 @@ DockId=0x0000000A,0
Pos=0,17
Size=1680,730
Collapsed=0
DockId=0x00000001,0
DockId=0x00000005,0
[Window][Provider]
ViewportPos=43,95
@@ -41,10 +41,10 @@ ViewportId=0x78C57832
Pos=0,651
Size=897,468
Collapsed=0
DockId=0x00000001,0
DockId=0x00000005,0
[Window][Message]
Pos=661,1426
Pos=661,1321
Size=716,455
Collapsed=0
@@ -54,10 +54,9 @@ Size=1111,773
Collapsed=0
[Window][Tool Calls]
Pos=520,1144
Size=663,232
Pos=1039,464
Size=587,510
Collapsed=0
DockId=0x00000006,0
[Window][Comms History]
ViewportPos=43,95
@@ -74,10 +73,10 @@ Collapsed=0
DockId=0xAFC85805,2
[Window][Theme]
Pos=0,703
Size=630,737
Pos=2671,24
Size=1169,2136
Collapsed=0
DockId=0x00000002,2
DockId=0x00000002,1
[Window][Text Viewer - Entry #7]
Pos=379,324
@@ -88,13 +87,13 @@ Collapsed=0
Pos=1649,24
Size=580,1284
Collapsed=0
DockId=0x00000010,2
DockId=0x00000004,2
[Window][Context Hub]
Pos=0,703
Size=630,737
Pos=0,1719
Size=999,441
Collapsed=0
DockId=0x00000002,1
DockId=0x00000006,0
[Window][AI Settings Hub]
Pos=406,17
@@ -103,28 +102,28 @@ Collapsed=0
DockId=0x0000000D,0
[Window][Discussion Hub]
Pos=1263,22
Size=709,1418
Pos=1762,24
Size=907,2136
Collapsed=0
DockId=0x00000013,0
DockId=0x00000011,0
[Window][Operations Hub]
Pos=632,22
Size=629,1418
Pos=1001,24
Size=759,2136
Collapsed=0
DockId=0x00000005,0
DockId=0x00000010,0
[Window][Files & Media]
Pos=0,703
Size=630,737
Pos=0,1719
Size=999,441
Collapsed=0
DockId=0x00000002,0
DockId=0x00000006,1
[Window][AI Settings]
Pos=0,22
Size=630,679
Pos=0,24
Size=999,1693
Collapsed=0
DockId=0x00000001,0
DockId=0x00000005,0
[Window][Approve Tool Execution]
Pos=3,524
@@ -132,16 +131,16 @@ Size=416,325
Collapsed=0
[Window][MMA Dashboard]
Pos=1974,22
Size=586,1418
Pos=2671,24
Size=1169,2136
Collapsed=0
DockId=0x00000010,0
DockId=0x00000002,0
[Window][Log Management]
Pos=1974,22
Size=586,1418
Pos=1931,24
Size=629,1416
Collapsed=0
DockId=0x00000010,1
DockId=0x00000002,1
[Window][Track Proposal]
Pos=709,326
@@ -167,7 +166,7 @@ Collapsed=0
Pos=2822,1717
Size=1018,420
Collapsed=0
DockId=0x0000000C,0
DockId=0x00000004,0
[Window][Approve PowerShell Command]
Pos=649,435
@@ -210,7 +209,7 @@ Size=3840,32
Collapsed=0
[Window][Text Viewer - message]
Pos=568,1226
Pos=562,588
Size=900,700
Collapsed=0
@@ -220,7 +219,7 @@ Size=900,700
Collapsed=0
[Window][Text Viewer - text]
Pos=60,60
Pos=555,644
Size=900,700
Collapsed=0
@@ -240,7 +239,7 @@ Size=900,700
Collapsed=0
[Window][Text Viewer - tool_calls]
Pos=60,60
Pos=589,490
Size=900,700
Collapsed=0
@@ -330,10 +329,9 @@ Size=967,499
Collapsed=0
[Window][Usage Analytics]
Pos=1739,1107
Size=586,269
Pos=1702,689
Size=566,438
Collapsed=0
DockId=0x0000000F,0
[Window][Tool Preset Manager]
Pos=1301,302
@@ -366,7 +364,7 @@ Size=900,700
Collapsed=0
[Window][Text Viewer - Entry #4]
Pos=1127,922
Pos=828,397
Size=900,700
Collapsed=0
@@ -381,8 +379,13 @@ Size=900,700
Collapsed=0
[Window][Shader Editor]
Pos=457,710
Size=493,252
Pos=753,637
Size=493,487
Collapsed=0
[Window][Text Viewer - list_directory]
Pos=60,60
Size=900,700
Collapsed=0
[Table][0xFB6E3870,4]
@@ -442,7 +445,7 @@ Column 3 Width=120
RefScale=16
Column 0 Width=48
Column 1 Weight=1.0000
Column 2 Width=118
Column 2 Width=117
Column 3 Width=48
[Table][0xD99F45C5,4]
@@ -498,23 +501,19 @@ Column 1 Weight=1.0000
DockNode ID=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,22 Size=2560,1418 Split=X
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=1640,1183 Split=X
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=3840,2136 Split=X
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=1617,1183 Split=X
DockNode ID=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2
DockNode ID=0x00000007 Parent=0x0000000B SizeRef=630,858 Split=Y Selected=0x8CA2375C
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=824,525 CentralNode=1 Selected=0x7BD57D6A
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=824,737 Selected=0x8CA2375C
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1340,858 Split=X Selected=0x418C7449
DockNode ID=0x00000012 Parent=0x0000000E SizeRef=629,402 Split=Y Selected=0x418C7449
DockNode ID=0x00000005 Parent=0x00000012 SizeRef=876,1749 Selected=0x418C7449
DockNode ID=0x00000006 Parent=0x00000012 SizeRef=876,362 Selected=0x1D56B311
DockNode ID=0x00000013 Parent=0x0000000E SizeRef=709,402 Selected=0x6F2B5B04
DockNode ID=0x00000007 Parent=0x0000000B SizeRef=999,858 Split=Y Selected=0x7BD57D6A
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=639,904 CentralNode=1 Selected=0x7BD57D6A
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=639,441 Selected=0x1DCB2623
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=2839,858 Split=X Selected=0x418C7449
DockNode ID=0x00000001 Parent=0x0000000E SizeRef=1668,1288 Split=X Selected=0x6F2B5B04
DockNode ID=0x00000010 Parent=0x00000001 SizeRef=759,1416 Selected=0x418C7449
DockNode ID=0x00000011 Parent=0x00000001 SizeRef=907,1416 Selected=0x6F2B5B04
DockNode ID=0x00000002 Parent=0x0000000E SizeRef=1169,1288 Selected=0x8CA2375C
DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=586,1183 Split=Y Selected=0x3AEC3498
DockNode ID=0x00000010 Parent=0x00000004 SizeRef=1199,1689 Selected=0x2C0206CE
DockNode ID=0x00000011 Parent=0x00000004 SizeRef=1199,420 Split=X Selected=0xDEB547B6
DockNode ID=0x0000000C Parent=0x00000011 SizeRef=916,380 Selected=0x655BC6E9
DockNode ID=0x0000000F Parent=0x00000011 SizeRef=281,380 Selected=0xDEB547B6
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=511,1183 Selected=0x3AEC3498
;;;<<<Layout_655921752_Default>>>;;;
;;;<<<HelloImGui_Misc>>>;;;
+193 -83
View File
@@ -21,6 +21,7 @@ from src import theme_2 as theme
from src import theme_nerv_fx as theme_fx
from src import api_hooks
import numpy as np
import OpenGL.GL as gl
from src import log_registry
from src import log_pruner
from src import models
@@ -28,6 +29,7 @@ from src import app_controller
from src import mcp_client
from src import markdown_helper
from src import bg_shader
from src.shader_manager import ShaderManager
import re
import subprocess
if sys.platform == "win32":
@@ -211,7 +213,17 @@ class App:
self._nerv_flicker = theme_fx.StatusFlicker()
self.ui_tool_filter_category = "All"
self.ui_discussion_split_h = 300.0
self.shader_uniforms = {'crt': 1.0, 'scanline': 0.5, 'bloom': 0.8}
self.shader_uniforms = {
'crt': 1.0,
'scanline': 0.5,
'bloom': 0.8,
'frosted_blur_radius': theme.get_frosted_blur_radius(),
'frosted_tint_intensity': theme.get_frosted_tint_intensity(),
'frosted_opacity': theme.get_frosted_opacity()
}
self.shader_manager = ShaderManager()
self.start_time = time.time()
def _handle_approve_tool(self, user_data=None) -> None:
"""UI-level wrapper for approving a pending tool execution ask."""
@@ -341,6 +353,111 @@ class App:
imgui.pop_style_var(2)
imgui.pop_id()
def _render_frosted_background(self, pos: imgui.ImVec2, size: imgui.ImVec2) -> None:
if not theme.get_frosted_glass_enabled(): return
if size.x <= 0 or size.y <= 0: return
try:
ws = immapp.get_window_size()
display_size = imgui.ImVec2(float(ws[0]), float(ws[1]))
except Exception:
display_size = imgui.get_io().display_size
if display_size.x <= 0 or display_size.y <= 0: return
uv_min = imgui.ImVec2(pos.x / display_size.x, 1.0 - pos.y / display_size.y)
uv_max = imgui.ImVec2((pos.x + size.x) / display_size.x, 1.0 - (pos.y + size.y) / display_size.y)
if self.shader_manager.blur_tex:
imgui.get_window_draw_list().add_image(
imgui.ImTextureRef(self.shader_manager.blur_tex),
pos,
imgui.ImVec2(pos.x + size.x, pos.y + size.y),
uv_min,
uv_max
)
else:
# Fallback: semi-transparent rectangle to help debug visibility
imgui.get_window_draw_list().add_rect_filled(
pos,
imgui.ImVec2(pos.x + size.x, pos.y + size.y),
imgui.get_color_u32(vec4(30, 30, 40, 0.7))
)
# Increase contrast with a 0.2 alpha tint overlay
imgui.get_window_draw_list().add_rect_filled(
pos,
imgui.ImVec2(pos.x + size.x, pos.y + size.y),
imgui.get_color_u32(vec4(0, 0, 0, 0.2))
)
def _begin_window(self, name: str, p_open: Any = None, flags: int = 0) -> tuple[bool, Any]:
frosted = theme.get_frosted_glass_enabled()
if frosted:
imgui.push_style_color(imgui.Col_.window_bg, vec4(0, 0, 0, 0))
expanded, opened = imgui.begin(name, p_open, flags)
if expanded and frosted:
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
return expanded, opened
def _end_window(self) -> None:
imgui.end()
if theme.get_frosted_glass_enabled():
imgui.pop_style_color()
def _render_operations_hub(self) -> None:
exp, opened = self._begin_window("Operations Hub", self.show_windows["Operations Hub"])
self.show_windows["Operations Hub"] = bool(opened)
if exp:
imgui.text("Focus Agent:")
imgui.same_line()
focus_label = self.ui_focus_agent or "All"
if imgui.begin_combo("##focus_agent", focus_label, imgui.ComboFlags_.width_fit_preview):
if imgui.selectable("All", self.ui_focus_agent is None)[0]:
self.ui_focus_agent = None
for tier in ["Tier 2", "Tier 3", "Tier 4"]:
if imgui.selectable(tier, self.ui_focus_agent == tier)[0]:
self.ui_focus_agent = tier
imgui.end_combo()
imgui.same_line()
if self.ui_focus_agent:
if imgui.button("x##clear_focus"):
self.ui_focus_agent = None
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
ch1, self.ui_separate_tool_calls_panel = imgui.checkbox("Pop Out Tool Calls", self.ui_separate_tool_calls_panel)
if ch1: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel
imgui.same_line()
ch2, self.ui_separate_usage_analytics = imgui.checkbox("Pop Out Usage Analytics", self.ui_separate_usage_analytics)
if ch2: self.show_windows["Usage Analytics"] = self.ui_separate_usage_analytics
imgui.same_line()
ch3, self.ui_separate_external_tools = imgui.checkbox('Pop Out External Tools', self.ui_separate_external_tools)
if ch3: self.show_windows['External Tools'] = self.ui_separate_external_tools
imgui.pop_style_var()
show_tc_tab = not self.ui_separate_tool_calls_panel
show_usage_tab = not self.ui_separate_usage_analytics
if imgui.begin_tab_bar("ops_tabs"):
if imgui.begin_tab_item("Comms History")[0]:
self._render_comms_history_panel()
imgui.end_tab_item()
if show_tc_tab:
if imgui.begin_tab_item("Tool Calls")[0]:
self._render_tool_calls_panel()
imgui.end_tab_item()
if show_usage_tab:
if imgui.begin_tab_item("Usage Analytics")[0]:
self._render_usage_analytics_panel()
imgui.end_tab_item()
if not self.ui_separate_external_tools:
if imgui.begin_tab_item("External Tools")[0]:
self._render_external_tools_panel()
imgui.end_tab_item()
imgui.end_tab_bar()
self._end_window()
def _show_menus(self) -> None:
if imgui.begin_menu("manual slop"):
if imgui.menu_item("Quit", "Ctrl+Q", False)[0]:
@@ -440,24 +557,55 @@ class App:
changed_crt, self.shader_uniforms['crt'] = imgui.slider_float('CRT Curvature', self.shader_uniforms['crt'], 0.0, 2.0)
changed_scan, self.shader_uniforms['scanline'] = imgui.slider_float('Scanline Intensity', self.shader_uniforms['scanline'], 0.0, 1.0)
changed_bloom, self.shader_uniforms['bloom'] = imgui.slider_float('Bloom Threshold', self.shader_uniforms['bloom'], 0.0, 1.0)
imgui.separator()
imgui.text("Frosted Glass")
changed_fbr, self.shader_uniforms['frosted_blur_radius'] = imgui.slider_float('Blur Radius', self.shader_uniforms['frosted_blur_radius'], 0.0, 64.0)
if changed_fbr: theme.set_frosted_blur_radius(self.shader_uniforms['frosted_blur_radius'])
changed_fti, self.shader_uniforms['frosted_tint_intensity'] = imgui.slider_float('Tint Intensity', self.shader_uniforms['frosted_tint_intensity'], 0.0, 1.0)
if changed_fti: theme.set_frosted_tint_intensity(self.shader_uniforms['frosted_tint_intensity'])
changed_fo, self.shader_uniforms['frosted_opacity'] = imgui.slider_float('Frosted Opacity', self.shader_uniforms['frosted_opacity'], 0.0, 1.0)
if changed_fo: theme.set_frosted_opacity(self.shader_uniforms['frosted_opacity'])
imgui.end()
def _gui_func(self) -> None:
self._render_custom_title_bar()
self._render_shader_live_editor()
pushed_prior_tint = False
pushed_frosted_style = False
# Render background shader
bg = bg_shader.get_bg()
if bg.enabled:
frosted_enabled = theme.get_frosted_glass_enabled()
if bg.enabled and not frosted_enabled:
ws = imgui.get_io().display_size
bg.render(ws.x, ws.y)
if frosted_enabled:
ws = imgui.get_io().display_size
self.shader_manager.prepare_global_blur(
int(ws.x), int(ws.y),
self.shader_uniforms['frosted_blur_radius'],
self.shader_uniforms['frosted_tint_intensity'],
self.shader_uniforms['frosted_opacity'],
time.time()
)
# Draw background texture
dl = imgui.get_background_draw_list()
dl.add_image(imgui.ImTextureRef(self.shader_manager.scene_tex), (0, 0), (ws.x, ws.y))
imgui.push_style_color(imgui.Col_.window_bg, vec4(0, 0, 0, 0))
pushed_frosted_style = True
if theme.is_nerv_active():
ws = imgui.get_io().display_size
self._nerv_alert.update(self.ai_status)
self._nerv_alert.render(ws.x, ws.y)
self._nerv_crt.enabled = self.ui_crt_filter
self._nerv_crt.render(ws.x, ws.y)
if self.perf_profiling_enabled: self.perf_monitor.start_component("_gui_func")
if self.is_viewing_prior_session:
imgui.push_style_color(imgui.Col_.window_bg, vec4(50, 40, 20))
@@ -530,7 +678,7 @@ class App:
self._tool_log_dirty = False
if self.show_windows.get("Context Hub", False):
exp, opened = imgui.begin("Context Hub", self.show_windows["Context Hub"])
exp, opened = self._begin_window("Context Hub", self.show_windows["Context Hub"])
self.show_windows["Context Hub"] = bool(opened)
if exp:
if imgui.begin_tab_bar('context_hub_tabs'):
@@ -541,18 +689,18 @@ class App:
self._render_paths_panel()
imgui.end_tab_item()
imgui.end_tab_bar()
imgui.end()
self._end_window()
if self.show_windows.get("Files & Media", False):
exp, opened = imgui.begin("Files & Media", self.show_windows["Files & Media"])
exp, opened = self._begin_window("Files & Media", self.show_windows["Files & Media"])
self.show_windows["Files & Media"] = bool(opened)
if exp:
if imgui.collapsing_header("Files"):
self._render_files_panel()
if imgui.collapsing_header("Screenshots"):
self._render_screenshots_panel()
imgui.end()
self._end_window()
if self.show_windows.get("AI Settings", False):
exp, opened = imgui.begin("AI Settings", self.show_windows["AI Settings"])
exp, opened = self._begin_window("AI Settings", self.show_windows["AI Settings"])
self.show_windows["AI Settings"] = bool(opened)
if exp:
self._render_persona_selector_panel()
@@ -562,56 +710,56 @@ class App:
self._render_system_prompts_panel()
self._render_agent_tools_panel()
imgui.end()
self._end_window()
if self.ui_separate_usage_analytics and self.show_windows.get("Usage Analytics", False):
exp, opened = imgui.begin("Usage Analytics", self.show_windows["Usage Analytics"])
exp, opened = self._begin_window("Usage Analytics", self.show_windows["Usage Analytics"])
self.show_windows["Usage Analytics"] = bool(opened)
if exp:
self._render_usage_analytics_panel()
imgui.end()
self._end_window()
if self.show_windows.get("MMA Dashboard", False):
exp, opened = imgui.begin("MMA Dashboard", self.show_windows["MMA Dashboard"])
exp, opened = self._begin_window("MMA Dashboard", self.show_windows["MMA Dashboard"])
self.show_windows["MMA Dashboard"] = bool(opened)
if exp:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_mma_dashboard")
self._render_mma_dashboard()
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_mma_dashboard")
imgui.end()
self._end_window()
if self.ui_separate_task_dag and self.show_windows.get("Task DAG", False):
exp, opened = imgui.begin("Task DAG", self.show_windows["Task DAG"])
exp, opened = self._begin_window("Task DAG", self.show_windows["Task DAG"])
self.show_windows["Task DAG"] = bool(opened)
if exp:
self._render_task_dag_panel()
imgui.end()
self._end_window()
if self.ui_separate_tier1 and self.show_windows.get("Tier 1: Strategy", False):
exp, opened = imgui.begin("Tier 1: Strategy", self.show_windows["Tier 1: Strategy"])
exp, opened = self._begin_window("Tier 1: Strategy", self.show_windows["Tier 1: Strategy"])
self.show_windows["Tier 1: Strategy"] = bool(opened)
if exp:
self._render_tier_stream_panel("Tier 1", "Tier 1")
imgui.end()
self._end_window()
if self.ui_separate_tier2 and self.show_windows.get("Tier 2: Tech Lead", False):
exp, opened = imgui.begin("Tier 2: Tech Lead", self.show_windows["Tier 2: Tech Lead"])
exp, opened = self._begin_window("Tier 2: Tech Lead", self.show_windows["Tier 2: Tech Lead"])
self.show_windows["Tier 2: Tech Lead"] = bool(opened)
if exp:
self._render_tier_stream_panel("Tier 2", "Tier 2 (Tech Lead)")
imgui.end()
self._end_window()
if self.ui_separate_tier3 and self.show_windows.get("Tier 3: Workers", False):
exp, opened = imgui.begin("Tier 3: Workers", self.show_windows["Tier 3: Workers"])
exp, opened = self._begin_window("Tier 3: Workers", self.show_windows["Tier 3: Workers"])
self.show_windows["Tier 3: Workers"] = bool(opened)
if exp:
self._render_tier_stream_panel("Tier 3", None)
imgui.end()
self._end_window()
if self.ui_separate_tier4 and self.show_windows.get("Tier 4: QA", False):
exp, opened = imgui.begin("Tier 4: QA", self.show_windows["Tier 4: QA"])
exp, opened = self._begin_window("Tier 4: QA", self.show_windows["Tier 4: QA"])
self.show_windows["Tier 4: QA"] = bool(opened)
if exp:
self._render_tier_stream_panel("Tier 4", "Tier 4 (QA)")
imgui.end()
self._end_window()
if self.show_windows.get("Theme", False):
self._render_theme_panel()
if self.show_windows.get("Discussion Hub", False):
exp, opened = imgui.begin("Discussion Hub", self.show_windows["Discussion Hub"])
exp, opened = self._begin_window("Discussion Hub", self.show_windows["Discussion Hub"])
self.show_windows["Discussion Hub"] = bool(opened)
if exp:
# Top part for the history
@@ -658,86 +806,37 @@ class App:
else:
imgui.text_disabled("Message & Response panels are detached.")
imgui.end()
self._end_window()
if self.show_windows.get("Operations Hub", False):
exp, opened = imgui.begin("Operations Hub", self.show_windows["Operations Hub"])
self.show_windows["Operations Hub"] = bool(opened)
if exp:
imgui.text("Focus Agent:")
imgui.same_line()
focus_label = self.ui_focus_agent or "All"
if imgui.begin_combo("##focus_agent", focus_label, imgui.ComboFlags_.width_fit_preview):
if imgui.selectable("All", self.ui_focus_agent is None)[0]:
self.ui_focus_agent = None
for tier in ["Tier 2", "Tier 3", "Tier 4"]:
if imgui.selectable(tier, self.ui_focus_agent == tier)[0]:
self.ui_focus_agent = tier
imgui.end_combo()
imgui.same_line()
if self.ui_focus_agent:
if imgui.button("x##clear_focus"):
self.ui_focus_agent = None
if exp:
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
ch1, self.ui_separate_tool_calls_panel = imgui.checkbox("Pop Out Tool Calls", self.ui_separate_tool_calls_panel)
if ch1: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel
imgui.same_line()
ch2, self.ui_separate_usage_analytics = imgui.checkbox("Pop Out Usage Analytics", self.ui_separate_usage_analytics)
if ch2: self.show_windows["Usage Analytics"] = self.ui_separate_usage_analytics
imgui.same_line()
ch3, self.ui_separate_external_tools = imgui.checkbox('Pop Out External Tools', self.ui_separate_external_tools)
if ch3: self.show_windows['External Tools'] = self.ui_separate_external_tools
imgui.pop_style_var()
show_tc_tab = not self.ui_separate_tool_calls_panel
show_usage_tab = not self.ui_separate_usage_analytics
if imgui.begin_tab_bar("ops_tabs"):
if imgui.begin_tab_item("Comms History")[0]:
self._render_comms_history_panel()
imgui.end_tab_item()
if show_tc_tab:
if imgui.begin_tab_item("Tool Calls")[0]:
self._render_tool_calls_panel()
imgui.end_tab_item()
if show_usage_tab:
if imgui.begin_tab_item("Usage Analytics")[0]:
self._render_usage_analytics_panel()
imgui.end_tab_item()
if not self.ui_separate_external_tools:
if imgui.begin_tab_item("External Tools")[0]:
self._render_external_tools_panel()
imgui.end_tab_item()
imgui.end_tab_bar()
imgui.end()
self._render_operations_hub()
if self.ui_separate_message_panel and self.show_windows.get("Message", False):
exp, opened = imgui.begin("Message", self.show_windows["Message"])
exp, opened = self._begin_window("Message", self.show_windows["Message"])
self.show_windows["Message"] = bool(opened)
if exp:
self._render_message_panel()
imgui.end()
self._end_window()
if self.ui_separate_response_panel and self.show_windows.get("Response", False):
exp, opened = imgui.begin("Response", self.show_windows["Response"])
exp, opened = self._begin_window("Response", self.show_windows["Response"])
self.show_windows["Response"] = bool(opened)
if exp:
self._render_response_panel()
imgui.end()
self._end_window()
if self.ui_separate_tool_calls_panel and self.show_windows.get("Tool Calls", False):
exp, opened = imgui.begin("Tool Calls", self.show_windows["Tool Calls"])
exp, opened = self._begin_window("Tool Calls", self.show_windows["Tool Calls"])
self.show_windows["Tool Calls"] = bool(opened)
if exp:
self._render_tool_calls_panel()
imgui.end()
self._end_window()
if self.ui_separate_external_tools and self.show_windows.get('External Tools', False):
exp, opened = imgui.begin('External Tools', self.show_windows['External Tools'])
exp, opened = self._begin_window('External Tools', self.show_windows['External Tools'])
self.show_windows['External Tools'] = bool(opened)
if exp:
self._render_external_tools_panel()
imgui.end()
self._end_window()
if self.show_windows.get("Log Management", False):
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_log_management")
@@ -928,6 +1027,7 @@ class App:
expanded, opened = imgui.begin("Last Script Output", self.show_script_output)
self.show_script_output = bool(opened)
if expanded:
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
imgui.text("Script:")
imgui.same_line()
self._render_text_viewer("Last Script", self.ui_last_script_text)
@@ -959,6 +1059,7 @@ class App:
expanded, opened = imgui.begin(f"Text Viewer - {self.text_viewer_title}", self.show_text_viewer)
self.show_text_viewer = bool(opened)
if expanded:
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
if self.ui_word_wrap:
imgui.begin_child("tv_wrap", imgui.ImVec2(-1, -1), False)
imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
@@ -1018,6 +1119,8 @@ class App:
if pushed_prior_tint:
imgui.pop_style_color()
if pushed_frosted_style:
imgui.pop_style_color()
if self.perf_profiling_enabled: self.perf_monitor.end_component("_gui_func")
@@ -1904,7 +2007,6 @@ class App:
imgui.text(f"History: {key}")
hist_data = self.perf_monitor.get_history(key)
if hist_data:
import numpy as np
imgui.plot_lines(f"##plot_{key}", np.array(hist_data, dtype=np.float32), graph_size=imgui.ImVec2(-1, 60))
else:
imgui.text_disabled(f"(no history data for {key})")
@@ -2052,6 +2154,7 @@ def hello():
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_screenshots_panel")
def _render_discussion_panel(self) -> None:
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel")
# THINKING indicator
is_thinking = self.ai_status in ['sending...', 'streaming...', 'running powershell...']
@@ -3875,6 +3978,7 @@ def hello():
exp, opened = imgui.begin("Theme", self.show_windows["Theme"])
self.show_windows["Theme"] = bool(opened)
if exp:
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
imgui.text("Palette")
cp = theme.get_current_palette()
if imgui.begin_combo("##pal", cp):
@@ -3948,6 +4052,10 @@ def hello():
gui_cfg["crt_filter_enabled"] = self.ui_crt_filter
self._flush_to_config()
models.save_config(self.config)
ch_fg, fg_val = imgui.checkbox("Frosted Glass Effect", theme.get_frosted_glass_enabled())
if ch_fg:
theme.set_frosted_glass_enabled(fg_val)
self._flush_to_config()
models.save_config(self.config)
imgui.end()
@@ -3993,6 +4101,8 @@ def hello():
def _post_init(self) -> None:
theme.apply_current()
self.shader_manager.setup_background_shader()
self.shader_manager.setup_frosted_glass_shader()
def run(self) -> None:
"""Initializes the ImGui runner and starts the main application loop."""
+252 -1
View File
@@ -5,6 +5,113 @@ class ShaderManager:
self.program = None
self.bg_program = None
self.pp_program = None
self.blur_h_program = None
self.blur_v_program = None
self.blur_fbo = None
self.scene_fbo = None
self.temp_fbo = None
self.scene_tex = None
self.blur_tex = None
self.temp_tex = None
self.fbo_width = 0
self.fbo_height = 0
self._vao = None
def _ensure_vao(self):
if self._vao is None:
try:
import sys
if sys.platform == "win32":
self._vao = gl.glGenVertexArrays(1)
else:
# Some non-win32 environments might not support VAOs or need different handling
self._vao = gl.glGenVertexArrays(1)
except Exception:
pass
if self._vao is not None:
gl.glBindVertexArray(self._vao)
def setup_capture_fbo(self, width, height):
if self.blur_fbo is not None:
gl.glDeleteFramebuffers(1, [self.blur_fbo])
if self.scene_fbo is not None:
gl.glDeleteFramebuffers(1, [self.scene_fbo])
if self.temp_fbo is not None:
gl.glDeleteFramebuffers(1, [self.temp_fbo])
if self.scene_tex is not None:
gl.glDeleteTextures(1, [self.scene_tex])
if self.blur_tex is not None:
gl.glDeleteTextures(1, [self.blur_tex])
if self.temp_tex is not None:
gl.glDeleteTextures(1, [self.temp_tex])
self.scene_tex = gl.glGenTextures(1)
gl.glBindTexture(gl.GL_TEXTURE_2D, self.scene_tex)
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE)
self.scene_fbo = gl.glGenFramebuffers(1)
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.scene_fbo)
gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, self.scene_tex, 0)
if gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER) != gl.GL_FRAMEBUFFER_COMPLETE:
raise RuntimeError("Scene Framebuffer not complete")
self.temp_tex = gl.glGenTextures(1)
gl.glBindTexture(gl.GL_TEXTURE_2D, self.temp_tex)
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE)
self.temp_fbo = gl.glGenFramebuffers(1)
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.temp_fbo)
gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, self.temp_tex, 0)
if gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER) != gl.GL_FRAMEBUFFER_COMPLETE:
raise RuntimeError("Temp Framebuffer not complete")
self.blur_tex = gl.glGenTextures(1)
gl.glBindTexture(gl.GL_TEXTURE_2D, self.blur_tex)
gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGBA, width, height, 0, gl.GL_RGBA, gl.GL_UNSIGNED_BYTE, None)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MIN_FILTER, gl.GL_LINEAR)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_MAG_FILTER, gl.GL_LINEAR)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_S, gl.GL_CLAMP_TO_EDGE)
gl.glTexParameteri(gl.GL_TEXTURE_2D, gl.GL_TEXTURE_WRAP_T, gl.GL_CLAMP_TO_EDGE)
self.blur_fbo = gl.glGenFramebuffers(1)
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.blur_fbo)
gl.glFramebufferTexture2D(gl.GL_FRAMEBUFFER, gl.GL_COLOR_ATTACHMENT0, gl.GL_TEXTURE_2D, self.blur_tex, 0)
if gl.glCheckFramebufferStatus(gl.GL_FRAMEBUFFER) != gl.GL_FRAMEBUFFER_COMPLETE:
raise RuntimeError("Blur Framebuffer not complete")
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
self.fbo_width = width
self.fbo_height = height
def render_background_to_fbo(self, width, height, time):
if self.scene_fbo is None or self.fbo_width != width or self.fbo_height != height:
self.setup_capture_fbo(width, height)
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.scene_fbo)
gl.glViewport(0, 0, width, height)
self.render_background(width, height, time)
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
def prepare_global_blur(self, width, height, radius, tint, opacity, time):
self.render_background_to_fbo(width, height, time)
self.render_blur(self.scene_tex, width, height, radius, tint, opacity)
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
def capture_begin(self, width, height):
if self.blur_fbo is None or self.fbo_width != width or self.fbo_height != height:
self.setup_capture_fbo(width, height)
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.blur_fbo)
gl.glViewport(0, 0, width, height)
def capture_end(self):
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, 0)
def compile_shader(self, vertex_src: str, fragment_src: str) -> int:
program = gl.glCreateProgram()
@@ -79,9 +186,44 @@ void main() {
uniform float u_time;
uniform vec2 u_resolution;
out vec4 FragColor;
float hash(vec2 p) {
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
}
float noise(vec2 p) {
vec2 i = floor(p);
vec2 f = fract(p);
vec2 u = f * f * (3.0 - 2.0 * f);
return mix(mix(hash(i + vec2(0.0, 0.0)), hash(i + vec2(1.0, 0.0)), u.x),
mix(hash(i + vec2(0.0, 1.0)), hash(i + vec2(1.0, 1.0)), u.x), u.y);
}
void main() {
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
vec3 col = 0.5 + 0.5 * cos(u_time + uv.xyx + vec3(0, 2, 4));
vec2 p = uv * 2.0 - 1.0;
p.x *= u_resolution.x / u_resolution.y;
// Deep sea background gradient (dark blue)
vec3 col = mix(vec3(0.01, 0.03, 0.08), vec3(0.0, 0.08, 0.15), uv.y);
// Moving blobs / caustics
float n = 0.0;
float t = u_time * 0.15;
n += noise(p * 1.2 + vec2(t * 0.8, t * 0.5)) * 0.4;
n += noise(p * 2.5 - vec2(t * 0.4, t * 0.9)) * 0.2;
col += vec3(0.05, 0.12, 0.22) * n;
// Bright highlights (caustics approximation)
float c = 0.0;
for(int i=0; i<3; i++) {
vec2 p2 = p * (float(i) + 1.0) * 0.4;
p2 += vec2(sin(t + p2.y * 1.5), cos(t + p2.x * 1.5));
c += abs(0.015 / (length(p2) - 0.4));
}
col += vec3(0.1, 0.25, 0.45) * c * 0.12;
FragColor = vec4(col, 1.0);
}
"""
@@ -97,6 +239,7 @@ void main() {
u_res_loc = gl.glGetUniformLocation(self.bg_program, "u_resolution")
if u_res_loc != -1:
gl.glUniform2f(u_res_loc, float(width), float(height))
self._ensure_vao()
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
gl.glUseProgram(0)
@@ -148,6 +291,114 @@ void main() {
u_time_loc = gl.glGetUniformLocation(self.pp_program, "u_time")
if u_time_loc != -1:
gl.glUniform1f(u_time_loc, float(time))
self._ensure_vao()
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
gl.glUseProgram(0)
def setup_frosted_glass_shader(self):
vertex_src = """
#version 330 core
const vec2 positions[4] = vec2[](
vec2(-1.0, -1.0),
vec2( 1.0, -1.0),
vec2(-1.0, 1.0),
vec2( 1.0, 1.0)
);
const vec2 uvs[4] = vec2[](
vec2(0.0, 0.0),
vec2(1.0, 0.0),
vec2(0.0, 1.0),
vec2(1.0, 1.0)
);
out vec2 v_uv;
void main() {
gl_Position = vec4(positions[gl_VertexID], 0.0, 1.0);
v_uv = uvs[gl_VertexID];
}
"""
fragment_src_h = """
#version 330 core
in vec2 v_uv;
uniform sampler2D u_texture;
uniform float u_blur_radius;
uniform vec2 u_direction;
out vec4 FragColor;
void main() {
float weight[5] = float[](0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
vec2 res = vec2(textureSize(u_texture, 0));
vec2 tex_offset = (u_blur_radius / res) * u_direction * 2.5; // Multiplied by 2.5 for milky effect
vec4 result = texture(u_texture, v_uv) * weight[0];
for(int i = 1; i < 5; ++i) {
result += texture(u_texture, v_uv + tex_offset * float(i)) * weight[i];
result += texture(u_texture, v_uv - tex_offset * float(i)) * weight[i];
}
FragColor = result;
}
"""
fragment_src_v = """
#version 330 core
in vec2 v_uv;
uniform sampler2D u_texture;
uniform float u_blur_radius;
uniform vec2 u_direction;
uniform float u_tint_intensity;
uniform float u_opacity;
out vec4 FragColor;
void main() {
float weight[5] = float[](0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216);
vec2 res = vec2(textureSize(u_texture, 0));
vec2 tex_offset = (u_blur_radius / res) * u_direction * 2.5; // Multiplied by 2.5 for milky effect
vec4 result = texture(u_texture, v_uv) * weight[0];
for(int i = 1; i < 5; ++i) {
result += texture(u_texture, v_uv + tex_offset * float(i)) * weight[i];
result += texture(u_texture, v_uv - tex_offset * float(i)) * weight[i];
}
vec3 tint_color = vec3(0.05, 0.07, 0.12); // Slightly deeper tint
vec3 tinted = mix(result.rgb, tint_color, u_tint_intensity);
FragColor = vec4(tinted, result.a * u_opacity);
}
"""
self.blur_h_program = self.compile_shader(vertex_src, fragment_src_h)
self.blur_v_program = self.compile_shader(vertex_src, fragment_src_v)
def render_blur(self, texture_id, width, height, radius, tint, opacity):
if not self.blur_h_program or not self.blur_v_program:
return
self._ensure_vao()
# Pass 1: Horizontal blur to temp_fbo
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.temp_fbo)
gl.glViewport(0, 0, width, height)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
gl.glUseProgram(self.blur_h_program)
gl.glActiveTexture(gl.GL_TEXTURE0)
gl.glBindTexture(gl.GL_TEXTURE_2D, texture_id)
gl.glUniform1i(gl.glGetUniformLocation(self.blur_h_program, "u_texture"), 0)
gl.glUniform1f(gl.glGetUniformLocation(self.blur_h_program, "u_blur_radius"), float(radius))
gl.glUniform2f(gl.glGetUniformLocation(self.blur_h_program, "u_direction"), 1.0, 0.0)
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
# Pass 2: Vertical blur to blur_fbo
gl.glBindFramebuffer(gl.GL_FRAMEBUFFER, self.blur_fbo)
gl.glViewport(0, 0, width, height)
gl.glClear(gl.GL_COLOR_BUFFER_BIT)
gl.glUseProgram(self.blur_v_program)
gl.glActiveTexture(gl.GL_TEXTURE0)
gl.glBindTexture(gl.GL_TEXTURE_2D, self.temp_tex)
gl.glUniform1i(gl.glGetUniformLocation(self.blur_v_program, "u_texture"), 0)
gl.glUniform1f(gl.glGetUniformLocation(self.blur_v_program, "u_blur_radius"), float(radius))
gl.glUniform2f(gl.glGetUniformLocation(self.blur_v_program, "u_direction"), 0.0, 1.0)
gl.glUniform1f(gl.glGetUniformLocation(self.blur_v_program, "u_tint_intensity"), float(tint))
gl.glUniform1f(gl.glGetUniformLocation(self.blur_v_program, "u_opacity"), float(opacity))
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
gl.glUseProgram(0)
+41 -1
View File
@@ -235,6 +235,10 @@ _current_font_size: float = 16.0
_current_scale: float = 1.0
_transparency: float = 1.0
_child_transparency: float = 1.0
_frosted_glass_enabled: bool = False
_frosted_blur_radius: float = 8.0
_frosted_tint_intensity: float = 0.1
_frosted_opacity: float = 1.0
# ------------------------------------------------------------------ public API
@@ -269,6 +273,34 @@ def set_child_transparency(val: float) -> None:
_child_transparency = val
apply(_current_palette)
def get_frosted_glass_enabled() -> bool:
return _frosted_glass_enabled
def set_frosted_glass_enabled(val: bool) -> None:
global _frosted_glass_enabled
_frosted_glass_enabled = val
def get_frosted_blur_radius() -> float:
return _frosted_blur_radius
def set_frosted_blur_radius(val: float) -> None:
global _frosted_blur_radius
_frosted_blur_radius = val
def get_frosted_tint_intensity() -> float:
return _frosted_tint_intensity
def set_frosted_tint_intensity(val: float) -> None:
global _frosted_tint_intensity
_frosted_tint_intensity = val
def get_frosted_opacity() -> float:
return _frosted_opacity
def set_frosted_opacity(val: float) -> None:
global _frosted_opacity
_frosted_opacity = val
def apply(palette_name: str) -> None:
"""
Apply a named palette by setting all ImGui style colors and applying global professional styling.
@@ -350,13 +382,17 @@ def save_to_config(config: dict) -> None:
config["theme"]["scale"] = _current_scale
config["theme"]["transparency"] = _transparency
config["theme"]["child_transparency"] = _child_transparency
config["theme"]["frosted_glass_enabled"] = _frosted_glass_enabled
config["theme"]["frosted_blur_radius"] = _frosted_blur_radius
config["theme"]["frosted_tint_intensity"] = _frosted_tint_intensity
config["theme"]["frosted_opacity"] = _frosted_opacity
sys.stderr.write(f"[DEBUG theme_2] save_to_config: palette={_current_palette}, transparency={_transparency}\n")
sys.stderr.flush()
def load_from_config(config: dict) -> None:
"""Read [theme] from config. Font is handled separately at startup."""
import sys
global _current_font_path, _current_font_size, _current_scale, _current_palette, _transparency, _child_transparency
global _current_font_path, _current_font_size, _current_scale, _current_palette, _transparency, _child_transparency, _frosted_glass_enabled, _frosted_blur_radius, _frosted_tint_intensity, _frosted_opacity
t = config.get("theme", {})
sys.stderr.write(f"[DEBUG theme_2] load_from_config raw: {t}\n")
sys.stderr.flush()
@@ -369,6 +405,10 @@ def load_from_config(config: dict) -> None:
_current_scale = float(t.get("scale", 1.0))
_transparency = float(t.get("transparency", 1.0))
_child_transparency = float(t.get("child_transparency", 1.0))
_frosted_glass_enabled = bool(t.get("frosted_glass_enabled", False))
_frosted_blur_radius = float(t.get("frosted_blur_radius", 8.0))
_frosted_tint_intensity = float(t.get("frosted_tint_intensity", 0.1))
_frosted_opacity = float(t.get("frosted_opacity", 1.0))
sys.stderr.write(f"[DEBUG theme_2] load_from_config effective: palette={_current_palette}, transparency={_transparency}\n")
sys.stderr.flush()
+42
View File
@@ -0,0 +1,42 @@
import pytest
from unittest.mock import patch, MagicMock
import OpenGL.GL as gl
def test_shader_manager_fbo_initialization():
with patch("src.shader_manager.gl") as mock_gl:
mock_gl.glGenFramebuffers.side_effect = [1, 2]
mock_gl.glGenTextures.side_effect = [3, 4]
mock_gl.glCheckFramebufferStatus.return_value = mock_gl.GL_FRAMEBUFFER_COMPLETE
from src.shader_manager import ShaderManager
manager = ShaderManager()
manager.setup_capture_fbo(800, 600)
assert manager.scene_fbo == 1
assert manager.blur_fbo == 2
assert manager.scene_tex == 3
assert manager.blur_tex == 4
assert mock_gl.glGenFramebuffers.call_count == 2
assert mock_gl.glGenTextures.call_count == 2
assert mock_gl.glCheckFramebufferStatus.call_count == 2
def test_shader_manager_capture_lifecycle():
with patch("src.shader_manager.gl") as mock_gl:
mock_gl.glCheckFramebufferStatus.return_value = mock_gl.GL_FRAMEBUFFER_COMPLETE
mock_gl.glGenFramebuffers.side_effect = [1, 2]
mock_gl.glGenTextures.side_effect = [3, 4]
from src.shader_manager import ShaderManager
manager = ShaderManager()
# Ensure setup is called on first capture
manager.capture_begin(1024, 768)
assert manager.fbo_width == 1024
assert manager.fbo_height == 768
# Should bind the blur FBO
mock_gl.glBindFramebuffer.assert_any_call(mock_gl.GL_FRAMEBUFFER, manager.blur_fbo)
mock_gl.glBindFramebuffer.reset_mock()
manager.capture_end()
# Verify unbind (glBindFramebuffer(..., 0))
mock_gl.glBindFramebuffer.assert_called_with(mock_gl.GL_FRAMEBUFFER, 0)
+19
View File
@@ -0,0 +1,19 @@
import pytest
from unittest.mock import patch, MagicMock
def test_shader_manager_frosted_glass_compilation():
# Mock OpenGL before importing ShaderManager
with patch("src.shader_manager.gl") as mock_gl:
mock_gl.glCreateProgram.return_value = 1
mock_gl.glCreateShader.return_value = 2
mock_gl.glGetShaderiv.return_value = mock_gl.GL_TRUE
mock_gl.glGetProgramiv.return_value = mock_gl.GL_TRUE
from src.shader_manager import ShaderManager
manager = ShaderManager()
# This should fail initially because the method doesn't exist
manager.setup_frosted_glass_shader()
assert manager.blur_program is not None
assert mock_gl.glCreateProgram.called
+89
View File
@@ -0,0 +1,89 @@
import pytest
import time
from unittest.mock import patch, MagicMock
def test_gui_frosted_background_call():
# Mock ShaderManager and OpenGL functions
with patch("src.gui_2.ShaderManager") as mock_sm_class, \
patch("src.gui_2.gl") as mock_gl, \
patch("src.gui_2.imgui") as mock_imgui, \
patch("src.gui_2.theme") as mock_theme:
mock_sm = mock_sm_class.return_value
mock_sm.blur_tex = 2
mock_theme.get_frosted_glass_enabled.return_value = True
mock_imgui.get_io().display_size = MagicMock(x=1920, y=1080)
from src.gui_2 import App
with patch.object(App, '__init__', return_value=None):
app = App()
app.shader_manager = mock_sm
# Simulate frame
app._render_frosted_background(pos=MagicMock(x=10, y=10), size=MagicMock(x=100, y=100))
# Now it should only call add_image
assert mock_imgui.get_window_draw_list().add_image.called
# It no longer calls these
assert not mock_sm.setup_capture_fbo.called
assert not mock_sm.render_blur.called
assert not mock_sm.capture_begin.called
assert not mock_sm.capture_end.called
def test_gui_global_blur_call():
with patch("src.gui_2.ShaderManager") as mock_sm_class, \
patch("src.gui_2.imgui") as mock_imgui, \
patch("src.gui_2.theme") as mock_theme, \
patch("src.gui_2.bg_shader") as mock_bg:
mock_sm = mock_sm_class.return_value
mock_theme.get_frosted_glass_enabled.return_value = True
mock_theme.is_nerv_active.return_value = False
mock_imgui.get_io().display_size = MagicMock(x=1920, y=1080)
mock_bg.get_bg.return_value = MagicMock(enabled=False)
from src.gui_2 import App
with patch.object(App, '__init__', return_value=None):
app = App()
app.shader_manager = mock_sm
app.shader_uniforms = {
'frosted_blur_radius': 10.0,
'frosted_tint_intensity': 0.5,
'frosted_opacity': 0.8
}
app.ai_status = "idle"
app.ui_crt_filter = False
app.controller = MagicMock()
app.perf_profiling_enabled = False
app.is_viewing_prior_session = False
app.config = {}
app.project = {}
app.show_windows = {}
app.ui_auto_scroll_comms = False
app._comms_log_dirty = False
app._tool_log_dirty = False
app._pending_comms_lock = MagicMock()
app._pending_comms = []
app.ui_focus_agent = None
app._last_ui_focus_agent = None
app.perf_monitor = MagicMock()
app._process_pending_gui_tasks = MagicMock()
app._process_pending_history_adds = MagicMock()
app._render_track_proposal_modal = MagicMock()
app._render_patch_modal = MagicMock()
app._render_save_preset_modal = MagicMock()
app._render_preset_manager_window = MagicMock()
app._render_tool_preset_manager_window = MagicMock()
app._render_persona_editor_window = MagicMock()
app._last_autosave = time.time()
app._autosave_interval = 60
app._render_custom_title_bar = MagicMock()
app._render_shader_live_editor = MagicMock()
try:
app._gui_func()
except Exception:
pass
assert mock_sm.prepare_global_blur.called
+41 -26
View File
@@ -3,33 +3,48 @@ from unittest.mock import MagicMock
from src import theme_2 as theme
def test_theme_apply_sets_rounding_and_padding(monkeypatch):
# Mock imgui
mock_style = MagicMock()
mock_imgui = MagicMock()
mock_imgui.get_style.return_value = mock_style
mock_imgui.ImVec2.side_effect = lambda x, y: (x, y)
monkeypatch.setattr(theme, "imgui", mock_imgui)
# Mock imgui
mock_style = MagicMock()
mock_imgui = MagicMock()
mock_imgui.get_style.return_value = mock_style
mock_imgui.ImVec2.side_effect = lambda x, y: (x, y)
monkeypatch.setattr(theme, "imgui", mock_imgui)
# Call apply with the default palette
theme.apply("ImGui Dark")
# Call apply with the default palette
theme.apply("ImGui Dark")
# Verify subtle rounding styles
assert mock_style.window_rounding == 6.0
assert mock_style.child_rounding == 4.0
assert mock_style.frame_rounding == 4.0
assert mock_style.popup_rounding == 4.0
assert mock_style.scrollbar_rounding == 12.0
assert mock_style.grab_rounding == 4.0
assert mock_style.tab_rounding == 4.0
# Verify subtle rounding styles
assert mock_style.window_rounding == 6.0
assert mock_style.child_rounding == 4.0
assert mock_style.frame_rounding == 4.0
assert mock_style.popup_rounding == 4.0
assert mock_style.scrollbar_rounding == 12.0
assert mock_style.grab_rounding == 4.0
assert mock_style.tab_rounding == 4.0
# Verify borders
assert mock_style.window_border_size == 1.0
assert mock_style.frame_border_size == 1.0
assert mock_style.popup_border_size == 1.0
# Verify borders
assert mock_style.window_border_size == 1.0
assert mock_style.frame_border_size == 1.0
assert mock_style.popup_border_size == 1.0
# Verify padding/spacing
assert mock_style.window_padding == (8.0, 8.0)
assert mock_style.frame_padding == (8.0, 4.0)
assert mock_style.item_spacing == (8.0, 4.0)
assert mock_style.item_inner_spacing == (4.0, 4.0)
assert mock_style.scrollbar_size == 14.0
# Verify padding/spacing
assert mock_style.window_padding == (8.0, 8.0)
assert mock_style.frame_padding == (8.0, 4.0)
assert mock_style.item_spacing == (8.0, 4.0)
assert mock_style.item_inner_spacing == (4.0, 4.0)
assert mock_style.scrollbar_size == 14.0
def test_frosted_glass_enabled_toggle():
theme.set_frosted_glass_enabled(True)
assert theme.get_frosted_glass_enabled() is True
theme.set_frosted_glass_enabled(False)
assert theme.get_frosted_glass_enabled() is False
def test_theme_config_frosted_glass():
config = {"theme": {"frosted_glass_enabled": True}}
theme.load_from_config(config)
assert theme.get_frosted_glass_enabled() is True
new_config = {}
theme.save_to_config(new_config)
assert new_config["theme"]["frosted_glass_enabled"] is True