Compare commits
6 Commits
5470f2106f
...
b4396697dd
| Author | SHA1 | Date | |
|---|---|---|---|
| b4396697dd | |||
| 31b38f0c77 | |||
| 2826ad53d8 | |||
| a91b8dcc99 | |||
| 74c9d4b992 | |||
| e28af48ae9 |
@@ -79,7 +79,7 @@ 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/)*
|
*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.*
|
*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**
|
11. [x] **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/)*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
# Implementation Plan: Advanced Text Viewer with Syntax Highlighting
|
# Implementation Plan: Advanced Text Viewer with Syntax Highlighting
|
||||||
|
|
||||||
## Phase 1: State & Interface Update
|
## Phase 1: State & Interface Update
|
||||||
- [ ] Task: Audit `src/gui_2.py` to ensure all `text_viewer_*` state variables are explicitly initialized in `App.__init__`.
|
- [x] Task: Audit `src/gui_2.py` to ensure all `text_viewer_*` state variables are explicitly initialized in `App.__init__`. e28af48
|
||||||
- [ ] Task: Implement: Update `App.__init__` to initialize `self.show_text_viewer`, `self.text_viewer_title`, `self.text_viewer_content`, and new `self.text_viewer_type` (defaulting to "text").
|
- [x] Task: Implement: Update `App.__init__` to initialize `self.show_text_viewer`, `self.text_viewer_title`, `self.text_viewer_content`, and new `self.text_viewer_type` (defaulting to "text"). e28af48
|
||||||
- [ ] Task: Implement: Update `self.text_viewer_wrap` (defaulting to True) to allow independent word wrap.
|
- [x] Task: Implement: Update `self.text_viewer_wrap` (defaulting to True) to allow independent word wrap. e28af48
|
||||||
- [ ] Task: Implement: Update `_render_text_viewer(self, label: str, content: str, text_type: str = "text")` signature and caller usage.
|
- [x] Task: Implement: Update `_render_text_viewer(self, label: str, content: str, text_type: str = "text")` signature and caller usage. e28af48
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 1: State & Interface Update' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 1: State & Interface Update' (Protocol in workflow.md) e28af48
|
||||||
|
|
||||||
## Phase 2: Core Rendering Logic (Code & MD)
|
## Phase 2: Core Rendering Logic (Code & MD)
|
||||||
- [ ] Task: Write Tests: Create a simulation test in `tests/test_gui_text_viewer.py` to verify the viewer opens and switches rendering paths based on `text_type`.
|
- [x] Task: Write Tests: Create a simulation test in `tests/test_gui_text_viewer.py` to verify the viewer opens and switches rendering paths based on `text_type`. a91b8dc
|
||||||
- [ ] Task: Implement: In `src/gui_2.py`, refactor the text viewer window loop to:
|
- [x] Task: Implement: In `src/gui_2.py`, refactor the text viewer window loop to: a91b8dc
|
||||||
- Use `MarkdownRenderer.render` if `text_type == "markdown"`.
|
- Use `MarkdownRenderer.render` if `text_type == "markdown"`. a91b8dc
|
||||||
- Use a cached `ImGuiColorTextEdit.TextEditor` if `text_type` matches a code language.
|
- Use a cached `ImGuiColorTextEdit.TextEditor` if `text_type` matches a code language. a91b8dc
|
||||||
- Fallback to `imgui.input_text_multiline` for plain text.
|
- Fallback to `imgui.input_text_multiline` for plain text. a91b8dc
|
||||||
- [ ] Task: Implement: Ensure the `TextEditor` instance is properly cached using a unique key for the text viewer to maintain state.
|
- [x] Task: Implement: Ensure the `TextEditor` instance is properly cached using a unique key for the text viewer to maintain state. a91b8dc
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 2: Core Rendering Logic' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 2: Core Rendering Logic' (Protocol in workflow.md) a91b8dc
|
||||||
|
|
||||||
## Phase 3: UI Features (Copy, Line Numbers, Wrap)
|
## Phase 3: UI Features (Copy, Line Numbers, Wrap)
|
||||||
- [ ] Task: Write Tests: Update `tests/test_gui_text_viewer.py` to verify the copy-to-clipboard functionality and word wrap toggle.
|
- [x] Task: Write Tests: Update `tests/test_gui_text_viewer.py` to verify the copy-to-clipboard functionality and word wrap toggle. a91b8dc
|
||||||
- [ ] Task: Implement: Add a "Copy" button to the text viewer title bar or a small toolbar at the top of the window.
|
- [x] Task: Implement: Add a "Copy" button to the text viewer title bar or a small toolbar at the top of the window. a91b8dc
|
||||||
- [ ] Task: Implement: Add a "Word Wrap" checkbox inside the text viewer window.
|
- [x] Task: Implement: Add a "Word Wrap" checkbox inside the text viewer window. a91b8dc
|
||||||
- [ ] Task: Implement: Configure the `TextEditor` instance to show line numbers and be read-only.
|
- [x] Task: Implement: Configure the `TextEditor` instance to show line numbers and be read-only. a91b8dc
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 3: UI Features' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 3: UI Features' (Protocol in workflow.md) a91b8dc
|
||||||
|
|
||||||
## Phase 4: Integration & Rollout
|
## Phase 4: Integration & Rollout
|
||||||
- [ ] Task: Implement: Update all existing calls to `_render_text_viewer` in `src/gui_2.py` (e.g., in `_render_files_panel`, `_render_tool_calls_panel`) to pass the correct `text_type` based on file extension or content.
|
- [x] Task: Implement: Update all existing calls to `_render_text_viewer` in `src/gui_2.py` (e.g., in `_render_files_panel`, `_render_tool_calls_panel`) to pass the correct `text_type` based on file extension or content. 2826ad5
|
||||||
- [ ] Task: Implement: Add "Markdown Preview" support for system prompt presets using the new text viewer logic.
|
- [x] Task: Implement: Add "Markdown Preview" support for system prompt presets using the new text viewer logic. 2826ad5
|
||||||
- [ ] Task: Conductor - User Manual Verification 'Phase 4: Integration & Rollout' (Protocol in workflow.md)
|
- [x] Task: Conductor - User Manual Verification 'Phase 4: Integration & Rollout' (Protocol in workflow.md) 2826ad5
|
||||||
|
|||||||
@@ -225,6 +225,9 @@ class HookHandler(BaseHTTPRequestHandler):
|
|||||||
for key, attr in gettable.items():
|
for key, attr in gettable.items():
|
||||||
val = _get_app_attr(app, attr, None)
|
val = _get_app_attr(app, attr, None)
|
||||||
result[key] = _serialize_for_api(val)
|
result[key] = _serialize_for_api(val)
|
||||||
|
result['show_text_viewer'] = _get_app_attr(app, 'show_text_viewer', False)
|
||||||
|
result['text_viewer_title'] = _get_app_attr(app, 'text_viewer_title', '')
|
||||||
|
result['text_viewer_type'] = _get_app_attr(app, 'text_viewer_type', 'markdown')
|
||||||
finally: event.set()
|
finally: event.set()
|
||||||
lock = _get_app_attr(app, "_pending_gui_tasks_lock")
|
lock = _get_app_attr(app, "_pending_gui_tasks_lock")
|
||||||
tasks = _get_app_attr(app, "_pending_gui_tasks")
|
tasks = _get_app_attr(app, "_pending_gui_tasks")
|
||||||
|
|||||||
@@ -252,6 +252,7 @@ class AppController:
|
|||||||
self.show_text_viewer: bool = False
|
self.show_text_viewer: bool = False
|
||||||
self.text_viewer_title: str = ''
|
self.text_viewer_title: str = ''
|
||||||
self.text_viewer_content: str = ''
|
self.text_viewer_content: str = ''
|
||||||
|
self.text_viewer_type: str = 'text'
|
||||||
self._pending_comms: List[Dict[str, Any]] = []
|
self._pending_comms: List[Dict[str, Any]] = []
|
||||||
self._pending_tool_calls: List[Dict[str, Any]] = []
|
self._pending_tool_calls: List[Dict[str, Any]] = []
|
||||||
self._pending_history_adds: List[Dict[str, Any]] = []
|
self._pending_history_adds: List[Dict[str, Any]] = []
|
||||||
@@ -375,7 +376,10 @@ class AppController:
|
|||||||
'ui_separate_tier1': 'ui_separate_tier1',
|
'ui_separate_tier1': 'ui_separate_tier1',
|
||||||
'ui_separate_tier2': 'ui_separate_tier2',
|
'ui_separate_tier2': 'ui_separate_tier2',
|
||||||
'ui_separate_tier3': 'ui_separate_tier3',
|
'ui_separate_tier3': 'ui_separate_tier3',
|
||||||
'ui_separate_tier4': 'ui_separate_tier4'
|
'ui_separate_tier4': 'ui_separate_tier4',
|
||||||
|
'show_text_viewer': 'show_text_viewer',
|
||||||
|
'text_viewer_title': 'text_viewer_title',
|
||||||
|
'text_viewer_type': 'text_viewer_type'
|
||||||
}
|
}
|
||||||
self._gettable_fields = dict(self._settable_fields)
|
self._gettable_fields = dict(self._settable_fields)
|
||||||
self._gettable_fields.update({
|
self._gettable_fields.update({
|
||||||
@@ -422,7 +426,10 @@ class AppController:
|
|||||||
'ui_separate_tier1': 'ui_separate_tier1',
|
'ui_separate_tier1': 'ui_separate_tier1',
|
||||||
'ui_separate_tier2': 'ui_separate_tier2',
|
'ui_separate_tier2': 'ui_separate_tier2',
|
||||||
'ui_separate_tier3': 'ui_separate_tier3',
|
'ui_separate_tier3': 'ui_separate_tier3',
|
||||||
'ui_separate_tier4': 'ui_separate_tier4'
|
'ui_separate_tier4': 'ui_separate_tier4',
|
||||||
|
'show_text_viewer': 'show_text_viewer',
|
||||||
|
'text_viewer_title': 'text_viewer_title',
|
||||||
|
'text_viewer_type': 'text_viewer_type'
|
||||||
})
|
})
|
||||||
self.perf_monitor = performance_monitor.get_monitor()
|
self.perf_monitor = performance_monitor.get_monitor()
|
||||||
self._perf_profiling_enabled = False
|
self._perf_profiling_enabled = False
|
||||||
|
|||||||
74
src/gui_2.py
74
src/gui_2.py
@@ -40,7 +40,7 @@ else:
|
|||||||
win32con = None
|
win32con = None
|
||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from imgui_bundle import imgui, hello_imgui, immapp, imgui_node_editor as ed
|
from imgui_bundle import imgui, hello_imgui, immapp, imgui_node_editor as ed, imgui_color_text_edit as ced
|
||||||
|
|
||||||
PROVIDERS: list[str] = ["gemini", "anthropic", "gemini_cli", "deepseek", "minimax"]
|
PROVIDERS: list[str] = ["gemini", "anthropic", "gemini_cli", "deepseek", "minimax"]
|
||||||
COMMS_CLAMP_CHARS: int = 300
|
COMMS_CLAMP_CHARS: int = 300
|
||||||
@@ -107,9 +107,16 @@ class App:
|
|||||||
self.controller.init_state()
|
self.controller.init_state()
|
||||||
self.show_windows.setdefault("Diagnostics", False)
|
self.show_windows.setdefault("Diagnostics", False)
|
||||||
self.controller.start_services(self)
|
self.controller.start_services(self)
|
||||||
|
self.controller._predefined_callbacks['_render_text_viewer'] = self._render_text_viewer
|
||||||
self.show_preset_manager_window = False
|
self.show_preset_manager_window = False
|
||||||
self.show_tool_preset_manager_window = False
|
self.show_tool_preset_manager_window = False
|
||||||
self.show_persona_editor_window = False
|
self.show_persona_editor_window = False
|
||||||
|
self.show_text_viewer = False
|
||||||
|
self.text_viewer_title = ''
|
||||||
|
self.text_viewer_content = ''
|
||||||
|
self.text_viewer_type = 'text'
|
||||||
|
self.text_viewer_wrap = True
|
||||||
|
self._text_viewer_editor: Optional[ced.TextEditor] = None
|
||||||
self.ui_active_tool_preset = ""
|
self.ui_active_tool_preset = ""
|
||||||
self.ui_active_bias_profile = ""
|
self.ui_active_bias_profile = ""
|
||||||
self.ui_active_persona = ""
|
self.ui_active_persona = ""
|
||||||
@@ -281,8 +288,9 @@ class App:
|
|||||||
f.write(data)
|
f.write(data)
|
||||||
# ---------------------------------------------------------------- helpers
|
# ---------------------------------------------------------------- helpers
|
||||||
|
|
||||||
def _render_text_viewer(self, label: str, content: str) -> None:
|
def _render_text_viewer(self, label: str, content: str, text_type: str = 'text', force_open: bool = False) -> None:
|
||||||
if imgui.button("[+]##" + str(id(content))):
|
self.text_viewer_type = text_type
|
||||||
|
if imgui.button("[+]##" + str(id(content))) or force_open:
|
||||||
self.show_text_viewer = True
|
self.show_text_viewer = True
|
||||||
self.text_viewer_title = label
|
self.text_viewer_title = label
|
||||||
self.text_viewer_content = content
|
self.text_viewer_content = content
|
||||||
@@ -292,6 +300,7 @@ class App:
|
|||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
if imgui.button("[+]##" + label + id_suffix):
|
if imgui.button("[+]##" + label + id_suffix):
|
||||||
self.show_text_viewer = True
|
self.show_text_viewer = True
|
||||||
|
self.text_viewer_type = 'markdown' if label in ('message', 'text', 'content', 'system') else 'json' if label in ('tool_calls', 'data') else 'powershell' if label == 'script' else 'text'
|
||||||
self.text_viewer_title = label
|
self.text_viewer_title = label
|
||||||
self.text_viewer_content = content
|
self.text_viewer_content = content
|
||||||
|
|
||||||
@@ -997,14 +1006,42 @@ 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:
|
||||||
if self.ui_word_wrap:
|
# Toolbar
|
||||||
imgui.begin_child("tv_wrap", imgui.ImVec2(-1, -1), False)
|
if imgui.button("Copy"):
|
||||||
imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
|
imgui.set_clipboard_text(self.text_viewer_content)
|
||||||
imgui.text(self.text_viewer_content)
|
imgui.same_line()
|
||||||
imgui.pop_text_wrap_pos()
|
_, self.text_viewer_wrap = imgui.checkbox("Word Wrap", self.text_viewer_wrap)
|
||||||
|
imgui.separator()
|
||||||
|
|
||||||
|
renderer = markdown_helper.get_renderer()
|
||||||
|
tv_type = getattr(self, "text_viewer_type", "text")
|
||||||
|
|
||||||
|
if tv_type == 'markdown':
|
||||||
|
imgui.begin_child("tv_md_scroll", imgui.ImVec2(-1, -1), True)
|
||||||
|
markdown_helper.render(self.text_viewer_content, context_id='text_viewer')
|
||||||
imgui.end_child()
|
imgui.end_child()
|
||||||
|
elif tv_type in renderer._lang_map:
|
||||||
|
if self._text_viewer_editor is None:
|
||||||
|
self._text_viewer_editor = ced.TextEditor()
|
||||||
|
self._text_viewer_editor.set_read_only_enabled(True)
|
||||||
|
self._text_viewer_editor.set_show_line_numbers_enabled(True)
|
||||||
|
|
||||||
|
# Sync text and language
|
||||||
|
lang_id = renderer._lang_map[tv_type]
|
||||||
|
if self._text_viewer_editor.get_text().strip() != self.text_viewer_content.strip():
|
||||||
|
self._text_viewer_editor.set_text(self.text_viewer_content)
|
||||||
|
self._text_viewer_editor.set_language_definition(lang_id)
|
||||||
|
|
||||||
|
self._text_viewer_editor.render('##tv_editor', a_size=imgui.ImVec2(-1, -1))
|
||||||
else:
|
else:
|
||||||
imgui.input_text_multiline("##tv_c", self.text_viewer_content, imgui.ImVec2(-1, -1), imgui.InputTextFlags_.read_only)
|
if self.text_viewer_wrap:
|
||||||
|
imgui.begin_child("tv_wrap", imgui.ImVec2(-1, -1), False)
|
||||||
|
imgui.push_text_wrap_pos(imgui.get_content_region_avail().x)
|
||||||
|
imgui.text(self.text_viewer_content)
|
||||||
|
imgui.pop_text_wrap_pos()
|
||||||
|
imgui.end_child()
|
||||||
|
else:
|
||||||
|
imgui.input_text_multiline("##tv_c", self.text_viewer_content, imgui.ImVec2(-1, -1), imgui.InputTextFlags_.read_only)
|
||||||
imgui.end()
|
imgui.end()
|
||||||
# Inject File Modal
|
# Inject File Modal
|
||||||
if getattr(self, "show_inject_modal", False):
|
if getattr(self, "show_inject_modal", False):
|
||||||
@@ -1138,16 +1175,14 @@ class App:
|
|||||||
imgui.separator()
|
imgui.separator()
|
||||||
imgui.text("Prompt Content:")
|
imgui.text("Prompt Content:")
|
||||||
imgui.same_line()
|
imgui.same_line()
|
||||||
if imgui.button("MD Preview" if not self._prompt_md_preview else "Edit Mode"):
|
if imgui.button("Pop out MD Preview"):
|
||||||
self._prompt_md_preview = not self._prompt_md_preview
|
self.text_viewer_title = f"Preset: {self._editing_preset_name}"
|
||||||
|
self.text_viewer_content = self._editing_preset_system_prompt
|
||||||
|
self.text_viewer_type = "markdown"
|
||||||
|
self.show_text_viewer = True
|
||||||
|
|
||||||
rem_y = imgui.get_content_region_avail().y
|
rem_y = imgui.get_content_region_avail().y
|
||||||
if self._prompt_md_preview:
|
_, self._editing_preset_system_prompt = imgui.input_text_multiline("##pcont", self._editing_preset_system_prompt, imgui.ImVec2(-1, rem_y))
|
||||||
if imgui.begin_child("prompt_preview", imgui.ImVec2(-1, rem_y), True):
|
|
||||||
markdown_helper.render(self._editing_preset_system_prompt, context_id="prompt_preset_preview")
|
|
||||||
imgui.end_child()
|
|
||||||
else:
|
|
||||||
_, self._editing_preset_system_prompt = imgui.input_text_multiline("##pcont", self._editing_preset_system_prompt, imgui.ImVec2(-1, rem_y))
|
|
||||||
imgui.end_child()
|
imgui.end_child()
|
||||||
|
|
||||||
# Footer Buttons
|
# Footer Buttons
|
||||||
@@ -2328,6 +2363,7 @@ def hello():
|
|||||||
if res:
|
if res:
|
||||||
self.text_viewer_title = path
|
self.text_viewer_title = path
|
||||||
self.text_viewer_content = res
|
self.text_viewer_content = res
|
||||||
|
self.text_viewer_type = Path(path).suffix.lstrip('.') if Path(path).suffix else 'text'
|
||||||
self.show_text_viewer = True
|
self.show_text_viewer = True
|
||||||
if code_block:
|
if code_block:
|
||||||
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
|
if is_nerv: imgui.push_style_color(imgui.Col_.text, vec4(80, 255, 80))
|
||||||
@@ -3021,7 +3057,7 @@ def hello():
|
|||||||
script = entry.get("script", "")
|
script = entry.get("script", "")
|
||||||
res = entry.get("result", "")
|
res = entry.get("result", "")
|
||||||
# Use a clear, formatted combined view for the detail window
|
# Use a clear, formatted combined view for the detail window
|
||||||
combined = f"COMMAND:\n{script}\n\n{'='*40}\nOUTPUT:\n{res}"
|
combined = f"**COMMAND:**\n```powershell\n{script}\n```\n\n---\n**OUTPUT:**\n```text\n{res}\n```"
|
||||||
|
|
||||||
script_preview = script.replace("\n", " ")[:150]
|
script_preview = script.replace("\n", " ")[:150]
|
||||||
if len(script) > 150: script_preview += "..."
|
if len(script) > 150: script_preview += "..."
|
||||||
@@ -3029,6 +3065,7 @@ def hello():
|
|||||||
if imgui.is_item_clicked():
|
if imgui.is_item_clicked():
|
||||||
self.text_viewer_title = f"Tool Call #{i+1} Details"
|
self.text_viewer_title = f"Tool Call #{i+1} Details"
|
||||||
self.text_viewer_content = combined
|
self.text_viewer_content = combined
|
||||||
|
self.text_viewer_type = 'markdown'
|
||||||
self.show_text_viewer = True
|
self.show_text_viewer = True
|
||||||
|
|
||||||
imgui.table_next_column()
|
imgui.table_next_column()
|
||||||
@@ -3038,6 +3075,7 @@ def hello():
|
|||||||
if imgui.is_item_clicked():
|
if imgui.is_item_clicked():
|
||||||
self.text_viewer_title = f"Tool Call #{i+1} Details"
|
self.text_viewer_title = f"Tool Call #{i+1} Details"
|
||||||
self.text_viewer_content = combined
|
self.text_viewer_content = combined
|
||||||
|
self.text_viewer_type = 'markdown'
|
||||||
self.show_text_viewer = True
|
self.show_text_viewer = True
|
||||||
|
|
||||||
imgui.end_table()
|
imgui.end_table()
|
||||||
|
|||||||
28
tests/test_gui_text_viewer.py
Normal file
28
tests/test_gui_text_viewer.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import pytest
|
||||||
|
import time
|
||||||
|
from src.api_hook_client import ApiHookClient
|
||||||
|
|
||||||
|
def test_text_viewer_state_update(live_gui) -> None:
|
||||||
|
"""
|
||||||
|
Verifies that we can set text viewer state and it is reflected in GUI state.
|
||||||
|
"""
|
||||||
|
client = ApiHookClient()
|
||||||
|
label = "Test Viewer Label"
|
||||||
|
content = "This is test content for the viewer."
|
||||||
|
text_type = "markdown"
|
||||||
|
|
||||||
|
# Add a task to push a custom callback that mutates the app state
|
||||||
|
def set_viewer_state(app):
|
||||||
|
app.show_text_viewer = True
|
||||||
|
app.text_viewer_title = label
|
||||||
|
app.text_viewer_content = content
|
||||||
|
app.text_viewer_type = text_type
|
||||||
|
|
||||||
|
client.push_event("custom_callback", {"callback": set_viewer_state})
|
||||||
|
time.sleep(0.5)
|
||||||
|
|
||||||
|
state = client.get_gui_state()
|
||||||
|
assert state is not None
|
||||||
|
assert state.get('show_text_viewer') == True
|
||||||
|
assert state.get('text_viewer_title') == label
|
||||||
|
assert state.get('text_viewer_type') == text_type
|
||||||
Reference in New Issue
Block a user