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,402 @@
1
+ """Tensor types and data transformations, independent of rendering and GL."""
2
+
3
+ import math
4
+
5
+ import numpy as np
6
+
7
+ # Display-axis position in a sliced (z, y, x) volume.
8
+ _AXIS_POS = {"z": 0, "y": 1, "x": 2}
9
+
10
+
11
+
12
+ class TensorDim(int):
13
+ """A tensor dim index that is still an int everywhere it matters
14
+ (indexing, comparisons, arithmetic, `int()`, pickling) but carries its own
15
+ TYPE, so meltygui routes it to its own renderer instead of the plain int one
16
+ — a dim picker rather than a number field.
17
+
18
+ Values only stay TensorDim if whatever writes them keeps the type: a
19
+ renderer registered `@render_func(is_default_for=TensorDim)` should return
20
+ TensorDim(...), otherwise the first edit stores a plain int and the row
21
+ falls back to the int renderer."""
22
+
23
+ __slots__ = ()
24
+
25
+ def __repr__(self):
26
+ return f"TensorDim({int(self)})"
27
+
28
+
29
+ class TensorDims(tuple):
30
+ """A SET of tensor dim indices (`mean_dims`) — tuple everywhere it
31
+ matters, but typed so it routes to the same dim picker as TensorDim
32
+ (multi-select tabs). A tuple needs SOME type to route by; this is the
33
+ minimal one, and the renderer is shared."""
34
+
35
+ __slots__ = ()
36
+
37
+ def __repr__(self):
38
+ return f"TensorDims({tuple(int(v) for v in self)})"
39
+
40
+
41
+ from meltygui.model.lut_model import Lut
42
+
43
+
44
+ def _clean_dim_name(x, i):
45
+ """A dim name is a short single-line LABEL, whatever lands in the list —
46
+ DnD/paste can drop arbitrary objects whose str() is a multi-KB code repr,
47
+ and one of those blows up every radio row and billboard bake."""
48
+ first = (str(x).splitlines() or [""])[0].strip()
49
+ return first[:48] if first else f"dim{i}"
50
+
51
+
52
+ def _collection_dim_labels(col):
53
+ """Dim-name labels from the collection's `dim_names` entry; [] when no
54
+ names are in reach."""
55
+ raw_names = col.get("dim_names", ()) if col is not None else ()
56
+ return [_clean_dim_name(x, i) for i, x in enumerate(raw_names or ())]
57
+
58
+
59
+ def _resolve_dim(dim_names, v, n):
60
+ """A dim given by INDEX or by NAME (resolved through dim_names); None
61
+ stays None, out-of-range collapses to None."""
62
+ if v is None:
63
+ return None
64
+ if isinstance(v, str):
65
+ names = list(dim_names or ())
66
+ if v not in names:
67
+ return None
68
+ v = names.index(v)
69
+ try:
70
+ v = int(v)
71
+ except (TypeError, ValueError):
72
+ # params are user-editable from the panel and from source, so a dim
73
+ # can arrive as anything at all. Unusable = unset.
74
+ return None
75
+ return v if 0 <= v < n else None
76
+
77
+
78
+ def _resolve_axes(shape, dim_names, x_dim, y_dim, z_dim):
79
+ """(z, y, x) display dims for a shape: dims by index or NAME, None
80
+ derives the default (last three dims → z/y/x, like the old viewer).
81
+
82
+ ALWAYS returns three DISTINCT in-range dims (for n >= 3) — the params are
83
+ editable from the panel and from source, so two axes can name the same dim
84
+ or a garbage one. A dim already claimed by an earlier axis is treated as
85
+ unset and re-derived, which keeps the slicing downstream well-formed
86
+ (duplicate picks collapse the sliced volume to 2 dims and the permute
87
+ blows up). z wins over y wins over x, so the LAST axis you retarget onto a
88
+ taken dim is the one that moves."""
89
+ n = len(shape)
90
+ resolved = []
91
+ taken = set()
92
+ for cur in (z_dim, y_dim, x_dim):
93
+ d = _resolve_dim(dim_names, cur, n)
94
+ if d is None or d in taken:
95
+ resolved.append(None) # unset, or a duplicate: re-derive
96
+ else:
97
+ taken.add(d)
98
+ resolved.append(d)
99
+
100
+ def fill(default):
101
+ # The default dim, else the nearest free one scanning down then up.
102
+ # (The old walk stopped at 0 and could hand back a taken 0.)
103
+ if default not in taken:
104
+ return default
105
+ for d in range(default - 1, -1, -1):
106
+ if d not in taken:
107
+ return d
108
+ for d in range(default + 1, n):
109
+ if d not in taken:
110
+ return d
111
+ return default # n < 3: nothing free left
112
+
113
+ for i, default in enumerate((max(0, n - 3), max(0, n - 2), max(0, n - 1))):
114
+ if resolved[i] is None:
115
+ resolved[i] = fill(default)
116
+ taken.add(resolved[i])
117
+ return tuple(resolved)
118
+
119
+
120
+ def to_display_dtype(t):
121
+ """Coerce ANY torch tensor into something the raymarcher can sample:
122
+ a dense, real, float16/float32 tensor. float16/32 pass through untouched
123
+ (they upload as R16F/R32F with no copy); every other dtype maps to
124
+ float32 by meaning, not by bit pattern — complex → magnitude, bool →
125
+ 0/1, ints/uints → their values, float64/bfloat16 → narrowed (bfloat16
126
+ must NOT go to float16: its exponent range overflows). Quantized tensors
127
+ dequantize, sparse layouts densify. Raises ValueError with a readable
128
+ reason for anything that can't become a real float volume."""
129
+ import torch
130
+ if t.is_quantized:
131
+ t = t.dequantize()
132
+ if t.layout != torch.strided:
133
+ try:
134
+ t = t.to_dense()
135
+ except Exception as e:
136
+ raise ValueError(f"cannot densify {t.layout} tensor: {e}") from e
137
+ if t.dtype in (torch.float16, torch.float32):
138
+ return t
139
+ if t.is_complex():
140
+ return t.abs().float()
141
+ if t.dtype == torch.bool or not t.is_floating_point():
142
+ return t.to(torch.float32) # bool, int8..int64, uint8..
143
+ try:
144
+ return t.float() # float64, bfloat16, float8_*...
145
+ except Exception as e:
146
+ raise ValueError(f"unsupported tensor dtype {t.dtype}: {e}") from e
147
+
148
+
149
+ def _display_view_dtype(t):
150
+ """Keep supported CUDA dtypes/layouts intact; reject implicit conversions.
151
+ CPU reference paths can still materialize sparse/complex/quantized inputs.
152
+ bf16/ints/bool/f64 decode in the CUDA kernel without an f32 copy."""
153
+ import torch
154
+ if t.is_cuda and (t.is_quantized or t.layout != torch.strided or t.is_complex()):
155
+ raise ValueError("Direct CUDA rendering requires a dense real-valued tensor; "
156
+ "convert explicitly in user code to visualize this value.")
157
+ if t.is_quantized:
158
+ t = t.dequantize()
159
+ if t.layout != torch.strided:
160
+ try:
161
+ t = t.to_dense()
162
+ except Exception as e:
163
+ raise ValueError(f"cannot densify {t.layout} tensor: {e}") from e
164
+ if t.is_complex():
165
+ t = t.abs().float()
166
+ return t
167
+
168
+
169
+ def _slice_core(t, dim_names, x_dim, y_dim, z_dim, slices, mean_dims, sort_dim,
170
+ nf_on, nf_chop, nf_along, materialize):
171
+ """Shared slice logic: tensor → (z, y, x) volume as a VIEW (no
172
+ contiguous() — `materialize` decides the dtype pre-pass), plus the
173
+ mapping, source shape and the resolved neural-flow axes (positions in
174
+ the (z, y, x) volume, None = off)."""
175
+ import torch
176
+ t = (to_display_dtype if materialize else _display_view_dtype)(t.detach())
177
+ if t.numel() == 0:
178
+ raise ValueError(f"empty tensor (shape {tuple(t.shape)}) — nothing to display")
179
+ while t.dim() < 3:
180
+ t = t.unsqueeze(0)
181
+ n = t.dim()
182
+ shape = tuple(int(s) for s in t.shape)
183
+ zd, yd, xd = _resolve_axes(shape, dim_names, x_dim, y_dim, z_dim)
184
+ if 0 <= int(sort_dim) < n:
185
+ t = torch.sort(t, dim=int(sort_dim), descending=True).values
186
+ picked = (zd, yd, xd)
187
+ mean_set = {int(d) for d in (mean_dims or ()) if 0 <= int(d) < n}
188
+ for d in mean_set:
189
+ # f32 accumulate + result regardless of the input dtype (int inputs
190
+ # need it; bf16 inputs would otherwise round the mean - the view
191
+ # path must match the materialized one bit for bit).
192
+ m = t.mean(dim=d, keepdim=True, dtype=torch.float32)
193
+ # A DISPLAYED dim keeps its extent with the mean BROADCAST along it
194
+ # (the same value repeats across the plot - visual convenience);
195
+ # an unmapped dim stays collapsed and pins at 0 below.
196
+ t = m.expand(t.shape) if d in picked else m
197
+ def _pin(d):
198
+ # A pinned index from `slices` can be anything the panel/source lets;
199
+ # clamp into range instead of letting torch raise (or silently wrap on
200
+ # a negative).
201
+ try:
202
+ v = int(slices[d]) if d < len(slices) else 0
203
+ except (TypeError, ValueError):
204
+ v = 0
205
+ return max(0, min(v, shape[d] - 1))
206
+
207
+ index = tuple(
208
+ slice(None) if d in picked
209
+ else (0 if d in mean_set else _pin(d))
210
+ for d in range(n))
211
+ sub = t[index] # picked 3 dims keep original order
212
+ remaining = sorted(picked)
213
+ vol = sub.permute(remaining.index(zd), remaining.index(yd),
214
+ remaining.index(xd))
215
+ chop = along = None
216
+ if nf_on:
217
+ # Flow is pinned to TENSOR DIMS (remapping x/y/z never changes WHICH
218
+ # data gets chopped); unset dims default to chop=x, along=z. A chop
219
+ # or along dim that isn't mapped makes it a no-op.
220
+ chop_d = _resolve_dim(dim_names, nf_chop, n)
221
+ along_d = _resolve_dim(dim_names, nf_along, n)
222
+ dim_to_axis = {xd: "x", yd: "y", zd: "z"}
223
+ chop = dim_to_axis.get(xd if chop_d is None else chop_d)
224
+ along = dim_to_axis.get(zd if along_d is None else along_d)
225
+ if not (chop and along and chop != along):
226
+ chop = along = None
227
+ return vol, (zd, yd, xd), shape, chop, along
228
+
229
+
230
+ def slice_volume(t, dim_names=(), x_dim=None, y_dim=None, z_dim=None,
231
+ slices=(), mean_dims=(), sort_dim=-1, normalize=False,
232
+ nf_on=False, nf_chop=None, nf_along=None, nf_chunk=128,
233
+ nf_pad=False):
234
+ """tensor → (depth, height, width) display volume, PURE: every choice
235
+ arrives as an argument (the draw_voxels params), nothing is stored.
236
+ Unmapped dims pin to their `slices` index (missing entries → 0) or
237
+ average when listed in mean_dims (keepdim, then pinned at 0); a
238
+ DISPLAYED dim in mean_dims keeps its extent with the mean broadcast
239
+ along it (the value repeats across the plot); sort
240
+ orders fibers along a dim; normalize min-max stretches the DISPLAYED
241
+ volume (signed data scales by max-magnitude so zero stays anchored).
242
+ Stays on t's device. Returns (vol3, (z_dim, y_dim, x_dim), shape)."""
243
+ import torch
244
+ vol, mapping, shape, chop, along = _slice_core(
245
+ t, dim_names, x_dim, y_dim, z_dim, slices, mean_dims, sort_dim,
246
+ nf_on, nf_chop, nf_along, materialize=True)
247
+ vol = vol.contiguous()
248
+ if chop is not None:
249
+ vol = neural_flow_volume(vol, chop, along, int(nf_chunk), pad=nf_pad)
250
+ if normalize:
251
+ lo, hi = vol.min(), vol.max()
252
+ if lo < 0:
253
+ vol = vol / (torch.maximum(hi.abs(), lo.abs()) + 1e-12)
254
+ else:
255
+ vol = (vol - lo) / (hi - lo + 1e-12)
256
+ return vol, mapping, shape
257
+
258
+
259
+ class CudaVolumeView:
260
+ """The cuda_march stand-in for the volume GLTexture: NO GL object — the
261
+ kernel samples `view` (a strided (z, y, x) torch view of the source, on
262
+ whatever GPU it lives) in place. Carries the same metadata draw_voxels
263
+ reads off a volume texture (`shape` = DISPLAYED extents after neural
264
+ flow, source_shape, mapping, clamp_note) plus the kernel's sampling
265
+ facts: `nf` = (chop_axis, along_axis, chunk) with axes 0=z 1=y 2=x
266
+ (chop -1 = off) and `norm` = (lo, hi, mode)."""
267
+
268
+ __slots__ = ("view", "shape", "nf", "norm", "source_shape", "source_ndim",
269
+ "mapping", "clamp_note", "_vol_key", "dim_names")
270
+
271
+ def __init__(self, view, shape, nf, norm, mapping, source_shape):
272
+ self.view, self.shape, self.nf, self.norm = view, tuple(shape), nf, norm
273
+ self.mapping, self.source_shape = mapping, tuple(source_shape)
274
+ self.source_ndim = len(source_shape)
275
+ self.clamp_note = None
276
+ self._vol_key = None
277
+ self.dim_names = ()
278
+
279
+ def __repr__(self):
280
+ return f"CudaVolumeView({self.shape} of {tuple(self.view.shape)} on {self.view.device})"
281
+
282
+
283
+ def slice_volume_view(t, dim_names=(), x_dim=None, y_dim=None, z_dim=None,
284
+ slices=(), mean_dims=(), sort_dim=-1, normalize=False,
285
+ nf_on=False, nf_chop=None, nf_along=None, nf_chunk=128,
286
+ nf_pad=False):
287
+ """slice_volume for the cuda_march path: the same choices, but the result
288
+ is a CudaVolumeView over a strided VIEW of the source — no contiguous(),
289
+ no dtype copy, neural flow as in-kernel index math, normalize as a
290
+ (lo, hi) pair the kernel applies per sample. Only sort/mean (genuine
291
+ transforms) and densify/complex materialize anything; those run once
292
+ per vol_key like everything else behind draw_voxels' cache gate."""
293
+ vol, mapping, shape, chop, along = _slice_core(
294
+ t, dim_names, x_dim, y_dim, z_dim, slices, mean_dims, sort_dim,
295
+ nf_on, nf_chop, nf_along, materialize=False)
296
+ display_shape, nf = tuple(int(s) for s in vol.shape), (-1, -1, 0)
297
+ if chop is not None:
298
+ display_shape, nf = nf_display_shape(
299
+ vol.shape, _AXIS_POS[chop], _AXIS_POS[along], int(nf_chunk), pad=nf_pad)
300
+ norm = (0.0, 1.0, 0)
301
+ if normalize:
302
+ lo, hi = float(vol.min()), float(vol.max())
303
+ norm = (lo, max(abs(hi), abs(lo)), 2) if lo < 0 else (lo, hi, 1)
304
+ return CudaVolumeView(vol, display_shape, nf, norm, mapping, shape)
305
+
306
+
307
+ def _volume_scale(shape):
308
+ """Box extents per axis for a (depth, height, width) = (z, y, x) volume,
309
+ proportional to voxel counts (longest axis = 1), so every voxel renders
310
+ as a CUBE and a (4, 32, 48) tensor reads as a flat slab. Returned as the
311
+ shader's (x, y, z) order. No visibility floor: an earlier max(0.02, …)
312
+ per axis inflated the short side of anything past 50:1 (a (2048, 16)
313
+ time tensor drew its 16-voxel side 2.5× too wide). Thin slabs don't need
314
+ it — opacity accumulates in volume-NORMALIZED segment lengths, so a
315
+ 1-voxel dim still reads at full density. The epsilon only guards the
316
+ `/ volume_scale` divisions (labels/silhouette use the same scale)."""
317
+ t_depth, t_height, t_width = (max(1, int(s)) for s in shape)
318
+ longest = float(max(t_depth, t_height, t_width))
319
+ return (max(1e-5, t_width / longest),
320
+ max(1e-5, t_height / longest),
321
+ max(1e-5, t_depth / longest))
322
+
323
+
324
+ def neural_flow_volume(vol, chop_axis, along_axis, chunk, pad=False):
325
+ """The old viewer's neural flow on the DISPLAY volume: chop one axis into
326
+ `chunk`-wide blocks and concatenate them group-major along another —
327
+ identical layout to the original get_neural_flow's j*orig+i ordering,
328
+ which is exactly cat(split). No-op when the axes coincide, or when the
329
+ chop doesn't divide evenly — unless `pad`, which zero-fills the chop
330
+ axis up to the next multiple first (the auto-wrap path: any chunk must
331
+ work, a ragged last block is fine)."""
332
+ chop, along = _AXIS_POS[chop_axis], _AXIS_POS[along_axis]
333
+ size = int(vol.shape[chop])
334
+ if chop == along or chunk <= 0 or size <= chunk:
335
+ return vol
336
+ import torch
337
+ if size % chunk != 0:
338
+ if not pad:
339
+ return vol
340
+ extra = chunk - size % chunk
341
+ # F.pad's (before, after) pairs run from the last dim backwards.
342
+ spec = [0, 0] * (vol.dim() - 1 - chop) + [0, extra]
343
+ vol = torch.nn.functional.pad(vol, spec)
344
+ return torch.cat(vol.split(chunk, dim=chop), dim=along).contiguous()
345
+
346
+
347
+ def auto_neural_flow(shape, dim_names, x_dim, y_dim, z_dim, max_extent):
348
+ """The auto-wrap decision for a DISPLAYED axis longer than `max_extent`
349
+ (the GL limit, or the user's readability cap): returns
350
+ (chop_dim, along_dim, chunk) tensor-dim indices for neural flow, or None
351
+ when every displayed extent fits. Chops the LONGEST over-limit axis into
352
+ ~sqrt-sized chunks — the smallest divisor >= sqrt(size) when one exists
353
+ below the limit, else ceil(sqrt) with padding — and lays the blocks along
354
+ the SHORTEST other displayed axis (a (1, 32000) row becomes a ~180x180
355
+ slab). One pass only; anything still over-limit afterwards clamps."""
356
+ shape = tuple(int(s) for s in shape)
357
+ shape = (1,) * (3 - len(shape)) + shape if len(shape) < 3 else shape
358
+ if max_extent <= 0:
359
+ return None
360
+ zd, yd, xd = _resolve_axes(shape, dim_names, x_dim, y_dim, z_dim)
361
+ shown = (zd, yd, xd)
362
+ over = [d for d in shown if shape[d] > max_extent]
363
+ if not over:
364
+ return None
365
+ chop = max(over, key=lambda d: shape[d])
366
+ along = min((d for d in shown if d != chop), key=lambda d: shape[d])
367
+ size = shape[chop]
368
+ root = int(math.ceil(math.sqrt(size)))
369
+ chunk = next((c for c in range(root, min(size, max_extent) + 1) if size % c == 0),
370
+ root)
371
+ return chop, along, chunk
372
+
373
+
374
+ def _is_tensorish(v):
375
+ """A torch tensor / ndarray, or a container whose top level holds one."""
376
+ if isinstance(v, np.ndarray):
377
+ return True
378
+ if type(v).__module__.startswith("torch") and hasattr(v, "data_ptr"):
379
+ return True
380
+ if isinstance(v, (list, tuple)):
381
+ return any(_is_tensorish(x) for x in v)
382
+ if isinstance(v, dict):
383
+ return any(_is_tensorish(x) for x in v.values())
384
+ return False
385
+
386
+
387
+ def nf_display_shape(src_shape, chop_axis, along_axis, chunk, pad=True):
388
+ """Displayed (z, y, x) extents after neural flow of a (z, y, x) source
389
+ view — the cat(split) layout: chop axis → chunk, along axis →
390
+ along * ceil(size/chunk) (the ragged last block zero-padded in the
391
+ sampler). Axes are 0=z 1=y 2=x; chop -1 = off. Mirrors
392
+ neural_flow_volume: a chunk that doesn't divide the axis is a no-op
393
+ unless `pad`."""
394
+ shape = list(int(s) for s in src_shape)
395
+ if chop_axis < 0 or chop_axis == along_axis or chunk <= 0 or shape[chop_axis] <= chunk:
396
+ return tuple(shape), (-1, -1, 0)
397
+ if shape[chop_axis] % chunk != 0 and not pad:
398
+ return tuple(shape), (-1, -1, 0)
399
+ blocks = -(-shape[chop_axis] // chunk)
400
+ shape[along_axis] *= blocks
401
+ shape[chop_axis] = chunk
402
+ return tuple(shape), (chop_axis, along_axis, chunk)