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.
Files changed (372) hide show
  1. meltygui/__init__.py +107 -0
  2. meltygui/accounts/__init__.py +0 -0
  3. meltygui/accounts/internet_accounts.py +1355 -0
  4. meltygui/chat/__init__.py +91 -0
  5. meltygui/chat/activity.py +75 -0
  6. meltygui/chat/backends.py +36 -0
  7. meltygui/chat/chat_interface.py +732 -0
  8. meltygui/chat/chat_proxy.py +352 -0
  9. meltygui/chat/codex_proxy.py +592 -0
  10. meltygui/chat/codex_settings.py +100 -0
  11. meltygui/chat/codex_transport.py +60 -0
  12. meltygui/chat/command_parser.py +204 -0
  13. meltygui/chat/images.py +227 -0
  14. meltygui/chat/messages.py +417 -0
  15. meltygui/chat/metadata.py +139 -0
  16. meltygui/chat/writer_locks.py +64 -0
  17. meltygui/code/__init__.py +0 -0
  18. meltygui/code/basic_converters.py +533 -0
  19. meltygui/code/chain_converters.py +2111 -0
  20. meltygui/code/code_checks.py +2209 -0
  21. meltygui/code/core_syntax.py +1430 -0
  22. meltygui/code/file_converters.py +1933 -0
  23. meltygui/code/fileref.py +702 -0
  24. meltygui/code/hotswap_guard.py +144 -0
  25. meltygui/code/libcst_conversion.py +9724 -0
  26. meltygui/code/live_instrument.py +392 -0
  27. meltygui/code/live_view.py +2490 -0
  28. meltygui/code/melty_scan.py +2684 -0
  29. meltygui/code/new_codecs.py +1255 -0
  30. meltygui/code/new_converters.py +3017 -0
  31. meltygui/code/project_code.py +278 -0
  32. meltygui/code/source_context.py +63 -0
  33. meltygui/code/symbol_roster.py +1588 -0
  34. meltygui/code/syntax_check.py +34 -0
  35. meltygui/code/syntax_check_worker.py +114 -0
  36. meltygui/completion/__init__.py +0 -0
  37. meltygui/completion/fim.py +1232 -0
  38. meltygui/completion/fim_context.py +481 -0
  39. meltygui/completion/providers/__init__.py +0 -0
  40. meltygui/completion/providers/anthropic_oauth.py +446 -0
  41. meltygui/completion/providers/anthropic_requests.py +69 -0
  42. meltygui/completion/providers/claude.py +203 -0
  43. meltygui/completion/providers/claude_usage.py +499 -0
  44. meltygui/completion/providers/codex_accounts.py +180 -0
  45. meltygui/completion/providers/copilot.py +617 -0
  46. meltygui/completion/providers/oauth_popup.py +220 -0
  47. meltygui/completion/providers/ollama.py +320 -0
  48. meltygui/completion/providers/profiles.py +23 -0
  49. meltygui/core/README.md +88 -0
  50. meltygui/core/__init__.py +1 -0
  51. meltygui/core/automation/__init__.py +1 -0
  52. meltygui/core/automation/action_core.py +153 -0
  53. meltygui/core/automation/collection_action.py +45 -0
  54. meltygui/core/automation/mcp_eval.py +167 -0
  55. meltygui/core/automation/mcp_hotswap.py +107 -0
  56. meltygui/core/automation/mcp_query.py +552 -0
  57. meltygui/core/automation/mcp_server.py +657 -0
  58. meltygui/core/automation/orchestration_core.py +2636 -0
  59. meltygui/core/automation/query_core.py +124 -0
  60. meltygui/core/automation/search_core.py +26 -0
  61. meltygui/core/automation/selector_core.py +340 -0
  62. meltygui/core/automation/value_core.py +1752 -0
  63. meltygui/core/cache/__init__.py +1 -0
  64. meltygui/core/cache/cache_diagnostics.py +0 -0
  65. meltygui/core/cache/invalidation_decoration.py +153 -0
  66. meltygui/core/cache/invalidation_tracker.py +43 -0
  67. meltygui/core/cache/tile_cache.py +5968 -0
  68. meltygui/core/conversion/__init__.py +1 -0
  69. meltygui/core/conversion/bubbling.py +599 -0
  70. meltygui/core/conversion/cache_tree.py +188 -0
  71. meltygui/core/conversion/chain.py +113 -0
  72. meltygui/core/conversion/converter_register.py +145 -0
  73. meltygui/core/conversion/data_decoration.py +67 -0
  74. meltygui/core/conversion/dict_conversion.py +1815 -0
  75. meltygui/core/conversion/dict_conversion_util.py +177 -0
  76. meltygui/core/conversion/dynamic_obj.py +89 -0
  77. meltygui/core/conversion/graph_compare.py +183 -0
  78. meltygui/core/conversion/load_save_v2.py +1032 -0
  79. meltygui/core/conversion/missing_saved_class.py +39 -0
  80. meltygui/core/conversion/path_finder.py +606 -0
  81. meltygui/core/conversion/render_host.py +1083 -0
  82. meltygui/core/core_render.py +6537 -0
  83. meltygui/core/definition_hotswap.py +231 -0
  84. meltygui/core/diagnostics/__init__.py +1 -0
  85. meltygui/core/diagnostics/attribute_churn.py +38 -0
  86. meltygui/core/diagnostics/fps_counter.py +39 -0
  87. meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
  88. meltygui/core/diagnostics/inspection_core.py +169 -0
  89. meltygui/core/diagnostics/monitor_core.py +105 -0
  90. meltygui/core/diagnostics/notifications.py +706 -0
  91. meltygui/core/diagnostics/perf_trace.py +281 -0
  92. meltygui/core/diagnostics/profile_decoration.py +88 -0
  93. meltygui/core/diagnostics/resize_trace.py +62 -0
  94. meltygui/core/diagnostics/screenshot_core.py +247 -0
  95. meltygui/core/diagnostics/session_status.py +98 -0
  96. meltygui/core/diagnostics/trace_core.py +445 -0
  97. meltygui/core/files/__init__.py +1 -0
  98. meltygui/core/files/file_core.py +208 -0
  99. meltygui/core/files/file_explorer_core.py +104 -0
  100. meltygui/core/files/file_tree_core.py +198 -0
  101. meltygui/core/files/file_watch_core.py +43 -0
  102. meltygui/core/files/import_graph_core.py +43 -0
  103. meltygui/core/files/metadata_core.py +51 -0
  104. meltygui/core/graphics/__init__.py +1 -0
  105. meltygui/core/graphics/cuda_context_core.py +166 -0
  106. meltygui/core/graphics/cuda_interop_core.py +136 -0
  107. meltygui/core/graphics/cuda_kernel_core.py +91 -0
  108. meltygui/core/graphics/framebuffer_recorder.py +337 -0
  109. meltygui/core/graphics/gl_state.py +658 -0
  110. meltygui/core/graphics/lut_core.py +52 -0
  111. meltygui/core/graphics/overlay_renderer.py +984 -0
  112. meltygui/core/graphics/scene_target.py +180 -0
  113. meltygui/core/graphics/screenshot.py +439 -0
  114. meltygui/core/graphics/shader_func.py +478 -0
  115. meltygui/core/graphics/tensor_core.py +45 -0
  116. meltygui/core/graphics/text_texture.py +329 -0
  117. meltygui/core/graphics/wayland_color.py +635 -0
  118. meltygui/core/input/__init__.py +1 -0
  119. meltygui/core/input/collision.py +165 -0
  120. meltygui/core/input/drag_drop_core.py +1525 -0
  121. meltygui/core/input/hypr_left_drag.py +323 -0
  122. meltygui/core/input/input_core.py +245 -0
  123. meltygui/core/input/input_handler.py +1101 -0
  124. meltygui/core/input/mouse_cursor.py +355 -0
  125. meltygui/core/input/pynput_backend.py +1054 -0
  126. meltygui/core/input/space_mouse.py +338 -0
  127. meltygui/core/input/touchpad_backend.py +393 -0
  128. meltygui/core/input/view_selection.py +177 -0
  129. meltygui/core/layout/__init__.py +1 -0
  130. meltygui/core/layout/column_core.py +2153 -0
  131. meltygui/core/layout/cursor_core.py +161 -0
  132. meltygui/core/layout/dropdown_core.py +278 -0
  133. meltygui/core/layout/edge_constraints.py +155 -0
  134. meltygui/core/layout/grid_core.py +117 -0
  135. meltygui/core/layout/header_core.py +23 -0
  136. meltygui/core/layout/header_runtime.py +67 -0
  137. meltygui/core/layout/layout_core.py +87 -0
  138. meltygui/core/layout/tile_manager_core.py +591 -0
  139. meltygui/core/melty.py +6726 -0
  140. meltygui/core/module_map.json +896 -0
  141. meltygui/core/module_names.py +19 -0
  142. meltygui/core/rendering/__init__.py +1 -0
  143. meltygui/core/rendering/core_decoration.py +431 -0
  144. meltygui/core/rendering/core_render_helpers.py +328 -0
  145. meltygui/core/rendering/func_metadata.py +398 -0
  146. meltygui/core/rendering/mode.py +818 -0
  147. meltygui/core/rendering/mode_defaults.py +41 -0
  148. meltygui/core/rendering/modes.py +136 -0
  149. meltygui/core/rendering/parameter_core.py +1665 -0
  150. meltygui/core/rendering/render_dispatch.py +1891 -0
  151. meltygui/core/rendering/render_funcs.py +273 -0
  152. meltygui/core/rendering/shaped.py +312 -0
  153. meltygui/core/rendering/window_decoration.py +25 -0
  154. meltygui/core/runtime/__init__.py +1 -0
  155. meltygui/core/runtime/app.py +735 -0
  156. meltygui/core/runtime/app_session.py +140 -0
  157. meltygui/core/runtime/background.py +564 -0
  158. meltygui/core/runtime/extensions.py +53 -0
  159. meltygui/core/runtime/gc_manager.py +1163 -0
  160. meltygui/core/runtime/lifecycle.py +21 -0
  161. meltygui/core/runtime/paths.py +27 -0
  162. meltygui/core/runtime/settings.py +14 -0
  163. meltygui/core/runtime/singleton.py +16 -0
  164. meltygui/core/runtime/thread_safe_bool.py +24 -0
  165. meltygui/core/runtime/thread_signal.py +30 -0
  166. meltygui/core/runtime/toggles.py +3115 -0
  167. meltygui/core/services/__init__.py +1 -0
  168. meltygui/core/services/account_core.py +10 -0
  169. meltygui/core/services/chat_core.py +19 -0
  170. meltygui/core/services/claude_terminal_core.py +346 -0
  171. meltygui/core/services/terminal_core.py +458 -0
  172. meltygui/core/services/terminal_runtime.py +78 -0
  173. meltygui/core/styling/__init__.py +1 -0
  174. meltygui/core/styling/color_core.py +46 -0
  175. meltygui/core/styling/fonts.py +639 -0
  176. meltygui/core/styling/global_style.py +338 -0
  177. meltygui/core/styling/style.py +198 -0
  178. meltygui/core/styling/style_core.py +522 -0
  179. meltygui/core/styling/warm_start.py +149 -0
  180. meltygui/core/windowing/__init__.py +1 -0
  181. meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
  182. meltygui/core/windowing/backends/__init__.py +1 -0
  183. meltygui/core/windowing/backends/imgui_renderer.py +138 -0
  184. meltygui/core/windowing/backends/native_wayland.py +850 -0
  185. meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
  186. meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
  187. meltygui/core/windowing/backends/wayland_protocol.py +101 -0
  188. meltygui/core/windowing/dock_core.py +182 -0
  189. meltygui/core/windowing/frame_geometry.py +42 -0
  190. meltygui/core/windowing/geometry_feed.py +864 -0
  191. meltygui/core/windowing/glfw_utils.py +1343 -0
  192. meltygui/core/windowing/os_frame.py +1552 -0
  193. meltygui/core/windowing/surface.py +643 -0
  194. meltygui/core/windowing/titlebar.py +1560 -0
  195. meltygui/core/windowing/titlebar_buttons.py +281 -0
  196. meltygui/core/windowing/wayland_move.py +932 -0
  197. meltygui/core/windowing/window_api.py +62 -0
  198. meltygui/core/windowing/window_constants.py +339 -0
  199. meltygui/core/windowing/window_visibility.py +162 -0
  200. meltygui/debug/__init__.py +0 -0
  201. meltygui/debug/app_view_utils.py +9 -0
  202. meltygui/editor/__init__.py +0 -0
  203. meltygui/editor/bash_syntax.py +30 -0
  204. meltygui/editor/code_line_fast.py +152 -0
  205. meltygui/editor/diff.py +139 -0
  206. meltygui/editor/external_changes.py +159 -0
  207. meltygui/editor/file_header.py +41 -0
  208. meltygui/editor/live_usage.py +169 -0
  209. meltygui/editor/live_view_views.py +1803 -0
  210. meltygui/editor/pending_save.py +1351 -0
  211. meltygui/editor/roster_tints.py +547 -0
  212. meltygui/editor/source_preview.py +16 -0
  213. meltygui/editor/source_tools.py +10 -0
  214. meltygui/editor/source_ui.py +70 -0
  215. meltygui/editor/spell_check.py +77 -0
  216. meltygui/editor/text_editor.py +9076 -0
  217. meltygui/editor/usage_picker.py +538 -0
  218. meltygui/events/__init__.py +0 -0
  219. meltygui/events/example.py +118 -0
  220. meltygui/examples/__init__.py +0 -0
  221. meltygui/examples/columns_demo.py +33 -0
  222. meltygui/examples/columns_window_demo.py +60 -0
  223. meltygui/examples/context_menu_demo.py +24 -0
  224. meltygui/examples/context_menu_window_demo.py +49 -0
  225. meltygui/examples/gui_playground.py +127 -0
  226. meltygui/examples/live_view_playground.py +256 -0
  227. meltygui/examples/lora.py +43 -0
  228. meltygui/examples/lora_data.py +59 -0
  229. meltygui/examples/lora_policies.py +67 -0
  230. meltygui/examples/mode_demo.py +49 -0
  231. meltygui/examples/modifies_demo.py +119 -0
  232. meltygui/examples/scalar_policies.py +60 -0
  233. meltygui/examples/style_layouts.py +149 -0
  234. meltygui/examples/tile_manager_demo.py +74 -0
  235. meltygui/examples/tint_demo.py +160 -0
  236. meltygui/examples/tint_functions.py +56 -0
  237. meltygui/examples/trace_demo.py +88 -0
  238. meltygui/examples/two_windows.py +36 -0
  239. meltygui/files/__init__.py +0 -0
  240. meltygui/files/fast_file_explorer.py +439 -0
  241. meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
  242. meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
  243. meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
  244. meltygui/graphics/__init__.py +6 -0
  245. meltygui/graphics/base.py +85 -0
  246. meltygui/graphics/examples.py +507 -0
  247. meltygui/graphics/executor.py +520 -0
  248. meltygui/graphics/filter.py +667 -0
  249. meltygui/graphics/filter.pyi +856 -0
  250. meltygui/graphics/generate_stubs.py +22 -0
  251. meltygui/graphics/registry.py +203 -0
  252. meltygui/graphics/shader_compiler.py +155 -0
  253. meltygui/graphics/shaders.py +1302 -0
  254. meltygui/graphics/stub_generator.py +250 -0
  255. meltygui/graphics/texture_manager.py +170 -0
  256. meltygui/graphics/texture_min_max.py +295 -0
  257. meltygui/hdr_color.py +757 -0
  258. meltygui/image_load.py +308 -0
  259. meltygui/model/__init__.py +1 -0
  260. meltygui/model/account_model.py +142 -0
  261. meltygui/model/camera_model.py +159 -0
  262. meltygui/model/chat_model.py +86 -0
  263. meltygui/model/code_model.py +62 -0
  264. meltygui/model/code_proxy_model.py +876 -0
  265. meltygui/model/collection_model.py +41 -0
  266. meltygui/model/color_model.py +127 -0
  267. meltygui/model/cuda_tensor_model.py +36 -0
  268. meltygui/model/cuda_texture_model.py +149 -0
  269. meltygui/model/dropdown_model.py +137 -0
  270. meltygui/model/file_metadata_model.py +73 -0
  271. meltygui/model/file_model.py +289 -0
  272. meltygui/model/format_model.py +390 -0
  273. meltygui/model/graph_model.py +98 -0
  274. meltygui/model/icon_model.py +1024 -0
  275. meltygui/model/import_graph_model.py +468 -0
  276. meltygui/model/layout_model.py +15 -0
  277. meltygui/model/lut_model.py +266 -0
  278. meltygui/model/search_model.py +173 -0
  279. meltygui/model/tensor_model.py +402 -0
  280. meltygui/model/terminal_model.py +329 -0
  281. meltygui/model/texture_model.py +146 -0
  282. meltygui/model/tile_model.py +24 -0
  283. meltygui/model/trace_model.py +198 -0
  284. meltygui/model/trace_report_model.py +146 -0
  285. meltygui/models/__init__.py +0 -0
  286. meltygui/models/file_meta.py +613 -0
  287. meltygui/models/function_console.py +184 -0
  288. meltygui/models/orchestration.py +48 -0
  289. meltygui/pbr.py +1576 -0
  290. meltygui/png_unfilter.c +49 -0
  291. meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
  292. meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
  293. meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
  294. meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
  295. meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
  296. meltygui/resources/dejavu/LICENSE.txt +78 -0
  297. meltygui/resources/fontawesome-LICENSE.txt +121 -0
  298. meltygui/resources/fontawesome-webfont.ttf +0 -0
  299. meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
  300. meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
  301. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
  302. meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
  303. meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
  304. meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
  305. meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
  306. meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
  307. meltygui/resources/jetbrains-weights/OFL.txt +93 -0
  308. meltygui/resources/jetbrains-weights/README.md +2 -0
  309. meltygui/state/__init__.py +0 -0
  310. meltygui/state/account_state.py +22 -0
  311. meltygui/state/animation_state.py +97 -0
  312. meltygui/state/annotation_state.py +22 -0
  313. meltygui/state/chat_state.py +36 -0
  314. meltygui/state/code_state.py +10 -0
  315. meltygui/state/core_enums.py +59 -0
  316. meltygui/state/core_markers.py +115 -0
  317. meltygui/state/core_undo.py +1075 -0
  318. meltygui/state/file_state.py +75 -0
  319. meltygui/state/graph_state.py +26 -0
  320. meltygui/state/inspection_state.py +47 -0
  321. meltygui/state/menu_state.py +10 -0
  322. meltygui/state/model_enums.py +11 -0
  323. meltygui/state/new_core_model.py +2394 -0
  324. meltygui/state/orchestration_state.py +14 -0
  325. meltygui/state/query_state.py +24 -0
  326. meltygui/state/tensor_state.py +10 -0
  327. meltygui/state/terminal_state.py +23 -0
  328. meltygui/state/trace_state.py +32 -0
  329. meltygui/state/voxel_state.py +12 -0
  330. meltygui/text_index.py +816 -0
  331. meltygui/utils/__init__.py +0 -0
  332. meltygui/utils/jump_to_code.py +344 -0
  333. meltygui/utils/pkl_inspect.py +90 -0
  334. meltygui/utils/render_utils.py +1419 -0
  335. meltygui/view/__init__.py +1 -0
  336. meltygui/view/account_view.py +524 -0
  337. meltygui/view/action_view.py +73 -0
  338. meltygui/view/chat_decoration_view.py +112 -0
  339. meltygui/view/chat_view.py +1703 -0
  340. meltygui/view/code_view.py +2677 -0
  341. meltygui/view/collection_view.py +1185 -0
  342. meltygui/view/color_view.py +824 -0
  343. meltygui/view/control_view.py +559 -0
  344. meltygui/view/decoration_view.py +439 -0
  345. meltygui/view/diagnostic_view.py +177 -0
  346. meltygui/view/dropdown_view.py +1082 -0
  347. meltygui/view/file_view.py +1599 -0
  348. meltygui/view/graph_cuda_view.py +105 -0
  349. meltygui/view/graph_view.py +882 -0
  350. meltygui/view/header_view.py +848 -0
  351. meltygui/view/input_view.py +238 -0
  352. meltygui/view/inspection_view.py +1783 -0
  353. meltygui/view/layout_view.py +596 -0
  354. meltygui/view/lut_view.py +45 -0
  355. meltygui/view/menu_view.py +212 -0
  356. meltygui/view/orchestration_view.py +658 -0
  357. meltygui/view/query_view.py +118 -0
  358. meltygui/view/search_view.py +342 -0
  359. meltygui/view/tab_view.py +258 -0
  360. meltygui/view/tensor_view.py +430 -0
  361. meltygui/view/terminal_view.py +355 -0
  362. meltygui/view/text_view.py +8031 -0
  363. meltygui/view/texture_view.py +480 -0
  364. meltygui/view/tile_view.py +38 -0
  365. meltygui/view/trace_view.py +923 -0
  366. meltygui/view/voxel_cuda_view.py +824 -0
  367. meltygui/view/voxel_view.py +1860 -0
  368. meltygui/view/window_view.py +111 -0
  369. meltygui-0.1.0.dist-info/METADATA +143 -0
  370. meltygui-0.1.0.dist-info/RECORD +372 -0
  371. meltygui-0.1.0.dist-info/WHEEL +4 -0
  372. meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
@@ -0,0 +1,564 @@
1
+ import hashlib
2
+ import threading
3
+ from collections import OrderedDict
4
+ from concurrent.futures import ThreadPoolExecutor
5
+ import sys
6
+
7
+ from meltygui.core.runtime.toggles import Toggles
8
+ from meltygui.core.windowing.glfw_utils import print_stack_trace
9
+ from meltygui.core.windowing.glfw_utils import get_live_frames
10
+ from meltygui.core.windowing.glfw_utils import _print_lock
11
+ from meltygui.core.windowing.glfw_utils import trace_group
12
+ from meltygui.core.windowing.glfw_utils import request_render
13
+ from meltygui.core.conversion.path_finder import Pending
14
+ from meltygui.core.conversion.path_finder import PendingState
15
+ from meltygui.core.cache.invalidation_tracker import Note
16
+
17
+
18
+ class Background:
19
+ _user_cache = {} # user_id -> OrderedDict{hash -> result}
20
+ _cache_size = 1
21
+ _active = set()
22
+ _user_tasks = {}
23
+ _lock = threading.Lock()
24
+ _pool = ThreadPoolExecutor(max_workers=16)
25
+ _debounce_timers = {} # debounce_key -> Timer
26
+ _debounce_latest = {} # debounce_key -> dict of latest call params
27
+ _hash_times = {} # type_name -> [total_time_sec, count]
28
+ _dict_key_times = {} # dict key name -> [total_time_sec, count]
29
+ _task_times = {} # func_name -> [total_time_sec, count]
30
+ did_shutdown = False
31
+ @classmethod
32
+ def _record_dict_key_time(cls, key: str, elapsed: float):
33
+ # No lock here - called from simple_hash which may recurse deeply;
34
+ # some inaccuracy from races is acceptable for profiling.
35
+ entry = cls._dict_key_times.get(key)
36
+ if entry is None:
37
+ cls._dict_key_times[key] = [elapsed, 1]
38
+ else:
39
+ entry[0] += elapsed
40
+ entry[1] += 1
41
+
42
+ @classmethod
43
+ def _timed_hash(cls, value, user_id):
44
+ import time
45
+ type_name = type(value).__name__
46
+ t0 = time.perf_counter()
47
+ h = cls.simple_hash(value=value) + user_id
48
+ elapsed = time.perf_counter() - t0
49
+
50
+ with cls._lock:
51
+ if type_name not in cls._hash_times:
52
+ cls._hash_times[type_name] = [0.0, 0]
53
+ cls._hash_times[type_name][0] += elapsed
54
+ cls._hash_times[type_name][1] += 1
55
+
56
+ return h
57
+
58
+ @classmethod
59
+ def shutdown(cls):
60
+ print("Shutting down background system...")
61
+ # with cls._lock:
62
+ cls._active.clear()
63
+ cls._user_tasks.clear()
64
+ cls._user_cache.clear()
65
+ for timer in cls._debounce_timers.values():
66
+ timer.cancel()
67
+ cls._debounce_timers.clear()
68
+ cls._debounce_latest.clear()
69
+
70
+ print_timings = False
71
+
72
+ if print_timings:
73
+ print("\n--- Hash timing averages (by type) ---")
74
+ for type_name, (total, count) in sorted(cls._hash_times.items()):
75
+ avg_ms = (total / count) * 1000
76
+ print(f" {type_name:<30} avg={avg_ms:.3f}ms n={count}")
77
+ print("--------------------------------------\n")
78
+
79
+ print("--- Hash timing averages (dict keys, sorted by avg) ---")
80
+ sorted_keys = sorted(cls._dict_key_times.items(), key=lambda x: x[1][0] / x[1][1], reverse=True)
81
+ for key_name, (total, count) in sorted_keys:
82
+ avg_ms = (total / count) * 1000
83
+ print(f" {key_name:<40} avg={avg_ms:.3f}ms n={count}")
84
+ print("-------------------------------------------------------\n")
85
+
86
+ print("--- Task timing averages (by function, sorted by avg) ---")
87
+ sorted_tasks = sorted(cls._task_times.items(), key=lambda x: x[1][0] / x[1][1], reverse=True)
88
+ for func_name, (total, count) in sorted_tasks:
89
+ avg_ms = (total / count) * 1000
90
+ print(f" {func_name:<50} avg={avg_ms:.3f}ms n={count}")
91
+ print("----------------------------------------------------------\n")
92
+
93
+ print("Shutting down background thread pool...")
94
+
95
+ cls._pool.shutdown(wait=True)
96
+ print("Background thread pool shut down successfully.")
97
+ cls.did_shutdown = True
98
+
99
+ @classmethod
100
+ def run(cls, func, user_id, func_kwargs=None, *,
101
+ stateful=False, no_cache=False, invalidate_id=None,
102
+ on_frame=None, frames=None, debounce=10):
103
+ """Run func with func_kwargs, with caching, debouncing, and background dispatch.
104
+
105
+ Background.run's own parameters (user_id, stateful, no_cache, etc.) are
106
+ cleanly separated from the function's kwargs via the func_kwargs dict.
107
+ """
108
+ if func_kwargs is None:
109
+ func_kwargs = {}
110
+
111
+ if "input_value" in func_kwargs:
112
+ h = cls._timed_hash(func_kwargs.get("input_value", None), user_id)
113
+ else:
114
+ print(f"Warning no 'value' or 'input_value' in func_kwargs for {func.__name__} with user_id {user_id}, using 0 as hash")
115
+ h = "0_" + str(user_id)
116
+
117
+ # A side-effect payload (e.g. a save's `code_str`) isn't the hashed input
118
+ # but DOES change the operation. The hashed input for _do_save is the
119
+ # Address, which is stable across edits - so without folding the payload
120
+ # in, two writes to the same location share a hash and the debounce /
121
+ # in-flight dedup below sees the second as a duplicate and drops its
122
+ # content. That's the intermittent "lost save". Fold it in so distinct
123
+ # content gets a distinct key (identical content still dedups, correctly).
124
+ if "code_str" in func_kwargs:
125
+ h = f"{h}|{cls.simple_hash(value=func_kwargs.get('code_str'))}"
126
+
127
+ # if "search_text" in func_kwargs:
128
+ # h_s = cls._timed_hash(func_kwargs.get('search_text', None), user_id)
129
+ # h = str(h) + "_" + str(h_s)
130
+ # print(f"{func_kwargs.get('search_text')}")
131
+
132
+ # Inline path for stateful converters (no apply, short chains)
133
+ if stateful:
134
+ if not no_cache:
135
+ cache = cls._user_cache.get(user_id)
136
+ if cache and h in cache:
137
+ cache.move_to_end(h)
138
+ return cache[h]
139
+
140
+ try:
141
+ result = func(**func_kwargs)
142
+ except Exception:
143
+ result = Pending(originated=cls, status="Read Only", state=PendingState.ERROR)
144
+
145
+ if user_id not in cls._user_cache:
146
+ cls._user_cache[user_id] = OrderedDict()
147
+ cls._user_cache[user_id][h] = result
148
+ cls._user_cache[user_id].move_to_end(h)
149
+ while len(cls._user_cache[user_id]) > cls._cache_size:
150
+ cls._user_cache[user_id].popitem(last=False)
151
+
152
+ return result
153
+
154
+
155
+ from meltygui.core.melty import Melty
156
+ if Melty.frame_count < 2:
157
+ debounce = None
158
+
159
+ # --- debounce path ---
160
+ if debounce is not None:
161
+ debounce_key = user_id
162
+
163
+ cache = cls._user_cache.get(user_id)
164
+
165
+ stale_value = None
166
+ if cache and h in cache:
167
+ if no_cache:
168
+ stale_value = cache[h]
169
+ else:
170
+ cache.move_to_end(h)
171
+ return cache[h]
172
+
173
+ if h in cls._active:
174
+ return stale_value if stale_value is not None else Pending(originated=cls, status="background thread active", state=PendingState.BACKGROUND)
175
+
176
+ latest = cls._debounce_latest.get(debounce_key)
177
+ if latest is not None and latest.get("hash") == h:
178
+ return stale_value if stale_value is not None else Pending(originated=cls, status="debounce waiting", state=PendingState.BACKGROUND)
179
+
180
+ prev = cls._debounce_timers.pop(debounce_key, None)
181
+ if prev is not None:
182
+ prev.cancel()
183
+
184
+ cls._debounce_latest[debounce_key] = dict(
185
+ hash=h,
186
+ func=func, user_id=user_id, no_cache=no_cache,
187
+ invalidate_id=invalidate_id, on_frame=on_frame,
188
+ func_kwargs=func_kwargs,
189
+ )
190
+
191
+ if Toggles.debug_threads:
192
+ frames = get_live_frames()
193
+
194
+ def _fire():
195
+ cls._debounce_timers.pop(debounce_key, None)
196
+ latest = cls._debounce_latest.pop(debounce_key, None)
197
+ if latest is not None:
198
+ cls.run(
199
+ latest["func"], latest["user_id"],
200
+ func_kwargs=latest["func_kwargs"],
201
+ no_cache=latest["no_cache"],
202
+ invalidate_id=latest["invalidate_id"],
203
+ on_frame=latest["on_frame"],
204
+ debounce=None, frames=frames,
205
+ )
206
+ if Toggles.InvalidateTracker.invalidate_stack_trace:
207
+ print_stack_trace(frames=frames)
208
+
209
+ timer = threading.Timer(debounce / 1000.0, _fire)
210
+ cls._debounce_timers[debounce_key] = timer
211
+ timer.start()
212
+
213
+ return stale_value if stale_value is not None else Pending(originated=cls, status="debounce waiting", state=PendingState.BACKGROUND)
214
+
215
+ # --- normal (background thread) path ---
216
+
217
+ cache = cls._user_cache.get(user_id)
218
+ stale_value = None
219
+ if cache and h in cache:
220
+ if no_cache:
221
+ stale_value = cache[h]
222
+ else:
223
+ cache.move_to_end(h)
224
+ return cache[h]
225
+
226
+ cls._user_tasks[user_id] = h
227
+
228
+ if h in cls._active:
229
+ return stale_value if stale_value is not None else Pending(originated=cls, status="background thread active", state=PendingState.BACKGROUND)
230
+
231
+ if Toggles.debug_threads and frames is None:
232
+ frames = get_live_frames()
233
+ cls._active.add(h)
234
+
235
+ def _task():
236
+ # Check for shutdown
237
+ if cls.did_shutdown:
238
+ print("Background system is shut down; cannot run new tasks.")
239
+ print_stack_trace()
240
+ return Pending(originated=cls, status="shutdown", state=PendingState.ERROR)
241
+
242
+ import time
243
+ func_name = getattr(func, "__qualname__", None) or getattr(func, "__name__", repr(func))
244
+ sig = func_name
245
+ _t0 = time.perf_counter()
246
+
247
+ try:
248
+ result = func(**func_kwargs)
249
+ except Exception as e:
250
+ with trace_group(f"JOB {user_id}", hash=h) as g:
251
+ print_stack_trace(frames=frames, section="UI Thread",
252
+ group=g, watch=["draw_state.name", "input_value", "convert_path", "fn", "clean_args.input_value"])
253
+ print_stack_trace(exception=e, section="Background Thread",
254
+ group=g, watch=["value", "path", "watch", "watch.original_data", "data", "result", "result", "fn"])
255
+
256
+ result = Pending(originated=cls, status="Read Only", state=PendingState.ERROR)
257
+
258
+ elapsed = time.perf_counter() - _t0
259
+ entry = cls._task_times.get(sig)
260
+ if entry is None:
261
+ cls._task_times[sig] = [elapsed, 1]
262
+ else:
263
+ entry[0] += elapsed
264
+ entry[1] += 1
265
+ if on_frame is not None:
266
+ result = (result, on_frame)
267
+ for uid, task_h in cls._user_tasks.items():
268
+ if task_h == h:
269
+ if no_cache:
270
+ cls._user_cache.pop(uid, None)
271
+ if uid not in cls._user_cache:
272
+ cls._user_cache[uid] = OrderedDict()
273
+ cls._user_cache[uid][h] = result
274
+ cls._user_cache[uid].move_to_end(h)
275
+
276
+ while len(cls._user_cache[uid]) > cls._cache_size:
277
+ cls._user_cache[uid].popitem(last=False)
278
+ cls._active.discard(h)
279
+ if invalidate_id is not None:
280
+ from meltygui.core.melty import Melty
281
+ from meltygui.core.windowing.glfw_utils import request_render
282
+ if on_frame is None or abs(Melty.frame_count - on_frame) >= 1:
283
+ note = Note(name=f"Background Invalidate {invalidate_id}", reason=f"func={func_name}", tint=(0,0,1))
284
+ Melty.cache.invalidate_up(invalidate_id, max_depth=5, note=note)
285
+ request_render()
286
+
287
+ cls._pool.submit(_task)
288
+ return stale_value if stale_value is not None else Pending(originated=cls, status="background thread", state=PendingState.BACKGROUND)
289
+
290
+ @staticmethod
291
+ def compute_hash(cls, exclude=None, memo=None, depth=0, do_print=False, include_hidden=False):
292
+ """
293
+ Create a hash of the instance's content with custom attribute exclusions.
294
+ Recursively handles DictConversion objects, collections, and primitive types.
295
+
296
+ Args:
297
+ exclude: List of attribute names to exclude from hashing.
298
+ memo: Dictionary of already-processed objects to avoid infinite recursion.
299
+ depth: Current recursion depth for debugging.
300
+ do_print: Whether to print debug information.
301
+
302
+ Returns:
303
+ A 16-bit float value representing the instance's content.
304
+ """
305
+ if exclude is None:
306
+ exclude = set()
307
+
308
+ if memo is None:
309
+ memo = {}
310
+
311
+ # Check if self is already in memo to avoid infinite recursion
312
+ if isinstance(cls, (int, float, str, bool)):
313
+ return str(cls)
314
+
315
+ try:
316
+ if id(cls) in memo:
317
+ if memo[id(cls)] != "processing":
318
+ return memo[id(cls)]
319
+ except Exception as e:
320
+ print(f"Error checking memo for id(self): {e}")
321
+ return None
322
+
323
+ # Add self to memo immediately with a temporary value
324
+ # This is crucial to break recursion loops
325
+ memo[id(cls)] = "processing" # Temporary value
326
+
327
+ # Create a string builder for this object
328
+ content_str = f"{cls.__class__.__name__}:"
329
+
330
+ # Create set of attributes to exclude
331
+ excluded_attrs = {'outliner_expanded_h', 'expanded', 'hash', '_parent', '_children', 'kwargs', "tensor",
332
+ "tensor_b", "tensor_c", 'buffer', 'ctx',
333
+ 'texture', "texture3D", "cuda_buffer", "xy_renderer", "xyz_renderer",
334
+ 'previous_mouse_x', 'previous_mouse_y', 'last_mouse_x', 'last_mouse_y'}
335
+ if exclude:
336
+ for excl in exclude:
337
+ excluded_attrs.add(excl)
338
+
339
+ # Add all non-excluded attributes to the string representation
340
+ if hasattr(cls, '__dict__'):
341
+ for key, value in cls.__dict__.items():
342
+ key = str(key)
343
+ if not include_hidden:
344
+ if key.startswith('_'):
345
+ continue
346
+ if key in excluded_attrs or value is None:
347
+ continue
348
+ value_str = cls._hash_value_to_str(value, exclude, memo, depth, do_print,
349
+ include_hidden=include_hidden)
350
+ content_str += f"{value_str}"
351
+ else:
352
+ content_str = cls._hash_value_to_str(cls, exclude, memo, depth, do_print,
353
+ include_hidden=include_hidden)
354
+
355
+ # Calculate hash
356
+ hash_result = hashlib.sha256(content_str.encode('utf-8')).hexdigest()
357
+
358
+ # Convert the hash to a 16-bit float (float16)
359
+ # Take the first 4 hex chars (16 bits) and convert to integer, then normalize to float16 range
360
+ hash_int = int(hash_result[:4], 16)
361
+
362
+ # Float16 has 1 sign bit, 5 exponent bits, and 10 mantissa bits
363
+ # We'll use the range -65504 to +65504 (full range for float16)
364
+ float_value = (hash_int / 0xFFFF) * 65504 * 2 - 65504
365
+
366
+ # Update the memo with the final value
367
+ memo[id(cls)] = float_value
368
+
369
+ if do_print:
370
+ print(f"Depth: {depth}, Class: {cls.__class__.__name__}, Hash: {hash_result[:8]}..., Float16: {float_value}")
371
+
372
+ if hasattr(cls, 'hash'):
373
+ cls.hash = float_value
374
+ return float_value
375
+
376
+ @staticmethod
377
+ def simple_hash(value, exclude=None, memo=None, depth=0, do_print=False, include_hidden=False, internal=False):
378
+ """
379
+ Helper method to convert a value to a string representation based on its type.
380
+
381
+ Args:
382
+ value: The value to convert to string
383
+ exclude: List of attribute names to exclude from hashing
384
+ memo: Dictionary of already-processed objects
385
+ depth: Current recursion depth for debugging
386
+ do_print: Whether to print debug information
387
+
388
+ Returns:
389
+ A string representation of the value
390
+ """
391
+
392
+ # if not internal:
393
+ # return_val = Background.simple_hash(value=value, exclude=exclude, memo=memo, depth=depth, do_print=do_print,
394
+ # include_hidden=include_hidden, internal=True)
395
+ # hash_result = hashlib.sha256(return_val.encode('utf-8')).hexdigest()
396
+ #
397
+ # # Convert the hash to a 16-bit float (Float16)
398
+ # # Take the first 4 hex chars (16 bits) and convert to integer, then normalize to float16 range
399
+ # hash_int = int(hash_result[:4], 16)
400
+ # return str(hash_int)
401
+
402
+ if exclude is None:
403
+ exclude = {}
404
+ depth += 1
405
+
406
+ if memo is None:
407
+ memo = {}
408
+
409
+ # Check if hashable first. Catch everything, not just TypeError: the
410
+ # structural hashes are user objects (live-view frame buffers), and a
411
+ # buggy __hash__ - e.g. reading an attribute a mid-__init__ capture
412
+ # never set - must fall through to the structural path, not kill the
413
+ # render.
414
+ try:
415
+ standard_hash = hash(value)
416
+ return str(standard_hash)
417
+ except Exception:
418
+ pass
419
+
420
+ if do_print:
421
+ torch = sys.modules.get('torch') # lazy: never load torch just for a debug print
422
+ if torch is not None and hasattr(torch, 'cuda') and torch.cuda.is_available():
423
+ mem_str = ""
424
+ for i in range(torch.cuda.device_count()):
425
+ mem_alloc = torch.cuda.memory_allocated(i) / 1024 ** 3
426
+ mem_str += f"GPU {i}: {mem_alloc:.2f} GB\n"
427
+ print(f"Depth: {depth}, Value: {value}, Type: {type(value)}, Memory: {mem_str}")
428
+ else:
429
+ print(f"Depth: {depth}, Value: {value}, Type: {type(value)}")
430
+
431
+ # Check if value is already in memo - crucial for avoiding infinite recursion
432
+ if id(value) in memo:
433
+ return f"ref:{value}" # Return a reference indicator instead of recursing
434
+
435
+ # Handle None
436
+ if value is None:
437
+ return ""
438
+
439
+ # Handle tensors and other special types by using their type and shape/identity
440
+ if hasattr(value, "__class__") and value.__class__.__name__ == "Tensor":
441
+ try:
442
+ tensor_repr = f"Tensor:shape={list(value.shape)}:dtype={value.dtype}"
443
+ except:
444
+ tensor_repr = f"Tensor:{id(value)}"
445
+ memo[id(value)] = tensor_repr
446
+ return tensor_repr
447
+
448
+ if hasattr(value, "__class__") and "PreTrainedTokenizerBase" in str(value.__class__.__mro__):
449
+ tokenizer_repr = f"Tokenizer:{value.__class__.__name__}"
450
+ memo[id(value)] = tokenizer_repr
451
+ return tokenizer_repr
452
+
453
+ if hasattr(value, "__class__") and "Module" in str(value.__class__.__mro__):
454
+ module_repr = f"Module:{value.__class__.__name__}"
455
+ memo[id(value)] = module_repr
456
+ return module_repr
457
+
458
+ # Handle DictConversion objects - pass the depth argument correctly
459
+ if hasattr(value, "compute_hash") and isinstance(value, Background):
460
+ memo[id(value)] = "processing" # Add immediately to avoid recursion
461
+ result = Background.compute_hash(self=value, exclude=exclude, memo=memo, depth=depth,
462
+ do_print=do_print, include_hidden=include_hidden)
463
+ memo[id(value)] = result # Update with actual result
464
+ return str(result)
465
+
466
+ # Handle Enums
467
+ if hasattr(value, "__class__") and hasattr(value.__class__,
468
+ "__module__") and "enum" in value.__class__.__module__:
469
+ try:
470
+ enum_repr = f"Enum:{value.__class__.__name__}.{value.name}"
471
+ except:
472
+ enum_repr = f"Enum:{value.__class__.__name__}"
473
+ memo[id(value)] = enum_repr
474
+ return enum_repr
475
+
476
+ if isinstance(value, bytes):
477
+ bytes_repr = f"bytes:{len(value)}"
478
+ memo[id(value)] = bytes_repr
479
+ return bytes_repr
480
+
481
+ if isinstance(value, bytearray):
482
+ bytearray_repr = f"bytearray:{len(value)}"
483
+ memo[id(value)] = bytearray_repr
484
+ return bytearray_repr
485
+
486
+ # Handle lists
487
+ if isinstance(value, list):
488
+ memo[id(value)] = "list:processing" # Add immediately to avoid recursion
489
+ items_str = "["
490
+ for item in value:
491
+ items_str += Background.simple_hash(value=item, exclude=exclude, memo=memo, depth=depth,
492
+ do_print=do_print,
493
+ include_hidden=include_hidden) + ","
494
+ items_str += "]"
495
+ memo[id(value)] = items_str
496
+ return items_str
497
+
498
+ if isinstance(value, Pending):
499
+ memo[id(value)] = "tuple:processing" # Add immediately to avoid recursion
500
+ items_str = "("
501
+ items_str += Background.simple_hash(value=Pending.wrapped, exclude=exclude, memo=memo, depth=depth,
502
+ do_print=do_print, include_hidden=include_hidden) + ","
503
+ items_str += ")"
504
+ memo[id(value)] = items_str
505
+ return items_str
506
+
507
+ # Handle tuples
508
+ if isinstance(value, tuple):
509
+ memo[id(value)] = "tuple:processing" # Add immediately to avoid recursion
510
+ items_str = "("
511
+ for item in value:
512
+ items_str += Background.simple_hash(value=item, exclude=exclude, memo=memo, depth=depth,
513
+ do_print=do_print, include_hidden=include_hidden) + ","
514
+ items_str += ")"
515
+ memo[id(value)] = items_str
516
+ return items_str
517
+
518
+ # Fast path for dict subclasses that know how to hash themselves cheaply.
519
+ # Implement __bg_hash__(self) -> str on any dict subclass to skip this recursion.
520
+ if isinstance(value, dict) and hasattr(value, "__bg_hash__"):
521
+ result = value.__bg_hash__()
522
+ memo[id(value)] = result
523
+ return result
524
+
525
+ # Handle dictionaries
526
+ if isinstance(value, dict):
527
+ import time
528
+ memo[id(value)] = "dict:processing" # Add immediately to avoid recursion
529
+ items_str = "{"
530
+ for k, v in value.items():
531
+ k = str(k)
532
+ if not include_hidden and k.startswith('_'):
533
+ continue
534
+ if k in exclude:
535
+ continue
536
+ _t0 = time.perf_counter()
537
+ val_str = Background.simple_hash(value=v, exclude=exclude, memo=memo, depth=depth,
538
+ do_print=do_print,
539
+ include_hidden=include_hidden)
540
+ Background._record_dict_key_time(k, time.perf_counter() - _t0)
541
+ items_str += f"{val_str}"
542
+ items_str += "}"
543
+ memo[id(value)] = items_str
544
+ return items_str
545
+
546
+ if isinstance(value, set):
547
+ memo[id(value)] = "set:processing" # Add immediately to avoid recursion
548
+ items_str = "{"
549
+ for item in value:
550
+ items_str += str(item) + ","
551
+ items_str += "}"
552
+ memo[id(value)] = items_str
553
+ return items_str
554
+
555
+ # Handle simple types (int, float, str, bool)
556
+ if isinstance(value, (int, float, str, bool)):
557
+ result = str(value)
558
+ memo[id(value)] = result
559
+ return result
560
+
561
+ # Any other types - use their string representation
562
+ other_repr = f"{str(type(value).__name__)}" # Just use ID to prevent recursion
563
+ memo[id(value)] = other_repr
564
+ return other_repr
@@ -0,0 +1,53 @@
1
+ """Optional application services; the UI toolkit never imports their providers.
2
+
3
+ Providers register callbacks when imported. Lookups happen at call time so live
4
+ reloads replace callbacks without pinning an earlier function or module.
5
+ """
6
+ _services = globals().get('_services', {})
7
+
8
+
9
+ def register(name, callback):
10
+ _services[name] = callback
11
+
12
+
13
+ def get(name):
14
+ return _services.get(name)
15
+
16
+
17
+ def call(name, *args, **kwargs):
18
+ callback = get(name)
19
+ return callback(*args, **kwargs) if callback is not None else None
20
+
21
+
22
+ def source_folders():
23
+ return call('source_folders') or ()
24
+
25
+
26
+ def framework_folders():
27
+ """Folders of component packages whose call sites count as framework code."""
28
+ return call('framework_folders') or ()
29
+
30
+
31
+ def source_window(instance=0):
32
+ return call('source_window', instance)
33
+
34
+
35
+ def source_views():
36
+ return call('source_views') or {}
37
+
38
+
39
+ def open_source(path, line_number=None, **kwargs):
40
+ callback = get('source_open')
41
+ if callback is not None:
42
+ return callback(path, line_number=line_number, **kwargs)
43
+ from meltygui.editor.source_preview import open_source_preview
44
+ return open_source_preview(path, line_number, kwargs.get('token'))
45
+
46
+
47
+ def jump_to_symbol(obj, path):
48
+ import inspect
49
+ try:
50
+ line = inspect.getsourcelines(inspect.unwrap(obj))[1] if obj is not None else None
51
+ except (TypeError, OSError):
52
+ line = None
53
+ return open_source(path, line_number=line, token=getattr(obj, '__name__', None))