Compare commits
15 Commits
d89f971270
...
BOTCHED-SH
| Author | SHA1 | Date | |
|---|---|---|---|
| 70bfcc61c2 | |||
| 976a3b63e0 | |||
| 0e0b185290 | |||
| 5b196dccf0 | |||
| 1771c32006 | |||
| 6a39f440d9 | |||
| cecbe2245a | |||
| ad9ffb68f5 | |||
| e9b7875de5 | |||
| 3d5c768598 | |||
| f297e7a3bd | |||
| 4a705a8060 | |||
| 55f3bd87e2 | |||
| e56d4af097 | |||
| 1328bc159b |
@@ -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/`.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
- **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.
|
||||||
|
|||||||
@@ -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.
|
- **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).
|
- **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 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.
|
- **Interface-Driven Development (IDD):** Enforces a "Stub-and-Resolve" pattern where cross-module dependencies are resolved by generating signatures/contracts before implementation.
|
||||||
|
|
||||||
|
|||||||
@@ -60,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)
|
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/)*
|
*Link: [./tracks/custom_shaders_20260309/](./tracks/custom_shaders_20260309/)*
|
||||||
|
|
||||||
7. [x] **Track: UI/UX Improvements - Presets and AI Settings**
|
7. [x] **Track: UI/UX Improvements - Presets and AI Settings**
|
||||||
@@ -82,7 +82,7 @@ This file tracks all major tracks for the project. Each track has its own detail
|
|||||||
11. [ ] **Track: Advanced Text Viewer with Syntax Highlighting**
|
11. [ ] **Track: Advanced Text Viewer with Syntax Highlighting**
|
||||||
*Link: [./tracks/text_viewer_rich_rendering_20260313/](./tracks/text_viewer_rich_rendering_20260313/)*
|
*Link: [./tracks/text_viewer_rich_rendering_20260313/](./tracks/text_viewer_rich_rendering_20260313/)*
|
||||||
|
|
||||||
12. [ ] **Track: Frosted Glass Background Effect**
|
12. [x] **Track: Frosted Glass Background Effect**
|
||||||
*Link: [./tracks/frosted_glass_20260313/](./tracks/frosted_glass_20260313/)*
|
*Link: [./tracks/frosted_glass_20260313/](./tracks/frosted_glass_20260313/)*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,26 +1,26 @@
|
|||||||
# Implementation Plan: Frosted Glass Background Effect
|
# Implementation Plan: Frosted Glass Background Effect
|
||||||
|
|
||||||
## Phase 1: Shader Development & Integration
|
## Phase 1: Shader Development & Integration [checkpoint: 55f3bd8]
|
||||||
- [ ] Task: Audit `src/shader_manager.py` to identify existing background/post-process integration points.
|
- [x] Task: Audit `src/shader_manager.py` to identify existing background/post-process integration points. [1328bc1]
|
||||||
- [ ] Task: Write Tests: Verify `ShaderManager` can compile and bind a multi-pass blur shader.
|
- [x] Task: Write Tests: Verify `ShaderManager` can compile and bind a multi-pass blur shader. [1328bc1]
|
||||||
- [ ] Task: Implement: Add `FrostedGlassShader` (GLSL) to `src/shader_manager.py`.
|
- [x] Task: Implement: Add `FrostedGlassShader` (GLSL) to `src/shader_manager.py`. [1328bc1]
|
||||||
- [ ] Task: Implement: Integrate the blur shader into the `ShaderManager` lifecycle.
|
- [x] Task: Implement: Integrate the blur shader into the `ShaderManager` lifecycle. [1328bc1]
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 1: Shader Development & Integration' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 1: Shader Development & Integration' (Protocol in workflow.md) [55f3bd8]
|
||||||
|
|
||||||
## Phase 2: Framebuffer Capture Pipeline
|
## Phase 2: Framebuffer Capture Pipeline [checkpoint: e9b7875]
|
||||||
- [ ] Task: Write Tests: Verify the FBO capture mechanism correctly samples the back buffer and stores it in a texture.
|
- [x] Task: Write Tests: Verify the FBO capture mechanism correctly samples the back buffer and stores it in a texture. [f297e7a]
|
||||||
- [ ] Task: Implement: Update `src/shader_manager.py` or `src/gui_2.py` to handle "pre-rendering" of the background into a texture for blurring.
|
- [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]
|
||||||
- [ ] Task: Implement: Ensure the blurred texture is updated every frame or on window move events.
|
- [x] Task: Implement: Ensure the blurred texture is updated every frame or on window move events. [f297e7a]
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 2: Framebuffer Capture Pipeline' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 2: Framebuffer Capture Pipeline' (Protocol in workflow.md) [e9b7875]
|
||||||
|
|
||||||
## Phase 3: GUI Integration & Rendering
|
## Phase 3: GUI Integration & Rendering [checkpoint: cecbe22]
|
||||||
- [ ] Task: Write Tests: Verify that a mocked ImGui window successfully calls the frosted glass rendering logic.
|
- [x] Task: Write Tests: Verify that a mocked ImGui window successfully calls the frosted glass rendering logic. [cecbe22]
|
||||||
- [ ] Task: Implement: Create a `_render_frosted_background(self, pos, size)` helper in `src/gui_2.py`.
|
- [x] Task: Implement: Create a `_render_frosted_background(self, pos, size)` helper in `src/gui_2.py`. [cecbe22]
|
||||||
- [ ] Task: Implement: Update panel rendering loops (e.g. `_gui_func`) to inject the frosted background before calling `imgui.begin()` for major panels.
|
- [x] Task: Implement: Update panel rendering loops (e.g. `_gui_func`) to inject the frosted background before calling `imgui.begin()` for major panels. [cecbe22]
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 3: GUI Integration & Rendering' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 3: GUI Integration & Rendering' (Protocol in workflow.md) [cecbe22]
|
||||||
|
|
||||||
## Phase 4: UI Controls & Configuration
|
## Phase 4: UI Controls & Configuration [checkpoint: cecbe22]
|
||||||
- [ ] Task: Write Tests: Verify that modifying blur uniforms via the Live Editor updates the shader state.
|
- [x] Task: Write Tests: Verify that modifying blur uniforms via the Live Editor updates the shader state. [cecbe22]
|
||||||
- [ ] Task: Implement: Add "Frosted Glass" sliders (Blur, Tint, Opacity) to the **Shader Editor** in `src/gui_2.py`.
|
- [x] Task: Implement: Add "Frosted Glass" sliders (Blur, Tint, Opacity) to the **Shader Editor** in `src/gui_2.py`. [cecbe22]
|
||||||
- [ ] Task: Implement: Update `src/theme.py` to parse and store frosted glass settings from `config.toml`.
|
- [x] Task: Implement: Update `src/theme.py` to parse and store frosted glass settings from `config.toml`. [cecbe22]
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 4: UI Controls & Configuration' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 4: UI Controls & Configuration' (Protocol in workflow.md) [cecbe22]
|
||||||
|
|||||||
22
config.toml
22
config.toml
@@ -23,15 +23,15 @@ active = "C:/projects/gencpp/gencpp_sloppy.toml"
|
|||||||
separate_message_panel = false
|
separate_message_panel = false
|
||||||
separate_response_panel = false
|
separate_response_panel = false
|
||||||
separate_tool_calls_panel = false
|
separate_tool_calls_panel = false
|
||||||
bg_shader_enabled = true
|
bg_shader_enabled = false
|
||||||
crt_filter_enabled = false
|
crt_filter_enabled = false
|
||||||
separate_task_dag = false
|
separate_task_dag = false
|
||||||
separate_usage_analytics = false
|
separate_usage_analytics = true
|
||||||
separate_tier1 = false
|
separate_tier1 = false
|
||||||
separate_tier2 = false
|
separate_tier2 = false
|
||||||
separate_tier3 = false
|
separate_tier3 = false
|
||||||
separate_tier4 = false
|
separate_tier4 = false
|
||||||
separate_external_tools = false
|
separate_external_tools = true
|
||||||
|
|
||||||
[gui.show_windows]
|
[gui.show_windows]
|
||||||
"Context Hub" = true
|
"Context Hub" = true
|
||||||
@@ -39,7 +39,7 @@ separate_external_tools = false
|
|||||||
"AI Settings" = true
|
"AI Settings" = true
|
||||||
"MMA Dashboard" = true
|
"MMA Dashboard" = true
|
||||||
"Task DAG" = false
|
"Task DAG" = false
|
||||||
"Usage Analytics" = false
|
"Usage Analytics" = true
|
||||||
"Tier 1" = false
|
"Tier 1" = false
|
||||||
"Tier 2" = false
|
"Tier 2" = false
|
||||||
"Tier 3" = false
|
"Tier 3" = false
|
||||||
@@ -52,20 +52,24 @@ separate_external_tools = false
|
|||||||
"Operations Hub" = true
|
"Operations Hub" = true
|
||||||
Message = false
|
Message = false
|
||||||
Response = true
|
Response = true
|
||||||
"Tool Calls" = false
|
"Tool Calls" = true
|
||||||
Theme = true
|
Theme = true
|
||||||
"Log Management" = true
|
"Log Management" = true
|
||||||
Diagnostics = false
|
Diagnostics = false
|
||||||
"External Tools" = false
|
"External Tools" = false
|
||||||
"Shader Editor" = false
|
"Shader Editor" = true
|
||||||
|
|
||||||
[theme]
|
[theme]
|
||||||
palette = "Nord Dark"
|
palette = "Nord Dark"
|
||||||
font_path = "C:/projects/manual_slop/assets/fonts/MapleMono-Regular.ttf"
|
font_path = "C:/projects/manual_slop/assets/fonts/MapleMono-Regular.ttf"
|
||||||
font_size = 18.0
|
font_size = 16.0
|
||||||
scale = 1.0
|
scale = 1.0
|
||||||
transparency = 0.5400000214576721
|
transparency = 0.699999988079071
|
||||||
child_transparency = 0.5899999737739563
|
child_transparency = 0.6899999976158142
|
||||||
|
frosted_blur_radius = 29.68400001525879
|
||||||
|
frosted_tint_intensity = 0.5659999847412109
|
||||||
|
frosted_opacity = 0.5389999747276306
|
||||||
|
frosted_glass_enabled = true
|
||||||
|
|
||||||
[mma]
|
[mma]
|
||||||
max_workers = 4
|
max_workers = 4
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ ViewportPos=43,95
|
|||||||
ViewportId=0x78C57832
|
ViewportId=0x78C57832
|
||||||
Size=897,649
|
Size=897,649
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000005,0
|
||||||
|
|
||||||
[Window][Files]
|
[Window][Files]
|
||||||
ViewportPos=3125,170
|
ViewportPos=3125,170
|
||||||
@@ -33,7 +33,7 @@ DockId=0x0000000A,0
|
|||||||
Pos=0,17
|
Pos=0,17
|
||||||
Size=1680,730
|
Size=1680,730
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000005,0
|
||||||
|
|
||||||
[Window][Provider]
|
[Window][Provider]
|
||||||
ViewportPos=43,95
|
ViewportPos=43,95
|
||||||
@@ -41,10 +41,10 @@ ViewportId=0x78C57832
|
|||||||
Pos=0,651
|
Pos=0,651
|
||||||
Size=897,468
|
Size=897,468
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000005,0
|
||||||
|
|
||||||
[Window][Message]
|
[Window][Message]
|
||||||
Pos=661,1426
|
Pos=661,1321
|
||||||
Size=716,455
|
Size=716,455
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
@@ -54,10 +54,9 @@ Size=1111,773
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Tool Calls]
|
[Window][Tool Calls]
|
||||||
Pos=520,1144
|
Pos=1039,464
|
||||||
Size=663,232
|
Size=587,510
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000006,0
|
|
||||||
|
|
||||||
[Window][Comms History]
|
[Window][Comms History]
|
||||||
ViewportPos=43,95
|
ViewportPos=43,95
|
||||||
@@ -74,10 +73,10 @@ Collapsed=0
|
|||||||
DockId=0xAFC85805,2
|
DockId=0xAFC85805,2
|
||||||
|
|
||||||
[Window][Theme]
|
[Window][Theme]
|
||||||
Pos=0,703
|
Pos=2671,24
|
||||||
Size=630,737
|
Size=1169,2136
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,2
|
DockId=0x00000002,1
|
||||||
|
|
||||||
[Window][Text Viewer - Entry #7]
|
[Window][Text Viewer - Entry #7]
|
||||||
Pos=379,324
|
Pos=379,324
|
||||||
@@ -88,13 +87,13 @@ Collapsed=0
|
|||||||
Pos=1649,24
|
Pos=1649,24
|
||||||
Size=580,1284
|
Size=580,1284
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000010,2
|
DockId=0x00000004,2
|
||||||
|
|
||||||
[Window][Context Hub]
|
[Window][Context Hub]
|
||||||
Pos=0,703
|
Pos=0,1719
|
||||||
Size=630,737
|
Size=999,441
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,1
|
DockId=0x00000006,0
|
||||||
|
|
||||||
[Window][AI Settings Hub]
|
[Window][AI Settings Hub]
|
||||||
Pos=406,17
|
Pos=406,17
|
||||||
@@ -103,28 +102,28 @@ Collapsed=0
|
|||||||
DockId=0x0000000D,0
|
DockId=0x0000000D,0
|
||||||
|
|
||||||
[Window][Discussion Hub]
|
[Window][Discussion Hub]
|
||||||
Pos=1263,22
|
Pos=1762,24
|
||||||
Size=709,1418
|
Size=907,2136
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000013,0
|
DockId=0x00000011,0
|
||||||
|
|
||||||
[Window][Operations Hub]
|
[Window][Operations Hub]
|
||||||
Pos=632,22
|
Pos=1001,24
|
||||||
Size=629,1418
|
Size=759,2136
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000005,0
|
DockId=0x00000010,0
|
||||||
|
|
||||||
[Window][Files & Media]
|
[Window][Files & Media]
|
||||||
Pos=0,703
|
Pos=0,1719
|
||||||
Size=630,737
|
Size=999,441
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,0
|
DockId=0x00000006,1
|
||||||
|
|
||||||
[Window][AI Settings]
|
[Window][AI Settings]
|
||||||
Pos=0,22
|
Pos=0,24
|
||||||
Size=630,679
|
Size=999,1693
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000005,0
|
||||||
|
|
||||||
[Window][Approve Tool Execution]
|
[Window][Approve Tool Execution]
|
||||||
Pos=3,524
|
Pos=3,524
|
||||||
@@ -132,16 +131,16 @@ Size=416,325
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][MMA Dashboard]
|
[Window][MMA Dashboard]
|
||||||
Pos=1974,22
|
Pos=2671,24
|
||||||
Size=586,1418
|
Size=1169,2136
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000010,0
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][Log Management]
|
[Window][Log Management]
|
||||||
Pos=1974,22
|
Pos=1931,24
|
||||||
Size=586,1418
|
Size=629,1416
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000010,1
|
DockId=0x00000002,1
|
||||||
|
|
||||||
[Window][Track Proposal]
|
[Window][Track Proposal]
|
||||||
Pos=709,326
|
Pos=709,326
|
||||||
@@ -167,7 +166,7 @@ Collapsed=0
|
|||||||
Pos=2822,1717
|
Pos=2822,1717
|
||||||
Size=1018,420
|
Size=1018,420
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x0000000C,0
|
DockId=0x00000004,0
|
||||||
|
|
||||||
[Window][Approve PowerShell Command]
|
[Window][Approve PowerShell Command]
|
||||||
Pos=649,435
|
Pos=649,435
|
||||||
@@ -210,7 +209,7 @@ Size=3840,32
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Text Viewer - message]
|
[Window][Text Viewer - message]
|
||||||
Pos=568,1226
|
Pos=562,588
|
||||||
Size=900,700
|
Size=900,700
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
@@ -220,7 +219,7 @@ Size=900,700
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Text Viewer - text]
|
[Window][Text Viewer - text]
|
||||||
Pos=60,60
|
Pos=555,644
|
||||||
Size=900,700
|
Size=900,700
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
@@ -240,7 +239,7 @@ Size=900,700
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Text Viewer - tool_calls]
|
[Window][Text Viewer - tool_calls]
|
||||||
Pos=60,60
|
Pos=589,490
|
||||||
Size=900,700
|
Size=900,700
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
@@ -330,10 +329,9 @@ Size=967,499
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Usage Analytics]
|
[Window][Usage Analytics]
|
||||||
Pos=1739,1107
|
Pos=1702,689
|
||||||
Size=586,269
|
Size=566,438
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x0000000F,0
|
|
||||||
|
|
||||||
[Window][Tool Preset Manager]
|
[Window][Tool Preset Manager]
|
||||||
Pos=1301,302
|
Pos=1301,302
|
||||||
@@ -366,7 +364,7 @@ Size=900,700
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Text Viewer - Entry #4]
|
[Window][Text Viewer - Entry #4]
|
||||||
Pos=1127,922
|
Pos=828,397
|
||||||
Size=900,700
|
Size=900,700
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
@@ -381,8 +379,13 @@ Size=900,700
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Shader Editor]
|
[Window][Shader Editor]
|
||||||
Pos=457,710
|
Pos=753,637
|
||||||
Size=493,252
|
Size=493,487
|
||||||
|
Collapsed=0
|
||||||
|
|
||||||
|
[Window][Text Viewer - list_directory]
|
||||||
|
Pos=60,60
|
||||||
|
Size=900,700
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Table][0xFB6E3870,4]
|
[Table][0xFB6E3870,4]
|
||||||
@@ -442,7 +445,7 @@ Column 3 Width=120
|
|||||||
RefScale=16
|
RefScale=16
|
||||||
Column 0 Width=48
|
Column 0 Width=48
|
||||||
Column 1 Weight=1.0000
|
Column 1 Weight=1.0000
|
||||||
Column 2 Width=118
|
Column 2 Width=117
|
||||||
Column 3 Width=48
|
Column 3 Width=48
|
||||||
|
|
||||||
[Table][0xD99F45C5,4]
|
[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=0x00000008 Pos=3125,170 Size=593,1157 Split=Y
|
||||||
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
|
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=1029,147 Selected=0x0469CA7A
|
||||||
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02
|
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=1029,145 Selected=0xDF822E02
|
||||||
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,22 Size=2560,1418 Split=X
|
DockSpace ID=0xAFC85805 Window=0x079D3A04 Pos=0,24 Size=3840,2136 Split=X
|
||||||
DockNode ID=0x00000003 Parent=0xAFC85805 SizeRef=1640,1183 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=0x0000000B Parent=0x00000003 SizeRef=404,1186 Split=X Selected=0xF4139CA2
|
||||||
DockNode ID=0x00000007 Parent=0x0000000B SizeRef=630,858 Split=Y Selected=0x8CA2375C
|
DockNode ID=0x00000007 Parent=0x0000000B SizeRef=999,858 Split=Y Selected=0x7BD57D6A
|
||||||
DockNode ID=0x00000001 Parent=0x00000007 SizeRef=824,525 CentralNode=1 Selected=0x7BD57D6A
|
DockNode ID=0x00000005 Parent=0x00000007 SizeRef=639,904 CentralNode=1 Selected=0x7BD57D6A
|
||||||
DockNode ID=0x00000002 Parent=0x00000007 SizeRef=824,737 Selected=0x8CA2375C
|
DockNode ID=0x00000006 Parent=0x00000007 SizeRef=639,441 Selected=0x1DCB2623
|
||||||
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=1340,858 Split=X Selected=0x418C7449
|
DockNode ID=0x0000000E Parent=0x0000000B SizeRef=2839,858 Split=X Selected=0x418C7449
|
||||||
DockNode ID=0x00000012 Parent=0x0000000E SizeRef=629,402 Split=Y Selected=0x418C7449
|
DockNode ID=0x00000001 Parent=0x0000000E SizeRef=1668,1288 Split=X Selected=0x6F2B5B04
|
||||||
DockNode ID=0x00000005 Parent=0x00000012 SizeRef=876,1749 Selected=0x418C7449
|
DockNode ID=0x00000010 Parent=0x00000001 SizeRef=759,1416 Selected=0x418C7449
|
||||||
DockNode ID=0x00000006 Parent=0x00000012 SizeRef=876,362 Selected=0x1D56B311
|
DockNode ID=0x00000011 Parent=0x00000001 SizeRef=907,1416 Selected=0x6F2B5B04
|
||||||
DockNode ID=0x00000013 Parent=0x0000000E SizeRef=709,402 Selected=0x6F2B5B04
|
DockNode ID=0x00000002 Parent=0x0000000E SizeRef=1169,1288 Selected=0x8CA2375C
|
||||||
DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6
|
DockNode ID=0x0000000D Parent=0x00000003 SizeRef=435,1186 Selected=0x363E93D6
|
||||||
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=586,1183 Split=Y Selected=0x3AEC3498
|
DockNode ID=0x00000004 Parent=0xAFC85805 SizeRef=511,1183 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
|
|
||||||
|
|
||||||
;;;<<<Layout_655921752_Default>>>;;;
|
;;;<<<Layout_655921752_Default>>>;;;
|
||||||
;;;<<<HelloImGui_Misc>>>;;;
|
;;;<<<HelloImGui_Misc>>>;;;
|
||||||
|
|||||||
276
src/gui_2.py
276
src/gui_2.py
@@ -21,6 +21,7 @@ from src import theme_2 as theme
|
|||||||
from src import theme_nerv_fx as theme_fx
|
from src import theme_nerv_fx as theme_fx
|
||||||
from src import api_hooks
|
from src import api_hooks
|
||||||
import numpy as np
|
import numpy as np
|
||||||
|
import OpenGL.GL as gl
|
||||||
from src import log_registry
|
from src import log_registry
|
||||||
from src import log_pruner
|
from src import log_pruner
|
||||||
from src import models
|
from src import models
|
||||||
@@ -28,6 +29,7 @@ from src import app_controller
|
|||||||
from src import mcp_client
|
from src import mcp_client
|
||||||
from src import markdown_helper
|
from src import markdown_helper
|
||||||
from src import bg_shader
|
from src import bg_shader
|
||||||
|
from src.shader_manager import ShaderManager
|
||||||
import re
|
import re
|
||||||
import subprocess
|
import subprocess
|
||||||
if sys.platform == "win32":
|
if sys.platform == "win32":
|
||||||
@@ -211,7 +213,17 @@ class App:
|
|||||||
self._nerv_flicker = theme_fx.StatusFlicker()
|
self._nerv_flicker = theme_fx.StatusFlicker()
|
||||||
self.ui_tool_filter_category = "All"
|
self.ui_tool_filter_category = "All"
|
||||||
self.ui_discussion_split_h = 300.0
|
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:
|
def _handle_approve_tool(self, user_data=None) -> None:
|
||||||
"""UI-level wrapper for approving a pending tool execution ask."""
|
"""UI-level wrapper for approving a pending tool execution ask."""
|
||||||
@@ -341,6 +353,111 @@ class App:
|
|||||||
imgui.pop_style_var(2)
|
imgui.pop_style_var(2)
|
||||||
imgui.pop_id()
|
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:
|
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]:
|
||||||
@@ -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_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_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)
|
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()
|
imgui.end()
|
||||||
|
|
||||||
def _gui_func(self) -> None:
|
def _gui_func(self) -> None:
|
||||||
self._render_custom_title_bar()
|
self._render_custom_title_bar()
|
||||||
self._render_shader_live_editor()
|
self._render_shader_live_editor()
|
||||||
pushed_prior_tint = False
|
pushed_prior_tint = False
|
||||||
|
pushed_frosted_style = False
|
||||||
|
|
||||||
# Render background shader
|
# Render background shader
|
||||||
bg = bg_shader.get_bg()
|
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
|
ws = imgui.get_io().display_size
|
||||||
bg.render(ws.x, ws.y)
|
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():
|
if theme.is_nerv_active():
|
||||||
ws = imgui.get_io().display_size
|
ws = imgui.get_io().display_size
|
||||||
self._nerv_alert.update(self.ai_status)
|
self._nerv_alert.update(self.ai_status)
|
||||||
self._nerv_alert.render(ws.x, ws.y)
|
self._nerv_alert.render(ws.x, ws.y)
|
||||||
self._nerv_crt.enabled = self.ui_crt_filter
|
self._nerv_crt.enabled = self.ui_crt_filter
|
||||||
self._nerv_crt.render(ws.x, ws.y)
|
self._nerv_crt.render(ws.x, ws.y)
|
||||||
|
|
||||||
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))
|
||||||
@@ -530,7 +678,7 @@ class App:
|
|||||||
self._tool_log_dirty = False
|
self._tool_log_dirty = False
|
||||||
|
|
||||||
if self.show_windows.get("Context Hub", 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)
|
self.show_windows["Context Hub"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
if imgui.begin_tab_bar('context_hub_tabs'):
|
if imgui.begin_tab_bar('context_hub_tabs'):
|
||||||
@@ -541,18 +689,18 @@ class App:
|
|||||||
self._render_paths_panel()
|
self._render_paths_panel()
|
||||||
imgui.end_tab_item()
|
imgui.end_tab_item()
|
||||||
imgui.end_tab_bar()
|
imgui.end_tab_bar()
|
||||||
imgui.end()
|
self._end_window()
|
||||||
if self.show_windows.get("Files & Media", False):
|
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)
|
self.show_windows["Files & Media"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
if imgui.collapsing_header("Files"):
|
if imgui.collapsing_header("Files"):
|
||||||
self._render_files_panel()
|
self._render_files_panel()
|
||||||
if imgui.collapsing_header("Screenshots"):
|
if imgui.collapsing_header("Screenshots"):
|
||||||
self._render_screenshots_panel()
|
self._render_screenshots_panel()
|
||||||
imgui.end()
|
self._end_window()
|
||||||
if self.show_windows.get("AI Settings", False):
|
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)
|
self.show_windows["AI Settings"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_persona_selector_panel()
|
self._render_persona_selector_panel()
|
||||||
@@ -562,56 +710,56 @@ class App:
|
|||||||
self._render_system_prompts_panel()
|
self._render_system_prompts_panel()
|
||||||
self._render_agent_tools_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):
|
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)
|
self.show_windows["Usage Analytics"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_usage_analytics_panel()
|
self._render_usage_analytics_panel()
|
||||||
imgui.end()
|
self._end_window()
|
||||||
if self.show_windows.get("MMA Dashboard", False):
|
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)
|
self.show_windows["MMA Dashboard"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_mma_dashboard")
|
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_mma_dashboard")
|
||||||
self._render_mma_dashboard()
|
self._render_mma_dashboard()
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.end_component("_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):
|
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)
|
self.show_windows["Task DAG"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_task_dag_panel()
|
self._render_task_dag_panel()
|
||||||
imgui.end()
|
self._end_window()
|
||||||
if self.ui_separate_tier1 and self.show_windows.get("Tier 1: Strategy", False):
|
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)
|
self.show_windows["Tier 1: Strategy"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_tier_stream_panel("Tier 1", "Tier 1")
|
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):
|
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)
|
self.show_windows["Tier 2: Tech Lead"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_tier_stream_panel("Tier 2", "Tier 2 (Tech Lead)")
|
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):
|
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)
|
self.show_windows["Tier 3: Workers"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_tier_stream_panel("Tier 3", None)
|
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):
|
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)
|
self.show_windows["Tier 4: QA"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_tier_stream_panel("Tier 4", "Tier 4 (QA)")
|
self._render_tier_stream_panel("Tier 4", "Tier 4 (QA)")
|
||||||
imgui.end()
|
self._end_window()
|
||||||
if self.show_windows.get("Theme", False):
|
if self.show_windows.get("Theme", False):
|
||||||
self._render_theme_panel()
|
self._render_theme_panel()
|
||||||
if self.show_windows.get("Discussion Hub", False):
|
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)
|
self.show_windows["Discussion Hub"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
# Top part for the history
|
# Top part for the history
|
||||||
@@ -658,86 +806,37 @@ class App:
|
|||||||
else:
|
else:
|
||||||
imgui.text_disabled("Message & Response panels are detached.")
|
imgui.text_disabled("Message & Response panels are detached.")
|
||||||
|
|
||||||
imgui.end()
|
self._end_window()
|
||||||
if self.show_windows.get("Operations Hub", False):
|
if self.show_windows.get("Operations Hub", False):
|
||||||
exp, opened = imgui.begin("Operations Hub", self.show_windows["Operations Hub"])
|
self._render_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()
|
|
||||||
|
|
||||||
if self.ui_separate_message_panel and self.show_windows.get("Message", False):
|
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)
|
self.show_windows["Message"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_message_panel()
|
self._render_message_panel()
|
||||||
imgui.end()
|
self._end_window()
|
||||||
|
|
||||||
if self.ui_separate_response_panel and self.show_windows.get("Response", False):
|
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)
|
self.show_windows["Response"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_response_panel()
|
self._render_response_panel()
|
||||||
imgui.end()
|
self._end_window()
|
||||||
|
|
||||||
if self.ui_separate_tool_calls_panel and self.show_windows.get("Tool Calls", False):
|
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)
|
self.show_windows["Tool Calls"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_tool_calls_panel()
|
self._render_tool_calls_panel()
|
||||||
imgui.end()
|
self._end_window()
|
||||||
|
|
||||||
if self.ui_separate_external_tools and self.show_windows.get('External Tools', False):
|
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)
|
self.show_windows['External Tools'] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
self._render_external_tools_panel()
|
self._render_external_tools_panel()
|
||||||
imgui.end()
|
self._end_window()
|
||||||
|
|
||||||
if self.show_windows.get("Log Management", False):
|
if self.show_windows.get("Log Management", False):
|
||||||
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_log_management")
|
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)
|
expanded, opened = imgui.begin("Last Script Output", self.show_script_output)
|
||||||
self.show_script_output = bool(opened)
|
self.show_script_output = bool(opened)
|
||||||
if expanded:
|
if expanded:
|
||||||
|
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
|
||||||
imgui.text("Script:")
|
imgui.text("Script:")
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
self._render_text_viewer("Last Script", self.ui_last_script_text)
|
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)
|
expanded, opened = imgui.begin(f"Text Viewer - {self.text_viewer_title}", self.show_text_viewer)
|
||||||
self.show_text_viewer = bool(opened)
|
self.show_text_viewer = bool(opened)
|
||||||
if expanded:
|
if expanded:
|
||||||
|
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
|
||||||
if self.ui_word_wrap:
|
if self.ui_word_wrap:
|
||||||
imgui.begin_child("tv_wrap", imgui.ImVec2(-1, -1), False)
|
imgui.begin_child("tv_wrap", imgui.ImVec2(-1, -1), False)
|
||||||
imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
|
imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
|
||||||
@@ -1018,6 +1119,8 @@ class App:
|
|||||||
|
|
||||||
if pushed_prior_tint:
|
if pushed_prior_tint:
|
||||||
imgui.pop_style_color()
|
imgui.pop_style_color()
|
||||||
|
if pushed_frosted_style:
|
||||||
|
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")
|
||||||
|
|
||||||
@@ -1904,7 +2007,6 @@ class App:
|
|||||||
imgui.text(f"History: {key}")
|
imgui.text(f"History: {key}")
|
||||||
hist_data = self.perf_monitor.get_history(key)
|
hist_data = self.perf_monitor.get_history(key)
|
||||||
if hist_data:
|
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))
|
imgui.plot_lines(f"##plot_{key}", np.array(hist_data, dtype=np.float32), graph_size=imgui.ImVec2(-1, 60))
|
||||||
else:
|
else:
|
||||||
imgui.text_disabled(f"(no history data for {key})")
|
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")
|
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_screenshots_panel")
|
||||||
|
|
||||||
def _render_discussion_panel(self) -> None:
|
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")
|
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_discussion_panel")
|
||||||
# THINKING indicator
|
# THINKING indicator
|
||||||
is_thinking = self.ai_status in ['sending...', 'streaming...', 'running powershell...']
|
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"])
|
exp, opened = imgui.begin("Theme", self.show_windows["Theme"])
|
||||||
self.show_windows["Theme"] = bool(opened)
|
self.show_windows["Theme"] = bool(opened)
|
||||||
if exp:
|
if exp:
|
||||||
|
self._render_frosted_background(imgui.get_window_pos(), imgui.get_window_size())
|
||||||
imgui.text("Palette")
|
imgui.text("Palette")
|
||||||
cp = theme.get_current_palette()
|
cp = theme.get_current_palette()
|
||||||
if imgui.begin_combo("##pal", cp):
|
if imgui.begin_combo("##pal", cp):
|
||||||
@@ -3948,6 +4052,10 @@ def hello():
|
|||||||
gui_cfg["crt_filter_enabled"] = self.ui_crt_filter
|
gui_cfg["crt_filter_enabled"] = self.ui_crt_filter
|
||||||
self._flush_to_config()
|
self._flush_to_config()
|
||||||
models.save_config(self.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()
|
self._flush_to_config()
|
||||||
models.save_config(self.config)
|
models.save_config(self.config)
|
||||||
imgui.end()
|
imgui.end()
|
||||||
@@ -3993,6 +4101,8 @@ def hello():
|
|||||||
|
|
||||||
def _post_init(self) -> None:
|
def _post_init(self) -> None:
|
||||||
theme.apply_current()
|
theme.apply_current()
|
||||||
|
self.shader_manager.setup_background_shader()
|
||||||
|
self.shader_manager.setup_frosted_glass_shader()
|
||||||
|
|
||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
"""Initializes the ImGui runner and starts the main application loop."""
|
"""Initializes the ImGui runner and starts the main application loop."""
|
||||||
|
|||||||
@@ -5,6 +5,113 @@ class ShaderManager:
|
|||||||
self.program = None
|
self.program = None
|
||||||
self.bg_program = None
|
self.bg_program = None
|
||||||
self.pp_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:
|
def compile_shader(self, vertex_src: str, fragment_src: str) -> int:
|
||||||
program = gl.glCreateProgram()
|
program = gl.glCreateProgram()
|
||||||
@@ -79,9 +186,44 @@ void main() {
|
|||||||
uniform float u_time;
|
uniform float u_time;
|
||||||
uniform vec2 u_resolution;
|
uniform vec2 u_resolution;
|
||||||
out vec4 FragColor;
|
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() {
|
void main() {
|
||||||
vec2 uv = gl_FragCoord.xy / u_resolution.xy;
|
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);
|
FragColor = vec4(col, 1.0);
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
@@ -97,6 +239,7 @@ void main() {
|
|||||||
u_res_loc = gl.glGetUniformLocation(self.bg_program, "u_resolution")
|
u_res_loc = gl.glGetUniformLocation(self.bg_program, "u_resolution")
|
||||||
if u_res_loc != -1:
|
if u_res_loc != -1:
|
||||||
gl.glUniform2f(u_res_loc, float(width), float(height))
|
gl.glUniform2f(u_res_loc, float(width), float(height))
|
||||||
|
self._ensure_vao()
|
||||||
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
|
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
|
||||||
gl.glUseProgram(0)
|
gl.glUseProgram(0)
|
||||||
|
|
||||||
@@ -148,6 +291,114 @@ void main() {
|
|||||||
u_time_loc = gl.glGetUniformLocation(self.pp_program, "u_time")
|
u_time_loc = gl.glGetUniformLocation(self.pp_program, "u_time")
|
||||||
if u_time_loc != -1:
|
if u_time_loc != -1:
|
||||||
gl.glUniform1f(u_time_loc, float(time))
|
gl.glUniform1f(u_time_loc, float(time))
|
||||||
|
self._ensure_vao()
|
||||||
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
|
gl.glDrawArrays(gl.GL_TRIANGLE_STRIP, 0, 4)
|
||||||
gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
|
gl.glBindTexture(gl.GL_TEXTURE_2D, 0)
|
||||||
gl.glUseProgram(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)
|
||||||
|
|||||||
@@ -235,6 +235,10 @@ _current_font_size: float = 16.0
|
|||||||
_current_scale: float = 1.0
|
_current_scale: float = 1.0
|
||||||
_transparency: float = 1.0
|
_transparency: float = 1.0
|
||||||
_child_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
|
# ------------------------------------------------------------------ public API
|
||||||
|
|
||||||
@@ -269,6 +273,34 @@ def set_child_transparency(val: float) -> None:
|
|||||||
_child_transparency = val
|
_child_transparency = val
|
||||||
apply(_current_palette)
|
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:
|
def apply(palette_name: str) -> None:
|
||||||
"""
|
"""
|
||||||
Apply a named palette by setting all ImGui style colors and applying global professional styling.
|
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"]["scale"] = _current_scale
|
||||||
config["theme"]["transparency"] = _transparency
|
config["theme"]["transparency"] = _transparency
|
||||||
config["theme"]["child_transparency"] = _child_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.write(f"[DEBUG theme_2] save_to_config: palette={_current_palette}, transparency={_transparency}\n")
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
def load_from_config(config: dict) -> None:
|
def load_from_config(config: dict) -> None:
|
||||||
"""Read [theme] from config. Font is handled separately at startup."""
|
"""Read [theme] from config. Font is handled separately at startup."""
|
||||||
import sys
|
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", {})
|
t = config.get("theme", {})
|
||||||
sys.stderr.write(f"[DEBUG theme_2] load_from_config raw: {t}\n")
|
sys.stderr.write(f"[DEBUG theme_2] load_from_config raw: {t}\n")
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
@@ -369,6 +405,10 @@ def load_from_config(config: dict) -> None:
|
|||||||
_current_scale = float(t.get("scale", 1.0))
|
_current_scale = float(t.get("scale", 1.0))
|
||||||
_transparency = float(t.get("transparency", 1.0))
|
_transparency = float(t.get("transparency", 1.0))
|
||||||
_child_transparency = float(t.get("child_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.write(f"[DEBUG theme_2] load_from_config effective: palette={_current_palette}, transparency={_transparency}\n")
|
||||||
sys.stderr.flush()
|
sys.stderr.flush()
|
||||||
|
|
||||||
|
|||||||
42
tests/test_fbo_capture.py
Normal file
42
tests/test_fbo_capture.py
Normal 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
tests/test_frosted_glass_shader.py
Normal file
19
tests/test_frosted_glass_shader.py
Normal 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
tests/test_gui_frosted_integration.py
Normal file
89
tests/test_gui_frosted_integration.py
Normal 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
|
||||||
@@ -33,3 +33,18 @@ def test_theme_apply_sets_rounding_and_padding(monkeypatch):
|
|||||||
assert mock_style.item_spacing == (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.item_inner_spacing == (4.0, 4.0)
|
||||||
assert mock_style.scrollbar_size == 14.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
|
||||||
|
|||||||
Reference in New Issue
Block a user