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,208 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Folder files — a directory tree rendered through a RenderHost.
|
|
3
|
+
|
|
4
|
+
`folder_io` is the plain stateful wrapper — it knows nothing about RenderHost.
|
|
5
|
+
It follows the same shape as `code_file_io` / `claude_terminals_io`:
|
|
6
|
+
|
|
7
|
+
discover() → view_func(dict) → apply() (folder_io)
|
|
8
|
+
|
|
9
|
+
except discover/apply are RECURSIVE: it reconciles a persistent nested dict
|
|
10
|
+
against the directory tree on disk — subfolders become nested dicts, files
|
|
11
|
+
become Path leaves. RenderHost wraps it so the program just sees "a dict
|
|
12
|
+
shaped like the folder". File CONTENT never flows through here: each Path
|
|
13
|
+
leaf renders via Mode.FILE_TREE → code_file_io, whose TextFileCodec owns that
|
|
14
|
+
file's whole-file load/edit/save round-trip — the recursion is pure structure.
|
|
15
|
+
|
|
16
|
+
The dict is mutable the way the terminals dict is: a key the user deletes
|
|
17
|
+
deletes its file (claude_terminals kills the tmux session, we unlink); a key
|
|
18
|
+
the user adds creates one (a held dict → mkdir, a held str → its contents).
|
|
19
|
+
A background poller re-runs discovery when the tree changes on disk (the
|
|
20
|
+
wrapper's body is blit-cached, so it wouldn't otherwise notice a file that
|
|
21
|
+
appeared/vanished with no edit to invalidate it).
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
import threading
|
|
25
|
+
|
|
26
|
+
from meltygui.core.runtime.lifecycle import module_is_live
|
|
27
|
+
import time
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
import meltygui_imgui as imgui
|
|
31
|
+
from meltygui.core.melty import Melty
|
|
32
|
+
from meltygui.core.rendering.modes import Modes
|
|
33
|
+
from meltygui.view.file_view import draw_file_tree
|
|
34
|
+
from meltygui.view.file_view import draw_file_metadata
|
|
35
|
+
from meltygui.core.windowing.glfw_utils import request_render
|
|
36
|
+
from meltygui.core.conversion.render_host import RenderHost
|
|
37
|
+
from meltygui.core.core_render import render_func
|
|
38
|
+
from meltygui.core.rendering.window_decoration import window
|
|
39
|
+
from meltygui.core.rendering.core_decoration import Core
|
|
40
|
+
|
|
41
|
+
from meltygui.model.file_model import _scan
|
|
42
|
+
from meltygui.model.file_model import _reconcile
|
|
43
|
+
from meltygui.model.file_model import _file_meta
|
|
44
|
+
from meltygui.model.file_model import _apply_meta
|
|
45
|
+
from meltygui.model.file_model import _collect_meta
|
|
46
|
+
|
|
47
|
+
# Keep the original default folder when the implementation moves packages.
|
|
48
|
+
ROOT = Path(__file__).resolve().parents[2] / "files"
|
|
49
|
+
from meltygui.core.runtime.paths import application_root
|
|
50
|
+
TEST_FOLDER = application_root()
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
# ── persisted per-file metadata (tint / expanded / order ...) ─────────────────────
|
|
54
|
+
# The tree holds display params the framework ignores: each file dict holds
|
|
55
|
+
# __overrides__['__<name>__'] = {param: value}, which core_render feeds into the
|
|
56
|
+
# child row's view (same mechanism as '# [tint=...]' comment overrides). The
|
|
57
|
+
# durable copy lives in AppModel.file_meta_collection.file_meta, keyed by
|
|
58
|
+
# absolute path string, so it persists with the next save. folder_io syncs both
|
|
59
|
+
# ways every run: APPLY meta → tree __overrides__ + key order before render,
|
|
60
|
+
# COLLECT tree → meta after render (UI edits land in __overrides__ via
|
|
61
|
+
# _LazyOverrideEntry / bubbling, drag reorders land as tree key order).
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
# ── debug window: the persisted per-file metadata tree ──────────────────────────
|
|
65
|
+
@window(disable_scroll=False, use_cache=True, tint=(0.18, 0.11, 0.11))
|
|
66
|
+
def file_meta_debug(_, draw_state=None):
|
|
67
|
+
"""Raw view of AppModel.file_meta_collection.file_meta — the path-keyed
|
|
68
|
+
params store the folder tree and the codec layer read/write (tint, order,
|
|
69
|
+
…). Rendered as a plain editable dict: edits land directly in the store
|
|
70
|
+
(bubble-free plain dicts, so a manual touch persists on the next root
|
|
71
|
+
save; deleting an entry clears that file's attributes)."""
|
|
72
|
+
meta = _file_meta()
|
|
73
|
+
if meta is None:
|
|
74
|
+
imgui.text("No app model loaded")
|
|
75
|
+
return
|
|
76
|
+
if not meta:
|
|
77
|
+
imgui.text("No file metadata yet")
|
|
78
|
+
return
|
|
79
|
+
return draw_file_metadata(meta)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ── the stateful wrapper: discover -> view_func(dict) -> apply ──────────────────
|
|
83
|
+
# Shaped like claude_terminals_io. It does not know about RenderHost - the host
|
|
84
|
+
# gives it `view_func` and holds whatever dict it passes through.
|
|
85
|
+
@render_func(use_cache=True, selectable=False, show_bg=False)
|
|
86
|
+
def folder_io(input_value, draw_state, view_func=None, root=None, external_change=False, **kwargs):
|
|
87
|
+
# ── IN: reconcile the HELD tree IN PLACE against the disk snapshot - same
|
|
88
|
+
# reason as claude_terminals_io: `input_value` IS the dict the host holds
|
|
89
|
+
# and the @window reads; a separate store would leave it a stale copy.
|
|
90
|
+
# The snapshot is PER ROOT: the single shared global handed the second
|
|
91
|
+
# window the first root's tree - the the store saw every ROOT entry as
|
|
92
|
+
# "new on disk", complete with Path leaves pointing into the wrong folder.
|
|
93
|
+
disk = _disk_trees.get(root)
|
|
94
|
+
if disk is None:
|
|
95
|
+
disk = _disk_trees[root] = _scan(root)
|
|
96
|
+
store = input_value if isinstance(input_value, dict) else {}
|
|
97
|
+
seen = getattr(draw_state, "_seen_paths", None)
|
|
98
|
+
if seen is None:
|
|
99
|
+
seen = draw_state._seen_paths = set()
|
|
100
|
+
_reconcile(store, disk, root, seen)
|
|
101
|
+
|
|
102
|
+
# ── META IN: persisted per-file params → the tree's __overrides__ + key
|
|
103
|
+
# order. On a change (first load, or the store edited elsewhere) the cached
|
|
104
|
+
# rows below still hold the old capture - invalidate this subtree so they
|
|
105
|
+
# repaint with the fresh kwargs.
|
|
106
|
+
meta = _file_meta()
|
|
107
|
+
if meta is not None and _apply_meta(store, root, meta):
|
|
108
|
+
tid = getattr(draw_state, "_tile_id", None)
|
|
109
|
+
if tid is not None and Melty.cache is not None:
|
|
110
|
+
Melty.cache.invalidate_up(tid, force=True, max_depth=8)
|
|
111
|
+
request_render()
|
|
112
|
+
|
|
113
|
+
# ── VIEW: hand the tree to the host's view func (which materializes + renders)
|
|
114
|
+
edited, value = view_func(input_value=store, external_change=False, **kwargs)
|
|
115
|
+
|
|
116
|
+
# ── META OUT: UI edits landed in __overrides__ (bubbling re-ran this body);
|
|
117
|
+
# drag reorders changed key order. Mirror both into the persisted store.
|
|
118
|
+
if meta is not None:
|
|
119
|
+
_collect_meta(store, root, meta)
|
|
120
|
+
|
|
121
|
+
imgui.text(str(root))
|
|
122
|
+
return edited, value
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# ── the proxy: to the program it's just a dict shaped like the folder ───────────
|
|
126
|
+
if "files_proxy" not in globals():
|
|
127
|
+
files_proxy = RenderHost(io_function=folder_io, input_value=None,
|
|
128
|
+
name="Folder Files", root=ROOT)
|
|
129
|
+
if "test_folder_proxy" not in globals():
|
|
130
|
+
test_folder_proxy = RenderHost(io_function=folder_io, input_value=None,
|
|
131
|
+
name="TestFolderProxy", root=TEST_FOLDER)
|
|
132
|
+
|
|
133
|
+
# ── per-root state: every root gets its own snapshot, window draw_state, and
|
|
134
|
+
# poller entry. (These were single globals once: the second window reconciled
|
|
135
|
+
# against the first root's snapshot and rendered ROOT's files.)
|
|
136
|
+
_disk_trees = globals().get("_disk_trees", {}) # root -> the poller's nested snapshot ({name: Path | dict})
|
|
137
|
+
_window_dss = globals().get("_window_dss", {}) # root -> that root's @window draw_state, stashed each render
|
|
138
|
+
_proxies = globals().get("_proxies", {ROOT: files_proxy, TEST_FOLDER: test_folder_proxy}) # poller targets
|
|
139
|
+
_poller_running = globals().get("_poller_running", False)
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
@window(input_value=files_proxy, tint=(0.36, 0.46, 0.59), disable_scroll=False, mode=Modes.WINDOW)
|
|
143
|
+
@render_func(show_bg=True, use_cache=True, shadow=True, selectable=False)
|
|
144
|
+
def draw_folder_files(input_value, draw_state, **kwargs):
|
|
145
|
+
_draw_tree(input_value, draw_state, ROOT)
|
|
146
|
+
return False, None
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
@window(input_value=test_folder_proxy, tint=(0.84, 0.933, 0.98), bg_offset=4, disable_scroll=False, mode=Modes.WINDOW)
|
|
150
|
+
@render_func(show_bg=False, use_cache=True, selectable=False)
|
|
151
|
+
def draw_test_folders(input_value, draw_state, **kwargs):
|
|
152
|
+
_draw_tree(input_value, draw_state, TEST_FOLDER)
|
|
153
|
+
return False, None
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def folder_proxy(root, name):
|
|
157
|
+
"""A RenderHost over folder_io for `root`, registered with the poller —
|
|
158
|
+
the reusable entry point for other views (e.g. playground.file_tree)."""
|
|
159
|
+
proxy = _proxies.get(root)
|
|
160
|
+
if proxy is None:
|
|
161
|
+
proxy = _proxies[root] = RenderHost(io_function=folder_io, input_value=None,
|
|
162
|
+
name=name, root=root)
|
|
163
|
+
return proxy
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def watch_folder(root, draw_state):
|
|
167
|
+
"""Per-frame from a folder window's body: start the (single, all-roots)
|
|
168
|
+
poller and stash this root's draw_state so a disk change re-renders it."""
|
|
169
|
+
global _poller_running
|
|
170
|
+
if not _poller_running:
|
|
171
|
+
threading.Thread(target=_poll_loop, daemon=True, name="folder-files-poller").start()
|
|
172
|
+
_poller_running = True
|
|
173
|
+
_window_dss[root] = draw_state
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _draw_tree(input_value, draw_state, root):
|
|
177
|
+
"""Shared @window body: watch the root and draw the held tree."""
|
|
178
|
+
watch_folder(root, draw_state)
|
|
179
|
+
|
|
180
|
+
# The tree is held one LEVEL UP under value name ("value") - same as
|
|
181
|
+
# claude_terminals: draw_collection on the proxy itself would render the
|
|
182
|
+
# single {"value": tree} key, not the files.
|
|
183
|
+
tree = input_value.get("value") if isinstance(input_value, dict) else {}
|
|
184
|
+
return draw_file_tree(tree if tree is not None else {}, root=root)
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# ── discovery poller: the wrappers' bodies are blit-cached, so they wouldn't
|
|
188
|
+
# show a file that appeared/vanished with no way to render them. One
|
|
189
|
+
# thread sweeps EVERY registered root; on a change to a tree, swap in the new
|
|
190
|
+
# snapshot AND re-render that root's @window.
|
|
191
|
+
def _poll_loop():
|
|
192
|
+
while module_is_live(globals()): # exits once script restart purges this module
|
|
193
|
+
if Core.melty.frame_count < 4:
|
|
194
|
+
time.sleep(2)
|
|
195
|
+
for root, proxy in list(_proxies.items()):
|
|
196
|
+
try:
|
|
197
|
+
cur = _scan(root)
|
|
198
|
+
if cur != _disk_trees.get(root):
|
|
199
|
+
_disk_trees[root] = cur
|
|
200
|
+
for ds in (getattr(proxy, "_wrapper_draw_state", None),
|
|
201
|
+
getattr(proxy, "_draw_state", None),
|
|
202
|
+
_window_dss.get(root)):
|
|
203
|
+
if ds is not None:
|
|
204
|
+
ds.invalidate()
|
|
205
|
+
request_render()
|
|
206
|
+
except Exception:
|
|
207
|
+
pass
|
|
208
|
+
time.sleep(1.0)
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""Directory subscriptions and browser runtime diagnostics."""
|
|
2
|
+
import os
|
|
3
|
+
import sys
|
|
4
|
+
from meltygui.core.melty import Melty, FileWatch
|
|
5
|
+
|
|
6
|
+
# ── the directory watch ─────────────────────────────────────────────────────
|
|
7
|
+
# directory (str) -> the listing draw_states showing it. One FileWatch emitter per
|
|
8
|
+
# dir in this map; a listing moves its emitter on navigation (watch_directory)
|
|
9
|
+
# and the observer-thread listener posts an invalidate to the render thread.
|
|
10
|
+
def _existing_directory_watchers():
|
|
11
|
+
# One-time resource transfer when this module is first loaded by hotswap.
|
|
12
|
+
# Existing FileWatch callbacks and per-view state retain this same mapping.
|
|
13
|
+
previous_module = sys.modules.get("meltygui.files.fast_file_explorer")
|
|
14
|
+
if previous_module is not None:
|
|
15
|
+
return vars(previous_module).get("_WATCHERS", {})
|
|
16
|
+
return {}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
_WATCHERS = globals().get("_WATCHERS")
|
|
20
|
+
if _WATCHERS is None:
|
|
21
|
+
_WATCHERS = _existing_directory_watchers()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _on_file_event(src_path):
|
|
25
|
+
"""FileWatch global listener (observer thread): an entry of a watched
|
|
26
|
+
directory changed — created, modified, moved, deleted — repaint the
|
|
27
|
+
listings showing that directory. Bumps nothing else; the listing's
|
|
28
|
+
mtime memo notices what changed."""
|
|
29
|
+
directory = os.path.dirname(src_path)
|
|
30
|
+
watchers = _WATCHERS.get(directory) or _WATCHERS.get(src_path)
|
|
31
|
+
if not watchers:
|
|
32
|
+
return
|
|
33
|
+
|
|
34
|
+
def repaint(draw_states=tuple(watchers)):
|
|
35
|
+
for draw_state in draw_states:
|
|
36
|
+
draw_state.invalidate()
|
|
37
|
+
Melty.post_to_render(repaint)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def watch_directory(draw_state, state, dir_key):
|
|
41
|
+
"""Point this listing's emitter at `dir_key`: the previous directory's
|
|
42
|
+
emitter is retired when no other listing shows it (the inotify instance
|
|
43
|
+
cap is per user), the new one scheduled through FileWatch.watch_dir."""
|
|
44
|
+
if state._watched == dir_key:
|
|
45
|
+
return
|
|
46
|
+
if _on_file_event not in FileWatch.global_listeners:
|
|
47
|
+
# Hotswap-safe: an older copy of this function is replaced by itself.
|
|
48
|
+
FileWatch.global_listeners[:] = [f for f in FileWatch.global_listeners
|
|
49
|
+
if getattr(f, "__name__", "") != "_on_file_event"]
|
|
50
|
+
FileWatch.global_listeners.append(_on_file_event)
|
|
51
|
+
FileWatch.start()
|
|
52
|
+
previous = state._watched
|
|
53
|
+
if previous is not None:
|
|
54
|
+
holders = _WATCHERS.get(previous)
|
|
55
|
+
if holders is not None:
|
|
56
|
+
holders.discard(draw_state)
|
|
57
|
+
if not holders:
|
|
58
|
+
del _WATCHERS[previous]
|
|
59
|
+
FileWatch.unwatch_dir(previous)
|
|
60
|
+
_WATCHERS.setdefault(dir_key, set()).add(draw_state)
|
|
61
|
+
FileWatch.watch_dir(dir_key)
|
|
62
|
+
state._watched = dir_key
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
from weakref import WeakKeyDictionary
|
|
67
|
+
_browser_size_traces = globals().get("_browser_size_traces", WeakKeyDictionary())
|
|
68
|
+
|
|
69
|
+
def _trace_browser_size(stage, draw_state, **details):
|
|
70
|
+
"""File browser size diagnostics (the nested-OS-window resize glitch):
|
|
71
|
+
one line per CHANGE of the view's box / content rect / the surface's
|
|
72
|
+
GLFW window and framebuffer size, to the bounded resize trace
|
|
73
|
+
(.melty cache root, resize-<pid>.log) and stderr. Never raises."""
|
|
74
|
+
try:
|
|
75
|
+
import sys
|
|
76
|
+
import meltygui.core.diagnostics.resize_trace as resize_trace
|
|
77
|
+
from meltygui import window_api as glfw
|
|
78
|
+
window = getattr(Melty, "glfw_window", None)
|
|
79
|
+
window_size = fb_size = None
|
|
80
|
+
if window is not None:
|
|
81
|
+
window_size = tuple(glfw.get_window_size(window))
|
|
82
|
+
fb_size = tuple(glfw.get_framebuffer_size(window))
|
|
83
|
+
try:
|
|
84
|
+
import meltygui.core.windowing.geometry_feed as geometry_feed
|
|
85
|
+
frame = geometry_feed._current_frame()
|
|
86
|
+
details["feed"] = (geometry_feed.backend(), geometry_feed._hypr_selector(),
|
|
87
|
+
geometry_feed.hypr_honors_geometry(),
|
|
88
|
+
None if frame is None else (frame.get("at"), frame.get("size")))
|
|
89
|
+
except Exception as error:
|
|
90
|
+
details["feed"] = f"error {error!r}"
|
|
91
|
+
stamp = (draw_state.width, draw_state.height, draw_state.abs_left, draw_state.abs_top,
|
|
92
|
+
window_size, fb_size, tuple(sorted(details.items())))
|
|
93
|
+
if _browser_size_traces.get(draw_state) == stamp:
|
|
94
|
+
return
|
|
95
|
+
_browser_size_traces[draw_state] = stamp
|
|
96
|
+
resize_trace.record(stage, draw_state, window_size=window_size, fb_size=fb_size,
|
|
97
|
+
gesture=bool(Melty.resize_gesture_live()), **details)
|
|
98
|
+
print(f"[{stage}] f{Melty.frame_count} view {draw_state.width}x{draw_state.height} "
|
|
99
|
+
f"at ({draw_state.abs_left}, {draw_state.abs_top}) window {window_size} "
|
|
100
|
+
f"fb {fb_size} {details}", file=sys.stderr, flush=True)
|
|
101
|
+
except Exception:
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
"""File tree — a general-purpose directory browser with collapsable folders.
|
|
2
|
+
|
|
3
|
+
Unlike folder_files (which materializes file CONTENT through a RenderHost),
|
|
4
|
+
this view is display-only: it walks the directory and draws just the names,
|
|
5
|
+
straight to the window draw_list — no per-row widgets, no draw_states per
|
|
6
|
+
file (the fast_dock.py interaction model: event params for clicks, blit
|
|
7
|
+
cache while idle, wrapper re-renders every frame while hovered). Click a
|
|
8
|
+
folder to expand/collapse it; double-click a file to open it
|
|
9
|
+
(FileTreeState.open_file, a stub for now).
|
|
10
|
+
|
|
11
|
+
Frame-to-frame state (which folders are expanded, selection) lives in
|
|
12
|
+
FileTreeState, injected by annotation the same way GLState/CodeState are.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import colorsys
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
import meltygui_imgui as imgui
|
|
19
|
+
from meltygui.hdr_color import pack_color
|
|
20
|
+
from meltygui.hdr_color import with_alpha
|
|
21
|
+
from meltygui.core.melty import Melty
|
|
22
|
+
from meltygui.core.conversion.dict_conversion import DictConversion
|
|
23
|
+
from meltygui.core.rendering.modes import Modes
|
|
24
|
+
from meltygui.core.windowing.glfw_utils import request_render
|
|
25
|
+
from meltygui.core.core_render import render_func
|
|
26
|
+
from meltygui.core.layout.header_runtime import _brightness_clamp_fn
|
|
27
|
+
import meltygui.model.import_graph_model as file_graph
|
|
28
|
+
from meltygui.core.runtime.toggles import Toggles
|
|
29
|
+
from meltygui.core.files.file_core import folder_proxy
|
|
30
|
+
from meltygui.core.files.file_core import watch_folder
|
|
31
|
+
from meltygui.model.file_model import _file_meta
|
|
32
|
+
from meltygui.core.input.drag_drop_core import DragDrop
|
|
33
|
+
from meltygui.core.cache.tile_cache import add_shadow
|
|
34
|
+
from meltygui.core.rendering.window_decoration import window
|
|
35
|
+
|
|
36
|
+
from meltygui.core.runtime.paths import application_root
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def open_file(path):
|
|
40
|
+
"""Route `path` into the code editor (summons the editor window)."""
|
|
41
|
+
from meltygui.core.runtime.extensions import open_source as open_in_editor
|
|
42
|
+
open_in_editor(str(path))
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _children(folder):
|
|
46
|
+
"""Visible entries, folders first, case-insensitive by name."""
|
|
47
|
+
try:
|
|
48
|
+
entries = [p for p in folder.iterdir()
|
|
49
|
+
if not p.name.startswith(".") and p.name != "__pycache__"]
|
|
50
|
+
except OSError:
|
|
51
|
+
return []
|
|
52
|
+
return sorted(entries, key=lambda p: (p.is_file(), p.name.lower()))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _flatten(folder, expanded, depth=0, rows=None):
|
|
56
|
+
"""The tree as drawn: one (path, depth) per visible row."""
|
|
57
|
+
if rows is None:
|
|
58
|
+
rows = []
|
|
59
|
+
for p in _children(folder):
|
|
60
|
+
rows.append((p, depth))
|
|
61
|
+
if p.is_dir() and str(p) in expanded:
|
|
62
|
+
_flatten(p, expanded, depth + 1, rows)
|
|
63
|
+
return rows
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _meta():
|
|
67
|
+
"""AppModel's path → FileMeta store (None before the model exists)."""
|
|
68
|
+
return _file_meta()
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _tint_of(meta, path):
|
|
72
|
+
"""The row's background tint (rgb), or None for an unpainted file — no
|
|
73
|
+
stored tint, or FileMeta's black-transparent default."""
|
|
74
|
+
from meltygui.models.file_meta import FileMeta
|
|
75
|
+
entry = meta.get(str(path)) if meta is not None else None
|
|
76
|
+
tint = FileMeta.painted_tint(entry)
|
|
77
|
+
return tuple(tint[:3]) if tint else None
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _ordered_children(folder, meta, position):
|
|
81
|
+
"""`folder`'s visible entries in FILE-META ORDER: an entry's position in
|
|
82
|
+
the file_meta dict is its rank; entries the dict doesn't know yet trail
|
|
83
|
+
in the natural order (folders first, case-insensitive by name)."""
|
|
84
|
+
natural = _children(folder)
|
|
85
|
+
unknown = float("inf")
|
|
86
|
+
ranked = [(position.get(str(p), unknown), i, p) for i, p in enumerate(natural)]
|
|
87
|
+
ranked.sort(key=lambda t: (t[0], t[1]))
|
|
88
|
+
return [p for _r, _i, p in ranked]
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _flatten_ordered(folder, expanded, meta, position, depth=0, rows=None):
|
|
92
|
+
"""The tree as drawn — one (path, depth) per visible row — in meta order."""
|
|
93
|
+
if rows is None:
|
|
94
|
+
rows = []
|
|
95
|
+
for p in _ordered_children(folder, meta, position):
|
|
96
|
+
rows.append((p, depth))
|
|
97
|
+
if p.is_dir() and str(p) in expanded:
|
|
98
|
+
_flatten_ordered(p, expanded, meta, position, depth + 1, rows)
|
|
99
|
+
return rows
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def reorder_siblings(meta, siblings, dragged, insert_index):
|
|
103
|
+
"""Move `dragged` to `insert_index` (pre-removal coordinates, the Reorder
|
|
104
|
+
convention) within `siblings` and write the new order into the file_meta
|
|
105
|
+
dict: every sibling gets an entry, and the siblings' existing SLOTS in the
|
|
106
|
+
dict (their key positions) are refilled in the new order, so nothing else
|
|
107
|
+
in the dict moves. Returns True when the order changed."""
|
|
108
|
+
from meltygui.models.file_meta import FileMeta
|
|
109
|
+
keys = [str(p) for p in siblings]
|
|
110
|
+
dragged_key = str(dragged)
|
|
111
|
+
if dragged_key not in keys:
|
|
112
|
+
return False
|
|
113
|
+
current = keys.index(dragged_key)
|
|
114
|
+
new_keys = list(keys)
|
|
115
|
+
new_keys.pop(current)
|
|
116
|
+
target = insert_index - 1 if current < insert_index else insert_index
|
|
117
|
+
target = max(0, min(target, len(new_keys)))
|
|
118
|
+
if target == current:
|
|
119
|
+
return False
|
|
120
|
+
new_keys.insert(target, dragged_key)
|
|
121
|
+
for k in keys:
|
|
122
|
+
if not isinstance(meta.get(k), dict):
|
|
123
|
+
meta[k] = FileMeta()
|
|
124
|
+
slots = [i for i, k in enumerate(meta) if k in set(keys)]
|
|
125
|
+
items = list(meta.items())
|
|
126
|
+
for slot, k in zip(slots, new_keys):
|
|
127
|
+
items[slot] = (k, meta[k])
|
|
128
|
+
meta.clear()
|
|
129
|
+
meta.update(items)
|
|
130
|
+
return True
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
from meltygui.view.file_view import render_file_tree
|
|
134
|
+
render_file_tree = window(initial={'width': 320, 'height': 540}, tint=(0.72, 0.79, 0.85))(render_file_tree)
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def _apply_row_drop(meta, dragged, visible, insert_index, position):
|
|
138
|
+
"""Translate a flattened-row drop into a sibling reorder. Returns True
|
|
139
|
+
when the file_meta order changed."""
|
|
140
|
+
if meta is None:
|
|
141
|
+
return False
|
|
142
|
+
parent = dragged.parent
|
|
143
|
+
siblings = _ordered_children(parent, meta, position)
|
|
144
|
+
below = visible[insert_index][0] if 0 <= insert_index < len(visible) else None
|
|
145
|
+
above = visible[insert_index - 1][0] if 0 < insert_index <= len(visible) else None
|
|
146
|
+
if below is not None and below.parent == parent:
|
|
147
|
+
sibling_index = siblings.index(below)
|
|
148
|
+
elif above is not None and above.parent == parent:
|
|
149
|
+
sibling_index = siblings.index(above) + 1
|
|
150
|
+
elif above is not None and above.is_dir() and above == parent:
|
|
151
|
+
sibling_index = 0 # dropped right above its own folder row
|
|
152
|
+
else:
|
|
153
|
+
return False
|
|
154
|
+
return reorder_siblings(meta, siblings, dragged, sibling_index)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
# # ── Framework file tree ──────────────────────────────────────────────────────
|
|
158
|
+
# # The same directory rendered through the framework: folder_files' RenderHost
|
|
159
|
+
# # (folder_io) holds the {name: Path | dict} tree - background loading, diff
|
|
160
|
+
# # reconcile, and the change poller for free - and draw_collection renders it
|
|
161
|
+
# # under Mode.FILE_TREE_NAMES (view/modes.py): folders are drawn with
|
|
162
|
+
# # expandollapsing headers, add/delete, drag reorder - all framework - files route
|
|
163
|
+
# # by name to draw_file_name below, which shows just the name. Contrast with
|
|
164
|
+
# # the flat draw-list tree above: ~no code here, one draw_state per file item.
|
|
165
|
+
#
|
|
166
|
+
# # Disabled while diagnosing frame-time: registering the repo ROOT causes the
|
|
167
|
+
# # folder poller to _poll_loop rescan 143k entries (venv included) every
|
|
168
|
+
# # second, starving the render thread.
|
|
169
|
+
# # files_host = folder_proxy(ROOT, "FileTreeRoot")
|
|
170
|
+
#
|
|
171
|
+
#
|
|
172
|
+
# @render_func(is_render_for="PosixPath", show_bg=False, selectable=True,
|
|
173
|
+
# use_cache=True, is_tree=True, with_header=draw_header)
|
|
174
|
+
# def draw_file_name(input_value=None, draw_state=None,
|
|
175
|
+
# left_mouse_double_clicked=False, **kwargs):
|
|
176
|
+
# # The header draws the name (the dict key) and carries selection/drag -
|
|
177
|
+
# # the body is only the double-click → open handler.
|
|
178
|
+
# if left_mouse_double_clicked:
|
|
179
|
+
# open_file(input_value)
|
|
180
|
+
# return False, input_value
|
|
181
|
+
#
|
|
182
|
+
#
|
|
183
|
+
# # @window(input_value=files_host, tint=(0.42, 0.36, 0.54), disable_scroll=False, mode=Modes.WINDOW)
|
|
184
|
+
# @render_func(show_bg=True, use_cache=False, shadow=True, selectable=False)
|
|
185
|
+
# def render_file_tree_melty(input_value=None, draw_state=None, **kwargs):
|
|
186
|
+
# from meltygui.core.rendering.render_funcs import RenderFuncs
|
|
187
|
+
# watch_folder(ROOT, draw_state)
|
|
188
|
+
# # Same shape as draw_folder_files: the host holds the tree one level down
|
|
189
|
+
# # under "value"; a simple top-level draw_collection, and the names-only
|
|
190
|
+
# # mode passed to the host.
|
|
191
|
+
# tree = input_value.get("value") if isinstance(input_value, dict) else {}
|
|
192
|
+
# RenderFuncs.draw_collection(tree if tree is not None else {}, name=ROOT.name,
|
|
193
|
+
# show_add_delete=True, new_item_type=str, temp=True,
|
|
194
|
+
# width=draw_state.content_width,
|
|
195
|
+
# show_scroll=True, show_bg=True,
|
|
196
|
+
# child_kwargs={"show_bg": True, "bg_offset": -4,
|
|
197
|
+
# "mode": Modes.FILE_TREE_NAMES})
|
|
198
|
+
# return True, None
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""File Watch Debug window — what the watcher and symbol index can see, now.
|
|
2
|
+
|
|
3
|
+
One row per known file, grouped by watched directory: whether its text is
|
|
4
|
+
baselined in Melty.code_cache (an external edit to an UNCACHED file is
|
|
5
|
+
invisible — nothing to diff), how many views watch it, whether it's
|
|
6
|
+
project-tracked (watch_project_files), whether external drift is currently
|
|
7
|
+
recorded for it, and the usage-symbol index status — the warmer's per-file
|
|
8
|
+
refs cache (fresh/stale vs disk mtime) plus how many cached span-usage
|
|
9
|
+
results are fresh under the current (mtime, pending-gen, index-gen)
|
|
10
|
+
signature. The rescan button re-runs the project walk to pick up files/dirs
|
|
11
|
+
created since startup.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import os
|
|
15
|
+
import sys
|
|
16
|
+
from collections import defaultdict
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
|
|
19
|
+
from meltygui.core.rendering.render_funcs import RenderFuncs
|
|
20
|
+
from meltygui.core.core_render import render_func
|
|
21
|
+
from meltygui.core.rendering.window_decoration import window
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _symbol_index_view():
|
|
25
|
+
"""Merged read-only view of the symbol index's per-file state across BOTH
|
|
26
|
+
module identities (src.lsd.… / lsd.… — the span/mtime stores are
|
|
27
|
+
sys-anchored and shared, but _index_refs_cache is module-level, so the
|
|
28
|
+
warmer may be filling either twin's dict)."""
|
|
29
|
+
refs, spans, snap, gen = {}, {}, {}, 0
|
|
30
|
+
for name in ("meltygui.code.libcst_conversion",
|
|
31
|
+
"lsd.gl_gui.view.core_conversion.libcst_conversion"):
|
|
32
|
+
m = sys.modules.get(name)
|
|
33
|
+
if m is None:
|
|
34
|
+
continue
|
|
35
|
+
refs.update(getattr(m, "_index_refs_cache", {}))
|
|
36
|
+
spans.update(getattr(m, "_symbol_usage_cache", {}))
|
|
37
|
+
snap.update(getattr(m, "_mtime_snapshot", {}))
|
|
38
|
+
gen = max(gen, getattr(m, "_index_generation", 0))
|
|
39
|
+
return refs, spans, snap, gen
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
from meltygui.view.file_view import file_watch_debug
|
|
43
|
+
file_watch_debug = window(disable_scroll=False, tint=(0.16296297311782837, 0.2611111, 0.24118687212467194))(file_watch_debug)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"""Import graph — every project file as a labelled box, every import a line.
|
|
2
|
+
|
|
3
|
+
The whole `file_graph.ImportGraph` drawn straight to the window draw_list
|
|
4
|
+
(the file_tree / fast_dock model: no per-node widgets, event params for
|
|
5
|
+
gestures, blit cache while idle). The graph's layered layout
|
|
6
|
+
(file_graph.layout_graph: column = dependency depth, left → right, rows
|
|
7
|
+
ordered to keep lines short) gives the ORDER; this view sizes it in
|
|
8
|
+
pixels — each node is a rounded box around its file name, a column is as
|
|
9
|
+
wide as its widest box, rows share one pitch — so nothing overlaps, then
|
|
10
|
+
projects it: screen = origin + (box − centre) · fit · zoom + pan, where
|
|
11
|
+
zoom 1 fits the whole graph in the window.
|
|
12
|
+
|
|
13
|
+
Gestures: middle-drag pans, wheel zooms about the cursor, click a box to
|
|
14
|
+
select it (its importers light in the file's tint, its imports in the
|
|
15
|
+
washed variant, everything else fades), Esc clears, `/` resets the camera.
|
|
16
|
+
Box size follows USAGE (importer count, log scale) through the font scale,
|
|
17
|
+
so the hubs read at a glance. Shares the graph with render_file_tree
|
|
18
|
+
through file_graph.current(): either window's Build button serves both.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
import colorsys
|
|
24
|
+
import math
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
import meltygui_imgui as imgui
|
|
28
|
+
from meltygui.hdr_color import pack_color
|
|
29
|
+
from meltygui.core.melty import Melty
|
|
30
|
+
from meltygui.core.conversion.dict_conversion import DictConversion
|
|
31
|
+
from meltygui.core.runtime.toggles import Toggles
|
|
32
|
+
from meltygui.core.windowing.glfw_utils import request_render
|
|
33
|
+
from meltygui.core.core_render import render_func
|
|
34
|
+
from meltygui.core.rendering.window_decoration import window
|
|
35
|
+
from meltygui.core.layout.header_runtime import _brightness_clamp_fn
|
|
36
|
+
import meltygui.model.import_graph_model as file_graph
|
|
37
|
+
from meltygui.core.files.file_tree_core import _meta
|
|
38
|
+
from meltygui.core.files.file_tree_core import _tint_of
|
|
39
|
+
from meltygui.core.files.file_tree_core import open_file
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
from meltygui.view.graph_view import render_import_graph
|
|
43
|
+
render_import_graph = window(initial={'width': 720, 'height': 620}, tint=(0.42, 0.36, 0.54), disable_scroll=True)(render_import_graph)
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import inspect
|
|
2
|
+
from functools import wraps
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def meta_preset(func, *o_args, **o_kwargs):
|
|
7
|
+
sig = inspect.signature(func)
|
|
8
|
+
params = sig.parameters
|
|
9
|
+
wanted_params = list(params.keys())
|
|
10
|
+
|
|
11
|
+
@wraps(func)
|
|
12
|
+
def wrapper(*args, **kwargs):
|
|
13
|
+
first_arg = args[0] if args else None
|
|
14
|
+
|
|
15
|
+
if 'for_type' in kwargs and not isinstance(first_arg, type):
|
|
16
|
+
def class_wrapper(cls):
|
|
17
|
+
inner_args = args[1:]
|
|
18
|
+
return wrapper(cls, *inner_args, **kwargs)
|
|
19
|
+
return class_wrapper
|
|
20
|
+
|
|
21
|
+
if isinstance(first_arg, type):
|
|
22
|
+
if 'default_value' in kwargs:
|
|
23
|
+
default_value = kwargs['default_value']
|
|
24
|
+
else:
|
|
25
|
+
default_value = None
|
|
26
|
+
for_type = kwargs.get('for_type', None)
|
|
27
|
+
kwargs.pop('for_type', None)
|
|
28
|
+
kwargs.pop('default_value', None)
|
|
29
|
+
args = args[1:] if len(args) > 1 else ()
|
|
30
|
+
|
|
31
|
+
retrieved_meta = func(default_value=default_value, *args, **kwargs)
|
|
32
|
+
if for_type is not None:
|
|
33
|
+
first_arg.default_meta_for = getattr(first_arg, 'default_meta_for', {})
|
|
34
|
+
first_arg.default_meta_for[for_type] = retrieved_meta
|
|
35
|
+
else:
|
|
36
|
+
first_arg.meta = retrieved_meta
|
|
37
|
+
|
|
38
|
+
return first_arg
|
|
39
|
+
else:
|
|
40
|
+
arg_idx = 0
|
|
41
|
+
# Clean up kwargs to only what the function wants
|
|
42
|
+
for wanted_param in wanted_params:
|
|
43
|
+
if wanted_param not in kwargs:
|
|
44
|
+
kwargs[wanted_param] = args[arg_idx] if arg_idx < len(args) else None
|
|
45
|
+
arg_idx += 1
|
|
46
|
+
|
|
47
|
+
retrieved_meta = func(**kwargs)
|
|
48
|
+
return retrieved_meta
|
|
49
|
+
|
|
50
|
+
wrapper.__meta_preset__ = True
|
|
51
|
+
return wrapper
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Shared framework graphics machinery."""
|