meltygui 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meltygui/__init__.py +107 -0
- meltygui/accounts/__init__.py +0 -0
- meltygui/accounts/internet_accounts.py +1355 -0
- meltygui/chat/__init__.py +91 -0
- meltygui/chat/activity.py +75 -0
- meltygui/chat/backends.py +36 -0
- meltygui/chat/chat_interface.py +732 -0
- meltygui/chat/chat_proxy.py +352 -0
- meltygui/chat/codex_proxy.py +592 -0
- meltygui/chat/codex_settings.py +100 -0
- meltygui/chat/codex_transport.py +60 -0
- meltygui/chat/command_parser.py +204 -0
- meltygui/chat/images.py +227 -0
- meltygui/chat/messages.py +417 -0
- meltygui/chat/metadata.py +139 -0
- meltygui/chat/writer_locks.py +64 -0
- meltygui/code/__init__.py +0 -0
- meltygui/code/basic_converters.py +533 -0
- meltygui/code/chain_converters.py +2111 -0
- meltygui/code/code_checks.py +2209 -0
- meltygui/code/core_syntax.py +1430 -0
- meltygui/code/file_converters.py +1933 -0
- meltygui/code/fileref.py +702 -0
- meltygui/code/hotswap_guard.py +144 -0
- meltygui/code/libcst_conversion.py +9724 -0
- meltygui/code/live_instrument.py +392 -0
- meltygui/code/live_view.py +2490 -0
- meltygui/code/melty_scan.py +2684 -0
- meltygui/code/new_codecs.py +1255 -0
- meltygui/code/new_converters.py +3017 -0
- meltygui/code/project_code.py +278 -0
- meltygui/code/source_context.py +63 -0
- meltygui/code/symbol_roster.py +1588 -0
- meltygui/code/syntax_check.py +34 -0
- meltygui/code/syntax_check_worker.py +114 -0
- meltygui/completion/__init__.py +0 -0
- meltygui/completion/fim.py +1232 -0
- meltygui/completion/fim_context.py +481 -0
- meltygui/completion/providers/__init__.py +0 -0
- meltygui/completion/providers/anthropic_oauth.py +446 -0
- meltygui/completion/providers/anthropic_requests.py +69 -0
- meltygui/completion/providers/claude.py +203 -0
- meltygui/completion/providers/claude_usage.py +499 -0
- meltygui/completion/providers/codex_accounts.py +180 -0
- meltygui/completion/providers/copilot.py +617 -0
- meltygui/completion/providers/oauth_popup.py +220 -0
- meltygui/completion/providers/ollama.py +320 -0
- meltygui/completion/providers/profiles.py +23 -0
- meltygui/core/README.md +88 -0
- meltygui/core/__init__.py +1 -0
- meltygui/core/automation/__init__.py +1 -0
- meltygui/core/automation/action_core.py +153 -0
- meltygui/core/automation/collection_action.py +45 -0
- meltygui/core/automation/mcp_eval.py +167 -0
- meltygui/core/automation/mcp_hotswap.py +107 -0
- meltygui/core/automation/mcp_query.py +552 -0
- meltygui/core/automation/mcp_server.py +657 -0
- meltygui/core/automation/orchestration_core.py +2636 -0
- meltygui/core/automation/query_core.py +124 -0
- meltygui/core/automation/search_core.py +26 -0
- meltygui/core/automation/selector_core.py +340 -0
- meltygui/core/automation/value_core.py +1752 -0
- meltygui/core/cache/__init__.py +1 -0
- meltygui/core/cache/cache_diagnostics.py +0 -0
- meltygui/core/cache/invalidation_decoration.py +153 -0
- meltygui/core/cache/invalidation_tracker.py +43 -0
- meltygui/core/cache/tile_cache.py +5968 -0
- meltygui/core/conversion/__init__.py +1 -0
- meltygui/core/conversion/bubbling.py +599 -0
- meltygui/core/conversion/cache_tree.py +188 -0
- meltygui/core/conversion/chain.py +113 -0
- meltygui/core/conversion/converter_register.py +145 -0
- meltygui/core/conversion/data_decoration.py +67 -0
- meltygui/core/conversion/dict_conversion.py +1815 -0
- meltygui/core/conversion/dict_conversion_util.py +177 -0
- meltygui/core/conversion/dynamic_obj.py +89 -0
- meltygui/core/conversion/graph_compare.py +183 -0
- meltygui/core/conversion/load_save_v2.py +1032 -0
- meltygui/core/conversion/missing_saved_class.py +39 -0
- meltygui/core/conversion/path_finder.py +606 -0
- meltygui/core/conversion/render_host.py +1083 -0
- meltygui/core/core_render.py +6537 -0
- meltygui/core/definition_hotswap.py +231 -0
- meltygui/core/diagnostics/__init__.py +1 -0
- meltygui/core/diagnostics/attribute_churn.py +38 -0
- meltygui/core/diagnostics/fps_counter.py +39 -0
- meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
- meltygui/core/diagnostics/inspection_core.py +169 -0
- meltygui/core/diagnostics/monitor_core.py +105 -0
- meltygui/core/diagnostics/notifications.py +706 -0
- meltygui/core/diagnostics/perf_trace.py +281 -0
- meltygui/core/diagnostics/profile_decoration.py +88 -0
- meltygui/core/diagnostics/resize_trace.py +62 -0
- meltygui/core/diagnostics/screenshot_core.py +247 -0
- meltygui/core/diagnostics/session_status.py +98 -0
- meltygui/core/diagnostics/trace_core.py +445 -0
- meltygui/core/files/__init__.py +1 -0
- meltygui/core/files/file_core.py +208 -0
- meltygui/core/files/file_explorer_core.py +104 -0
- meltygui/core/files/file_tree_core.py +198 -0
- meltygui/core/files/file_watch_core.py +43 -0
- meltygui/core/files/import_graph_core.py +43 -0
- meltygui/core/files/metadata_core.py +51 -0
- meltygui/core/graphics/__init__.py +1 -0
- meltygui/core/graphics/cuda_context_core.py +166 -0
- meltygui/core/graphics/cuda_interop_core.py +136 -0
- meltygui/core/graphics/cuda_kernel_core.py +91 -0
- meltygui/core/graphics/framebuffer_recorder.py +337 -0
- meltygui/core/graphics/gl_state.py +658 -0
- meltygui/core/graphics/lut_core.py +52 -0
- meltygui/core/graphics/overlay_renderer.py +984 -0
- meltygui/core/graphics/scene_target.py +180 -0
- meltygui/core/graphics/screenshot.py +439 -0
- meltygui/core/graphics/shader_func.py +478 -0
- meltygui/core/graphics/tensor_core.py +45 -0
- meltygui/core/graphics/text_texture.py +329 -0
- meltygui/core/graphics/wayland_color.py +635 -0
- meltygui/core/input/__init__.py +1 -0
- meltygui/core/input/collision.py +165 -0
- meltygui/core/input/drag_drop_core.py +1525 -0
- meltygui/core/input/hypr_left_drag.py +323 -0
- meltygui/core/input/input_core.py +245 -0
- meltygui/core/input/input_handler.py +1101 -0
- meltygui/core/input/mouse_cursor.py +355 -0
- meltygui/core/input/pynput_backend.py +1054 -0
- meltygui/core/input/space_mouse.py +338 -0
- meltygui/core/input/touchpad_backend.py +393 -0
- meltygui/core/input/view_selection.py +177 -0
- meltygui/core/layout/__init__.py +1 -0
- meltygui/core/layout/column_core.py +2153 -0
- meltygui/core/layout/cursor_core.py +161 -0
- meltygui/core/layout/dropdown_core.py +278 -0
- meltygui/core/layout/edge_constraints.py +155 -0
- meltygui/core/layout/grid_core.py +117 -0
- meltygui/core/layout/header_core.py +23 -0
- meltygui/core/layout/header_runtime.py +67 -0
- meltygui/core/layout/layout_core.py +87 -0
- meltygui/core/layout/tile_manager_core.py +591 -0
- meltygui/core/melty.py +6726 -0
- meltygui/core/module_map.json +896 -0
- meltygui/core/module_names.py +19 -0
- meltygui/core/rendering/__init__.py +1 -0
- meltygui/core/rendering/core_decoration.py +431 -0
- meltygui/core/rendering/core_render_helpers.py +328 -0
- meltygui/core/rendering/func_metadata.py +398 -0
- meltygui/core/rendering/mode.py +818 -0
- meltygui/core/rendering/mode_defaults.py +41 -0
- meltygui/core/rendering/modes.py +136 -0
- meltygui/core/rendering/parameter_core.py +1665 -0
- meltygui/core/rendering/render_dispatch.py +1891 -0
- meltygui/core/rendering/render_funcs.py +273 -0
- meltygui/core/rendering/shaped.py +312 -0
- meltygui/core/rendering/window_decoration.py +25 -0
- meltygui/core/runtime/__init__.py +1 -0
- meltygui/core/runtime/app.py +735 -0
- meltygui/core/runtime/app_session.py +140 -0
- meltygui/core/runtime/background.py +564 -0
- meltygui/core/runtime/extensions.py +53 -0
- meltygui/core/runtime/gc_manager.py +1163 -0
- meltygui/core/runtime/lifecycle.py +21 -0
- meltygui/core/runtime/paths.py +27 -0
- meltygui/core/runtime/settings.py +14 -0
- meltygui/core/runtime/singleton.py +16 -0
- meltygui/core/runtime/thread_safe_bool.py +24 -0
- meltygui/core/runtime/thread_signal.py +30 -0
- meltygui/core/runtime/toggles.py +3115 -0
- meltygui/core/services/__init__.py +1 -0
- meltygui/core/services/account_core.py +10 -0
- meltygui/core/services/chat_core.py +19 -0
- meltygui/core/services/claude_terminal_core.py +346 -0
- meltygui/core/services/terminal_core.py +458 -0
- meltygui/core/services/terminal_runtime.py +78 -0
- meltygui/core/styling/__init__.py +1 -0
- meltygui/core/styling/color_core.py +46 -0
- meltygui/core/styling/fonts.py +639 -0
- meltygui/core/styling/global_style.py +338 -0
- meltygui/core/styling/style.py +198 -0
- meltygui/core/styling/style_core.py +522 -0
- meltygui/core/styling/warm_start.py +149 -0
- meltygui/core/windowing/__init__.py +1 -0
- meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
- meltygui/core/windowing/backends/__init__.py +1 -0
- meltygui/core/windowing/backends/imgui_renderer.py +138 -0
- meltygui/core/windowing/backends/native_wayland.py +850 -0
- meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
- meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
- meltygui/core/windowing/backends/wayland_protocol.py +101 -0
- meltygui/core/windowing/dock_core.py +182 -0
- meltygui/core/windowing/frame_geometry.py +42 -0
- meltygui/core/windowing/geometry_feed.py +864 -0
- meltygui/core/windowing/glfw_utils.py +1343 -0
- meltygui/core/windowing/os_frame.py +1552 -0
- meltygui/core/windowing/surface.py +643 -0
- meltygui/core/windowing/titlebar.py +1560 -0
- meltygui/core/windowing/titlebar_buttons.py +281 -0
- meltygui/core/windowing/wayland_move.py +932 -0
- meltygui/core/windowing/window_api.py +62 -0
- meltygui/core/windowing/window_constants.py +339 -0
- meltygui/core/windowing/window_visibility.py +162 -0
- meltygui/debug/__init__.py +0 -0
- meltygui/debug/app_view_utils.py +9 -0
- meltygui/editor/__init__.py +0 -0
- meltygui/editor/bash_syntax.py +30 -0
- meltygui/editor/code_line_fast.py +152 -0
- meltygui/editor/diff.py +139 -0
- meltygui/editor/external_changes.py +159 -0
- meltygui/editor/file_header.py +41 -0
- meltygui/editor/live_usage.py +169 -0
- meltygui/editor/live_view_views.py +1803 -0
- meltygui/editor/pending_save.py +1351 -0
- meltygui/editor/roster_tints.py +547 -0
- meltygui/editor/source_preview.py +16 -0
- meltygui/editor/source_tools.py +10 -0
- meltygui/editor/source_ui.py +70 -0
- meltygui/editor/spell_check.py +77 -0
- meltygui/editor/text_editor.py +9076 -0
- meltygui/editor/usage_picker.py +538 -0
- meltygui/events/__init__.py +0 -0
- meltygui/events/example.py +118 -0
- meltygui/examples/__init__.py +0 -0
- meltygui/examples/columns_demo.py +33 -0
- meltygui/examples/columns_window_demo.py +60 -0
- meltygui/examples/context_menu_demo.py +24 -0
- meltygui/examples/context_menu_window_demo.py +49 -0
- meltygui/examples/gui_playground.py +127 -0
- meltygui/examples/live_view_playground.py +256 -0
- meltygui/examples/lora.py +43 -0
- meltygui/examples/lora_data.py +59 -0
- meltygui/examples/lora_policies.py +67 -0
- meltygui/examples/mode_demo.py +49 -0
- meltygui/examples/modifies_demo.py +119 -0
- meltygui/examples/scalar_policies.py +60 -0
- meltygui/examples/style_layouts.py +149 -0
- meltygui/examples/tile_manager_demo.py +74 -0
- meltygui/examples/tint_demo.py +160 -0
- meltygui/examples/tint_functions.py +56 -0
- meltygui/examples/trace_demo.py +88 -0
- meltygui/examples/two_windows.py +36 -0
- meltygui/files/__init__.py +0 -0
- meltygui/files/fast_file_explorer.py +439 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
- meltygui/graphics/__init__.py +6 -0
- meltygui/graphics/base.py +85 -0
- meltygui/graphics/examples.py +507 -0
- meltygui/graphics/executor.py +520 -0
- meltygui/graphics/filter.py +667 -0
- meltygui/graphics/filter.pyi +856 -0
- meltygui/graphics/generate_stubs.py +22 -0
- meltygui/graphics/registry.py +203 -0
- meltygui/graphics/shader_compiler.py +155 -0
- meltygui/graphics/shaders.py +1302 -0
- meltygui/graphics/stub_generator.py +250 -0
- meltygui/graphics/texture_manager.py +170 -0
- meltygui/graphics/texture_min_max.py +295 -0
- meltygui/hdr_color.py +757 -0
- meltygui/image_load.py +308 -0
- meltygui/model/__init__.py +1 -0
- meltygui/model/account_model.py +142 -0
- meltygui/model/camera_model.py +159 -0
- meltygui/model/chat_model.py +86 -0
- meltygui/model/code_model.py +62 -0
- meltygui/model/code_proxy_model.py +876 -0
- meltygui/model/collection_model.py +41 -0
- meltygui/model/color_model.py +127 -0
- meltygui/model/cuda_tensor_model.py +36 -0
- meltygui/model/cuda_texture_model.py +149 -0
- meltygui/model/dropdown_model.py +137 -0
- meltygui/model/file_metadata_model.py +73 -0
- meltygui/model/file_model.py +289 -0
- meltygui/model/format_model.py +390 -0
- meltygui/model/graph_model.py +98 -0
- meltygui/model/icon_model.py +1024 -0
- meltygui/model/import_graph_model.py +468 -0
- meltygui/model/layout_model.py +15 -0
- meltygui/model/lut_model.py +266 -0
- meltygui/model/search_model.py +173 -0
- meltygui/model/tensor_model.py +402 -0
- meltygui/model/terminal_model.py +329 -0
- meltygui/model/texture_model.py +146 -0
- meltygui/model/tile_model.py +24 -0
- meltygui/model/trace_model.py +198 -0
- meltygui/model/trace_report_model.py +146 -0
- meltygui/models/__init__.py +0 -0
- meltygui/models/file_meta.py +613 -0
- meltygui/models/function_console.py +184 -0
- meltygui/models/orchestration.py +48 -0
- meltygui/pbr.py +1576 -0
- meltygui/png_unfilter.c +49 -0
- meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
- meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
- meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
- meltygui/resources/dejavu/LICENSE.txt +78 -0
- meltygui/resources/fontawesome-LICENSE.txt +121 -0
- meltygui/resources/fontawesome-webfont.ttf +0 -0
- meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
- meltygui/resources/jetbrains-weights/OFL.txt +93 -0
- meltygui/resources/jetbrains-weights/README.md +2 -0
- meltygui/state/__init__.py +0 -0
- meltygui/state/account_state.py +22 -0
- meltygui/state/animation_state.py +97 -0
- meltygui/state/annotation_state.py +22 -0
- meltygui/state/chat_state.py +36 -0
- meltygui/state/code_state.py +10 -0
- meltygui/state/core_enums.py +59 -0
- meltygui/state/core_markers.py +115 -0
- meltygui/state/core_undo.py +1075 -0
- meltygui/state/file_state.py +75 -0
- meltygui/state/graph_state.py +26 -0
- meltygui/state/inspection_state.py +47 -0
- meltygui/state/menu_state.py +10 -0
- meltygui/state/model_enums.py +11 -0
- meltygui/state/new_core_model.py +2394 -0
- meltygui/state/orchestration_state.py +14 -0
- meltygui/state/query_state.py +24 -0
- meltygui/state/tensor_state.py +10 -0
- meltygui/state/terminal_state.py +23 -0
- meltygui/state/trace_state.py +32 -0
- meltygui/state/voxel_state.py +12 -0
- meltygui/text_index.py +816 -0
- meltygui/utils/__init__.py +0 -0
- meltygui/utils/jump_to_code.py +344 -0
- meltygui/utils/pkl_inspect.py +90 -0
- meltygui/utils/render_utils.py +1419 -0
- meltygui/view/__init__.py +1 -0
- meltygui/view/account_view.py +524 -0
- meltygui/view/action_view.py +73 -0
- meltygui/view/chat_decoration_view.py +112 -0
- meltygui/view/chat_view.py +1703 -0
- meltygui/view/code_view.py +2677 -0
- meltygui/view/collection_view.py +1185 -0
- meltygui/view/color_view.py +824 -0
- meltygui/view/control_view.py +559 -0
- meltygui/view/decoration_view.py +439 -0
- meltygui/view/diagnostic_view.py +177 -0
- meltygui/view/dropdown_view.py +1082 -0
- meltygui/view/file_view.py +1599 -0
- meltygui/view/graph_cuda_view.py +105 -0
- meltygui/view/graph_view.py +882 -0
- meltygui/view/header_view.py +848 -0
- meltygui/view/input_view.py +238 -0
- meltygui/view/inspection_view.py +1783 -0
- meltygui/view/layout_view.py +596 -0
- meltygui/view/lut_view.py +45 -0
- meltygui/view/menu_view.py +212 -0
- meltygui/view/orchestration_view.py +658 -0
- meltygui/view/query_view.py +118 -0
- meltygui/view/search_view.py +342 -0
- meltygui/view/tab_view.py +258 -0
- meltygui/view/tensor_view.py +430 -0
- meltygui/view/terminal_view.py +355 -0
- meltygui/view/text_view.py +8031 -0
- meltygui/view/texture_view.py +480 -0
- meltygui/view/tile_view.py +38 -0
- meltygui/view/trace_view.py +923 -0
- meltygui/view/voxel_cuda_view.py +824 -0
- meltygui/view/voxel_view.py +1860 -0
- meltygui/view/window_view.py +111 -0
- meltygui-0.1.0.dist-info/METADATA +143 -0
- meltygui-0.1.0.dist-info/RECORD +372 -0
- meltygui-0.1.0.dist-info/WHEEL +4 -0
- meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,552 @@
|
|
|
1
|
+
"""Typed MCP queries over live Melty state (the render thread's view of it).
|
|
2
|
+
|
|
3
|
+
The tools in mcp_server.py (`find_views`, `describe_view`, `hit_test`,
|
|
4
|
+
`param_sources`, `tile_cache`) each call one `collect_*` function here through
|
|
5
|
+
`mcp_eval.request_call`, so the walk runs ON the render thread between frames
|
|
6
|
+
(draw_states, the BVH, the input handler's per-frame tables and the tile cache
|
|
7
|
+
are all render-thread state) and comes back as a JSON-serializable dict.
|
|
8
|
+
Every result carries `frame` — `Melty.frame_count` when it was read.
|
|
9
|
+
|
|
10
|
+
Views are addressed by `draw_state._tile_id` (the cache key AND the event
|
|
11
|
+
view_id, `"<name>##<hash>"`), matched exact → unique substring → `ds.id`
|
|
12
|
+
prefix. Plain functions, no imgui: a headless test renders a view through the
|
|
13
|
+
wrapper and calls the collectors directly (tests/test_mcp_query.py).
|
|
14
|
+
"""
|
|
15
|
+
import inspect
|
|
16
|
+
import json
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# ----------------------------------------------------------------------------
|
|
20
|
+
# Helpers
|
|
21
|
+
# ----------------------------------------------------------------------------
|
|
22
|
+
|
|
23
|
+
def _melty():
|
|
24
|
+
from meltygui.core.melty import Melty
|
|
25
|
+
return Melty
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def _short(value, limit=120):
|
|
29
|
+
"""One-line repr for kwargs / values, bounded (a tensor repr can be MBs)."""
|
|
30
|
+
try:
|
|
31
|
+
import torch
|
|
32
|
+
if isinstance(value, torch.Tensor):
|
|
33
|
+
return f"Tensor{tuple(value.shape)} {value.dtype} {value.device}"
|
|
34
|
+
except Exception:
|
|
35
|
+
pass
|
|
36
|
+
try:
|
|
37
|
+
text = repr(value)
|
|
38
|
+
except Exception as exc:
|
|
39
|
+
text = f"<repr failed: {type(exc).__name__}>"
|
|
40
|
+
text = text.replace("\n", "\\n")
|
|
41
|
+
return text if len(text) <= limit else text[:limit - 1] + "…"
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _func_info(func):
|
|
45
|
+
"""(qualname, "file:line") of a render func, unwrapped past @render_func."""
|
|
46
|
+
if func is None:
|
|
47
|
+
return None, None
|
|
48
|
+
try:
|
|
49
|
+
raw = inspect.unwrap(func)
|
|
50
|
+
except Exception:
|
|
51
|
+
raw = func
|
|
52
|
+
name = getattr(raw, "__qualname__", None) or getattr(raw, "__name__", None) or repr(raw)
|
|
53
|
+
where = None
|
|
54
|
+
code = getattr(raw, "__code__", None)
|
|
55
|
+
if code is not None:
|
|
56
|
+
where = f"{code.co_filename}:{code.co_firstlineno}"
|
|
57
|
+
return name, where
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _rect(ds):
|
|
61
|
+
"""Live [left, top, width, height]; the cached abs_* when the live
|
|
62
|
+
accessors aren't available (test stubs)."""
|
|
63
|
+
try:
|
|
64
|
+
left, top = ds._abs_left(), ds._abs_top()
|
|
65
|
+
except Exception:
|
|
66
|
+
left, top = getattr(ds, "abs_left", 0.0), getattr(ds, "abs_top", 0.0)
|
|
67
|
+
return [round(float(left), 1), round(float(top), 1),
|
|
68
|
+
round(float(getattr(ds, "width", 0.0) or 0.0), 1),
|
|
69
|
+
round(float(getattr(ds, "height", 0.0) or 0.0), 1)]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def _rect4(rect):
|
|
73
|
+
if rect is None:
|
|
74
|
+
return None
|
|
75
|
+
try:
|
|
76
|
+
return [round(float(v), 1) for v in rect]
|
|
77
|
+
except Exception:
|
|
78
|
+
return None
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _window_name(ds):
|
|
82
|
+
"""The title list_windows shows for a root window (ManagedWindow.name),
|
|
83
|
+
else the draw_state's own name / tile_id prefix (nested windows)."""
|
|
84
|
+
if ds is None:
|
|
85
|
+
return None
|
|
86
|
+
Melty = _melty()
|
|
87
|
+
tile_id = getattr(ds, "_tile_id", None)
|
|
88
|
+
managed = Melty.registered_windows.get(tile_id) if tile_id is not None else None
|
|
89
|
+
if managed is not None and getattr(managed, "name", None):
|
|
90
|
+
return managed.name
|
|
91
|
+
name = getattr(ds, "name", None)
|
|
92
|
+
if name:
|
|
93
|
+
return name
|
|
94
|
+
return str(tile_id).split("##")[0] if tile_id else None
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _all_views():
|
|
98
|
+
"""Every draw_state Melty knows about right now: BVH-indexed views, the
|
|
99
|
+
root windows and the paint-ordered window list, deduped by identity."""
|
|
100
|
+
Melty = _melty()
|
|
101
|
+
seen = {}
|
|
102
|
+
for ds in list(Melty._bvh_id_to_ds.values()):
|
|
103
|
+
seen.setdefault(id(ds), ds)
|
|
104
|
+
for managed in list(Melty.registered_windows.values()):
|
|
105
|
+
ds = getattr(managed, "draw_state", None)
|
|
106
|
+
if ds is not None:
|
|
107
|
+
seen.setdefault(id(ds), ds)
|
|
108
|
+
for ds in list(Melty.paint_ordered_ds):
|
|
109
|
+
seen.setdefault(id(ds), ds)
|
|
110
|
+
return list(seen.values())
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def resolve_view(ref):
|
|
114
|
+
"""The draw_state a tool argument names: exact _tile_id, else the unique
|
|
115
|
+
tile_id substring, else a ds.id prefix. Returns (ds, error_text)."""
|
|
116
|
+
ref = (ref or "").strip()
|
|
117
|
+
if not ref:
|
|
118
|
+
return None, "no view given — pass a _tile_id from find_views / hit_test"
|
|
119
|
+
views = _all_views()
|
|
120
|
+
for ds in views:
|
|
121
|
+
if getattr(ds, "_tile_id", None) == ref:
|
|
122
|
+
return ds, None
|
|
123
|
+
lowered = ref.lower()
|
|
124
|
+
partial = [ds for ds in views
|
|
125
|
+
if lowered in str(getattr(ds, "_tile_id", "")).lower()]
|
|
126
|
+
if len(partial) == 1:
|
|
127
|
+
return partial[0], None
|
|
128
|
+
by_id = [ds for ds in views if str(getattr(ds, "id", "")).startswith(ref)]
|
|
129
|
+
if len(by_id) == 1:
|
|
130
|
+
return by_id[0], None
|
|
131
|
+
candidates = partial or by_id
|
|
132
|
+
if candidates:
|
|
133
|
+
names = sorted(str(getattr(ds, "_tile_id", ds.id)) for ds in candidates)[:12]
|
|
134
|
+
return None, f"'{ref}' matches {len(candidates)} views: " + ", ".join(names)
|
|
135
|
+
return None, f"no view matches '{ref}'"
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _tile_state(ds):
|
|
139
|
+
"""The tile-cache facts of one view, or None when it has no tile."""
|
|
140
|
+
Melty = _melty()
|
|
141
|
+
cache = Melty.cache
|
|
142
|
+
key = getattr(ds, "_tile_id", None)
|
|
143
|
+
if cache is None or key is None:
|
|
144
|
+
return None
|
|
145
|
+
tile = cache._tiles.get(key)
|
|
146
|
+
if tile is None:
|
|
147
|
+
return None
|
|
148
|
+
tracker_note = None
|
|
149
|
+
try:
|
|
150
|
+
from meltygui.core.cache.invalidation_tracker import InvalidateTracker
|
|
151
|
+
note = InvalidateTracker.invalidations.get(key)
|
|
152
|
+
if note is not None:
|
|
153
|
+
tracker_note = {"name": note.name, "reason": note.reason, "frame": note.frame}
|
|
154
|
+
except Exception:
|
|
155
|
+
pass
|
|
156
|
+
return {
|
|
157
|
+
"dirty": bool(tile.dirty),
|
|
158
|
+
"force_invalidate": bool(tile.force_invalidate),
|
|
159
|
+
"last_clean_cache_frame": tile.last_clean_frame,
|
|
160
|
+
"last_invalidated_cache_frame": tile.last_invalidated_frame,
|
|
161
|
+
"cache_frame": cache._frame_id,
|
|
162
|
+
"size": list(tile.size) if tile.size else None,
|
|
163
|
+
"alloc_size": list(tile.alloc_size) if tile.alloc_size else None,
|
|
164
|
+
"last_bump": getattr(tile, "_last_bump", None),
|
|
165
|
+
"blit_served_frame": getattr(ds, "_blit_served_frame", None),
|
|
166
|
+
"did_use_cache": getattr(ds, "_did_use_cache", None),
|
|
167
|
+
"tracker_note": tracker_note,
|
|
168
|
+
"parent_key": cache.key_to_parent_key.get(key),
|
|
169
|
+
"child_count": len(cache.parent_key_to_child_keys.get(key, ())),
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
def view_summary(ds):
|
|
174
|
+
"""The one-row description of a view (find_views rows, hit_test stack
|
|
175
|
+
entries, parent / child chains)."""
|
|
176
|
+
Melty = _melty()
|
|
177
|
+
func_name, func_where = _func_info(getattr(ds, "_view_func", None))
|
|
178
|
+
parent_window = getattr(ds, "parent_window", None)
|
|
179
|
+
try:
|
|
180
|
+
root_window = ds.root_window
|
|
181
|
+
except Exception:
|
|
182
|
+
root_window = None
|
|
183
|
+
try:
|
|
184
|
+
clip = _rect4(ds.abs_clip_rect)
|
|
185
|
+
except Exception:
|
|
186
|
+
clip = None
|
|
187
|
+
try:
|
|
188
|
+
abs_closed = bool(ds.abs_closed)
|
|
189
|
+
except Exception:
|
|
190
|
+
abs_closed = None
|
|
191
|
+
try:
|
|
192
|
+
abs_layer = ds.abs_layer
|
|
193
|
+
except Exception:
|
|
194
|
+
abs_layer = None
|
|
195
|
+
raw_input = getattr(ds, "_raw_input_value", None)
|
|
196
|
+
return {
|
|
197
|
+
"tile_id": getattr(ds, "_tile_id", None),
|
|
198
|
+
"id": getattr(ds, "id", None),
|
|
199
|
+
"name": getattr(ds, "name", None),
|
|
200
|
+
"func": func_name,
|
|
201
|
+
"func_where": func_where,
|
|
202
|
+
"rect": _rect(ds),
|
|
203
|
+
"clip_rect": clip,
|
|
204
|
+
"closed": bool(getattr(ds, "closed", False)),
|
|
205
|
+
"abs_closed": abs_closed,
|
|
206
|
+
"hidden_offscreen": bool(getattr(ds, "_hidden_offscreen", False)),
|
|
207
|
+
"closable": bool(getattr(ds, "closable", False)),
|
|
208
|
+
"nested_window": bool(getattr(ds, "nested_window", False)),
|
|
209
|
+
"layer": getattr(ds, "layer", None),
|
|
210
|
+
"abs_layer": abs_layer,
|
|
211
|
+
"z_pos": getattr(ds, "z_pos", None),
|
|
212
|
+
"depth": getattr(ds, "depth", None),
|
|
213
|
+
"hovered": id(ds) in Melty.bvh_hover_ids,
|
|
214
|
+
"bounding_hovered": bool(getattr(ds, "_bounding_hovered", False)),
|
|
215
|
+
"selected": bool(getattr(ds, "selected", False)),
|
|
216
|
+
"parent_window": getattr(parent_window, "_tile_id", None),
|
|
217
|
+
"window": _window_name(root_window),
|
|
218
|
+
"input_type": type(raw_input).__name__ if raw_input is not None else None,
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
# ----------------------------------------------------------------------------
|
|
223
|
+
# 1. find_views / describe_view
|
|
224
|
+
# ----------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
def collect_find_views(func="", name="", window="", include_closed=False, limit=50):
|
|
227
|
+
"""Views matching the case-insensitive substring filters, front-most
|
|
228
|
+
first (z_pos descending). `window` filters on the ROOT window's title."""
|
|
229
|
+
Melty = _melty()
|
|
230
|
+
func = (func or "").lower()
|
|
231
|
+
name = (name or "").lower()
|
|
232
|
+
window = (window or "").lower()
|
|
233
|
+
rows = []
|
|
234
|
+
for ds in _all_views():
|
|
235
|
+
if not include_closed:
|
|
236
|
+
try:
|
|
237
|
+
if ds.closed or ds.abs_closed:
|
|
238
|
+
continue
|
|
239
|
+
except Exception:
|
|
240
|
+
pass
|
|
241
|
+
row = view_summary(ds)
|
|
242
|
+
if func and func not in str(row["func"] or "").lower():
|
|
243
|
+
continue
|
|
244
|
+
if name and name not in f"{row['name'] or ''} {row['tile_id'] or ''}".lower():
|
|
245
|
+
continue
|
|
246
|
+
if window and window not in str(row["window"] or "").lower():
|
|
247
|
+
continue
|
|
248
|
+
rows.append(row)
|
|
249
|
+
rows.sort(key=lambda r: (r["z_pos"] or 0), reverse=True)
|
|
250
|
+
total = len(rows)
|
|
251
|
+
rows = rows[:max(1, int(limit))]
|
|
252
|
+
return {"frame": Melty.frame_count, "total": total, "returned": len(rows), "views": rows}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _kwargs_summary(ds):
|
|
256
|
+
kwargs = getattr(ds, "_kwargs", None) or {}
|
|
257
|
+
skip = {"next_kwargs", "draw_state", "o_kwargs", "input_value", "style_manager"}
|
|
258
|
+
return {str(k): _short(v) for k, v in kwargs.items()
|
|
259
|
+
if k not in skip and not str(k).startswith("_")}
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _parent_chain(ds, max_hops=32):
|
|
263
|
+
"""Render-tree ancestors, nearest first (the root's _parent is itself)."""
|
|
264
|
+
chain = []
|
|
265
|
+
seen = {id(ds)}
|
|
266
|
+
node = getattr(ds, "_parent", None)
|
|
267
|
+
while node is not None and id(node) not in seen and len(chain) < max_hops:
|
|
268
|
+
seen.add(id(node))
|
|
269
|
+
chain.append(view_summary(node))
|
|
270
|
+
node = getattr(node, "_parent", None)
|
|
271
|
+
return chain
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
def _window_chain(ds, max_hops=32):
|
|
275
|
+
chain = []
|
|
276
|
+
seen = {id(ds)}
|
|
277
|
+
node = getattr(ds, "parent_window", None)
|
|
278
|
+
while node is not None and id(node) not in seen and len(chain) < max_hops:
|
|
279
|
+
seen.add(id(node))
|
|
280
|
+
chain.append(view_summary(node))
|
|
281
|
+
node = getattr(node, "parent_window", None)
|
|
282
|
+
return chain
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def _children(ds, depth, budget):
|
|
286
|
+
"""Render-tree children (`_view_children`, this frame's, else `_children`)
|
|
287
|
+
as summaries, recursing `depth` levels within a total `budget` of rows."""
|
|
288
|
+
if depth <= 0 or budget[0] <= 0:
|
|
289
|
+
return []
|
|
290
|
+
kids = list((getattr(ds, "_view_children", None) or {}).values())
|
|
291
|
+
if not kids:
|
|
292
|
+
kids = list((getattr(ds, "_children", None) or {}).values())
|
|
293
|
+
out = []
|
|
294
|
+
for kid in kids:
|
|
295
|
+
if budget[0] <= 0:
|
|
296
|
+
break
|
|
297
|
+
if not hasattr(kid, "_tile_id"):
|
|
298
|
+
continue
|
|
299
|
+
budget[0] -= 1
|
|
300
|
+
row = view_summary(kid)
|
|
301
|
+
row["children"] = _children(kid, depth - 1, budget)
|
|
302
|
+
out.append(row)
|
|
303
|
+
return out
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _subscriptions_for(tile_id):
|
|
307
|
+
"""This frame's event registrations (view_id, priority, [event names],
|
|
308
|
+
cursor) whose view_id belongs to `tile_id` — from the input handler's
|
|
309
|
+
per-frame hovered table, so only views under the real pointer have any."""
|
|
310
|
+
Melty = _melty()
|
|
311
|
+
handler = getattr(Melty, "event_handler", None)
|
|
312
|
+
if handler is None:
|
|
313
|
+
return []
|
|
314
|
+
import meltygui.core.input.input_handler as IH
|
|
315
|
+
subs = []
|
|
316
|
+
for view_id, priority, keys in list(getattr(handler, "_hovered", [])):
|
|
317
|
+
owner = IH._view_id_to_tile_id.get(view_id, view_id)
|
|
318
|
+
if owner != tile_id and view_id != tile_id:
|
|
319
|
+
continue
|
|
320
|
+
names = IH._view_id_names_cache.get(view_id, {})
|
|
321
|
+
flags = IH._view_id_flags_cache.get(view_id, {})
|
|
322
|
+
events = []
|
|
323
|
+
for key in sorted(keys):
|
|
324
|
+
entry = {"event": names.get(key, f"{key[0]}:{key[1]}")}
|
|
325
|
+
inverted, non_blocking = flags.get(key, (False, False))
|
|
326
|
+
if inverted:
|
|
327
|
+
entry["inverted"] = True
|
|
328
|
+
if non_blocking:
|
|
329
|
+
entry["non_blocking"] = True
|
|
330
|
+
events.append(entry)
|
|
331
|
+
cursor = getattr(handler, "_view_cursor", {}).get(view_id)
|
|
332
|
+
subs.append({
|
|
333
|
+
"view_id": view_id,
|
|
334
|
+
"priority": priority,
|
|
335
|
+
"blocker": view_id in getattr(handler, "_blocker_views", ()),
|
|
336
|
+
"events": events,
|
|
337
|
+
"cursor": (str(cursor[0]) if cursor else None),
|
|
338
|
+
"cursor_rect": (_rect4(cursor[1]) if cursor and cursor[1] else None),
|
|
339
|
+
})
|
|
340
|
+
subs.sort(key=lambda s: s["priority"])
|
|
341
|
+
return subs
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def collect_describe_view(view, children_depth=1, max_children=60):
|
|
345
|
+
"""Everything about one view: summary, kwargs, auto_params, event rects,
|
|
346
|
+
this frame's subscriptions, the tile-cache entry, the render-tree and
|
|
347
|
+
window ancestor chains and the children subtree."""
|
|
348
|
+
Melty = _melty()
|
|
349
|
+
ds, error = resolve_view(view)
|
|
350
|
+
if ds is None:
|
|
351
|
+
return {"frame": Melty.frame_count, "error": error}
|
|
352
|
+
out = {"frame": Melty.frame_count, "view": view_summary(ds)}
|
|
353
|
+
out["kwargs"] = _kwargs_summary(ds)
|
|
354
|
+
auto_params = getattr(ds, "auto_params", None) or {}
|
|
355
|
+
out["auto_params"] = {str(k): _short(v) for k, v in auto_params.items()}
|
|
356
|
+
event_rects = getattr(ds, "_event_rects", None) or {}
|
|
357
|
+
out["event_rects"] = {str(k): [_rect4(r) for r in rects] for k, rects in event_rects.items()}
|
|
358
|
+
out["subscriptions"] = _subscriptions_for(ds._tile_id)
|
|
359
|
+
out["tile"] = _tile_state(ds)
|
|
360
|
+
out["window_pos"] = _rect4(getattr(ds, "window_pos", None))
|
|
361
|
+
out["content_size"] = [getattr(ds, "content_width", None), getattr(ds, "content_height", None)]
|
|
362
|
+
out["header_height"] = getattr(ds, "header_height", None)
|
|
363
|
+
out["scroll"] = [getattr(ds, "scroll_offset", None), getattr(ds, "scroll_offset_x", None)]
|
|
364
|
+
out["parents"] = _parent_chain(ds)
|
|
365
|
+
out["windows"] = _window_chain(ds)
|
|
366
|
+
out["children"] = _children(ds, int(children_depth), [int(max_children)])
|
|
367
|
+
return out
|
|
368
|
+
|
|
369
|
+
|
|
370
|
+
# ----------------------------------------------------------------------------
|
|
371
|
+
# 2. hit_test
|
|
372
|
+
# ----------------------------------------------------------------------------
|
|
373
|
+
|
|
374
|
+
def collect_hit_test(x, y):
|
|
375
|
+
"""The BVH stack at (x, y), front to back (bvh_query's order: z_pos),
|
|
376
|
+
each with its priority and — for the frame's real pointer position — the
|
|
377
|
+
event subscriptions and cursor registrations the input handler holds.
|
|
378
|
+
Subscriptions are only registered for views under the ACTUAL pointer,
|
|
379
|
+
so `pointer` says where they apply; at any other (x, y) the stack is
|
|
380
|
+
still exact but `subscriptions` are empty."""
|
|
381
|
+
Melty = _melty()
|
|
382
|
+
x, y = float(x), float(y)
|
|
383
|
+
try:
|
|
384
|
+
stack = list(Melty.bvh_query(x, y))
|
|
385
|
+
except Exception as exc:
|
|
386
|
+
return {"frame": Melty.frame_count, "error": f"bvh_query failed: {exc!r}"}
|
|
387
|
+
handler = getattr(Melty, "event_handler", None)
|
|
388
|
+
pointer = None
|
|
389
|
+
if handler is not None:
|
|
390
|
+
pointer = [getattr(handler, "_cursor_x", None), getattr(handler, "_cursor_y", None)]
|
|
391
|
+
rows = []
|
|
392
|
+
for ds in stack:
|
|
393
|
+
row = view_summary(ds)
|
|
394
|
+
# The stored z_pos → the priority an on_action from this frame
|
|
395
|
+
# started with (the `priority` property reads the frame's LIVE
|
|
396
|
+
# paint state, meaningless between frames).
|
|
397
|
+
try:
|
|
398
|
+
row["priority"] = ds._action_base_priority(getattr(ds, "z_pos", None) or 0)
|
|
399
|
+
except Exception:
|
|
400
|
+
row["priority"] = None
|
|
401
|
+
row["subscriptions"] = _subscriptions_for(ds._tile_id)
|
|
402
|
+
rows.append(row)
|
|
403
|
+
hovered = getattr(Melty, "hovered_ds", None)
|
|
404
|
+
out = {
|
|
405
|
+
"frame": Melty.frame_count,
|
|
406
|
+
"point": [x, y],
|
|
407
|
+
"pointer": pointer,
|
|
408
|
+
"pointer_matches_point": (pointer is not None and pointer[0] is not None
|
|
409
|
+
and abs(pointer[0] - x) < 0.5 and abs(pointer[1] - y) < 0.5),
|
|
410
|
+
"hovered_ds": getattr(hovered, "_tile_id", None),
|
|
411
|
+
"cursor_shape": str(getattr(handler, "cursor_shape", None)) if handler else None,
|
|
412
|
+
"drag_capture": ({str(k): [str(v[0]), str(v[1])] for k, v in
|
|
413
|
+
getattr(handler, "_drag_capture", {}).items()} if handler else {}),
|
|
414
|
+
"blockers": sorted(str(v) for v in getattr(handler, "_blocker_views", ())) if handler else [],
|
|
415
|
+
"stack": rows,
|
|
416
|
+
}
|
|
417
|
+
return out
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
# ----------------------------------------------------------------------------
|
|
421
|
+
# 3. param_sources
|
|
422
|
+
# ----------------------------------------------------------------------------
|
|
423
|
+
|
|
424
|
+
def collect_param_sources(view, param=""):
|
|
425
|
+
"""The inputs tab as data: per parameter the value the view reads, the
|
|
426
|
+
driving source (the SourcePriority pick), and every source that sets it
|
|
427
|
+
in priority order."""
|
|
428
|
+
Melty = _melty()
|
|
429
|
+
ds, error = resolve_view(view)
|
|
430
|
+
if ds is None:
|
|
431
|
+
return {"frame": Melty.frame_count, "error": error}
|
|
432
|
+
import meltygui.core.rendering.parameter_core as A
|
|
433
|
+
from meltygui.core.rendering.render_dispatch import param_source_matrix
|
|
434
|
+
try:
|
|
435
|
+
srcs = A._sources_for(ds)
|
|
436
|
+
except Exception as exc:
|
|
437
|
+
return {"frame": Melty.frame_count, "view": view_summary(ds),
|
|
438
|
+
"error": f"collect_input_sources failed: {exc!r}"}
|
|
439
|
+
kinds = srcs.get("kinds", {})
|
|
440
|
+
writable = set(srcs.get("writable", ()))
|
|
441
|
+
locations = srcs.get("locations", {})
|
|
442
|
+
matrix = param_source_matrix(srcs["sources"], func=getattr(ds, "_view_func", None),
|
|
443
|
+
include_unmatched=True)
|
|
444
|
+
if isinstance(matrix, tuple):
|
|
445
|
+
matrix = matrix[1]
|
|
446
|
+
wanted = (param or "").strip()
|
|
447
|
+
params = {}
|
|
448
|
+
for name, per_source in matrix.items():
|
|
449
|
+
if wanted and name != wanted:
|
|
450
|
+
continue
|
|
451
|
+
setters = []
|
|
452
|
+
for sname, value in per_source.items():
|
|
453
|
+
setters.append({
|
|
454
|
+
"source": sname,
|
|
455
|
+
"kind": kinds.get(sname),
|
|
456
|
+
"priority": A._source_priority(kinds.get(sname))[0],
|
|
457
|
+
"writable": sname in writable,
|
|
458
|
+
"unset": A._unset_value(value),
|
|
459
|
+
"value": _short(value),
|
|
460
|
+
})
|
|
461
|
+
setters.sort(key=lambda s: (s["priority"], s["source"]))
|
|
462
|
+
try:
|
|
463
|
+
driving = A.get_source_for(name, ds)
|
|
464
|
+
except Exception:
|
|
465
|
+
driving = None
|
|
466
|
+
try:
|
|
467
|
+
value = _short(A.anywhere_value(name, ds))
|
|
468
|
+
except Exception:
|
|
469
|
+
value = None
|
|
470
|
+
params[name] = {"value": value, "driving": driving, "setters": setters}
|
|
471
|
+
# A source's dict is a placeholder `{}` until its code host has parsed
|
|
472
|
+
# (background work over the next frames): report that instead of an
|
|
473
|
+
# empty setter dict that reads as "nothing sets it".
|
|
474
|
+
sources = [{"source": s, "kind": kinds.get(s), "writable": s in writable,
|
|
475
|
+
"parsed": bool(srcs["sources"].get(s)),
|
|
476
|
+
"location": (list(locations[s]) if locations.get(s) else None)}
|
|
477
|
+
for s in sorted(srcs["sources"], key=lambda s: A._source_priority(kinds.get(s)))]
|
|
478
|
+
pending = [s["source"] for s in sources if not s["parsed"] and s["kind"] != "draw state"]
|
|
479
|
+
out = {"frame": Melty.frame_count, "view": view_summary(ds), "sources": sources,
|
|
480
|
+
"params": params}
|
|
481
|
+
if pending:
|
|
482
|
+
out["pending_sources"] = pending
|
|
483
|
+
out["note"] = "unparsed sources are still loading in the background — query again in a few frames"
|
|
484
|
+
if wanted and wanted not in params:
|
|
485
|
+
out["error"] = f"no param '{wanted}' on this view; params: {sorted(matrix)}"
|
|
486
|
+
return out
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
# ----------------------------------------------------------------------------
|
|
490
|
+
# 4. tile_cache
|
|
491
|
+
# ----------------------------------------------------------------------------
|
|
492
|
+
|
|
493
|
+
def collect_tile_cache(view="", history_frames=0, limit=100):
|
|
494
|
+
"""Tile-cache state. With `view`: that tile's entry plus its invalidation
|
|
495
|
+
history over the last `history_frames` frames. Without: totals, the
|
|
496
|
+
per-frame body-run / cache-hit / capture counts over `history_frames`
|
|
497
|
+
(newest last) and the most recent invalidations (`limit`)."""
|
|
498
|
+
Melty = _melty()
|
|
499
|
+
cache = Melty.cache
|
|
500
|
+
if cache is None:
|
|
501
|
+
return {"frame": Melty.frame_count, "error": "no tile cache"}
|
|
502
|
+
now = Melty.frame_count
|
|
503
|
+
since = now - int(history_frames)
|
|
504
|
+
history = list(getattr(cache, "_invalidation_history", None) or ())
|
|
505
|
+
out = {"frame": now, "cache_frame": cache._frame_id, "enabled": bool(getattr(cache, "enabled", True))}
|
|
506
|
+
if view:
|
|
507
|
+
ds, error = resolve_view(view)
|
|
508
|
+
if ds is None:
|
|
509
|
+
return {"frame": now, "error": error}
|
|
510
|
+
out["view"] = view_summary(ds)
|
|
511
|
+
out["tile"] = _tile_state(ds)
|
|
512
|
+
key = ds._tile_id
|
|
513
|
+
rows = [h for h in history if h[1] == key and (history_frames <= 0 or h[0] >= since)]
|
|
514
|
+
out["invalidations"] = [
|
|
515
|
+
{"frame": f, "name": n, "reason": r, "force": force} for (f, _k, n, r, force) in rows[-int(limit):]]
|
|
516
|
+
return out
|
|
517
|
+
tiles = cache._tiles
|
|
518
|
+
dirty = sum(1 for t in tiles.values() if t.dirty)
|
|
519
|
+
out["tiles"] = len(tiles)
|
|
520
|
+
out["dirty_tiles"] = dirty
|
|
521
|
+
out["this_frame"] = {"body_runs": cache._frame_body_runs, "cache_hits": cache._frame_cache_hits}
|
|
522
|
+
stats = list(getattr(cache, "_frame_stats", None) or ())
|
|
523
|
+
if history_frames > 0:
|
|
524
|
+
stats = [s for s in stats if s[0] >= since]
|
|
525
|
+
else:
|
|
526
|
+
stats = stats[-10:]
|
|
527
|
+
out["frames"] = [{"frame": f, "body_runs": runs, "cache_hits": hits, "captures": caps}
|
|
528
|
+
for (f, runs, hits, caps) in stats]
|
|
529
|
+
rows = [h for h in history if history_frames <= 0 or h[0] >= since]
|
|
530
|
+
out["invalidations"] = [
|
|
531
|
+
{"frame": f, "key": k, "name": n, "reason": r, "force": force}
|
|
532
|
+
for (f, k, n, r, force) in rows[-int(limit):]]
|
|
533
|
+
# Who invalidates most over the window of the "per-frame invalidation" tell.
|
|
534
|
+
counts = {}
|
|
535
|
+
for (_f, k, n, _r, _force) in rows:
|
|
536
|
+
counts[(k, n)] = counts.get((k, n), 0) + 1
|
|
537
|
+
top = sorted(counts.items(), key=lambda kv: kv[1], reverse=True)[:15]
|
|
538
|
+
out["top_invalidators"] = [{"key": k, "name": n, "count": c} for ((k, n), c) in top]
|
|
539
|
+
return out
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
# ----------------------------------------------------------------------------
|
|
543
|
+
# Tool plumbing
|
|
544
|
+
# ----------------------------------------------------------------------------
|
|
545
|
+
|
|
546
|
+
def run_query(collect, model_server, timeout=10.0):
|
|
547
|
+
"""Run a no-arg collector on the render thread and return JSON text."""
|
|
548
|
+
from meltygui.core.automation.mcp_eval import request_call
|
|
549
|
+
result, error = request_call(collect, model_server, timeout=timeout)
|
|
550
|
+
if error:
|
|
551
|
+
return json.dumps({"error": error}, indent=1)
|
|
552
|
+
return json.dumps(result, indent=1, default=str)
|