Compare commits
18 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 70bfcc61c2 | |||
| 976a3b63e0 | |||
| 0e0b185290 | |||
| 5b196dccf0 | |||
| 1771c32006 | |||
| 6a39f440d9 | |||
| cecbe2245a | |||
| ad9ffb68f5 | |||
| e9b7875de5 | |||
| 3d5c768598 | |||
| f297e7a3bd | |||
| 4a705a8060 | |||
| 55f3bd87e2 | |||
| e56d4af097 | |||
| 1328bc159b | |||
| 3a0d388502 | |||
| 879e0991c9 | |||
| d96adca67c |
@@ -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.
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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()
|
||||
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user