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,3115 @@
1
+ from dataclasses import dataclass
2
+ from enum import Enum
3
+
4
+ from meltygui.state.core_enums import ProfileMode
5
+ from meltygui.core.rendering.render_funcs import RenderFuncs
6
+ from meltygui.core.rendering.core_decoration import Core
7
+ from meltygui.core.rendering.core_decoration import defaults
8
+ from meltygui.core.rendering.window_decoration import window
9
+ from meltygui.core.styling.style_core import ImGuiStyleManager
10
+ import json
11
+ from meltygui.hdr_color import pack_color
12
+ from meltygui.hdr_color import scale_saturation
13
+
14
+
15
+ @dataclass(frozen=True)
16
+ class Snippet:
17
+ """One code-suggestion snippet row (see Toggles.TextEditor.AC_SNIPPETS):
18
+ `label` is the popup row text, `insert` REPLACES the typed trigger on
19
+ accept — put `$0` where the caret should land (defaults to the end) —
20
+ `detail` is the dim right-hand preview (falls back to `insert`), and
21
+ `tint` optionally colors the row like a tinted symbol (rgb or rgba)."""
22
+ label: str
23
+ insert: str
24
+ detail: str = ""
25
+ tint: tuple = None
26
+
27
+
28
+ class SwooshMode(Enum):
29
+ """Connector style for the nested-window swoosh. Pass per window as a
30
+ kwarg — swoosh=True, swoosh_mode=SwooshMode.RIBBON — to override the
31
+ global default (the Swoosh.ribbon toggle) for just that window."""
32
+ LINE = "line"
33
+ RIBBON = "ribbon"
34
+
35
+
36
+
37
+ class Counters:
38
+ # Nested window
39
+ nested_window_count = 18
40
+ some_dict = [1,1,1]
41
+ some_dict2 = {1:1}
42
+
43
+ def rgb_to_hsv(r, g, b):
44
+ maxc = max(r, g, b)
45
+ minc = min(r, g, b)
46
+ rangec = (maxc - minc)
47
+ v = maxc
48
+ if minc == maxc:
49
+ return 0.0, 0.0, v
50
+ s = rangec / maxc
51
+ rc = (maxc - r) / rangec
52
+ gc = (maxc - g) / rangec
53
+ bc = (maxc - b) / rangec
54
+ if r == maxc:
55
+ h = bc - gc
56
+ elif g == maxc:
57
+ h = 2.0 + rc - bc
58
+ else:
59
+ h = 4.0 + gc - rc
60
+ h = (h / 6.0) % 1.0
61
+ return h, s, v
62
+
63
+ def hsv_to_rgb(h, s, v):
64
+ if s == 0.0:
65
+ return v, v, v
66
+ i = int(h * 6.0) # XXX assume int() truncates!
67
+ f = (h * 6.0) - i
68
+ p = v * (1.0 - s)
69
+ q = v * (1.0 - s * f)
70
+ t = v * (1.0 - s * (1.0 - f))
71
+ i = i % 6
72
+ if i == 0:
73
+ return v, t, p
74
+ if i == 1:
75
+ return q, v, p
76
+ if i == 2:
77
+ return p, v, t
78
+ if i == 3:
79
+ return p, q, v
80
+ if i == 4:
81
+ return t, p, v
82
+ if i == 5:
83
+ return v, p, q
84
+
85
+ def mix(r1, g1, b1, r2, g2, b2, alpha):
86
+ """Mix two colors with alpha blending"""
87
+ return (
88
+ r1 * (1 - alpha) + r2 * alpha,
89
+ g1 * (1 - alpha) + g2 * alpha,
90
+ b1 * (1 - alpha) + b2 * alpha
91
+ )
92
+
93
+ @window
94
+ class Tint:
95
+ # Saturation boosts go through hdr_color.scale_saturation: never past
96
+ # the tint's own gamut edge (the sRGB edge for an SDR tint, its own
97
+ # extended saturation for a P3 one), so the old 3.0 cap is gone.
98
+ max_value = 10.0
99
+
100
+ @staticmethod
101
+ @defaults(tint=(1.00,0.90,0.7944444417953491))
102
+ def icon_tint():
103
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
104
+ active_hsv = style_manager.hsv
105
+ hue_delta = -0.03
106
+ saturation_factor = 2.0
107
+ value_factor = 1.648
108
+
109
+ active_hsv = ((active_hsv[0] + hue_delta),
110
+ scale_saturation(active_hsv[1], saturation_factor),
111
+ min(max(active_hsv[2] * value_factor, 0), Tint.max_value))
112
+ return hsv_to_rgb(*active_hsv)
113
+
114
+ @staticmethod
115
+ def checkbox_outline():
116
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
117
+ active_hsv = style_manager.hsv
118
+
119
+ hue_delta = 0.00
120
+ saturation_factor = 1.2
121
+ value_factor = -0.002
122
+ active_hsv = ((active_hsv[0] + hue_delta),
123
+ scale_saturation(active_hsv[1], saturation_factor),
124
+ min(max(active_hsv[2] * value_factor, -1), Tint.max_value))
125
+ return hsv_to_rgb(*active_hsv)
126
+
127
+ @staticmethod
128
+ def checkbox_bg():
129
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
130
+ active_hsv = style_manager.hsv
131
+
132
+ hue_delta = 0.00
133
+ saturation_factor = 1.1
134
+ value_factor = 0.068
135
+
136
+ active_hsv = ((active_hsv[0] + hue_delta),
137
+ scale_saturation(active_hsv[1], saturation_factor),
138
+ min(max(active_hsv[2] * value_factor, -1), Tint.max_value))
139
+ return hsv_to_rgb(*active_hsv)
140
+
141
+ @staticmethod
142
+ @defaults(tint=(0.0, 0.0, 0))
143
+ def checkbox_bg_selected():
144
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
145
+ active_hsv = style_manager.hsv
146
+
147
+ hue_delta = 0.00
148
+ saturation_factor = 0.9
149
+ value_factor = 0.108
150
+
151
+
152
+ active_hsv = ((active_hsv[0] + hue_delta),
153
+ scale_saturation(active_hsv[1], saturation_factor),
154
+ min(max(active_hsv[2] * value_factor, -1), Tint.max_value))
155
+ return hsv_to_rgb(*active_hsv)
156
+
157
+ @staticmethod
158
+ @defaults(tint=(0.0, 0.0, 0))
159
+ def checkbox_bg_hovered():
160
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
161
+ active_hsv = style_manager.hsv
162
+
163
+ hue_delta = 0.00
164
+ saturation_factor = 0.6
165
+ value_factor = 0.258
166
+ active_hsv = ((active_hsv[0] + hue_delta),
167
+ scale_saturation(active_hsv[1], saturation_factor),
168
+ min(max(active_hsv[2] * value_factor, -1), Tint.max_value))
169
+ return hsv_to_rgb(*active_hsv)
170
+
171
+ @staticmethod
172
+ @defaults(tint=(0.127, 0.989, 0.0))
173
+ def checkbox_text_true():
174
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
175
+ active_hsv = style_manager.hsv
176
+
177
+ hue_delta = 0.00
178
+ saturation_factor = 1.1
179
+ value_factor = 2.125
180
+
181
+ active_hsv = ((active_hsv[0] + hue_delta),
182
+ scale_saturation(active_hsv[1], saturation_factor),
183
+ min(max(active_hsv[2] * value_factor, 0), Tint.max_value))
184
+ return hsv_to_rgb(*active_hsv)
185
+
186
+ @staticmethod
187
+ @defaults(tint=(0.9, 0.0, 0))
188
+ def checkbox_text():
189
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
190
+ active_hsv = style_manager.hsv
191
+
192
+ hue_delta = 0.00
193
+ saturation_factor = 0.8
194
+ value_factor = 1.018
195
+
196
+ active_hsv = ((active_hsv[0] + hue_delta),
197
+ scale_saturation(active_hsv[1], saturation_factor),
198
+ min(max(active_hsv[2] * value_factor, 0), Tint.max_value))
199
+ return hsv_to_rgb(*active_hsv)
200
+
201
+ @staticmethod
202
+ @defaults(tint=(0.478, 0.053, 0.053))
203
+ def dd_text(requested_tint=None):
204
+ if requested_tint is None:
205
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
206
+ active_hsv = style_manager.hsv
207
+ else:
208
+ active_hsv = rgb_to_hsv(*requested_tint)
209
+
210
+ hue_delta = 0.00
211
+ saturation_factor = 0.4
212
+ value_factor = 2.161
213
+
214
+ active_hsv = ((active_hsv[0] + hue_delta),
215
+ min(0.25, scale_saturation(active_hsv[1], saturation_factor)),
216
+ min(max(active_hsv[2] * value_factor, 0.85), Tint.max_value))
217
+ return hsv_to_rgb(*active_hsv)
218
+
219
+ @staticmethod
220
+ @defaults(tint=(0.15, 0.95, 0.30))
221
+ def change_count(added=True):
222
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
223
+ hue = 0.36 if added else 0.0
224
+ saturation = 0.70
225
+ value_factor = 2.161
226
+ brightness = min(1.0, max(0.85, style_manager.hsv[2] * value_factor))
227
+ return hsv_to_rgb(hue, saturation, brightness)
228
+
229
+ @staticmethod
230
+ @defaults(tint=(0.54, 0.54, 0.54))
231
+ def cursor_tint():
232
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
233
+ active_hsv = style_manager.hsv
234
+
235
+ hue_delta = 0.00
236
+ saturation_factor = 0.958
237
+ value_factor = 2.899
238
+
239
+ active_hsv = ((active_hsv[0] + hue_delta),
240
+ scale_saturation(active_hsv[1], saturation_factor),
241
+ min(max(active_hsv[2] * value_factor, 0), Tint.max_value))
242
+ return hsv_to_rgb(*active_hsv)
243
+
244
+ @staticmethod
245
+ @defaults(tint=(0.54, 0.54, 0.54))
246
+ def cursor_line_tint():
247
+ # The caret ROW's wash in draw_text. Same hue as the caret, but the
248
+ # brightness is capped at Toggles.TextEditor.cursor_line_max_brightness
249
+ # rather than Tint.max_value: the caret may go too-bright on a bright
250
+ # theme, and the same colour behind a whole row of text at any alpha
251
+ # drowned the glyphs (09-13).
252
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
253
+ active_hsv = style_manager.hsv
254
+
255
+ hue_delta = 0.00
256
+ saturation_factor = 0.958
257
+ value_factor = 2.899
258
+
259
+ active_hsv = ((active_hsv[0] + hue_delta),
260
+ scale_saturation(active_hsv[1], saturation_factor),
261
+ min(max(active_hsv[2] * value_factor, 0),
262
+ Toggles.TextEditor.cursor_line_max_brightness))
263
+ return hsv_to_rgb(*active_hsv)
264
+
265
+ @staticmethod
266
+ @defaults(tint=(0.45, 0.45, 0.45))
267
+ def line_number_tint(requested_tint=None):
268
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
269
+ active_hsv = (rgb_to_hsv(*requested_tint[:3]) if requested_tint is not None
270
+ else style_manager.hsv)
271
+
272
+ hue_delta = 0.00
273
+ saturation_factor = 0.75
274
+ value_factor = 4.10
275
+ # Live guards - see Toggles.TextEditor.gutter_text_min/max_brightness.
276
+ min_value = Toggles.TextEditor.gutter_text_min_brightness
277
+ max_value = max(Toggles.TextEditor.gutter_text_max_brightness, min_value)
278
+
279
+
280
+
281
+ active_hsv = ((active_hsv[0] + hue_delta),
282
+ scale_saturation(active_hsv[1], saturation_factor),
283
+ min(max(active_hsv[2] * value_factor, min_value), max_value))
284
+ return hsv_to_rgb(*active_hsv)
285
+
286
+ @staticmethod
287
+ @defaults(tint=(0.17, 0.2, 0.228))
288
+ def line_number_bg():
289
+ # The gutter follows the editor BODY's painted background, not the main
290
+ # theme tint: the body is a depth ramp capped at
291
+ # Toggles.TextEditor.editor_value, but a theme-relative gutter sat a
292
+ # different distance under that for every file tint (a dark tint left
293
+ # the strip near black). bg_color_stack[-1] is the fill the editor
294
+ # painted for the view whose body is running (draw_text is show_bg).
295
+ hue_delta = 0.00
296
+ # Custom knobs - see Toggles.TextEditor.gutter_saturation, gutter_value.
297
+ saturation_factor = Toggles.TextEditor.gutter_saturation
298
+ value_factor = Toggles.TextEditor.gutter_value
299
+
300
+ bg_color_stack = Core.melty.bg_color_stack
301
+ body_bg = bg_color_stack[-1] if isinstance(bg_color_stack, list) and bg_color_stack else None
302
+ if isinstance(body_bg, (tuple, list)) and len(body_bg) >= 3 and (len(body_bg) < 4 or body_bg[3] > 0):
303
+ active_hsv = rgb_to_hsv(*body_bg[:3])
304
+ else:
305
+ # No painted body (a bare call outside a show_bg run): fall back
306
+ # to the theme tint, as before.
307
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
308
+ active_hsv = style_manager.hsv
309
+
310
+ active_hsv = ((active_hsv[0] + hue_delta),
311
+ scale_saturation(active_hsv[1], saturation_factor),
312
+ min(max(active_hsv[2] * value_factor, 0), Tint.max_value))
313
+ return hsv_to_rgb(*active_hsv)
314
+
315
+ @staticmethod
316
+ @defaults(tint=(0.0, 0.325, 0.972))
317
+ def text_selection():
318
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
319
+ active_hsv = style_manager.hsv
320
+
321
+ hue_delta = 0.00
322
+ saturation_factor = 1.313
323
+ value_factor = 1.714
324
+ max_value = 0.6
325
+
326
+ active_hsv = ((active_hsv[0] + hue_delta),
327
+ scale_saturation(active_hsv[1], saturation_factor),
328
+ min(max(active_hsv[2] * value_factor, 0), max_value))
329
+ return hsv_to_rgb(*active_hsv)
330
+
331
+ @staticmethod
332
+ @defaults(tint=(0.30, 0.34, 0.40))
333
+ def scope_guide():
334
+ # The text editor's indent guides for the file with no tint: the
335
+ # window background lifted a little, so the line reads as
336
+ # "background, slightly brighter" whatever the theme (the
337
+ # Toggles.TextEditor.scope_guide_* knobs adjust it later).
338
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
339
+ active_hsv = style_manager.hsv
340
+
341
+ hue_delta = 0.00
342
+ saturation_factor = 0.85
343
+ value_factor = 2.2
344
+ min_value = 0.12
345
+
346
+ active_hsv = ((active_hsv[0] + hue_delta),
347
+ scale_saturation(active_hsv[1], saturation_factor),
348
+ min(max(active_hsv[2] * value_factor, min_value), Tint.max_value))
349
+ return hsv_to_rgb(*active_hsv)
350
+
351
+ @staticmethod
352
+ @defaults(tint=(0.367, 0.112, 0.112))
353
+ def subtle_text():
354
+ style_manager: ImGuiStyleManager = Core.melty.style_manager
355
+ active_hsv = style_manager.hsv
356
+
357
+ hue_delta = 0.00
358
+ saturation_factor = 0.7
359
+ value_factor = 0.668
360
+
361
+ active_hsv = ((active_hsv[0] + hue_delta),
362
+ scale_saturation(active_hsv[1], saturation_factor),
363
+ min(max(active_hsv[2] * value_factor, 0), Tint.max_value))
364
+ return hsv_to_rgb(*active_hsv)
365
+
366
+ # Context menu tints
367
+ context_select_tint = (1.0, 0.7, 0.2)
368
+ context_select_outline_alpha = -0.242
369
+ context_select_bg_alpha = 0.592
370
+ context_select_rounding = 4.988
371
+
372
+ # Background constants
373
+ context_menu_bg_offset = -1.0
374
+
375
+ # Highlight outline boxes (parent + child views)
376
+ highlight_outline_thickness = 3.0 # outline line thickness
377
+ highlight_outline_rounding = 7.868 # corner radius of the outline boxes
378
+ highlight_outline_alpha = 0.472 # outline opacity
379
+ highlight_bg_alpha = 0.0 # parent fill opacity
380
+
381
+ # Selection rect (child views)
382
+ select_outline_thickness = 2.0 # selection outline line thickness
383
+ select_outline_alpha = 0.65 # selection outline opacity
384
+ select_bg_alpha = 0.08 # selection fill opacity
385
+
386
+ @window
387
+ class Swoosh:
388
+ # Nested-window "swoosh" connector (parent outline -> nested view)
389
+ tint = (1.0, 0.683, 0.0) # fallback color if no style tint is available
390
+ value = 1.023 # brightness of the highlight (super-bright tint)
391
+ saturation = 0.791 # saturation scale applied to the current tint
392
+ alpha = 1.0 # opacity of the swoosh
393
+ end_thickness = 1.433 # half-width at the anchor endpoints (thick)
394
+ cap_scale = 0.962 # end-cap dot radius as a multiple of end thickness
395
+ mid_thickness = 0.441 # half-width at the middle (thin)
396
+ curve = 0.029 # max arc bow as a fraction of endpoint distance
397
+ curve_ramp = 2.00 # how the bow eases in to slope (>1 stays straighter longer)
398
+ edge_softness = 1.138 # px smoothing window for the shared-edge anchor (0 = hard)
399
+ segments = 31 # tessellation count (higher = smoother)
400
+ taper = 10.0 # slope of the end->middle thickness falloff
401
+ aa_width = 1.5 # antialiased edge-stroke width in px (0 = none)
402
+
403
+ # Ribbon mode: replace the thin connector line with a full band bridging the
404
+ # two views' facing edges, s-curving between them when the views are offset
405
+ # (see Melty._draw_ribbon). Each end is sized from ITS OWN edge length, so
406
+ # a small child on a big parent gets a funnel. Per-window override:
407
+ # swoosh_mode=SwooshMode.RIBBON / OUTLINE. Views with no facing gap
408
+ # (overlapping) fall back to the thin line, which knows how to route
409
+ # around the overlap.
410
+ ribbon = True # global default: ribbon instead of the thin line
411
+ ribbon_axis_bias = 0.9 # which axis the band comes from: 0.5 picks the axis
412
+ # with the wider facing gap (current behavior); 1.0
413
+ # biases fully to the left/right (x) edges,
414
+ # 0.0 fully to the top/bottom (y) edges. An
415
+ # axis with no facing gap can't be bridged, so an
416
+ # extreme bias falls back to whichever axis has a gap.
417
+ ribbon_coverage = 2.13 # each end's band width as a fraction of its own edge
418
+ # (clamped at the full edge, so >=1 spans the edge)
419
+ ribbon_max_width = 0 # px cap on either end's band width (0 = uncapped)
420
+ ribbon_curve = 0.33 # s-curve tangent reach as a fraction of the gap the
421
+ # ribbon spans (x for left-right, y for down)
422
+ ribbon_curve_across = 0.00 # how much of a side's CROSS-axis travel adds to that
423
+ # reach - the offset matters less than the gap (0 = none at all)
424
+ ribbon_bow = -0.02 # single-sided bow: how far the band bulges through
425
+ # the swerve, scaled by width/length so wide short
426
+ # ribbons arc as one C and long thin ones keep the
427
+ # S (negative = bow "in" against the swerve, 0 = off)
428
+ ribbon_bow_shape = 2.0 # bow profile exponent: <1 broad arc, >1 mid bulge
429
+
430
+
431
+ # [tint=(0.55, 0.073, 0.073, 1.0), show_tint=True]
432
+ ribbon_alpha = 0.36 # fill opacity of the band (below the fade area)
433
+ ribbon_fade_size = 328.2 # px: the fill starts thinning once the band's AREA
434
+ # exceeds fade_size x fade_size; alpha then scales
435
+ # inversely with area (constant total ink, 0 = off)
436
+ ribbon_edge_alpha = 0.05 # opacity of the band's two boundary strokes
437
+ ribbon_edge_fade_length = 7.4 # px: a boundary stroke starts thinning once its
438
+ # own arc length exceeds this; alpha scales inversely
439
+ # with length, per side (0 = off)
440
+ ribbon_edge_thickness = 2.7 # boundary stroke thickness in px (0 = no stroke/AA)
441
+
442
+ # When the child overlaps the parent, slide both endpoints along their own
443
+ # rect edge out of the intersection area to flank the reentrant corner of the
444
+ # union, then bow the curve smoothly towards that corner so the connector hugs the
445
+ # outside of the overlap instead of crossing either view. See
446
+ # Melty._closest_perimeter_points.
447
+ avoid_intersection = True # slide the endpoints out of the overlap, avoid the corner
448
+ intersect_hook = 24.0 # px the endpoints slide along the edge past the overlap
449
+ intersect_soft = 50.0 # px of overlap depth over which to ease in from the plain cur
450
+ overlap_padding = 20.0 # grow each rect by this so the transition starts before they touch
451
+ envelop_tie = 0.04 # enveloped child: near-tie window for the round corner blend
452
+ # (0 = always flat edges, hard switch; 0.5 = always blending)
453
+ envelop_corner = 0.25 # enveloped child: only round the corner when the nearer gap is
454
+ # within this fraction of the parent's shorter side (else stay flat)
455
+
456
+ # Mouse-proximity fade: scale the whole connector's opacity by how close
457
+ # the cursor is to the views it joins, so only the swooshes near the mouse
458
+ # stay bright and a busy screen of connectors declutters. Each END fades on
459
+ # its OWN distance scale (parent vs child), and the connector takes whichever
460
+ # side is brighter — so the parent end can dim sooner than the child end.
461
+ # Distance is measured to each rect (0 when the mouse is inside it). Applies
462
+ # to both the line and ribbon styles.
463
+ # Drag-focus opacity (alternative to the proximity fade below): hold EVERY
464
+ # connector at rest_alpha and light one to drag_alpha only when it is in
465
+ # play - its child window is being dragged/resized, the parent window it
466
+ # hangs off is, or the parent view (or the window itself) is hovered.
467
+ # Dragging a PARENT window lights every connector hanging off it; dragging
468
+ # a CHILD window lights only that window's own connector. False = the
469
+ # original behavior: distance fade + hover override.
470
+ drag_focus = True
471
+ drag_alpha = 0.6 # connector opacity while lit (dragging / parent hovered)
472
+ rest_alpha = 0.29 # opacity of every other connector at rest (0 = invisible)
473
+
474
+ mouse_falloff = False # enable the distance-based opacity fade
475
+ mouse_falloff_dist_parent = 49.576 # px: parent-end falloff distance (lower =
476
+ # the parent side dims sooner as you leave it)
477
+ mouse_falloff_dist_child = 638.1 # px: child-end falloff distance
478
+ mouse_falloff_floor = 0.07 # opacity multiplier when far away (0 = invisible)
479
+ mouse_falloff_exp = 2.2 # falloff curve exponent (>1 = stay bright near
480
+ selectable = False
481
+ # the rect, then drop off; 1 = linear)
482
+
483
+
484
+ @window(tint=(0.27, 0.19, 0.14))
485
+ class Toggles:
486
+ @defaults(tint=(0.85, 0.75, 0.05))
487
+ class ColorPicker:
488
+ # [tint=(0.85, 0.75, 0.05)]
489
+ square_size = 180
490
+ # [tint=(0.85, 0.75, 0.05)]
491
+ tabs_height = 30
492
+ # [tint=(0.85, 0.75, 0.05)]
493
+ extension_width = 60
494
+ # [tint=(0.85, 0.75, 0.05)]
495
+ exposure_band_height = 54
496
+ # [tint=(0.85, 0.75, 0.05)]
497
+ anchor_gap = 10
498
+ # [tint=(0.85, 0.75, 0.05)]
499
+ offsets_height = 8 + 2 * 20
500
+ # [tint=(0.85, 0.75, 0.05)]
501
+ view_offset_rows = (("bg_offset", "Bg offset", 0.05),
502
+ ("z_offset", "Z offset", 0.05))
503
+
504
+ @defaults(tint=(0.85, 0.75, 0.05))
505
+ class Dropdown:
506
+ # [tint=(0.85, 0.75, 0.05)]
507
+ menu_width = 170
508
+ # [tint=(0.85, 0.75, 0.05)]
509
+ row_height = 24
510
+ # [tint=(0.85, 0.75, 0.05)]
511
+ min_width = 300
512
+ # [tint=(0.85, 0.75, 0.05)]
513
+ min_height = 33
514
+ # [tint=(0.85, 0.75, 0.05)]
515
+ max_height = 500
516
+ # [tint=(0.85, 0.75, 0.05)]
517
+ code_label_max_width = 150.0
518
+ # [tint=(0.85, 0.75, 0.05)]
519
+ tag_color = (0.55, 0.6, 0.72, 0.85)
520
+ # [tint=(0.85, 0.75, 0.05)]
521
+ tag_gap = 8.0
522
+ # [tint=(0.85, 0.75, 0.05)]
523
+ row_tint_alpha = 0.35
524
+
525
+ @defaults(tint=(0.811, 0.59, 0.29))
526
+ class UsagePicker:
527
+ # [tint=(0.811, 0.59, 0.29)]
528
+ row_height = 24.0
529
+ row_gap = 2.0
530
+ min_width = 680
531
+ min_height = 33
532
+
533
+
534
+ # Allow window gestures to edit caller/default source code as a GUI editor.
535
+ # Comment overrides and local window state do not require this flag.
536
+ dangerous_edit_mode = False
537
+
538
+ @defaults(tint=(0.103, 0.341, 0.617))
539
+ class windows:
540
+ # Native EGL/Wayland for app windows; GLFW on other display systems.
541
+ # Chosen when an application's window share group is created.
542
+ # [tint=(0.103, 0.341, 0.617)]
543
+ native_os_windows = True
544
+
545
+ # [tint=(0.811, 0.59, 0.29)]
546
+ dynamic_styles = False
547
+ # [tint=(0.811, 0.59, 0.29)]
548
+ dynamic_style_root = (0.12, 0.13, 0.15)
549
+ # [tint=(0.811, 0.59, 0.29)]
550
+ dynamic_text_contrast = 4.5
551
+
552
+ @defaults(tint=(0.811, 0.59, 0.29))
553
+ class TextEditor:
554
+
555
+ # Completion menus stay compact; more candidates scroll.
556
+ completion_max_height = 202
557
+
558
+ some_list = [122,-21,311]
559
+
560
+ some_new_dict= {
561
+ "key": ""
562
+ }
563
+
564
+ enable_spell_check = False
565
+ # Long-line token clipping (_window_tokens band): a line longer
566
+ # than long_line_cols chars is tokenized/drawn only over the visible
567
+ # column span (+ margin), the rest riding as O(1) 'clipped' tokens.
568
+ # long_line_band_cols is the band's quantum/margin in columns - the
569
+ # window_tokens only misses when the x-scroll crosses a step.
570
+ long_line_cols = 1500
571
+ long_line_band_cols = 512
572
+ text_focus_stack_trace = False
573
+ # TEMP: dump a def-tint coordinate trace in /tmp/lsd_tint_flicker.log
574
+ # while hunting the one-frame wash misplacement on edits - logs each
575
+ # traced line's drawn wash column vs the indent the live text claims,
576
+ # and the mismatch frame names which side (editor vs draw map) lied.
577
+ tint_flicker_trace = False
578
+ # Scope-derived code folds (_scope_fold_ranges): off = editors derive
579
+ # no per-def/class fold ranges (no chevrons, no default-collapsed
580
+ # scopes; explicit fold_ranges from callers still work). The O(buffer)
581
+ # scan (~22ms on a large buffer) is debounced off the keystroke path
582
+ # (input-quiet rescan on splice - the collapse state is stored as
583
+ # line-independent fold KEYS) but this switch skips the layer entirely.
584
+ # Read live.
585
+ scope_fold_ranges = True
586
+ # Compound-statement folds (if/elif/else/for/while/try/except/
587
+ # finally/with/match/case) alongside the def/class scopes. Read on
588
+ # the next fold rescan (text edit), not per frame.
589
+ block_fold_ranges = True
590
+ # Collapsed comment runs that carry a melongui `# [...]` metadata line
591
+ # hide their HEADER line too: the whole run drops the display and
592
+ # only the fold chevron remains, on the gutter of the line BELOW the
593
+ # run (the line the metadata annotates; edits splice around the
594
+ # seam at the end of the hidden run). Off = a collapsed run keeps
595
+ # its first comment line visible like every other line. A run at
596
+ # the top / end of the file, or one whose metadata below heads a fold
597
+ # of its own (the gutter has one chevron per row), keeps its header
598
+ # either way. Read live.
599
+ hide_meta_comment_folds = True
600
+ # Diff fold spans (draw_text's diff_fold_ranges — the compare
601
+ # splits' unchanged gaps) wear THIS tint on their chevrons/badges
602
+ # and, while collapsed, a thin separator band across the row — so
603
+ # they read apart from the grey scope folds at a glance. 4th value
604
+ # is the badge alpha at rest (hover lifts it). Read live.
605
+ # [tint=(0.36, 0.62, 0.85)]
606
+ diff_fold_tint = (0.36, 0.62, 0.85, 0.55) # rgb = fallback for a buffer with no file; alpha = chevron / label alpha
607
+ # Diff-gap PREVIEW: a collapsed gap keeps this many of its hidden
608
+ # lines visible (faded) on each side — `below` = under the context
609
+ # lines that follow the change ABOVE the gap (the fold badge sits on
610
+ # the last of them), `above` = over the context lines that lead
611
+ # into the change BELOW the gap — so a collapsed diff still hints
612
+ # at what the gap holds. 0 = the bare fold on that side. Built
613
+ # into the gap spans (open_files._diff_gap_folds) — takes effect on
614
+ # the next re-derive.
615
+ # [tint=(0.36, 0.62, 0.85)]
616
+ diff_preview_lines_below = 5
617
+ # [tint=(0.36, 0.62, 0.85)]
618
+ diff_preview_lines_above = 5
619
+ # Glyph alpha factor on those preview rows (1.0 = not faded). Read
620
+ # live.
621
+ # [tint=(0.36, 0.62, 0.85)]
622
+ diff_preview_alpha = 0.45
623
+ # Enter inside a single-quoted string literal closes it and reopens
624
+ # it on the next line (implicit concatenation, parenthesised when
625
+ # not already inside parentheses) instead of leaving an unterminated
626
+ # string. Read live.
627
+ enter_splits_strings = True
628
+ # Master switch for the live-view pipeline: off = the editor draws no
629
+ # live-view/snapshot markers (and drops the gutter toggle column), and
630
+ # opening a context menu no longer collects - the menu-open stack
631
+ # capture skips the frame-snapshot publish and the one-shot body-locals
632
+ # too. Captured stores persist untouched and the markers come back
633
+ # on re-enable. Read live.
634
+ enable_live_view = True
635
+
636
+ # Hovering a live-view marker shows its value window as a TEMPORARY
637
+ # preview (closes on mouse-leave); double-click still latches it
638
+ # open permanently. Read live per marker render.
639
+ live_hover_preview = False
640
+
641
+ # Auto-open the value window for 3-D+ tensors (typically voxel volumes)
642
+ # the moment an instrumented run captures them. Off = every snapshot
643
+ # marker starts closed (click-gutter to open) - with loop
644
+ # accumulation stacking per-layer tensors into volumes, a run would
645
+ # otherwise pop one window per captured tensor. A site can opt back
646
+ # in with `# [auto_open=True]`. Read live per marker render.
647
+ live_auto_open_volumes = False
648
+
649
+ # Paint a captured SIMPLE value over all USAGES of its symbol too
650
+ # (e x = 301, then `.view(1, 301, ...)` further down). Display-time
651
+ # only: usages are resolved from the text (live_usage.py) and read
652
+ # the binding's single store entry - no extra runs and nothing
653
+ # is captured in the instrumented function. Read live per repaint.
654
+ live_inline_usages = True
655
+ # First-render budget for live markers per overlay pass: a diff
656
+ # expand / a fresh snapshot can reveal thousands of markers at once,
657
+ # each costing a render_func call + draw_state; the rest are
658
+ # created over the following frames (their pills show at once).
659
+ # [tint=(0.36, 0.62, 0.66)]
660
+ live_marker_create_budget = 300
661
+ token_match_tint = (0.277, 0.50, 0.50, 0.22)
662
+ # The caret row's wash (Tint.cursor_line_tint, drawn under the text
663
+ # by draw_text): the brightest the wash colour may get (HSV value,
664
+ # 1.0 = SDR white) and its alpha. Raise max_brightness to make the
665
+ # row pop on dark themes; lower it if glyphs get hard to read. Read
666
+ # live.
667
+ # [tint=(0.277, 0.50, 0.50)]
668
+ cursor_line_max_brightness = 0.6
669
+ # [tint=(0.277, 0.50, 0.50)]
670
+ cursor_line_alpha = 0.08
671
+ # [tint=(0.55, 0.496, 0.147, 1.0), show_tint=True]
672
+ check_syntax_errors = True
673
+
674
+ # [tint=(0.152, 0.143, 0.628), show_tint=True]
675
+ freeze_cst_dict = False
676
+
677
+ # Fast-path syntax check: re-run a bare compile() over the buffer
678
+ # INLINE on every edit and swap the red marker immediately, instead of
679
+ # hiding it until the debounced background reparse lands (~300ms after
680
+ # typing goes quiet). compile() is the C parser — no libcst — so a
681
+ # typical span buffer costs well under 1ms; buffers over
682
+ # fast_check_max_chars skip it and keep the debounced-only behavior.
683
+ # No effect with check_syntax_errors off. Read live.
684
+ # [tint=(0.72, 0.11, 0.11), show_tint=True]
685
+ fast_syntax_check = True
686
+
687
+ # Size cap for the fast path above: buffers larger than this skip the
688
+ # inline per-keystroke compile() + import scan and stay on the
689
+ # debounced background pass. compile() is O(buffer) on the render
690
+ # thread — measured ~0.07ms @ 2KB, ~1.2ms @ 18KB, ~11ms @ 128KB — so
691
+ # this bounds the worst-case frame hit. 0 disables the fast path on
692
+ # every edit. Over-cap buffers fall back to the changed-region path
693
+ # below (import suggestions stay debounced-only there). Read live.
694
+ fast_check_max_chars = 128 * 1024
695
+
696
+ # Changed-region fast check for buffers OVER fast_check_max_chars:
697
+ # diff old vs new text (including prefix/suffix lines), expand the edit
698
+ # to its enclosing top-level block(s), and compile just that snippet
699
+ # (dedent + fake-functioning - the _compile_check machinery) -
700
+ # real-time syntax markers on very large files at O(edited block)
701
+ # cost. Differential: the whole region must compile clean for a new
702
+ # failure to be reported, so a region cut mid-string or mid-bracket
703
+ # can never false-flag. Read live.
704
+ check_changed_region = True
705
+
706
+ # Whole-buffer static lint cap: check_source (undefined names /
707
+ # call-signature checks) and the relint's full import rescan are
708
+ # O(buffer) GIL-held passes (~90ms + ~40ms on a 340KB file) that the
709
+ # relint's re-runs per queued keystroke save - a recurring render-
710
+ # thread convoy on big files. Buffers over this cap skip check_source
711
+ # and downgrade the full import rescan to the incremental step; the
712
+ # fast-path syntax markers and import popovers are unaffected.
713
+ # Trade-off over the cap: an import REMOVED elsewhere in the file can
714
+ # keep its name wrongly suppressed until a full pass runs again.
715
+ # Read live (worker-side).
716
+ lint_max_chars = 64 * 1024
717
+
718
+ # Master switch for the static lint (check_source: undefined names /
719
+ # call-signature checks) in both the chain_in and relint passes —
720
+ # independent of incremental_lint / lint_max_chars, for isolating
721
+ # other pipeline features while profiling. Off = lint never runs;
722
+ # syntax-error markers and import suggestions are unaffected.
723
+ # [tint=(0.256, 0.189, 0.244, 1.0), show_tint=True]
724
+ enable_lint = True
725
+ # Call-signature checks for SPAN buffers (a function/class edited on
726
+ # its own): bare-name calls resolve through the enclosing module's
727
+ # PENDING text (code_checks._signature_table), so a signature edited
728
+ # in another view flags wrong call sites before any recompile. Off =
729
+ # span buffers report missing imports only (the old behavior).
730
+ # [tint=(0.256, 0.189, 0.244, 1.0), show_tint=True]
731
+ lint_span_calls = True
732
+ # Literal-argument TYPE checks in the call lint: a LITERAL argument
733
+ # (False, 3, "x") against a DECLARED param type (a doc C type like
734
+ # `float position`, or a float/int/str/bool annotation) — catches
735
+ # imgui.same_line(False). Stricter than Python's coercions on
736
+ # purpose: numeric params reject bool literals. Expressions, names
737
+ # and None literals are never judged.
738
+ # [tint=(0.256, 0.189, 0.244, 1.0), show_tint=True]
739
+ lint_literal_types = True
740
+
741
+ # Master switch for the import-suggestions scan (the Alt+Enter
742
+ # quick-fix channel) in the chain_in / relint passes and the editor's
743
+ # per-keystroke fast path - for isolating pipeline features while
744
+ # profiling. Off = no scans run and the suggestion data dries up after
745
+ # the next pass; error markers and lint are unaffected.
746
+ enable_import_scan = True
747
+
748
+ # Typing debounce (ms) used by the two O(buffer) passes that key off
749
+ # keystrokes: the chain_in cst→dict reparse (deferred until input goes
750
+ # idle, re-queued on every key) and the symbol-usage recompute
751
+ # quiet-gate (serves the last-good graph while input is hotter than
752
+ # this). In the inter-key gap of fast typing a burst coalesces into
753
+ # ONE reparse; lower = fresher structure/usages but more mid-burst
754
+ # GIL-held parse convoying the render thread. 0 = no debounce (every
755
+ # keystroke reparses - pathological on big buffers). First parses are
756
+ # exempt. Read live.
757
+ parse_debounce_ms = 32
758
+
759
+ # Alternative debounce (ms) for SMALL buffers: under
760
+ # small_file_max_chars a full cst→dict parse + symbol pass costs a few
761
+ # ms, not the 150-550ms that made the big-file debounce necessary - so
762
+ # they can run almost per keystroke without convoying the render
763
+ # thread. Applies to the same two passes as parse_debounce_ms above.
764
+ # 0 = no debounce at all on small buffers. Set equal to
765
+ # parse_debounce_ms to disable the split. Read live.
766
+ small_file_debounce_ms = 119
767
+
768
+ # Size gate for small_file_debounce_ms: buffers up to this many chars
769
+ # take the fast debounce, larger ones use parse_debounce_ms. 0
770
+ # disables the small-file path entirely (everything uses
771
+ # parse_debounce_ms). Read live.
772
+ small_file_max_chars = 16 * 1024
773
+
774
+ # Trailing debounce (ms) on the def-widget's TEXT → params-panel
775
+ # sync: typing a new default in the signature updates the open panel
776
+ # only after this quiet interval, so a half-typed value ("5" on the
777
+ # way to "50") never lands mid-keystroke — which matters with Auto
778
+ # Execute on, where the synced value triggers a run. Re-armed per
779
+ # keystroke. 0 = sync immediately. Read live.
780
+ fnrun_text_sync_debounce_ms = 400
781
+
782
+ # Trailing debounce (ms) on Auto Execute's CODE-EDIT trigger: with
783
+ # a def and Auto Execute on, an edit to the def's text (body, params,
784
+ # annotations - literal defaults excluded, the panel path already
785
+ # runs those) recompiles + instrument-runs it once typing has been
786
+ # quiet this long. Re-armed per keystroke; the trigger reads
787
+ # PendingSave, so expiry waits (short retries) for the deferred
788
+ # save channel to carry the edit. 0 = run on every buffer change
789
+ # that reaches pending. Read live.
790
+ fnrun_auto_exec_edit_debounce_ms = 700
791
+
792
+ # Incremental cst→dict conversion: when a previous good parse exists,
793
+ # re-convert only the changed top-level statements and splice them
794
+ # into the held parse + module cst (cst_dict_incremental_update) -
795
+ # O(edited statements) instead of the 150-550ms whole-buffer parse.
796
+ # Falls back to the full conversion on any doubt. Read live.
797
+ incremental_cst_parse = True
798
+
799
+ # melty_syntax: parse code with core_syntax (Python's ast + a TEXT
800
+ # residual, like libcst) instead of libcst - ~7x faster forward, ~50x
801
+ # on the reverse, but the round-trip is text surgery-only so unchanged
802
+ # code comes back byte-identical. Decides the parser for NEW parses
803
+ # (and the next edit of a held one: the parser switch forces a full
804
+ # reparse); every chain node dispatches on the parse it is handed
805
+ # (__origin__ = core_syntax, __cst__ = libcst), so it flips live.
806
+ # The cst-dict cache is keyed by parser. Read live.
807
+ melty_syntax = True
808
+
809
+ # melty_scanner: with melty_syntax on, parse with core_syntax's own
810
+ # tokenize-based scanner (melty_scan.scan - the "cst-lite" replacement
811
+ # for ast.parse; off = Python's ast, the oracle the scanner is tested
812
+ # against). It is what makes the above knob possible. Read live.
813
+ melty_scanner = True
814
+
815
+ # Buffers at least this many chars parse in the scan WORKER: a 3.12
816
+ # subinterpreter with its own GIL, so the parse runs truly in parallel
817
+ # and never stalls the render thread; smaller buffers parse in-process
818
+ # (their parse is a few ms). 0 = always the worker. Read live.
819
+ melty_async_min_chars = 32 * 1024
820
+
821
+
822
+ # Fidelity gate for the merge above: regenerate the spliced module's
823
+ # code and require it to EQUAL the new buffer (one O(file) codegen,
824
+ # ~a sixth of full-parse cost) - any comment/whitespace attribution
825
+ # drift falls back to the full parse instead of corrupting the
826
+ # round-trip. Turn off once trusted for the last bit of speed.
827
+ verify_incremental_cst = True
828
+
829
+ # Incremental static lint: per-path re-diff - findings outside the
830
+ # edited top-level block are kept (line-shifted), only the block
831
+ # itself is re-linted, and the live-module + pending-binds fallbacks
832
+ # resolving cross-buffer names (the same way span lint already
833
+ # works). ~1ms per edit instead of the ~90ms whole-buffer pass, after
834
+ # a one-time full pass per path - so with this ON, big buffers lint
835
+ # again (lint_max_chars stops skipping them). Trade-off: a binding
836
+ # added/removed OUTSIDE the edited block doesn't re-verify findings
837
+ # elsewhere until the next full pass. Read live (worker-side).
838
+ incremental_lint = True
839
+
840
+ # Chain_in no-mutation skip: a newline-only edit (blank lines added
841
+ # or removed, or a byte-identical echo) can't change the parse
842
+ # structure or introduce a syntax error, so the full reparse -
843
+ # 150-550ms of GIL-held libcst + dict conversion + compile that
844
+ # convoys the render thread - is skipped inline for it. The held
845
+ # parse and its src_good baseline stay put, so the first CONTENT edit
846
+ # afterwards is non-safe against it and pays the one full parse it
847
+ # always would have. Read live (on the chain_in worker).
848
+ skip_reparse_on_blank_edits = True
849
+
850
+ @defaults(tint=(0.22, 0.429, 0.844))
851
+ class SymbolUsages:
852
+ # Auto-attach symbol usages to every editor parse (background, fast
853
+ # path only); the Index button stays as a force refresh.
854
+ auto_index = False
855
+
856
+ # Incremental symbol-usage refresh on live edits: reuse the prior
857
+ # compute's expensive half (cross-file callers + defs, ~80% of cost)
858
+ # and rescan only the changed file + new names. Off = full recompute.
859
+ # Fast path only.
860
+ incremental_symbol_index = True
861
+
862
+ # Also seed the incremental path from a STALE-generation cached
863
+ # span (cross-session pickle restore, or a gen bump because some
864
+ # OTHER file changed) instead of cold-recomputing (~50-100ms vs
865
+ # 0.4-2s). Cross-file callers reused from the stale seed can drift;
866
+ # the span is marked and replaced with one full pass at the next
867
+ # generation bump. Off = a stale-gen miss recomputes cold.
868
+ stale_gen_incremental = True
869
+
870
+ # Per-edit incremental patching on top of the incremental path: re-derive
871
+ # only the top-level-statement region around the edit (patched parse
872
+ # artifacts + a region-restricted pass) and merge into the prior
873
+ # graph - O(edit) per keystroke instead of O(file). A final pass
874
+ # reconciles when typing goes idle. Off = each live edit runs the
875
+ # incremental_symbol_index pass.
876
+ live_incremental_usages = True
877
+
878
+ # Position-only fast path: when an edit only added/removed blank
879
+ # lines, remap the cached line numbers by the delta (~5ms vs ~42ms)
880
+ # instead of recomputing.
881
+ offset_symbol_positions = True
882
+
883
+ # Add function-local variables (params + in-function bindings) to
884
+ # the symbol usage graph so they wash + double-click to their users
885
+ # like any symbol. Cheap now that the editor's line<->index helpers
886
+ # are O(log n) (see _line_offsets); turn off to drop locals from
887
+ # the graph if ever needed.
888
+ local_symbol_usages = True
889
+
890
+ # Ctrl+B always consults FRESH data: run the synchronous
891
+ # single-line usage recheck (usage_data_for_line - full
892
+ # cross-file caller walk, on the UI thread, timed to log +
893
+ # /tmp/uj_debug.log) on EVERY Ctrl+B press and prefer its
894
+ # result, falling back to the background graph only when the
895
+ # recheck resolves nothing under the caret. Off = the recheck
896
+ # runs only as the no-targets fallback before the red flash.
897
+ ctrl_b_always_recheck = True
898
+
899
+ # Ctrl+B through the symbol roster FIRST (roster_tints.ctrl_b_lookup):
900
+ # name -> definition by forward resolution of the view's chain,
901
+ # definition -> usages via the trigram index + resolve-name
902
+ # filter. Pending/live-buffer included, nested defs and class
903
+ # members included, milliseconds instead of the ~1s live-object
904
+ # recheck. The usage graph / recheck below is the fallback
905
+ # when the roster can't resolve the caret's symbol.
906
+ ctrl_b_roster = True
907
+
908
+ # Ctrl+B picker: rows listed before a selectable "+ N more" row
909
+ # (Enter / click on it shows them all). The best-match row is
910
+ # always kept above the cut.
911
+ picker_max_rows = 24
912
+
913
+
914
+ # --- Code-suggestion snippets ---------------------------------
915
+ # trigger -> snippet rows offered when the text just typed ends
916
+ # with the trigger. The key is one trigger string or a TUPLE of
917
+ # alias triggers; the value is a list of Snippet rows. `insert`
918
+ # replaces the whole trigger; `$0` marks the final caret. Adding
919
+ # a shortcut = adding one entry here (read live, hotswap-safe).
920
+ # [expanded=False]
921
+ AC_SNIPPETS = {
922
+ ("#"): [
923
+ Snippet("", "# [$0]", "# [ ... ]", tint=(0.161, 0.027, 0.047)),
924
+ ],
925
+ ("#[", "# ["): [
926
+ Snippet("", "# [tint=($0), show_tint=True]", tint=(0.756, 0.283, 0.08, 1.0)),
927
+ ],
928
+ ("t"): [
929
+ Snippet("black", "tint=(0.0, 0.0, 0.0, 1.0)", "", tint=(0.05, 0.05, 0.05)),
930
+ Snippet("white", "tint=(1.0, 1.0, 1.0, 1.0)", "", tint=(1.0, 1.0, 1.0)),
931
+ Snippet("red", "tint=(0.72, 0.11, 0.11)", "", tint=(0.72, 0.11, 0.11)),
932
+ Snippet("green", "tint=(0.13, 0.55, 0.13)", "", tint=(0.13, 0.55, 0.13)),
933
+ Snippet("blue", "tint=(0.071, 0.354, 0.511)", "", tint=(0.071, 0.354, 0.511)),
934
+ Snippet("orange", "tint=(0.85, 0.45, 0.05)", "", tint=(0.85, 0.45, 0.05)),
935
+ Snippet("yellow", "tint=(0.85, 0.75, 0.05)", "", tint=(0.85, 0.75, 0.05)),
936
+ Snippet("purple", "tint=(0.45, 0.15, 0.60)", "", tint=(0.45, 0.15, 0.60)),
937
+ Snippet("teal", "tint=(0.05, 0.55, 0.55)", "", tint=(0.05, 0.55, 0.55)),
938
+ Snippet("pink", "tint=(0.90, 0.40, 0.60)", "", tint=(0.90, 0.40, 0.60)),
939
+ Snippet("gray", "tint=(0.5, 0.5, 0.5)", "", tint=(0.5, 0.5, 0.5)),
940
+ Snippet("cyan", "tint=(0.05, 0.70, 0.85)", "", tint=(0.05, 0.70, 0.85)),
941
+ Snippet("magenta", "tint=(0.80, 0.10, 0.80)", "", tint=(0.80, 0.10, 0.80)),
942
+ Snippet("brown", "tint=(0.45, 0.28, 0.12)", "", tint=(0.45, 0.28, 0.12)),
943
+ ],
944
+
945
+ ("white", "(1"): [
946
+ Snippet("", "(1.0, 1.0, 1.0, 1.0)", ""),
947
+ ],
948
+ ("black", "(0"): [
949
+ Snippet("", "(0.0, 0.0, 0.0, 1.0)", "", tint=(0.05, 0.05, 0.05)),
950
+ ],
951
+ ("blue", "(0."): [
952
+ Snippet("", "(0.071, 0.354, 0.511)", "", tint=(0.071, 0.354, 0.511)),
953
+
954
+ ],
955
+ ("red", "(0."): [
956
+ Snippet("", "(0.72, 0.11, 0.11)", "", tint=(0.72, 0.11, 0.11)),
957
+ ],
958
+ ("green", "(0."): [
959
+ Snippet("", "(0.13, 0.55, 0.13)", "", tint=(0.13, 0.55, 0.13)),
960
+ ],
961
+ ("orange", "(0."): [
962
+ Snippet("", "(0.85, 0.45, 0.05)", "", tint=(0.85, 0.45, 0.05)),
963
+ ],
964
+ ("yellow", "(0."): [
965
+ Snippet("", "(0.85, 0.75, 0.05)", "", tint=(0.85, 0.75, 0.05)),
966
+ ],
967
+ ("purple", "(0."): [
968
+ Snippet("", "(0.45, 0.15, 0.60)", "", tint=(0.45, 0.15, 0.60)),
969
+ ],
970
+ ("teal", "(0."): [
971
+ Snippet("", "(0.05, 0.55, 0.55)", "", tint=(0.05, 0.55, 0.55)),
972
+ ],
973
+ ("pink", "(0."): [
974
+ Snippet("", "(0.90, 0.40, 0.60)", "", tint=(0.90, 0.40, 0.60)),
975
+ ],
976
+ ("gray", "(0."): [
977
+ Snippet("", "(0.5, 0.5, 0.5)", "", tint=(0.5, 0.5, 0.5)),
978
+ ],
979
+ ("cyan", "(0."): [
980
+ Snippet("", "(0.05, 0.70, 0.85)", "", tint=(0.05, 0.70, 0.85)),
981
+ ],
982
+ ("magenta", "(0."): [
983
+ Snippet("", "(0.80, 0.10, 0.80)", "", tint=(0.80, 0.10, 0.80)),
984
+ ],
985
+ ("brown", "(0."): [
986
+ Snippet("", "(0.45, 0.28, 0.12)", "", tint=(0.45, 0.28, 0.12)),
987
+ ],
988
+
989
+
990
+ # --- Melty view-authoring idioms (lifted from new_core_view.py) ---
991
+ ("@r",): [
992
+ Snippet("render_func view",
993
+ "@render_func(use_cache=True, show_bg=True, with_header=draw_header)\n"
994
+ "def draw_$0(input_value, draw_state=None, **kwargs):\n $1\n return False, None",
995
+ "view skeleton", tint=(0.93, 0.56, 0.23)),
996
+ Snippet("render_func default-for",
997
+ "@render_func(is_default_for=$0, use_cache=True, show_bg=True)",
998
+ "typed renderer", tint=(0.209, 0.383, 0.181)),
999
+ ],
1000
+ ("dl",): [
1001
+ Snippet("", "dl = imgui.get_window_draw_list()", ""),
1002
+ ],
1003
+ ("rect",): [
1004
+ Snippet("", "dl.add_rect_filled(x, y, x + w, y + h, pack_color($0), "
1005
+ "rounding=getattr(draw_state, 'corner_radius', 6))",
1006
+ ""),
1007
+ ],
1008
+ ("pos",): [
1009
+ Snippet("", "pos = imgui.get_cursor_screen_pos()", ""),
1010
+ ],
1011
+ ("u32",): [
1012
+ Snippet("", "pack_color($0)", ""),
1013
+ ],
1014
+ ("txc",): [
1015
+ Snippet("", "imgui.text_colored($0, 1.0, 1.0, 1.0, 1.0)", ""),
1016
+ ],
1017
+ ("ga",): [
1018
+ Snippet("", "getattr(draw_state, '$0', None)", ""),
1019
+ ],
1020
+ ("in",): [
1021
+ Snippet("invalidate_up",
1022
+ "Melty.cache.invalidate_up(draw_state._tile_id, max_depth=$0)",
1023
+ "", tint=(0.55, 0.20, 0.15)),
1024
+
1025
+ Snippet("Melty.cache.invalidate",
1026
+ "Melty.cache.invalidate(draw_state._tile_id)",
1027
+ "", tint=(0.55, 0.20, 0.15)),
1028
+ ],
1029
+ ("r"): [
1030
+ Snippet("", "request_render()", ""),
1031
+ ],
1032
+ }
1033
+
1034
+ @staticmethod
1035
+ def usage_tint(users):
1036
+ """Background-wash color for a symbol-usage span in the editor — a
1037
+ heat ramp on `users`, the number of jump targets that occurrence
1038
+ fans out to (the rows the usage-jump dropdown would show). One
1039
+ target is a faint washed-out steel blue; it climbs to a bright deep
1040
+ orange by ~six, so a click that fans out reads hot at a glance while
1041
+ a straight jump-to-definition stays cool. The hue walks the warm
1042
+ side of the wheel (blue → violet → red → orange) rather than lerping
1043
+ straight down through green.
1044
+
1045
+ Returns (r, g, b, a) floats in 0..1 (a = opacity). Edit freely to
1046
+ restyle the wash — it is read live, so changes show immediately."""
1047
+ import colorsys
1048
+ t = min(max(users, 1) - 1, 5) / 5.2
1049
+ v = max(0.13, 0.88 * t + 0.3)
1050
+ usage_tint = (0.204, 0.224, 0.239)
1051
+ usage_tint = (*usage_tint, v)
1052
+ return usage_tint
1053
+ # Definition tints: a class/def whose definition carries a tint
1054
+ # (@defaults(tint=...), a '# [tint=...]' override comment, or a
1055
+ # class-body tint=...) gets a full-body background wash in the editor,
1056
+ # and every occurrence of that symbol - even when its definition lives
1057
+ # in another file - gets a small wash of the same color. The washes take
1058
+ # the tint's rgb with these alphas (the tint's own alpha is a view-
1059
+ # wide opacity, not meant for text washes). All read live
1060
+
1061
+ # [tint=(0.0875, 0.2815, 0.477, 1.00), show_tint=True]
1062
+ definition_tints = True
1063
+
1064
+ # Definition tints from the text-derived SYMBOL ROSTER
1065
+ # (core_conversion/symbol_roster.py + core_views/roster_tints.py):
1066
+ # washes come from the live buffer + pending text of every file,
1067
+ # no cst-dict parse, no background usage graph, no live objects —
1068
+ # a tinted def typed anywhere paints its references on the next
1069
+ # rebuild without saving or hotswapping. Off = the legacy
1070
+ # _collect_def_tints path (cst-dict + __symbol_usages__).
1071
+ # [tint=(0.0875, 0.2815, 0.477, 1.00), show_tint=True]
1072
+ roster_def_tints = True
1073
+
1074
+ # Definition block washes of ROOT symbols (blocks no other block in
1075
+ # the buffer contains — top-level classes/defs). Embeds that paint
1076
+ # the enclosing class's background themselves (global-search rows)
1077
+ # ask draw_text to skip these via show_root_backgrounds=False; with
1078
+ # this True that request is ignored and root symbols keep their
1079
+ # tints everywhere, False honours it.
1080
+ # [tint=(0.0875, 0.2815, 0.477, 1.00), show_tint=True]
1081
+ root_symbol_tints = True
1082
+
1083
+ # When the caret rests on an identifier, every OTHER place that exact
1084
+ # token appears in the visible buffer gets this background wash. A dumb,
1085
+ # identifier-bounded character match - no CST / symbol-DB metadata is
1086
+ # involved, so it works in any text, mid-edit or unparseable. Flip the
1087
+ # toggle to disable; the (r, g, b, a) tint is read live.
1088
+ highlight_token_matches = True
1089
+
1090
+ # Scope guides: a thin vertical line down the indent column of every
1091
+ # indented block. A tinted def/class draws its guide in its
1092
+ # definition tint, nested blocks inherit the nearest enclosing
1093
+ # tinted block's, and outside any tinted block the file's tint
1094
+ # (else Tint.scope_guide) applies. Colours run through the
1095
+ # same hsv adjustment as the washes with the four knobs below
1096
+ # (saturation / value multipliers, then a brightness clamp).
1097
+ # [tint=(0.0875, 0.2815, 0.477, 1.00), show_tint=True]
1098
+ scope_guides = True
1099
+ # [tint=(0.72, 0.11, 0.11), show_tint=True]
1100
+ scope_guide_alpha = 0.5
1101
+ scope_guide_thickness = 1.0
1102
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
1103
+ scope_guide_saturation = 0.8
1104
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1105
+ scope_guide_value = 1.6
1106
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
1107
+ scope_guide_min_value = 0.16
1108
+ scope_guide_max_value = 0.32
1109
+ # The guide of the block the caret sits in (focused editor): its
1110
+ # own alpha / value / brightness cap, so it stands out from the rest.
1111
+ # [tint=(0.72, 0.11, 0.11), show_tint=True]
1112
+ scope_guide_active_alpha = 0.9
1113
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1114
+ scope_guide_active_value = 2.6
1115
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
1116
+ scope_guide_active_max_value = 0.55
1117
+
1118
+ # [tint=(0.72, 0.11, 0.11), show_tint=True]
1119
+ def_block_alpha = 1.0
1120
+
1121
+ # [tint=(0.72, 0.11, 0.11), show_tint=True]
1122
+ def_symbol_alpha = 1.0
1123
+
1124
+ # [tint=(0.72, 0.11, 0.11), show_tint=True]
1125
+ def_line_alpha = 0.078
1126
+
1127
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
1128
+ bg_tint_saturation = 0.67
1129
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1130
+ bg_tint_value = 0.12
1131
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
1132
+ bg_min_brightness = 0.01
1133
+ bg_max_brightness = 0.137
1134
+
1135
+ # Per-occurrence symbol-wash color adjustment - same hsv factor
1136
+ # pattern as bg_tint_*, but independent of the block/line washes so
1137
+ # the chips can run hotter or duller than the surfaces under them.
1138
+ # The shared brightness clamp (bg_min/max) still applies after.
1139
+ symbol_tint_saturation = 1.02
1140
+ symbol_tint_value = 0.25
1141
+
1142
+ # Line-band color adjustment - the third independent hsv pair
1143
+ # (blocks = bg_tint_*, symbols = symbol_tint_*). Applies to the
1144
+ # hard rect AND the blurred band alike. Shared clamp applies.
1145
+ line_tint_saturation = 0.72
1146
+ line_tint_value = 0.54
1147
+
1148
+ # Outline drawn around the BG washes ( class/func block rects and
1149
+ # hard line bands) - so the highlight edges read crisply against
1150
+ # the background. The outline color is the wash color BRIGHTENED by
1151
+ # def_outline_brightness (multiplied after the bg brightness clamp,
1152
+ # so it pops where the fill stays muted). 0 alpha disables.
1153
+ def_outline_alpha = 1.0
1154
+ def_outline_brightness = 1.6
1155
+ def_outline_thickness = 0.3
1156
+ # Same three knobs for the per-occurrence symbol wash outlines,
1157
+ # independent of the bg wash above. 0 alpha disables.
1158
+ def_symbol_outline_alpha = 1.0
1159
+ def_symbol_outline_brightness = 1.1
1160
+ def_symbol_outline_thickness = 0.3
1161
+ # Compositor shadows under the def-tint washes (add_shadow depth
1162
+ # marks): the signed depth offset for class/func block rects and for
1163
+ # per-occurrence symbol washes. Symbols sit above blocks so the widget
1164
+ # chip casts onto its enclosing block wash; negative values recess
1165
+ # instead; 0 disables.
1166
+ def_block_shadow_offset = 0.05
1167
+ def_symbol_shadow_offset = 3.7
1168
+ # Line-number shadows: negative = recessed below the editor surface
1169
+ # (the body casts into the gutter along its edge); 0 disables.
1170
+ gutter_shadow_offset = 0.157
1171
+ # Master switch for the gutter usage-heat buttons: the per-line
1172
+ # summed-usage boxes behind the line numbers AND their click-to-open
1173
+ # usage picker. Off also skips the per-frame heat aggregation pass
1174
+ # (gutter clicks fall through to caret placement). Read live.
1175
+ # [tint=(0.0875, 0.2815, 0.477, 1.00), show_tint=True]
1176
+ usage_heat_gutter = True
1177
+ # Compositor shadow under the gutter usage-heat boxes: each use
1178
+ # counted on the line adds this much lift, so hotter lines float
1179
+ # higher off the gutter background. The magnitude is capped at
1180
+ # usage_heat_shadow_max (sign preserved - negative recesses);
1181
+ # 0 disables.
1182
+ usage_heat_shadow_offset = 0.362
1183
+ usage_heat_shadow_max = 1.092
1184
+ # Gutter background saturation/value — the hsv multipliers
1185
+ # Tint.line_number_bg applies to the editor BODY's painted background
1186
+ # (the show_bg fill under the text, already capped by editor_value),
1187
+ # so gutter_value is the strip's brightness as a FRACTION of the
1188
+ # body's: 1.0 = same brightness as the text background, lower =
1189
+ # darker strip. Lower saturation = greyer, calmer strip. (Before
1190
+ # 09-14 both applied to the raw theme tint, value 0.325.)
1191
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
1192
+ gutter_saturation = 1.046
1193
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1194
+ gutter_value = 0.8
1195
+ # Gutter TEXT (line numbers) hsv-value guards, applied in
1196
+ # Tint.line_number_tint AFTER its value scale: the floor keeps the
1197
+ # numbers legible on a dark theme tint, the ceiling stops a bright
1198
+ # one from pushing them to full white over the body text. Hue and
1199
+ # saturation kept. The floor wins if they cross.
1200
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1201
+ gutter_text_min_brightness = 0.45
1202
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1203
+ gutter_text_max_brightness = 0.8
1204
+
1205
+ # Code editor body background — draw_code_editor forwards these into
1206
+ # its draw_text panes as the show_bg saturation multiplier and the
1207
+ # max_bg_value brightness cap (draw_text's decorator defaults are
1208
+ # saturation=0.9 / max_bg_value=0.05).
1209
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
1210
+ editor_saturation = 0.394
1211
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1212
+ editor_value = 0.346
1213
+
1214
+
1215
+ # Assignment propagation: a local defined FROM tinted symbols takes a
1216
+ # faded blend of their colors (single-symbol assignment averages the distinct
1217
+ # tints), fading a further step per hop so a value's color trail
1218
+ # weakens as it flows. Fade multiplies the wash alpha per hop -
1219
+ # LOWER = colors die out faster along assignment chains (0.55 puts
1220
+ # hop 1 at 55%, hop 2 at 30%; chains below 20% stop washing at all).
1221
+ def_tint_propagation = True
1222
+ def_propagation_fade = 0.025
1223
+
1224
+ # When enabled the line tint rect above fills the whole line -
1225
+ # gutter edge to the view's right edge - instead of hugging the
1226
+ # line's text extent (indent → last non-ws column).
1227
+ def_line_full_width = False
1228
+
1229
+ # Soft-edged line band: instead of a solid rect the line tint draws
1230
+ # as a feathered stack of expanding translucent rects, fading the
1231
+ # color out over def_line_blur_radius pixels past the band's edge
1232
+ # (a cheap drawcall gaussian - no blur pass). The radius also
1233
+ # bleeds vertically into neighboring lines, which is the point.
1234
+ def_line_blur = True
1235
+
1236
+ # Render the line band through the GL glow pipeline (add_glow →
1237
+ # low-res light buffer → shadow composite) instead of the draw-list
1238
+ # feather stack: the band becomes a real light source - it brightens
1239
+ # neighbors and pushes back compositor shadows - and costs one small
1240
+ # quad instead of def_line_blur_samples rects of overdraw. Falls back
1241
+ # to the draw-list stack when False (or Toggles.glow is off).
1242
+ def_line_glow = True
1243
+ # Intensity of the emitted light for line bands (on top of the band
1244
+ # alpha; Toggles.glow_intensity scales all glows globally).
1245
+ def_line_glow_intensity = 0.495
1246
+ # Emit light from each PER-OCCURRENCE chip (the per-token wash
1247
+ # rects) instead of / in addition to the line band - the glow then
1248
+ # highlights the individual token background's edges. Pair with
1249
+ # def_line_glow = False to make tokens the only light source.
1250
+ def_symbol_glow = True
1251
+ # Intensity of the emitted light per token chip (on top of
1252
+ # def_symbol_alpha and any per-hop propagation scale).
1253
+ def_symbol_glow_intensity = -0.04
1254
+ # Falloff skirt radius for token-chip light, px. Deliberately its
1255
+ # own knob - token halos want a far shorter throw than the
1256
+ # line-band def_line_blur_radius.
1257
+ def_symbol_glow_radius = 65.532
1258
+
1259
+ def_line_blur_radius = 126
1260
+ # Alpha multiplier for the blurred band only - feathering spreads
1261
+ # the color thin, so the blur usually wants MORE alpha than the
1262
+ # hard rect's def_line_alpha. 1.0 = same as the hard band.
1263
+ def_line_blur_alpha = 1.985
1264
+ # Falloff hardness for the blur's inverse-square profile - how
1265
+ # concentrated the "lightsource" is. Higher = tighter core with a
1266
+ # longer radial tail; 0 falls back to the default linear feather.
1267
+ def_line_blur_falloff = 3.044
1268
+ # Perceived-brightness clamp on the BLURRED band's color only -
1269
+ # applied on top of the line_tint_* adjustment (which already ran
1270
+ # through bg_min/max), so the feathered glow can hold a different
1271
+ # brightness window than the hard band. 0.0/1.0 = no extra clamp.
1272
+ def_line_blur_min_value = 0.401
1273
+ def_line_blur_max_value = 6.338
1274
+ # Layer count for the feather stack. More samples = smoother
1275
+ # gradient (fewer visible bands) at the cost of overdraw - large
1276
+ # radii need more; ~1 sample per 3-4px of radius reads smooth.
1277
+ def_line_blur_samples = 7
1278
+
1279
+ # Glyphs inside a symbol wash lean this fraction toward the wash
1280
+ # color (syntax color stays the base) — the slight text tinting used
1281
+ # app-wide so text reads as part of its panel. 0 disables.
1282
+ # [tint=(0.789, 0.18, 0.332, 1.0)]
1283
+ def_text_tint_mix = 0.293
1284
+
1285
+ # Brightness multiplier for glyphs on NON-tinted lines while
1286
+ # Toggles.presentation_mode is on (lines with a def-tint line wash
1287
+ # keep full brightness). 1 = no dimming; 0 = black. Read live.
1288
+ presentation_text_brightness = 0.292
1289
+
1290
+ # Compensation for the inline value widgets (bool/number) inside
1291
+ # tint comments: their text renders through the widget's own hsv
1292
+ # pipeline, so reads darker than the plain comment glyphs at the
1293
+ # same dimmed tint - multiply their dimming by this so both
1294
+ # land at the same visual level. 1 = no boost. Read live.
1295
+ presentation_widget_boost = 1.953
1296
+
1297
+ # Background-chip brightness for the faded comment widgets, relative
1298
+ # to their (already boosted) dimmed text color - a step brighter so
1299
+ # the widget stands out as a spot on a dim line. Read live.
1300
+ presentation_widget_bg_boost = 0.115
1301
+
1302
+ # Text alpha for the faded COLORED value widgets (ones whose
1303
+ # comment carries a tint) - saturated colors read brighter than the
1304
+ # grey at equal value, so they get a transparency cut on top of the
1305
+ # brightness boost. Untinted (grey) widgets stay opaque. 1 = opaque.
1306
+ # Read live.
1307
+ presentation_widget_alpha = 0.409
1308
+
1309
+ # The number widget's chip paints brighter than the bool's at the
1310
+ # same tint (depth-clamped blur + drag-frame fill stack) - extra dim
1311
+ # factor on its background in the presentation mode, applied on top
1312
+ # of presentation_widget_bg_boost (also lowers the chip's max_bg_value
1313
+ # legibility cap). 1 = same as bool. Read live.
1314
+ presentation_number_bg_dim = 4.001
1315
+
1316
+ # Glyph-mix TARGET color adjustment (which color text leans toward
1317
+ # inside a wash) - same hsv factor pattern as comment_tint_* /
1318
+ # bg_tint_*, independent of the wash's own factors; the shared
1319
+ # brightness clamp (bg_min/max) still applies after. 1/1 = raw tint.
1320
+ text_tint_saturation = 1.0
1321
+ text_tint_value = 1.0
1322
+
1323
+ # Tint-comment TEXT color adjustment (hsv factors, the Tint-class
1324
+ # pattern): an override comment wears its own [tint=...] color,
1325
+ # desaturated and darkened by these so it reads as commentary, not
1326
+ # code. Both read live; 1.0/1.0 = the raw tint.
1327
+ # [tint=(1.0, 0.661, 0.0, 1.0)]
1328
+ comment_tint_saturation = 0.49
1329
+
1330
+ # [tint=(0.3813193440437317, 0.7055555582046509, 0.14895063638687134), show_tint=True]
1331
+ comment_tint_value = 0.160
1332
+ # Legibility floor for tinted COMMENT TEXT - independent of the
1333
+ # washes' bg_min_brightness (text needs a higher floor than a
1334
+ # background does); the brightness clamp still shares bg_max_brightness.
1335
+ comment_min_brightness = 0.170
1336
+
1337
+
1338
+ # [tint=(0.013, 0.583, 0.013), show_tint=True]
1339
+ class Voxels:
1340
+ # Artistic curve on the finished volume image, applied to the LINEAR
1341
+ # light the raymarch hands the fp16 scene (the presentation pass
1342
+ # does the one sRGB encode): 1.0 = untouched (colorimetrically
1343
+ # "correct"), above 1 darkens the mids against the studio's darks.
1344
+ # Read live per frame by draw_voxels.
1345
+ gamma = 1.00
1346
+
1347
+ # Auto neural flow: when nf_on is OFF and a DISPLAYED axis is longer
1348
+ # than this (or than GL_MAX_3D_TEXTURE_SIZE, whichever is smaller),
1349
+ # draw_voxels wraps it itself - chops it into ~sqrt-sized chunks spaced
1350
+ # along the shortest visible axis - so a (1, 32000)) row renders
1351
+ # as a readable slab instead of a hairline (or a clamped prefix).
1352
+ # 0 disables. Read live per render.
1353
+ auto_flow_extent = 8192
1354
+
1355
+ # Navigation model of the MOUSE orbit (middle-drag) - the 3D mouse has
1356
+ # its own below (SpaceMouse.navigation), so each device can keep the
1357
+ # feel that suits it. "turntable": drag x spins about the world UP
1358
+ # axis, drag y tilts, the horizon never rolls. "trackball": the drag
1359
+ # is a rotation in VIEW space about the orbit center, roll included.
1360
+ # Read live per frame.
1361
+ mouse_navigation = "turntable"
1362
+
1363
+ # What the volume's FLOOR shadow darkens toward (both the GL and the
1364
+ # cuda_march paths). Neutral grey-black; the UI's own window
1365
+ # shadows keep their blue-black Toggles.shadow_color. Read live
1366
+ # per render.
1367
+ floor_shadow_color = (0.03, 0.03, 0.03)
1368
+
1369
+ @defaults(tint=(0.545, 0.451, 0.248))
1370
+ class UIScale:
1371
+ # Auto-pick the UI scale each frame from the resolution of the monitor
1372
+ # the OS window sits on: 4k-and-larger panels get 1.5, everything else
1373
+ # 1.0 (detect_auto_scale in fonts.py; rechecked every ~120 frames, so
1374
+ # dragging the window to another monitor retunes soon after). False =
1375
+ # use the manual scale below. Read live.
1376
+ auto_scale = False
1377
+
1378
+ # The scaling dial, used when auto_scale is off. It reaches the screen
1379
+ # exactly two ways (Melty.apply_ui_scale / Melty.begin_frame): every
1380
+ # font is RE-BAKED at scale x its authored physical size, and imgui's
1381
+ # style metrics (padding, spacing, rounding, borders) are multiplied
1382
+ # by it. Coordinates are untouched - window sizes and hand-placed
1383
+ # pixel offsets in the code do NOT scale, so this is a text-and-chrome
1384
+ # scale, not a zoom. That's on purpose: the whole-interface zoom this
1385
+ # replaced (logical display + magnify at present time) forced every
1386
+ # offscreen tile to allocate and repaint at physical resolution and
1387
+ # put a resample between tile and screen - slow, and soft at any scale
1388
+ # but 1. Sanitized through glfw_utils.clamp_ui_scale: outside
1389
+ # 0.5..3.0 (or NaN/garbage) is read as 1.0, so a typo can't bake a
1390
+ # 20x font atlas. Changing it re-rasterizes all 18 fonts - a ~0.3s
1391
+ # operation and a ~64->128MB atlas at 1.25 - so it is a setting to
1392
+ # change deliberately, not to animate. 1.0 = the authored look.
1393
+ scale = 1.00
1394
+
1395
+ @defaults(tint=(0.545, 0.451, 0.248))
1396
+ class Fonts:
1397
+ # Subpixel (LCD & "ClearType"-style) text anti-aliasing. The at
1398
+ # atlas is baked 3x oversampled horizontally, and each glyph quad
1399
+ # covers 3 atlas texels per screen pixel; the imgui renderer
1400
+ # (split_overlay_renderer.py) samples those as per-channel R/G/B
1401
+ # coverage and blends them with dual-source blending - tripling the
1402
+ # horizontal resolution of lines and curves the way IntelliJ and the
1403
+ # desktop do. Off = classic grayscale AA (same atlas, one tap).
1404
+ # Turn off on a ROTATED monitor (stripes run vertically there) or
1405
+ # when text must be fringe-free. Read live per frame.
1406
+ lcd_subpixel = True
1407
+
1408
+ # Subpixel stripe order of the panel. Nearly every desktop panel is
1409
+ # RGB left-to-right; a BGR panel shows orange/blue fringes on the
1410
+ # wrong sides of every glyph - flip this. Read live per frame.
1411
+ lcd_bgr = False
1412
+
1413
+ # Contrast curve on glyph coverage: coverage ** (1 / text_gamma).
1414
+ # > 1 darkens the anti-aliased mid-tones so strokes read thicker
1415
+ # (Java2D's "high contrast"); 1.0 = linear coverage, the unaltered
1416
+ # rasterizer output. Applies in both LCD and grayscale modes to glyphs
1417
+ # only - never to rects / lines / images. Read live per frame.
1418
+ text_gamma = 1.0
1419
+
1420
+ # Replace stb_truetype's glyph bitmaps with FreeType light-hinted
1421
+ # LCD renders at atlas bake (FontManager.hint_atlas): baselines,
1422
+ # x-heights and crossbars snap to pixel rows instead of smearing
1423
+ # over two - the other half of the LCD look, grayscale being the
1424
+ # first. Needs freetype-py; glyphs the hinter grows past their
1425
+ # atlas rect keep stb's bitmap. Read at atlas bake: must under a
1426
+ # UI-scale change to apply.
1427
+ freetype_hinting = True
1428
+
1429
+ # Style variants (FontManager.styled_font: a Font at another native
1430
+ # size / weight) stay in the atlas until they're not drawn for this
1431
+ # many flushes (one flush = one surface resize, Melty.apply_ui_scale)
1432
+ # AND a bake happens anyway. Too short and windows that draw in turns
1433
+ # evict each other's fonts, so every frame re-bakes the atlas (~0.6 s
1434
+ # each, text jumping to the fallback face in between; the 09-13
1435
+ # playground ran at 1.5 fps). Only a bake can evict.
1436
+ variant_idle_flushes = 600
1437
+ # Hard cap on retained variants past the idle window: the least
1438
+ # recently drawn go first. Bounds the atlas after a long style drag.
1439
+ max_font_variants = 96
1440
+
1441
+ # [icon=""]
1442
+
1443
+ @defaults(tint=(0.62, 0.36, 0.52))
1444
+ class HDR:
1445
+ # HDR / wide-gamut colour (gl_gui/hdr_color.py). Colour tuples are
1446
+ # EXTENDED sRGB: 1.0 = the panel's SDR reference white, above it is
1447
+ # brighter and negatives are outside the sRGB gamut (write them with
1448
+ # white(n) / p3(r, g, b)). The GPU works in linear scRGB fp16 and
1449
+ # the presentation pass (Melty.post_frame) encodes for the output.
1450
+
1451
+ # Ceiling of the HDR byte curve, in multiples of reference white on
1452
+ # P3 primaries: the brightest fully saturated colour a vertex can
1453
+ # carry (white(64) on a 250-nit desktop is 16 000 nits — headroom
1454
+ # for a dimmed desktop, not a target). Raising it spreads the same
1455
+ # 255 codes over more octaves (see vertex_octaves). Read live by the
1456
+ # packer and the shaders.
1457
+ # [tint=(0.62, 0.36, 0.52)]
1458
+ vertex_range = 64.0
1459
+ # Octaves below vertex_range the 255 codes cover: code 1 sits at
1460
+ # vertex_range / 2^vertex_octaves, code 0 is exactly zero. 12 -> ~21
1461
+ # codes per doubling (a 3.3 % step), enough for accents; long
1462
+ # smooth HDR gradients want more codes, not more octaves.
1463
+ # [tint=(0.62, 0.36, 0.52)]
1464
+ vertex_octaves = 12.0
1465
+ # Presentation encode of the linear scRGB scene into the 8-bit
1466
+ # swapchain AND the surface's colour tag (wayland_color.py):
1467
+ # "auto" — PQ whenever the compositor offers colour management (the
1468
+ # Hyprland HDR session), sRGB otherwise (GNOME); "pq" — BT.2020 +
1469
+ # ST 2084, surface tagged PQ; "srgb" — SDR, untagged: everything
1470
+ # clips to the sRGB gamut and to white. Read live.
1471
+ # [tint=(0.62, 0.36, 0.52)]
1472
+ output = "auto"
1473
+ # Under the "pq" encode a colour of 1.0 is shown at the desktop's
1474
+ # SDR reference white — read from the compositor's preferred image
1475
+ # description for our surface (wayland_color.query_preferred;
1476
+ # Hyprland: the monitor's sdr_max_luminance) and re-read whenever
1477
+ # it changes, so the studio's white tracks the desktop's SDR white
1478
+ # setting like every untagged window. Off = pin pq_reference_nits.
1479
+ # [tint=(0.62, 0.36, 0.52)]
1480
+ follow_desktop_white = True
1481
+ # Nits of reference white (a colour of 1.0) under the "pq" encode
1482
+ # when the compositor doesn't say (no luminances in its preferred
1483
+ # description) or follow_desktop_white is off.
1484
+ # [tint=(0.62, 0.36, 0.52)]
1485
+ pq_reference_nits = 250.0
1486
+ # The colour picker's Wide tab: how far above white its square
1487
+ # reaches, in stops (4 = white(16)), and the fraction of the
1488
+ # square's height that exposure band takes (the rest is the classic
1489
+ # value axis). Read live.
1490
+ # [tint=(0.62, 0.36, 0.52)]
1491
+ picker_max_stops = 4.0
1492
+ # [tint=(0.62, 0.36, 0.52)]
1493
+ picker_top_fraction = 0.3
1494
+ # Emphasis flashes (Melty.emphasize / emphasize_click, the overlay
1495
+ # pass): how far above the desktop's white the flash is lifted, in
1496
+ # stops of linear light — the outline / ripple rings at
1497
+ # 2^emphasis_stops (3 = white(8): 2000 nits on a 250-nit desktop),
1498
+ # the rect's fill at 2^emphasis_fill_stops, the bloom halo around
1499
+ # the outline at emphasis_stops too but faint. Callers pass a hue
1500
+ # tint (SDR or P3) and the lift is applied at draw time, so an edit
1501
+ # here is live. Under the "srgb" output everything clips to white
1502
+ # and the flash just reads as a bright, saturated highlight.
1503
+ # [tint=(0.62, 0.36, 0.52)]
1504
+ emphasis_stops = 3.0
1505
+ # [tint=(0.62, 0.36, 0.52)]
1506
+ emphasis_fill_stops = 1.0
1507
+ # Ceiling on TEXT brightness, in stops above the desktop's white:
1508
+ # glyph pixels of the draw-list renderer and the text-texture bake
1509
+ # are scaled down (hue kept) so no channel exceeds 2^text_max_stops
1510
+ # — an HDR colour reaching a label through a style, a tint or an
1511
+ # emphasis lift read as blinding white on the PQ desktop (09-14).
1512
+ # Backgrounds, icons drawn as rects and images keep their full
1513
+ # headroom; 0 = clamp text at reference white. Read live.
1514
+ # [tint=(0.62, 0.36, 0.52)]
1515
+ text_max_stops = 1.0
1516
+
1517
+ @defaults(tint=(0.36, 0.42, 0.52))
1518
+ class Melty:
1519
+ # Every tree / expand arrow (the header arrows, the chat's carets and
1520
+ # the sidebar's folder arrows) draws at this fraction of its recipe's
1521
+ # brightness and alpha; 1 = as designed.
1522
+ # [tint=(0.62, 0.36, 0.52), show_tint=True]
1523
+ arrow_brightness = 0.5
1524
+ # Custom client-side titlebar: undecorated OS window so the UI sticks
1525
+ # to the top of the display, with min/max/close drawn to the overlay
1526
+ # drawlist top-right and drag/edge-resize handed to the WM via
1527
+ # _NET_WM_MOVERESIZE (titlebar.py). X11/XWayland only: on native
1528
+ # Wayland this is NOT consulted - with wayland_native_frame (below)
1529
+ # the min/max/close buttons always draw (GLFW's fallback frame has
1530
+ # none of its own) while the OS window KEEPS that frame (its caption
1531
+ # strip + borders are the only move/resize surface there; no GLFW
1532
+ # route to xdg_toplevel.move). Applied live per frame
1533
+ # (glfw.set_window_attrib) and read at boot for the DECORATED hint.
1534
+ enhanced_titlebar = False
1535
+
1536
+ # GNOME Wayland only. GNOME offers no server-side decorations, so
1537
+ # GLFW hands its OS window to libdecor, and its GNOME plugin
1538
+ # repaints the whole title bar + frame on the fly for EVERY resize
1539
+ # configure: 38 ms a step at a 4072×2136 window, 59 ms at 7000×2000,
1540
+ # inside glfw.wait_events before render() even starts, and the
1541
+ # configures queue up while the frame runs - the 2s OS-window
1542
+ # resize. On, GLFW skips libdecor (WAYLAND_DISABLE_LIBDECOR init
1543
+ # hint) and draws its own fallback frame: a plain caption strip +
1544
+ # 4 px borders that move/resize through the compositor at <1 ms a
1545
+ # step, with no buttons - titlebar.py draws min/max/close over it. Read
1546
+ # by the FIRST glfw.init() of the day (the launcher process,
1547
+ # glfw_utils.apply_wayland_frame_hint), so a change takes effect on
1548
+ # the next model_server restart, like the cursor size.
1549
+ wayland_native_frame = True
1550
+
1551
+ # With wayland_native_frame: keep GLFW's fallback frame - a 24 px
1552
+ # caption strip (drag = move) + 4 px borders (drag = resize), sizes
1553
+ # GLFW hardcodes - or drop it and run frameless: the buttons are
1554
+ # titlebar.py's, resizing is the right-drag anywhere in the window
1555
+ # (app-driven glfw.set_window_size, bottom-right corner - Wayland
1556
+ # prefers the top-left; the edge zones ask the compositor:
1557
+ # xdg_toplevel.resize) and MOVING is xdg_toplevel.move from the
1558
+ # drag strip / drag-anywhere (gl_gui/wayland_move.py - Super+drag
1559
+ # semantics, driven by the compositor). Applied live
1560
+ # (sync_decoration).
1561
+ wayland_show_frame = False
1562
+
1563
+ # px height of the invisible drag strip along the top edge - a drag
1564
+ # outside inside it (a few px of travel past the press) moves the OS
1565
+ # window; a clean click there falls through to whatever view is under
1566
+ # the cursor. Double-click toggles maximize (unless
1567
+ # disable_double_click_maximize below).
1568
+ drag_strip_height = 50
1569
+
1570
+ # On (default): the drag strip never subscribes to double-click, so
1571
+ # it falls through to the view under the cursor like any other
1572
+ # click. Off: a double-click on the strip toggles the OS window's
1573
+ # maximize, the WM caption rule. Applied live (each frame).
1574
+ disable_double_click_maximize = True
1575
+
1576
+ # Left-drag ANYWHERE in the window moves the OS window, the meltygui
1577
+ # way — as the worst-priority drag subscriber: a view that wants the
1578
+ # drag (window headers, sliders, text selection, dnd) always wins,
1579
+ # while an unclaimed drag on bare background moves the window. Off =
1580
+ # only the strip moves. Double-click-maximize stays strip-only.
1581
+ move_drag_anywhere = True
1582
+
1583
+ # Show the desktop's "move" pointer (mouse_state.MOVE) wherever a
1584
+ # left drag would move a window: a meltygui window's bare areas (its
1585
+ # resize handle) and the OS window's drag strip / drag-anywhere
1586
+ # background. Off = the normal arrow there.
1587
+ window_move_cursor = True
1588
+
1589
+ # Which window controls the frameless chrome shows, and on which
1590
+ # side. "" (the default) follows the DESKTOP's own title-bar button
1591
+ # setting, what GTK / Qt header bars follow (gl_gui/titlebar_buttons
1592
+ # .py: the GNOME key `org.gnome.desktop.wm.preferences button-layout`
1593
+ # via gsettings — GNOME, Budgie, and any desktop with a dconf
1594
+ # profile, Hyprland included — Cinnamon's and MATE's keys, KDE's
1595
+ # kwinrc, xfwm4's button_layout, then the settings portal), read on
1596
+ # a background thread at boot, on focus gain and every
1597
+ # titlebar_button_refresh_s. A GNOME-syntax string pins it instead:
1598
+ # "left:right", comma-separated minimize / maximize / close —
1599
+ # "close,minimize,maximize:" puts every control on the left,
1600
+ # ":minimize,close" drops the maximize button, "" + no colon is all
1601
+ # left (mutter's rule). Applied live (each frame).
1602
+ # [tint=(0.55, 0.75, 0.35)]
1603
+ titlebar_button_layout = ""
1604
+ # Seconds between re-reads of the desktop's button setting while
1605
+ # frames render (0 = only at boot and on focus gain).
1606
+ # [tint=(0.55, 0.75, 0.35)]
1607
+ titlebar_button_refresh_s = 30
1608
+ # On Lukas's patched Hyprland (the compositor has
1609
+ # general:left_drag_move) the chrome also shows the desktop's
1610
+ # window-gesture toggle — the hyprbars `state = "left_drag_move"`
1611
+ # button: lit while the COMPOSITOR moves this app's windows on a
1612
+ # plain left drag and resizes them on a right drag, faded while the
1613
+ # app is in left_drag_move_exclude + right_drag_resize_exclude and
1614
+ # does both itself (drag-anywhere xdg move, right-drag through the
1615
+ # edge physics, nested meltygui windows' own right-drag). A click flips
1616
+ # it through desktop/left-drag-toggle --gestures (persisted by the
1617
+ # desktop's Settings). Innermost of the right group.
1618
+ # Off = never shown. (gl_gui/hypr_left_drag.py)
1619
+ # [tint=(0.55, 0.75, 0.35)]
1620
+ titlebar_move_toggle = True
1621
+
1622
+ # px hit zones for edge/corner resize on the undecorated window.
1623
+ resize_border = 6
1624
+ resize_corner = 18
1625
+
1626
+ # The GLFW window's four edges are collision edges in the column
1627
+ # edge system, one level above the nested meltygui windows
1628
+ # (gl_gui/os_edges.py): a meltygui window edge or a column cascade
1629
+ # pushed into the OS edge moves it (the surface grows, or the
1630
+ # window moves), the screen's work area is the wall outside it (its
1631
+ # position from the GNOME extension's feed — installation_helper
1632
+ # — glfw on X11), and an edge blocked by a wall grows its window on
1633
+ # the other side, just like a meltygui window's edge against the
1634
+ # display. Off, or with no position feed: the display edges are
1635
+ # immovable walls and the old in-display pin-and-slide remains.
1636
+ push_os_window_edges = True
1637
+ # A window MOVED by hand (left-drag) pushes the OS window's edge it
1638
+ # runs into out of its way, like any collision — but the move
1639
+ # itself is not clamped: the OS edge stops at the screen and the
1640
+ # window keeps going, so a window can be dragged partly off the
1641
+ # display on purpose (Lukas 08-27) - except above the display's
1642
+ # TOP (window_top_hard_limit).
1643
+ window_move_pushes_os_edges = True
1644
+ # A meltygui window's TOP never passes the top of the DISPLAY (the
1645
+ # work area, os_frame.display_rect): a hand move - its own or a
1646
+ # parent's it rides with - first pushes the OS edge back to the
1647
+ # screen as usual, then the remainder is clamped, so the window's
1648
+ # header always stays reachable (Lukas 08-28). The other three
1649
+ # sides stay free.
1650
+ window_top_hard_limit = True
1651
+ # Console trace of the OS edge model (foreign moves / resizes seen,
1652
+ # OS edges pushed and the surface request per frame). Off: it prints
1653
+ # per push and per frame, a real cost at 120 fps.
1654
+ push_os_window_edges_trace = False
1655
+ # Hyprland has no window-geometry event, so the position feed
1656
+ # (geometry_feed's hyprland backend) POLLS its request intervals
1657
+ # this often on its own thread - a `j/clients` round trip is
1658
+ # ~0.03 ms, so 120 Hz is reasonable; lower it temporarily to reduce
1659
+ # the OS edge physics' reaction time on purpose.
1660
+ hyprland_feed_poll_hz = 120
1661
+
1662
+ # Tint of the OS-window chrome: the minimize / maximize / close
1663
+ # controls titlebar.py draws top-right. Each is painted exactly like
1664
+ # a window header's close button (draw_header_end's flat_button),
1665
+ # with this tint set in the style manager the way the header runs
1666
+ # under its window's tint — copy a window's tint here to match it.
1667
+ # [tint=(0.55, 0.75, 0.35)]
1668
+ melty_window_tint = (0.653, 0.758, 0.806)
1669
+
1670
+ # Tint of a meltygui APP's root surface (surface.Surface.frame): the
1671
+ # ground every @glfw_window body draws on, painted through draw_bg
1672
+ # exactly as the studio's Main Window paints its desktop, so a
1673
+ # window filling the surface sits at bg depth 1 like a studio window
1674
+ # (at depth 0 over a black bg stack every root came out black,
1675
+ # 09-12). A `@glfw_window(tint=...)` overrides it per window. The
1676
+ # studio's default draw_state tint, so the two look alike.
1677
+ # [tint=(0.55, 0.75, 0.35)]
1678
+ app_root_tint = (0.11, 0.12, 0.14)
1679
+
1680
+ # Rounded corners on the frameless OS window (px; 0 = square). The
1681
+ # window is created with a transparent framebuffer (boot-time -
1682
+ # restart to change 0 ↔ >0) and titlebar.composite_window_frame runs
1683
+ # last in Melty.post_frame: a fullscreen pass premultiplying the
1684
+ # frame by the surface's rounded-rect coverage (anti-aliased mask),
1685
+ # which also clips the alpha imgui's blending leaves below 1 -
1686
+ # without it the desktop would bleed through every translucent
1687
+ # draw. The radius itself applies live; 0 while maximized.
1688
+ window_corner_radius = 14
1689
+
1690
+ # Client-side window shadow (GNOME draws none for Wayland clients):
1691
+ # px of transparent margin the OS surface keeps around the content.
1692
+ # imgui sees only the content (SplitOverlayRenderer insets the
1693
+ # viewport and offsets the pointer); the masks cover the whole
1694
+ # surface, and the shadow pass (ShadowPass, frame_* uniforms)
1695
+ # writes whatever shadow lands outside the content with premultiplied
1696
+ # alpha - the inner windows' own shadow casts, continued beyond the
1697
+ # content. This is the FLOOR: the ceiling grows to the shadow's reach on
1698
+ # the current surface (titlebar.shadow_reach, from the cast
1699
+ # shader's terms - ~80 px at 4072 wide, ~144 at 7680), since a
1700
+ # margin shorter than the shadow cuts it mid-fall as a hard band.
1701
+ # Collapses to 0 while maximized / fullscreen. 0 = no margin.
1702
+ # Applies live.
1703
+ window_shadow_margin = 40
1704
+
1705
+ # add_shadow lift of the whole content rect over the transparent
1706
+ # surroundings (draw_melty_windows) - how pronounced the OS window's
1707
+ # shadow is. Depth is at paint rank 0: keep it deep below the
1708
+ # windows' ranks (they start at layer 64) or it would flatten the
1709
+ # inner windows' own shadow. 0 = only the root's own mark casts.
1710
+ window_shadow_lift = 20
1711
+ # add_shadow() rect marks stamped as ONE instanced draw per blend
1712
+ # equation (blit_offscreen._stamp_shadow_marks_batched) instead of
1713
+ # ~10 GL calls per mark: 2.5 ms → ~0.3 ms a frame with ~280 marks.
1714
+ # Off = the per-mark path (compare if a shadow looks different).
1715
+ # [tint=(0.95, 0.55, 0.15)]
1716
+ batch_shadow_stamps = True
1717
+ # finalize_captures rebuilds the tile / full depth masks and the glow
1718
+ # buffer (passes 4–6) only when their INPUTS changed — mask rects,
1719
+ # shadow marks, retained emitters, window order — and keeps last
1720
+ # frame's textures otherwise (a keystroke or selection drag changes
1721
+ # pixels, not geometry). Off = the old rebuild-every-frame path.
1722
+ # [tint=(0.95, 0.55, 0.15)]
1723
+ mask_rebuild_on_change = True
1724
+
1725
+ @defaults(tint=(0.635, 0.728, 0.725))
1726
+ class Style:
1727
+ # Ceiling on the PERCEIVED brightness (0.299r + 0.587g + 0.114b) of
1728
+ # the imgui widget fills the style manager derives from the window
1729
+ # tint — buttons, frame backgrounds (text edits, drag/slider tracks)
1730
+ # and slider grabs — applied in ImGuiStyleManager.set_imgui_tint
1731
+ # right after the hsv transform. Those fills sit under light text,
1732
+ # so a bright background tint (v -> 1) lifted them to the text's
1733
+ # brightness and the widgets read blank; the cap SCALES the channels
1734
+ # (hue and saturation kept) instead of washing toward gray. Text,
1735
+ # check-mark and window/header colors are not capped. Read live on
1736
+ # every set_imgui_tint call. 0 disables.
1737
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
1738
+ widget_max_brightness = 0.3
1739
+ # How far a tint chip's edit (draw_tuple_fast: window-header tints,
1740
+ # editor tab tints, file-row tints) cascades through the blit cache.
1741
+ # Every LIVE change from the picker invalidates the chip's host and
1742
+ # its children to tint_edit_live_depth (1 = the host + its direct
1743
+ # children — the bg the tint paints and the rows/headers sitting
1744
+ # right on it); a full invalidate_up per frame walked the whole
1745
+ # subtree and was the drag's cost. CLOSING the picker (click away,
1746
+ # Esc, re-click) runs one deep cascade to tint_edit_close_depth so
1747
+ # every nested tile under the host settles on the final colour.
1748
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
1749
+ tint_edit_live_depth = 1
1750
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
1751
+ tint_edit_close_depth = 10
1752
+
1753
+ @defaults(tint=(0.103, 0.341, 0.617))
1754
+ class WindowSettings:
1755
+ # Sticky resize: re-anchor the window top at the drag-start point each
1756
+ # frame so only the min-on-display clamp displaces it.
1757
+ sticky_drag = True
1758
+
1759
+ # Breathing room, in px, left at every display edge by
1760
+ # Melty.clamp_window_pos - a bound applied whenever a window is
1761
+ # PLACED programmatically (summoned by the dock / a search hit /
1762
+ # Ctrl+Shift+F, or shown for the first time), so it can't open half
1763
+ # off-screen or with its bottom below the bottom of the display.
1764
+ # Dragging is unaffected: a window you drag off the edge just
1765
+ # stays where you put it. A window taller/wider than the display
1766
+ # keeps its top-left in view and overflows the far edge. Read live.
1767
+ edge_margin = 20
1768
+
1769
+ @defaults(tint=(0.719, 0.478, 0.208))
1770
+ class Orchestrator:
1771
+ # Replay pacing multiplier over the recorded timeline: 1.0 replays in
1772
+ # real time, 2.0 twice as fast. The event stream keeps its ordering
1773
+ # either way; cues still gate progress regardless of speed. Read live.
1774
+ # [tint=(0.939, 0.453, 0.245)]
1775
+ replay_speed = 1.0
1776
+ # How many frames a replay waits at a cue for the expected undo-stack
1777
+ # change to appear before trying its correction (re-aiming the click
1778
+ # at the cue target's LIVE rect), and again before aborting. At 120fps
1779
+ # this is ~0.75s — enough for background converters to land a value.
1780
+ # [tint=(0.939, 0.453, 0.245)]
1781
+ cue_wait_frames = 90
1782
+ # Cursor moves smaller than this (px, either axis) are not recorded —
1783
+ # keeps a long hover from bloating the event list with jitter. Replay
1784
+ # interpolates nothing, so keep it small or drags get steppy.
1785
+ # [tint=(0.939, 0.453, 0.245)]
1786
+ move_sample_min_px = 1.0
1787
+ # When True a cue only matches a live change whose recorded value repr
1788
+ # equals the recorded one - strict verification. Off by default:
1789
+ # value reprs drift for coalesced drags, the change type + target
1790
+ # name is the reliable part of the anchor.
1791
+ cue_match_values = False
1792
+ # change_value's drag servo: the first nudge (px) that measures the
1793
+ # live gain, the per-step travel cap (a wild residual/gain estimate
1794
+ # can't fling the cursor across the screen), and the step budget
1795
+ # before "didn't converge".
1796
+ # [tint=(0.939, 0.453, 0.245)]
1797
+ servo_probe_px = 8.0
1798
+ # [tint=(0.939, 0.453, 0.245)]
1799
+ servo_max_step_px = 400.0
1800
+ # [tint=(0.939, 0.453, 0.245)]
1801
+ servo_max_steps = 60
1802
+ # How many times change_value re-tries the SAME collapsed/closed gate
1803
+ # (it re-closed, or the fix missed) before giving up. Distinct gates
1804
+ # on the way in are passes, not attempts — a deep nest never counts
1805
+ # against this.
1806
+ # [tint=(0.939, 0.453, 0.245)]
1807
+ gate_attempts = 100
1808
+ # Precondition fixes, ranked by ONE number — how much of the user's
1809
+ # state each disturbs: the cheapest applicable fix runs first, the
1810
+ # predicate is re-checked, the next runs only when it did not help.
1811
+ # A command may cap what it is allowed to disturb (max_disturbance).
1812
+ # [tint=(0.62, 0.55, 0.85)]
1813
+ fix_costs = {"re_pick": 0, "raise": 1, "scroll": 2, "expand": 2,
1814
+ "move": 3, "open": 4, "anchor_window_penalty": 1}
1815
+ # [tint=(0.62, 0.55, 0.85)]
1816
+ max_disturbance = 10
1817
+ # A fix's own press point can need fixing (an obscurer's header is
1818
+ # itself covered): sub-goals nest at most this deep. Expanding a
1819
+ # covered gate is already three levels (expand → raise its window →
1820
+ # the header's own re-pick / move), so 2 gave up on the common case.
1821
+ # [tint=(0.62, 0.55, 0.85)]
1822
+ fix_depth = 4
1823
+ # The orchestrator window re-lists each visible command's unmet
1824
+ # preconditions (closed / collapsed / covered …) this often, in
1825
+ # frames — a live view of what the solver would do next.
1826
+ # [tint=(0.62, 0.55, 0.85)]
1827
+ precondition_refresh_frames = 20
1828
+ # A window raise only records as a "raise" cue when the press that
1829
+ # caused it was an EXPLICIT one: on a window's header band, or
1830
+ # outside the raised window altogether (a fast-dock row, another
1831
+ # window's button). A click inside a window's body raises it too,
1832
+ # and those press-raises were landing in takes as stray raise
1833
+ # commands (Lukas 09-01). Off = every actual restack records.
1834
+ # [tint=(0.62, 0.55, 0.85)]
1835
+ raise_cue_needs_explicit_press = True
1836
+ # A gate's control (the expand chevron) is small: a re-pick for a
1837
+ # covered gate press stays within this many px of the demonstrated
1838
+ # offset, else the cover is answered by a raise / move.
1839
+ # [tint=(0.62, 0.55, 0.85)]
1840
+ gate_hit_radius_px = 6.0
1841
+ # A synthesized header press (raise / move) lands at the MIDDLE of
1842
+ # the header strip between these two margins: the left one clears
1843
+ # the collapse chevron, the right one the close / pin buttons.
1844
+ # [tint=(0.62, 0.55, 0.85)]
1845
+ header_safe_left_px = 40.0
1846
+ # [tint=(0.62, 0.55, 0.85)]
1847
+ header_safe_right_px = 80.0
1848
+ # Height of the strip a header press may land in.
1849
+ # [tint=(0.62, 0.55, 0.85)]
1850
+ header_height_px = 24.0
1851
+ # How far past the press point a moved window is pushed.
1852
+ # [tint=(0.62, 0.55, 0.85)]
1853
+ uncover_margin_px = 12.0
1854
+ # A replayed window move verifies by geometry: the window's corner
1855
+ # must land within this many px of the recorded delta.
1856
+ # [tint=(0.62, 0.55, 0.85)]
1857
+ move_tolerance_px = 4.0
1858
+ # Wheel steps a scroll fix may send before giving up.
1859
+ # [tint=(0.62, 0.55, 0.85)]
1860
+ scroll_attempts = 40
1861
+ # change_value presses only once the target's tile reports hover
1862
+ # (its wrapper ran live, so the imgui widget is submitted — a press
1863
+ # on a blit-cached tile reaches only the window's move handle). This
1864
+ # many pumps is the cap for a view that never reports hover.
1865
+ # [tint=(0.939, 0.453, 0.245)]
1866
+ settle_hover_pumps = 30
1867
+ # After a fix reflowed the UI (expand / scroll / move): how many pumps
1868
+ # to wait for the target to be laid out again and hold still before
1869
+ # its press point is read.
1870
+ # [tint=(0.939, 0.453, 0.245)]
1871
+ layout_settle_pumps = 30
1872
+ # The virtual cursor's travel speed for synthesized moves (an
1873
+ # approach to a target, a gate click, the continuous-mouse glide):
1874
+ # WALL-CLOCK paced — a per-frame step (60 px/pump) was ~7,000 px/s
1875
+ # at 120 fps, i.e. invisible. A glide lasts distance /
1876
+ # glide_px_per_second, clamped to [glide_min_s, glide_max_s].
1877
+ # [tint=(0.939, 0.453, 0.245)]
1878
+ glide_px_per_second = 1400.0
1879
+ # [tint=(0.939, 0.453, 0.245)]
1880
+ glide_min_s = 0.18
1881
+ # [tint=(0.939, 0.453, 0.245)]
1882
+ glide_max_s = 0.9
1883
+ # Continuous mouse for EVERY injection (not just change_value's own
1884
+ # approach): a move/press far from the virtual cursor glides there
1885
+ # first (glide_px_per_second / glide_min_s / glide_max_s), so reused takes —
1886
+ # gate fragments, offset clicks, a replay resuming after a remap —
1887
+ # travel instead of snapping. Off = the raw recorded/synthesized
1888
+ # positions.
1889
+ # [tint=(0.939, 0.453, 0.245)]
1890
+ continuous_mouse = True
1891
+
1892
+ @defaults(tint=(0.103, 0.341, 0.617))
1893
+ class FastDock:
1894
+ # Floor on the hsv VALUE of an OPEN (active) row's name/icon text in
1895
+ # the Fast Dock, applied AFTER the theme mix (fast_dock.draw_fast_dock
1896
+ # -> _floor_value). The row text is the window's tint pushed through
1897
+ # make_color_rgb, so a dark window tint scaled toward black and the
1898
+ # open row read no brighter than a closed one; the floor lifts just
1899
+ # the value (hue and saturation kept) so every active row stays
1900
+ # legible. The summon button is untouched. 0 disables. Read live.
1901
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1902
+ active_text_min_brightness = 0.55
1903
+ # Same floor for CLOSED (inactive) rows. Their text mix is already
1904
+ # dim by design (closed_text_value in draw_fast_dock), so a dark
1905
+ # window tint took it below reading contrast against the dock
1906
+ # background. Keep this under active_text_min_brightness or open and
1907
+ # closed rows stop reading as different states. 0 disables. Read live.
1908
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1909
+ inactive_text_min_brightness = 0.45
1910
+ # How many of the most recently first-seen windows
1911
+ # (AppModel.render_windows) the Important tab lists under its
1912
+ # "Recently added" heading, newest first. 0 hides the section.
1913
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1914
+ recently_added_count = 3
1915
+
1916
+ @defaults(tint=(0.478, 0.053, 0.053))
1917
+ class CrashReports:
1918
+ # Every trace print_stack_trace prints is also written, ANSI-stripped,
1919
+ # as a text file under `directory` (glfw_utils.save_crash_report);
1920
+ # the Crash Reports window (view/playground/crash_reports.py) lists
1921
+ # them. Off = print only.
1922
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
1923
+ auto_save = True
1924
+ # Where the reports go — one file per trace, named by time + error.
1925
+ # [tint=(0.35, 0.85, 0.94), show_tint=True]
1926
+ directory = "~/.lsd/crash_reports"
1927
+ # The oldest reports are deleted once more than this many exist, so
1928
+ # a crash loop can't fill the disk. 0 = keep everything.
1929
+ # [tint=(0.994, 0.872, 0.0), show_tint=True]
1930
+ max_reports = 300
1931
+
1932
+ # [icon=""]
1933
+ @defaults(tint=(0.427, 0.541, 0.616))
1934
+ class ContextMenu:
1935
+ # Which tab a newly opened context menu selects, as an index into its
1936
+ # tab bar: 0 Info, 1 Config, 2 view type, 3 Eval, 4 Input, 5 Tint.
1937
+ default_tab = 0
1938
+
1939
+ # Code tab: hide render-dispatch stack frames (the render_func
1940
+ # dispatch / draw_inner_main, draw_any re-dispatch - the same
1941
+ # _is_dispatch_frame filter the func-stack stacks use) so the trace
1942
+ # reads caller → caller → view function.
1943
+ code_tab_hide_dispatch = True
1944
+
1945
+ # ── Info tab source dropdown styling ────────────────────────────────
1946
+ # Row wash + trigger tint marking the source actively driving a param.
1947
+ active_source_tint = (0.9, 0.8, 0.2)
1948
+ # Row LABEL text pulled toward the menu background (0 = the stock
1949
+ # near/black dd text, 1 = invisible) so the active-source yellow
1950
+ # stands out against quiet rows.
1951
+ source_text_toward_bg = 0.55
1952
+
1953
+ @defaults(tint=(0.378, 0.286, 0.201))
1954
+ class Collection:
1955
+ pre_load_items = 26
1956
+ placeholder_height = 30.0
1957
+ drop_tail_height = 8
1958
+ # Drag-and-drop chrome (slot lines, the home icon) starts INVISIBLE
1959
+ # at pickup and eases up to full opacity once the cursor has
1960
+ # travelled this many px (cumulative path length, never fading back
1961
+ # on a return trip) - nothing pops in on a short drag. 0 = instant.
1962
+ dnd_reveal_distance = 10.0
1963
+
1964
+ max_preferred_header_width = 70
1965
+ preferred_header_width = 132
1966
+
1967
+ # [icon=""]
1968
+ @defaults(tint=(0.65, 0.385, 0.069, 1.0))
1969
+ class SearchSettings:
1970
+ # Auto-scroll to the current match while the search term is being
1971
+ # typed. When False, typing only recounts/highlights in place; the
1972
+ # view scrolls to the current match only on explicit navigation
1973
+ # (Enter / Shift+Enter / the find bar's arrows).
1974
+ scroll_while_typing = True
1975
+
1976
+ # Search matches are highlighted with a circular gradient "glow" that
1977
+ # radiates out from the matched rectangle (rounded-rect cutout), with the
1978
+ # rect itself cut out so the matched text stays readable. The CURRENT
1979
+ # match uses ActiveElement; every other match uses InactiveElements, so
1980
+ # the two can be tuned (color/falloff/alpha/...) independently. Glows
1981
+ # combine where they overlap. Applies in both the text editor (draw_text)
1982
+ # and collections (draw_collection rows). See view/core_views/search_glow.py.
1983
+
1984
+ @defaults(tint=(0.406, 0.30, 0.16))
1985
+ class ActiveElement:
1986
+ gradient_color = (0.86, 0.67, 0.23) # RGB of the halo
1987
+ outline_color = (0.88, 0.56, 0.15) # RGB of the optional cutout outline
1988
+ falloff = 26.696 # px the glow radiates out past the match edge
1989
+ opacity = 0.176 # peak opacity, % at the cutout edge
1990
+ falloff_exp = 2.105 # >1 = bright inside the word, then drops off fast
1991
+ inner_pad = 0.00 # px the cutout is grown beyond the match rect
1992
+ cutout_radius = 5.0 # corner radius of the rounded cutout
1993
+ rings = 86 # radial tessellation steps (higher = smoother)
1994
+ corner_segments = 15 # arc subdivisions in each rounded cutout corner
1995
+ outline_alpha = 1.00 # 0 = rely on the glow's bright inner halo alone
1996
+ outline_thickness = 1.626
1997
+
1998
+ @defaults(tint=(0.36, 0.52, 0.93, 0.484))
1999
+ class InactiveElements:
2000
+ gradient_color = (0.73, 0.84, 0.91) # cooler hue so the active match stands out
2001
+ outline_color = (0.6, 0.72, 1.0)
2002
+ falloff = 15.664
2003
+ opacity = 0.077
2004
+ falloff_exp = 2.491
2005
+ inner_pad = 0.00
2006
+ cutout_radius = 5.00
2007
+ rings = 16
2008
+ corner_segments = 5
2009
+ outline_alpha = 1.00
2010
+ outline_thickness = 1.366
2011
+
2012
+ @defaults(tint=(0.58, 0.47, 0.24))
2013
+ class GlobalSearch:
2014
+ # Category order for the search selector and the All tab's interleave:
2015
+ # All shows each category's #1 hit first (in this order), then each
2016
+ # category's next-best hits up to all_tab_per_category per category.
2017
+ # Kinds not matching here fall in after these.
2018
+ search_priority = ("Toggles", "Actions", "Windows", "Code", "Text")
2019
+ # Hits each category contributes to the All tab (the #1 lands in the
2020
+ # top block; the rest sit under the category's own label).
2021
+ all_tab_per_category = 13
2022
+ # Files the Code tab lists at first - the rest fold into a trailing
2023
+ # "+ n more files" row that reveals this many more per pick. The
2024
+ # per-file row cap (FILE_ROW_CAP; "+ n more" inside a file) is
2025
+ # separate. 0 = list every file.
2026
+ code_files_per_page = 4
2027
+ # Lay the All tab out horizontally: one column per category (its
2028
+ # first on top, hits below), instead of the vertical Top-block
2029
+ # interleave.
2030
+ all_tab_horizontal = False
2031
+ # Per-category cap for the horizontal layout's columns (replaces
2032
+ # all_tab_per_category there - columns have the vertical room).
2033
+ all_tab_horizontal_per_category = 15
2034
+ # Where the window may reappear on Ctrl+Shift+S: its top edge is
2035
+ # pushed down to at least this fraction of the display height. The
2036
+ # window remembers its last spot otherwise - 0.5 = never above the
2037
+ # middle of the screen, 0.0 = reopen it where it was closed.
2038
+ summon_min_top_fraction = 0.5
2039
+ # Seconds a keystroke must sit unchanged before a search pass runs
2040
+ # (the exact pass and the full-text trigram pass share this; tab
2041
+ # switches and load-all skip this - no text changed). Typing pays
2042
+ # nothing on the render thread: the passes run on a background
2043
+ # worker (_kick_search) and repaint when their hits arrive.
2044
+ input_debounce_s = 0.15
2045
+ # Seconds the query must sit unchanged before the typo-tolerant
2046
+ # (fuzzy) Code pass runs (measured from the keystroke, so the worker
2047
+ # sleeps the remainder of input_debounce_s). Fuzzy hits arrive as
2048
+ # a trailing section, never moving the hits already shown.
2049
+ fuzzy_debounce_s = 0.2
2050
+
2051
+ @defaults(tint=(0.47, 0.463, 0.417))
2052
+ class ScrollSettings:
2053
+ scroll_speed = 214
2054
+ max_increment_fraction = 0.169
2055
+ acceleration_threshold = 0.036 # ms
2056
+ bg_offset = 30
2057
+ debug_scroll = False
2058
+ # Compositor shadow under the scrollbar grab (add_shadow depth offset,
2059
+ # signed px from the view's surface; 0 disables). The grab gets
2060
+ # its own plane in the depth map, so it reads the same whether
2061
+ # it is drawn in the view list - lit by the composite, so it
2062
+ # otherwise inherits the view edge's specular rim and any
2063
+ # neighbour's cast shadow - or on the overlay list mid freeze-drag,
2064
+ # which renders after the composite.
2065
+ scrollbar_shadow_offset = 1.0
2066
+
2067
+ # [icon=""]
2068
+ @defaults(tint=(0.315, 0.489, 0.322))
2069
+ class LoadSave:
2070
+ # When True, save() ALSO writes the raw custom.ini (root_new eval blob)
2071
+ # as a backout alongside the new-pickle custom.pkl. Set False to go
2072
+ # pickle-only (skip the .ini dual-write). NOTE: model_server still treats
2073
+ # custom.ini as the main host identity / hot-reload cache anchor, so kee
2074
+ # this off until the .ini is fully deprecated.
2075
+ ini_save = False
2076
+
2077
+
2078
+ @defaults(tint=(0.04, 0.05, 0.07))
2079
+ class CodeEditor:
2080
+ # Record navigation (file tab switches, jump-to, split open/close)
2081
+ # onto NavUndo's own stack - separate from the Ctrl+Z edit history.
2082
+ # Step back/forward with Ctrl+Shift+Left/Right or the Fast Dock's
2083
+ # arrow buttons.
2084
+ undo_navigation = True
2085
+ # Also record the text caret (NavUndo.poll_caret): moves inside a
2086
+ # draw_text (arrow keys, clicks) and tab focus hopping between
2087
+ # draw_texts. Consecutive moves in one view fold into a single step
2088
+ # while they come within nav_caret_coalesce_s seconds of each other
2089
+ # AND stay within nav_caret_step_lines lines of where the step
2090
+ # already ended (an arrow-key walk = one step; a far click = a new
2091
+ # one). Typing never records (the edit stack does that caret), and
2092
+ # jumps / tab switches keep recording as locations. Needs
2093
+ # undo_navigation.
2094
+ undo_navigation_caret = True
2095
+ nav_caret_coalesce_s = 0.6
2096
+ nav_caret_step_lines = 10
2097
+
2098
+ # ── Text undo steps (UndoManager.record, IntelliJ-style) ──
2099
+ # Typing folds into one Ctrl+Z step while it stays in one place ( one
2100
+ # editor, keystrokes landing on the selection's caret edge, same
2101
+ # insert/delete direction) and, with undo_word_steps, until a new
2102
+ # word starts with a non-space typed character after whitespace: "hello
2103
+ # world" undoes as "world" then "hello"; a Backspace run mirrors this
2104
+ # (" world" then "hello"). No pause ever commits a step, and nothing
2105
+ # folds across an undo/redo. An edit that is not keystroke-sized -
2106
+ # inserts or removes a line (Enter + auto-indent), more than
2107
+ # undo_typing_max_chars characters at once (paste, tab, a completion,
2108
+ # a comment toggle) or replaces a selection - is always its own step.
2109
+ # Raise undo_typing_max_chars if fast typing on a slow frame splits
2110
+ # words too often (a frame's keystrokes arrive as one edit).
2111
+ undo_word_steps = True
2112
+ undo_typing_max_chars = 3
2113
+
2114
+ # ── Compare-split ribbons (open_files._draw_compare_ribbons) ──
2115
+ # Block colors by kind. Read live per frame.
2116
+ ribbon_insert_tint = (0.315, 0.928, 0.294) # lines only in the buffer
2117
+ ribbon_delete_tint = (0.737, 0.76, 0.767) # lines only in the reference
2118
+ ribbon_replace_tint = (0.294, 0.675, 0.928) # changed in place
2119
+ # Merge mode (merge_files) colors: a CONFLICT region - a pending edit
2120
+ # and an external edit touch the same original row and disagree.
2121
+ # Red on purpose: the eye must land here first.
2122
+ ribbon_conflict_tint = (0.93, 0.25, 0.25)
2123
+ # A conflict region whose pending side already equals the external
2124
+ # side (taken with the arrow, or edited to match) - no longer red.
2125
+ ribbon_resolved_tint = (0.55, 0.85, 0.55)
2126
+ # A DECLINED side's band (its ✗ marker set): grey, and the 4th
2127
+ # component scales the band's fill/edge alpha down - a rejected
2128
+ # change should recede, not glow.
2129
+ ribbon_declined_tint = (0.55, 0.58, 0.62, 0.45)
2130
+ # Shared fill alpha for the block washes AND the seam band - same fill
2131
+ # so highlight → band → highlight reads as ONE continuous shape.
2132
+ ribbon_fill_alpha = 0.10
2133
+ # Boundary stroke around the whole shape (wash edges + S-curves).
2134
+ ribbon_edge_alpha = 0.00
2135
+ ribbon_edge_thickness = -0.35
2136
+ # Thin insertion line where a side has no rows (pure insert/delete).
2137
+ ribbon_insertion_alpha = 0.242
2138
+ ribbon_insertion_thickness = 3
2139
+ # Seam curve sampling (smoothstep slices).
2140
+ ribbon_curve_steps = 40
2141
+ # AA feather for the seam band's S-curve edges: the band fills with
2142
+ # aliased triangles (per-triangle AA reads as seams), so its two
2143
+ # boundary curves are stroked with an antialiased polyline in the
2144
+ # FILL_COLOR at this thickness - same trick as Swoosh.aa_width in
2145
+ # Melty._draw_ribbon. 0 disables.
2146
+ ribbon_aa_width = 1.0
2147
+ # Signed depth offset for the shadow cast behind the whole swoosh
2148
+ # (washes + seam band, add_shadow semantics: positive lifts it off
2149
+ # the editor surface, negative carves a hole). 0 disables.
2150
+ ribbon_shadow_offset = 0.397
2151
+ # Card shadow under each of the Merge Files column's five cells
2152
+ # (files spread over their whole band, panes below their chevron
2153
+ # row - add_shadow semantics: positive lifts the card off the
2154
+ # column, negative sinks it). 0 disables.
2155
+ merge_column_shadow_offset = 0.5
2156
+ # Take-arrow chips riding the swooshes (pull a block from the
2157
+ # reference pane into the buffer): flat_buttons colored by the
2158
+ # block's ribbon tint - hover boost and text color come from
2159
+ # flat_button's own pipeline.
2160
+ take_arrow_size = 20.6
2161
+ take_arrow_alpha = 1.0
2162
+ # ── Editor tab bar (open_files.draw_code_editor) ──
2163
+ # Styling knobs for the file tabs, read live per frame. The ACTIVE
2164
+ # tab draws a tinted bg + text; INACTIVE tabs draw a MUTED bg
2165
+ # (the tab_inactive_bg_* knobs below) plus their own text knobs.
2166
+ #
2167
+ # The bg pair feeds flat_button's theme-mix pipeline (value /
2168
+ # saturation_scale of make_color_rgb).
2169
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2170
+ tab_active_bg_brightness = 0.51
2171
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2172
+ tab_active_bg_saturation = 1.184
2173
+ # Hard cap the active-tab bg is clamped to AFTER the hsv transform —
2174
+ # raise it along with tab_active_bg_brightness or the brightness
2175
+ # knob tops out here.
2176
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2177
+ tab_active_bg_max_brightness = 0.31
2178
+ # Inactive-tab bg: the same theme-mix pipeline, muted — lower
2179
+ # brightness / saturation and a cap well under the active tab's so
2180
+ # the selected tab still reads first. Alpha 0 = no bg at all.
2181
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2182
+ tab_inactive_bg_brightness = 0.30
2183
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2184
+ tab_inactive_bg_saturation = 0.45
2185
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2186
+ tab_inactive_bg_max_brightness = 0.08
2187
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2188
+ tab_inactive_bg_alpha = 0.6
2189
+ # Shadow lift (add_shadow offset) of the tab bgs — keep the inactive
2190
+ # one under the active so the selected tab pops out.
2191
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2192
+ tab_active_shadow_offset = 2.0
2193
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2194
+ tab_inactive_shadow_offset = 0.21
2195
+ # Closing a tab HOLDS the bar's layout (Safari-style: the next tab
2196
+ # slides into the closed slot at the closed tab's width) until the
2197
+ # mouse travels this many pixels, so repeated close × / middle
2198
+ # clicks on one spot close tab after tab. 0 = no hold.
2199
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2200
+ tab_close_hold_move_px = 8.0
2201
+ # The text pairs are FULL-RANGE hsv multipliers applied directly to
2202
+ # each tab's tint (open_files._tab_text_color → flat_button
2203
+ # text_color): brightness scales hsv value (0 = black, 1 = the
2204
+ # tint's own value, higher pushes toward full-bright), saturation
2205
+ # scales hsv saturation (0 = greyscale, 1 = the tint's own).
2206
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2207
+ tab_active_text_brightness = 1.111
2208
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2209
+ tab_active_text_saturation = 0.420
2210
+ # Floor on the ACTIVE tab label's hsv value AFTER the brightness
2211
+ # scale (same floor as the inactive one below) — a dark file tint
2212
+ # otherwise scales the selected tab's text toward black against its
2213
+ # bright bg.
2214
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2215
+ tab_active_text_min_brightness = 0.83
2216
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2217
+ tab_inactive_text_brightness = 0.390
2218
+ # [tint=(0.923, 0.989, 0.0), show_tint=True]
2219
+ tab_inactive_text_saturation = 0.522
2220
+ # Floor on the inactive-tab label's hsv value AFTER the brightness
2221
+ # scale — dark tints stay legible instead of scaling toward black.
2222
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2223
+ tab_inactive_text_min_brightness = 0.387
2224
+ # Diff gap folds (open_files._diff_gap_folds): unchanged
2225
+ # context lines kept visible on each side of a change block; the
2226
+ # rest of the gap folds away, so collapse-all skims the changes
2227
+ # without scrolling.
2228
+ diff_fold_context = 2
2229
+ # Debug trace of the compare split's resolve, printed on every
2230
+ # CHANGE of its state (file, reference, loading, served/warm,
2231
+ # blocks, diff folds, switch, per-pane cursor offset + display
2232
+ # lines) - for pinning down a boot that shows the wrong fold
2233
+ # state. Read live; off = silent.
2234
+ trace_compare_boot = False
2235
+ # Tab tint for a file whose FileMeta carries none — the tab bar, the
2236
+ # compare files column, and the editor toolbar buttons all key off it.
2237
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2238
+ tab_tint_fallback = (0.485, 0.61, 0.76)
2239
+ # Editor toolbar back/forward buttons (open_files._draw_editor_toolbar):
2240
+ # hsv SCALES applied straight to the target file's tint (1.0 = the
2241
+ # tint as is) before it reaches flat_button — its own saturation /
2242
+ # tint_value knobs only shape the theme colour mixed in at `factor`,
2243
+ # which left these buttons unmoved. The disabled side is theme grey
2244
+ # and carries no file tint, so nothing here touches it.
2245
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2246
+ nav_button_saturation = 2.5
2247
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2248
+ nav_button_value = 0.34
2249
+ # Hard cap the button bg is clamped to AFTER the scale — raise it
2250
+ # along with nav_button_value or the value knob tops out here.
2251
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2252
+ nav_button_max_brightness = 0.25
2253
+ # The arrow glyph on an enabled button: hsv scales of the target
2254
+ # file's UNSCALED tint (not the bg above — dimming the bg leaves the
2255
+ # glyph alone), handed to flat_button as text_color the way the tab
2256
+ # labels are. brightness scales value (1.0 = the tint's own, higher
2257
+ # pushes toward full-bright), saturation scales hsv saturation.
2258
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2259
+ nav_button_text_brightness = 1.0
2260
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2261
+ nav_button_text_saturation = 0.8
2262
+ # Floor on the glyph's hsv value AFTER the brightness scale — a dark
2263
+ # file tint stays legible instead of scaling toward black.
2264
+ # [tint=(0.13, 0.55, 0.13), show_tint=True]
2265
+ nav_button_text_min_brightness = 0.6
2266
+ # ColumnLayout padding the compare column renders with (cell content
2267
+ # is inset by this from its dividers on both sides). The layout
2268
+ # reframe math (open_files._cmp_layout_reframe) keys on the SAME
2269
+ # value - change them together by changing only this.
2270
+ compare_padding = 14.0
2271
+ # Compact file-column rows; shared by layout, paint and keyboard scroll.
2272
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2273
+ compare_row_height = 20.0
2274
+ # Nearest baked JetBrains Mono size, in pixels before UI scaling.
2275
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2276
+ compare_font_size = 16
2277
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2278
+ compare_file_max_width = 650.0
2279
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2280
+ compare_file_bg_value = 0.020
2281
+ # Unselected file labels in the files column (draw_changed_file_header):
2282
+ # the same hsv scale / floor as the tab_inactive_text_* pair, but
2283
+ # brighter — the column's unpainted rows draw no bg to read against.
2284
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2285
+ compare_file_text_brightness = 0.75
2286
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2287
+ compare_file_text_min_brightness = 0.66
2288
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2289
+ compare_row_gap = 1.0
2290
+
2291
+ @defaults(tint=(0.72, 0.35, 0.3))
2292
+ class FileSafety:
2293
+ # Kill switch for folder_io's reconcile deletes: while True, a key
2294
+ # removed from a held folder tree never unlinks/rmtrees on disk (the
2295
+ # poller re-discovers the file and the key comes back). Flip off when
2296
+ # the file machinery has earned trust.
2297
+ block_file_delete = True
2298
+
2299
+ @defaults(tint=(0.103, 0.561, 0.145))
2300
+ class HostLifecycle:
2301
+ # Deregister a RenderHost from Melty.render_hosts (stops its background
2302
+ # draw/parse) once none of its consumer windows are active. Host + parse
2303
+ # stay cached; reopening re-registers (notify_on_change → register).
2304
+ deregister_idle = True
2305
+ # A consumer is gone when its window is abs_closed, or it hasn't
2306
+ # re-registered within this many frames (safety net for closes abs_closed
2307
+ # misses). Also the birth grace before a new host can be swept.
2308
+ idle_frames = 120
2309
+ # Trailing debounce (ms) on consumer-notify invalidations while the
2310
+ # host's value is being actively edited (typing). Each second editor
2311
+ # window over the same file is a consumer of the shared code_host -
2312
+ # without this it re-renders (and re-runs its background chain) on
2313
+ # every keystroke's finished reconvert. The notify is deferred and
2314
+ # re-armed per edit; it fires once, this long after the last local
2315
+ # edit. Notifies with no recent local edit (external file reload,
2316
+ # initial load) pass through live. 0 disables.
2317
+ consumer_notify_debounce_ms = 2000
2318
+ # Skip ALL RenderHost draws (the draw_main host loop) for this long
2319
+ # after each keypress while a draw_text editor is focused - the same
2320
+ # deferral the loop already applies during click/drag/scroll. Host
2321
+ # draws and deferrable background work (reconverts, saves) that
2322
+ # otherwise leak into typing frames; they catch up on a self-armed
2323
+ # wake once the window expires. 0 disables.
2324
+ host_typing_debounce_ms = 25
2325
+
2326
+ @defaults(tint=(0.181, 0.119, 0.294))
2327
+ class InputHandlerToggles:
2328
+ show_debug = False
2329
+
2330
+ # 3D mouse (3Dconnexion SpaceMouse). The device side is read by
2331
+ # events/space_mouse.py - the spacenavd socket client and the per-frame
2332
+ # pump that feeds the InputHandler a "space_mouse" axes event, each axis
2333
+ # the deflection integrated over the frame in full-deflection-seconds -
2334
+ # and the navigation knobs below are how draw_voxels turns those axes
2335
+ # into tilt / spin / roll / cam_zoom / pan (voxel_camera.apply_space_mouse),
2336
+ # so every sensitivity reads "per second at full push". All read live.
2337
+ @defaults(tint=(0.181, 0.119, 0.294))
2338
+ class SpaceMouse:
2339
+ # ── navigation (draw_voxels) ──
2340
+
2341
+ # Rotation model, independent of the mouse's
2342
+ # (Toggles.Voxels.mouse_navigation). "trackball": the puck's full
2343
+ # rotation vector rotates the view freely about the orbit center in
2344
+ # VIEW space, roll included (Blender's "Free" default) - the
2345
+ # camera's roll param carries the third degree of freedom.
2346
+ # "turntable": the puck's yaw (ry) spins about the world UP, and
2347
+ # its pitch (rx) tilts; the horizon never rolls, roll (rz) is ignored.
2348
+ navigation = "trackball"
2349
+
2350
+ # What a rotation turns about. "camera": the eye stays where it is
2351
+ # and the VIEW turns - looking around, the scene sweeps across the
2352
+ # screen (the orbit center rides along the new view direction).
2353
+ # "target": the camera orbits the volume's orbit target like the
2354
+ # mouse does - the volume turns in place on screen.
2355
+ pivot = "camera"
2356
+
2357
+ # Orbit sensitivity: radians per second at full deflection of a
2358
+ # rotation axis. Higher = faster orbit.
2359
+ # [tint=(0.181, 0.119, 0.294)]
2360
+ orbit_sensitivity = 1.50
2361
+
2362
+ # Translation sensitivity. pivot "camera": WORLD units per second
2363
+ # at full deflection on all three axes (right / up / forward — a
2364
+ # rigid flight, the volume is ~2 units across; nothing scales with
2365
+ # the camera distance). pivot "target": camera distances (cam_zoom)
2366
+ # per second in the screen plane, the mouse pan's rule. Higher =
2367
+ # faster.
2368
+ # [tint=(0.181, 0.119, 0.294)]
2369
+ pan_sensitivity = 1.0
2370
+
2371
+ # Zoom sensitivity, pivot "target" only: e-folds of cam_zoom per
2372
+ # second at full push / pull (tz). With pivot "camera" push / pull
2373
+ # is flight along the view at pan_sensitivity. Higher = faster.
2374
+ # [tint=(0.181, 0.119, 0.294)]
2375
+ zoom_sensitivity = 1.0
2376
+
2377
+ # Per-axis sensitivity, one float per degree of freedom, applied to
2378
+ # the normalized reading in space_mouse.normalize (so every view
2379
+ # sees it, on top of the orbit / pan / zoom sensitivities above).
2380
+ # 1.0 = as the device reports; 0 disables that axis. Translation:
2381
+ # tx right, ty up, tz push / pull. Rotation: rx pitch, ry yaw,
2382
+ # rz roll.
2383
+ # [tint=(0.181, 0.119, 0.294)]
2384
+ tx_sensitivity = 1.0
2385
+ # [tint=(0.181, 0.119, 0.294)]
2386
+ ty_sensitivity = 1.0
2387
+ # [tint=(0.181, 0.119, 0.294)]
2388
+ tz_sensitivity = 1.0
2389
+ # [tint=(0.181, 0.119, 0.294)]
2390
+ rx_sensitivity = 1.0
2391
+ # [tint=(0.181, 0.119, 0.294)]
2392
+ ry_sensitivity = 2.239
2393
+ # [tint=(0.181, 0.119, 0.294)]
2394
+ rz_sensitivity = 0.875
2395
+
2396
+ # Environment lighting the puck in draw_space_mouse: an HDR photo
2397
+ # from pbr.HDRIS ("studio", resources/hdri) or a cube map from
2398
+ # pbr.ENVIRONMENTS ("room", "outdoor"). Read live.
2399
+ environment = "studio"
2400
+
2401
+ # A real model of the device (OBJ / STL / GLB, absolute or relative
2402
+ # to the src tree) drawn instead of the procedural one. Parts whose
2403
+ # name contains "cap", "knob" or "puck" ride the reading (the
2404
+ # the cap); everything else is the base. Empty = procedural.
2405
+ # Model UP axis: "y" or "z" (CAD exports are usually Z-up).
2406
+ model_path = ""
2407
+ model_up = "z"
2408
+
2409
+ # How much of the WINDOW's tint colours the environment light on the
2410
+ # puck (0 = white light, 1 = fully the tint's hue). Read live.
2411
+ # [tint=(0.181, 0.119, 0.294)]
2412
+ environment_tint_strength = 0.6
2413
+
2414
+ # ── device (events/space_mouse.py) ──
2415
+
2416
+ # Master switch: off = the reader parks (socket closed) and no view
2417
+ # receives space_mouse events. Read live.
2418
+ enabled = True
2419
+
2420
+ # spacenavd's listen socket (protocol 0 stream). Debian/Ubuntu's
2421
+ # package installs here; /var/run/spnav.sock is the same path.
2422
+ socket_path = "/run/spnav.sock"
2423
+
2424
+ # The units spacenavd reports at full push / twist. 3Dconnexion
2425
+ # devices saturate at ±350 through spacenavd's default sensitivity;
2426
+ # raise your /etc/spnavrc sensitivity and this stays the ceiling
2427
+ # (readings clamp to ±1.0).
2428
+ full_deflection = 350
2429
+
2430
+ # Dead zone as a percentage of full deflection: readings below it are
2431
+ # zero (a resting cap never drifts the camera), the live range is
2432
+ # re-scaled so motion starts smoothly at the edge. spacenavd has its
2433
+ # own dead zone (spnavrc), this one stacks on top.
2434
+ dead_zone = 0.03
2435
+
2436
+ # Axis convention. The reader assumes spacenavd's graphics-style
2437
+ # frame: tx right, ty UP, tz toward the viewer (pull = +), rx pitch
2438
+ # about right, ry yaw about up, rz roll about the view axis. If the
2439
+ # daemon is configured for the device's original Z-up frame, flip
2440
+ # this so y / z (and ry / rz) swap into the frame above.
2441
+ swap_yz = False
2442
+
2443
+ # Flip the sign of individual axes: any of "tx", "ty", "tz", "rx",
2444
+ # "ry", "rz". The defaults match Blender (space_mouse.BLENDER_SIGNS,
2445
+ # measured); this is for a view that wants it otherwise. Applied
2446
+ # after swap_yz.
2447
+ invert_axes = ()
2448
+
2449
+ # A deflection older than this (no motion frame since) reads as
2450
+ # zero - spacenavd dying mid-push or a lost release frame must never
2451
+ # keep the camera moving. Devices report at 60–100 Hz while held.
2452
+ stale_s = 0.25
2453
+
2454
+ # Cap on the frame interval the pump integrates over: a stalled
2455
+ # frame (a load, a hotswap) hands the view at most this much motion
2456
+ # instead of a jump.
2457
+ max_frame_dt = 0.10
2458
+
2459
+ # Seconds between connection attempts while spacenavd is away.
2460
+ retry_s = 3.0
2461
+
2462
+ @defaults(tint=(0.652, 0.672, 0.733))
2463
+ class TerminalSettings:
2464
+ # Minimum logical terminal size, in pixels - independent of the window size.
2465
+ min_height = 605.7
2466
+ min_width = 94.154
2467
+
2468
+ @defaults(tint=(0.239, 0.435, 0.408))
2469
+ class Thermostat:
2470
+ # The thermostat web server (Desktop/thermostat/server.py). The proxy in
2471
+ # playground/thermostat_data.py pulls the chart histories from HERE,
2472
+ # not the raw jsonl files.
2473
+ server_url = "http://127.0.0.1:8765"
2474
+ # The charts' window query: hours back from now, or "all" for the whole
2475
+ # log (the server stride-samples each answer to ~2000 points either way).
2476
+ history_hours = "all"
2477
+ # How often the poller re-pulls the data (seconds).
2478
+ poll_s = 60.0
2479
+
2480
+ @defaults(tint=(0.478, 0.265, 0.265))
2481
+ class InvalidateTracker:
2482
+ keep_for_frames = 100
2483
+ enable = False
2484
+ draw_rect = True
2485
+ # [tint=(0.85, 0.45, 0.05), show_tint=True]
2486
+ invalidate_stack_trace = False
2487
+ # [tint=(0.028, 0.561, 0.115), show_tint=True]
2488
+ invalidate_request_render = False
2489
+ attrib_change_stack_trace = False
2490
+ # [tint=(0.0, 0.56, 0.872), show_tint=True]
2491
+ draw_bvh = False
2492
+
2493
+
2494
+ @defaults(tint=(0.16, 0.132, 0.194))
2495
+ class Fim:
2496
+ # Fill-in-the-middle code completion (ghost text) in code editors -
2497
+ # fim.py. Providers register with @fim_provider; `profile` names a
2498
+ # provider or a fim_profile() variant (fim_providers/profiles.py);
2499
+ # an editor can override it with draw_text(fim="...").
2500
+ enabled = True
2501
+ profile = "ollama"
2502
+
2503
+ # Idle time after the last keystroke before a request is sent.
2504
+ debounce_s = 0.25
2505
+
2506
+ # Only generate when the caret is at the end of its line (nothing but
2507
+ # whitespace after it) - never mid-line. Off = complete anywhere.
2508
+ only_at_line_end = True
2509
+
2510
+ # Ghost text is shown one CHUNK at a time: this many newline-
2511
+ # separated lines (a partial rest-of-line counts as one). Tab
2512
+ # accepts the chunk and the next one appears instantly from the
2513
+ # buffered completion - small chunks add more steering options.
2514
+ chunk_lines = 1
2515
+
2516
+ # Length of ONE provider request (tokens). The buffer refills with
2517
+ # a continuation request when it runs low, so this is the fetch
2518
+ # granularity, not a cap on how far repeated Tabs can go.
2519
+ max_tokens = 256
2520
+
2521
+ # Fetch the continuation while the current chunk is still showing
2522
+ # so the next Tab never waits.
2523
+ prefetch = True
2524
+
2525
+ # Token budget for the context block (definitions, enclosing code,
2526
+ # enclosing types, last-run values). Split ~60/25/15 across the
2527
+ # stable / run / volatile tiers.
2528
+ context_tokens = 4000
2529
+
2530
+
2531
+
2532
+ # A definition longer than this is truncated (its signature line
2533
+ # is kept as the budget-degrade form).
2534
+ definition_max_lines = 80
2535
+
2536
+ # Lines above and below the caret the per-request scans look at
2537
+ # (referenced definitions, runtime values). Bounds context assembly
2538
+ # on large-file buffers - a 14k-line span is not scanned end to end.
2539
+ scan_lines = 120
2540
+
2541
+ # Re-derive the stable context after this many seconds even when
2542
+ # its key (file, pending gen, enclosing def) hasn't moved.
2543
+ stable_refresh_s = 2.0
2544
+
2545
+ # Most last-run values to annotate (nearest the caret first).
2546
+ live_values_max = 40
2547
+ # Also report min/mean/max for tensors - a GPU reduce per value.
2548
+ live_value_stats = False
2549
+
2550
+ # Close a provider session (language client / Copilot LS process) that
2551
+ # no editor has used for this long.
2552
+ session_idle_s = 600.0
2553
+
2554
+ # How long Ollama keeps a model resident after a request / a Load
2555
+ # from Internet Accounts (Ollama duration string; 0 = disable).
2556
+ ollama_keep_alive = "30m"
2557
+
2558
+ # Print provider/context tracebacks.
2559
+ debug_print = False
2560
+
2561
+
2562
+ @defaults(tint=(0.85, 0.55, 0.35))
2563
+ class InternetAccounts:
2564
+ # Codex app-server CLI integration; empty executable searches PATH.
2565
+ codex_bin = ""
2566
+ codex_request_timeout_s = 30.0
2567
+ codex_login_timeout_s = 300.0
2568
+ # The Anthropic "Sign in" button (Internet Accounts.py →
2569
+ # fim_providers/anthropic_oauth.py): the same OAuth login that
2570
+ # `ant auth login` performs, written as an SDK profile the
2571
+ # anthropic client reads and refreshes for itself.
2572
+ #
2573
+ # OAuth client the login runs as - the official CLI's public id,
2574
+ # so the profile it mints is one `ant` and the SDKs share/refresh.
2575
+ anthropic_oauth_client_id = "41077d10-94b8-4194-be48-d251e9eb21b4"
2576
+ # Console that hosts the /oauth/authorize consent page.
2577
+ anthropic_console_url = "https://platform.claude.com"
2578
+ # Scopes requested at login (space separated).
2579
+ anthropic_oauth_scope = "user:profile user:inference user:developer"
2580
+ # SDK profile the sign-in writes: ~/.config/anthropic/{configs,
2581
+ # credentials}/<name>.json. Named - not "default" and never made the
2582
+ # active profile - so Claude Code / a bare Anthropic() elsewhere keep
2583
+ # their own login; the studio passes profile= explicitly. Extra
2584
+ # Anthropic accounts use "<name>-<account id>" unless their Login
2585
+ # profile field says otherwise.
2586
+ anthropic_profile = "lsd"
2587
+ # Give up waiting for the browser redirect after this long.
2588
+ anthropic_login_timeout_s = 300.0
2589
+ # Claude plan usage panel (Anthropic row): NO background poll (it
2590
+ # rate-limited the endpoint, 08-25). One GET /api/oauth/usage when
2591
+ # the panel opens, when a REPAINT finds the numbers older than
2592
+ # usage_refresh_s, and from the Refresh button. No panel = nothing.
2593
+ usage_refresh_s = 300.0
2594
+ # Every AUTOMATIC usage fetch (panel opened, numbers found stale by
2595
+ # a repaint, the persisted-open panel at boot) waits this long
2596
+ # before the request fires; last session's cached bars show
2597
+ # meanwhile (AccountsPanelState.usage), so a quick restart cycle
2598
+ # never reaches the endpoint. 0 = fire at once. Refresh is immediate.
2599
+ usage_fetch_delay_s = 60.0
2600
+ # Hard floor between two usage requests for one account, whatever
2601
+ # asks (a redraw, the poller, an identity change) - only the Refresh
2602
+ # button goes under it. A 429 backs off for its Retry-After, else
2603
+ # usage_backoff_s, doubling per repeat up to usage_backoff_max_s.
2604
+ usage_min_interval_s = 20.0
2605
+ usage_backoff_s = 300.0
2606
+ usage_backoff_max_s = 1800.0
2607
+ # The Claude Code executable the "Use in Claude Code" button runs
2608
+ # (`claude auth login --email ...`); empty = PATH / the usual installs.
2609
+ claude_code_bin = ""
2610
+ # Sign-in pages open as a PLACED popup: a chromeless Chromium --app
2611
+ # window forced into Xwayland (positioning works there) and parked
2612
+ # beside the pointer by xdotool (fim_providers/oauth_popup.py); a
2613
+ # dedicated profile ~/.lsd/oauth-browser keeps the Google session
2614
+ # between sign-ins. Off / no Chromium / no xdotool → plain xdg-open.
2615
+ use_oauth_popup = True
2616
+ oauth_popup_browser = ""
2617
+ oauth_popup_size = (520, 760)
2618
+ # A toast (tag "anthropic") + log line for EVERY Anthropic API
2619
+ # request the studio makes - usage fetches, FIM completions, Test,
2620
+ # sign-in token exchanges, retries (fim_providers/anthropic_requests.py).
2621
+ notify_requests = True
2622
+
2623
+
2624
+ @defaults(tint=(0.63, 0.44, 0.2))
2625
+ class GC:
2626
+ # Deliberate collector scheduling (gc_manager.tick in Melty.end_frame):
2627
+ # gen2's auto-trigger is deferred and full collects run at
2628
+ # input-idle instead of landing mid-keystroke (the observed 3.3s
2629
+ # gen2 stall in the render thread). Off = stock collector.
2630
+ enable = True
2631
+ manage = True
2632
+ # Auto gen2 threshold while managed - effectively "manual threshold";
2633
+ # the idle collector below is what actually runs full passes.
2634
+ gen2_threshold = 1000000
2635
+ # Seconds of input quiet before the boot freeze / an idle collect.
2636
+ idle_seconds = 15.0
2637
+ # Minimum spacing between idle collects.
2638
+ idle_collect_s = 120.0
2639
+ # Minimum spacing for a collect that at the moment the window
2640
+ # LOSES focus (alt-tab / minimize): the one frame nobody is watching.
2641
+ # Focus-gain restarts the idle clock, so returning never collects.
2642
+ unfocus_collect_s = 200.0
2643
+ # The focus-loss collect fires only after the window has stayed
2644
+ # unfocused, with no mouse presence, this long: the polled focus
2645
+ # edge is also what the first frame BACK reads (pointer over the
2646
+ # window, focus not regained yet), and collecting there froze the
2647
+ # return. Raise it if a loss still lands in your face; it costs
2648
+ # nothing while away.
2649
+ unfocus_confirm_s = 1.0
2650
+ # The BOOT pass (full-graph walk, seconds) needs a real absence when
2651
+ # it goes by idle rather than by focus loss: this long input quiet
2652
+ # with the window focused. Ordinary auto-freeze collects use
2653
+ # idle_seconds.
2654
+ boot_idle_seconds = 120.0
2655
+ # Minimum spacing between post-run collects (collect_after_run -
2656
+ # the live lab's per-run VRAM retirement). Auto Execute runs the
2657
+ # previewed function per mouse-drag tick; collecting after every
2658
+ # one was a ~120ms stall per frame, and even every few seconds the
2659
+ # accumulating garbage made each pass a ~400ms GIL stall. Within this
2660
+ # window runs coalesce onto a trailing one-shot collect that fires
2661
+ # once the burst rests, so the LAST run's garbage (the VRAM that
2662
+ # matters) always retires - at most one full pass per window. VRAM
2663
+ # from all generations inside the window stays pinned until then;
2664
+ # lower this if iterating on models that fill the card. 0 = collect
2665
+ # after every run.
2666
+ post_run_min_s = 120.0
2667
+ # Minimum spacing between post-run torch.cuda.empty_cache() calls -
2668
+ # separate from the collect above: freeing cached blocks is cheap
2669
+ # and is what makes freed activations actually leave VRAM (nvidia-
2670
+ # smi) while a typing burst runs the lab every keystroke. 0 = every run.
2671
+ post_run_cache_release_s = 2.0
2672
+ # Never freeze/collect before the app has been up this long (caches
2673
+ # still filling - freezing mid-load would pin a half-built graph).
2674
+ boot_delay_s = 30.0
2675
+ # On a CUDA out-of-memory, print gc_manager.report_vram_holders()
2676
+ # BEFORE the responder sweeps: the largest CUDA storages and who
2677
+ # references them (store key / draw / attr / frame / module).
2678
+ # A few seconds of gc walk, OOM-time only.
2679
+ oom_holder_report = True
2680
+ # Every scheduled collect writes a report of WHAT it reclaimed
2681
+ # (class / module / dict-signature / function, with sample
2682
+ # reprs) - one file per collect under report_dir - and its "lag"
2683
+ # toast names the top types; clicking the toast opens the report in
2684
+ # the code editor. Off = bare gc.collect(), no toast.
2685
+ reports = True
2686
+ report_dir = "~/meltygui/gc_reports"
2687
+ # Oldest reports pruned past this many.
2688
+ report_keep = 50
2689
+ cam_zoom = 1.5585
2690
+
2691
+ # Presentation mode: dim the text editor's glyphs everywhere EXCEPT on
2692
+ # lines carrying a def-tint line band, so the tinted lines read as the
2693
+ # selected content for an audience. Requires TextEditor.definition_tints
2694
+ # for the exception lines to exist - with it off, every line dims. Dimmed
2695
+ # brightness comes from TextEditor.presentation_text_brightness. Also
2696
+ # hides the Fast Dock (draw_main skips it; summon windows via the global
2697
+ # search instead). Read live.
2698
+ presentation_mode = False
2699
+
2700
+ # Master switch for the always-on debug chrome painted over the app: the
2701
+ # red/white texture-init tile counter in the top-left (LSDStudio's render
2702
+ # loop) and the notification / "Live" value bands along the right edge
2703
+ # (notifications.draw_notifications, gated in Melty.draw). Off = a clean
2704
+ # screen for demos and screenshots; notify()/display() keep recording, so
2705
+ # flipping it back shows the history. The GPU readout is unaffected.
2706
+ # also live.
2707
+ developer_mode = False
2708
+ # Frame rate (and, for app windows, render-thread frame time) in the
2709
+ # titlebar of every OS window (titlebar.paint_fps). Apps start with it off.
2710
+ show_fps = True
2711
+
2712
+ # The notification overlay (notifications.draw_notifications, gated by
2713
+ # developer_mode above): categories stack vertically along the right
2714
+ # edge, each in its own fixed-height scrolling band. Read live.
2715
+ @defaults(tint=(0.85, 0.64, 0.13))
2716
+ class Notifications:
2717
+ # Vertical space one category's band gets (title included). Entries
2718
+ # beyond it scroll: wheel over the band, sticky at the newest end,
2719
+ # "N new" badge while scrolled back.
2720
+ # [tint=(0.85, 0.64, 0.13), show_tint=True]
2721
+ category_height = 300
2722
+
2723
+ class Chat:
2724
+ # Centered transcript and right-aligned user bubble width limits.
2725
+ # [tint=(0.45, 0.48, 0.52)]
2726
+ transcript_max_width = 900.0
2727
+ # [tint=(0.72, 0.43, 0.28)]
2728
+ user_message_max_width = 680.0
2729
+ # Sidebar navigation stays neutral when the selected conversation changes.
2730
+ # [tint=(0.45, 0.48, 0.52), show_tint=True]
2731
+ navigation_tint = (0.45, 0.48, 0.52)
2732
+ # [tint=(0.58, 0.6, 0.63), show_tint=True]
2733
+ codex_tag_tint = (0.58, 0.6, 0.63)
2734
+ # [tint=(0.72, 0.43, 0.28), show_tint=True]
2735
+ claude_tag_tint = (0.72, 0.43, 0.28)
2736
+ # Selected sidebar chats rise above their folder plate.
2737
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2738
+ selected_chat_shadow_offset = 5.0
2739
+ # Drop-shadow offsets in the Chat window (cards, code blocks, buttons,
2740
+ # the transcript scrollbar thumb). 0 disables a shadow.
2741
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2742
+ shadow_offset = 1.5
2743
+ # A selected conversation card / pressed button lifts a little higher.
2744
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2745
+ selected_shadow_offset = 2.0
2746
+ # Bash / terminal blocks paint flat.
2747
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2748
+ bash_shadow_offset = 0.0
2749
+ # The file tags on write-file rows (the compare column's tab look), flat.
2750
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2751
+ file_tag_shadow_offset = 0.0
2752
+ # Code blocks darken the window's painted fill by these factors:
2753
+ # bash a small step under the window, python darker.
2754
+ # [tint=(0.95, 0.6, 0.25), show_tint=True]
2755
+ bash_darken = 0.74
2756
+ # [tint=(0.95, 0.6, 0.25), show_tint=True]
2757
+ python_darken = 0.65
2758
+ # The dimmed first line of a collapsed command row (1 = full text tint).
2759
+ # [tint=(0.95, 0.6, 0.25), show_tint=True]
2760
+ command_text_brightness = 0.6
2761
+ # The row icons (terminal / pencil / brain) in their chips (1 = full text tint).
2762
+ # [tint=(0.95, 0.6, 0.25), show_tint=True]
2763
+ icon_brightness = 0.7
2764
+ # The Failed badge's plate / icon / text, scaled down from their design colours.
2765
+ # [tint=(0.95, 0.6, 0.25), show_tint=True]
2766
+ failed_badge_brightness = 0.7
2767
+ # Layout: the provider / account dropdowns' trigger height, the margin
2768
+ # under them before the sidebar and transcript, the gap between the
2769
+ # sidebar column and the transcript column, and the gap between the
2770
+ # conversation list and the New conversation button (all px).
2771
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2772
+ dropdown_height = 30
2773
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2774
+ header_margin = 8
2775
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2776
+ column_gap = 14
2777
+ # [tint=(0.635, 0.728, 0.725, 1.0), show_tint=True]
2778
+ new_conversation_margin = 8
2779
+ # Tallest an inline picture (a pasted image, a Read of an image file)
2780
+ # draws in the transcript, in px; wider ones fit the span.
2781
+ # [tint=(0.95, 0.6, 0.25), show_tint=True]
2782
+ image_max_height = 360
2783
+ # Brightness cap (HSV value) of a user message's card; the sidebar's
2784
+ # conversation cards stay at draw_bg's 0.18.
2785
+ # [tint=(0.95, 0.6, 0.25), show_tint=True]
2786
+ user_message_bg_value = 0.3
2787
+ # Widest a command row (terminal block, file tags) or assistant prose
2788
+ # gets, in px; long lines clip / wrap at it.
2789
+ # [tint=(0.95, 0.6, 0.25), show_tint=True]
2790
+ terminal_max_width = 748
2791
+
2792
+ show_filled_tiles = False
2793
+ gl_check_error = False
2794
+
2795
+ # [tint=(0.0, 0.374, 0.744), show_tint=True]
2796
+ enable_jedi = True
2797
+ jedi_correctness = False
2798
+
2799
+ # Build the node→span map from Python's `ast` (C code) instead of libcst's
2800
+ # PositionProvider (whole-tree codegen, ~64% of cst→dict cost).
2801
+ new_position_map = True
2802
+
2803
+ # While typing, pause the background cst→dict parse at statement boundaries so
2804
+ # the render thread gets the GIL uncontended. Never sleeps render
2805
+ # [tint=(0.75, 0.46218, 0.00)]
2806
+ yield_to_ui = True
2807
+
2808
+ # Timeline logging of the symbol-index / code-host load path: every
2809
+ # meaningful unit of work (parse, graph compute, warmer pass, drag wait,
2810
+ # attach) writes a timestamped, thread-labeled line to
2811
+ # /tmp/lsd_symbol_perf.log (perf_trace.py). Near-zero cost when off.
2812
+ symbol_perf_log = False # TEMP: enabled to capture the 1300ms frame shortly after boot
2813
+ attrib_churn_log = False
2814
+ debug_threads = False
2815
+
2816
+ slow_down_threads = False
2817
+
2818
+ # [tint=(0.025, 0.372, 0.326)]
2819
+ profile_mode = ProfileMode.LIGHT
2820
+ debug_stale_tint = False
2821
+
2822
+ # Filter Settings
2823
+ # [tint=(0.418, 0.656, 0.744)]
2824
+ brightness = 0.144
2825
+ # [tint=(0.458, 0.474, 0.5)]
2826
+ contrast = 1.213
2827
+
2828
+ debug_z_depth = False
2829
+ filters = True
2830
+ filter_brightness = False
2831
+ show_excluded = True
2832
+ layer_stack_trace = False
2833
+ show_line_breaks = False
2834
+
2835
+ memory_profile = False
2836
+
2837
+ # Shadow Settings - the compositor shadow pass (meltygui.py post_frame:
2838
+ # ShadowCast at reduced res over the R16 rank mask, then
2839
+ # ShadowComposite's joint bilateral upsample onto the frame). Read live
2840
+ # per frame.
2841
+ shadow_downscale = 3
2842
+ # Per-view cap for add_shadow/add_glow marks: a view (draw_state)
2843
+ # can emit at most this many marks of each kind per frame; extra
2844
+ # emissions are dropped and the budget recycles next frame. Backstop
2845
+ # against a view leaking unbounded marks into the retained stores
2846
+ # (every retained mark re-stamps every finalize).
2847
+ shadow_cap = 100
2848
+ shadow_edge_sharpness = 0.021
2849
+ # Light direction the shadows are cast AWAY from, as a screen-space
2850
+ # vector (x right, y down in UV space). Only the direction matters -
2851
+ # the shader normalizes it; travel distance comes from
2852
+ # shadow_height_scale.
2853
+ shadow_light_dir = (-0.196, 0.265)
2854
+ # How far a shadow travels per unit of caster/receiver depth gap
2855
+ # (in units): higher = deeper stacks cast longer shadows.
2856
+ shadow_height_scale = 3.716
2857
+ # Penumbra widening per unit of depth gap: bigger = softer, more
2858
+ # diffuse shadows from tall casters.
2859
+ shadow_blur_scale = 0.16
2860
+ # Contact-hardening: curve of penumbra growth along the shadow's
2861
+ # LENGTH - 0 at the caster's silhouette edge, 1 at the shadow tip
2862
+ # (the shader measures the edge distance by bisecting along
2863
+ # light_dir). shadow_blur_scale stays the blur magnitude at the far
2864
+ # end; this shapes the ramp: < 1 blooms the blur rapidly just past
2865
+ # the contact edge (long shadows that go soft fast), 1 = linear
2866
+ # growth, > 1 stays crisp for most of the run and softens only the
2867
+ # tip. 0 = legacy uniform blur along the whole shadow.
2868
+ shadow_blur_exponent = -0.312
2869
+ # Blur samples in ShadowCast's penumbra ring (x3 radii per sample).
2870
+ # More = finer/less grainy penumbra, linearly more fragment work at
2871
+ # the shadow edge.
2872
+ shadow_blur_samples = 6
2873
+ # Occlusion each caster hit contributes before the depth-gap decay -
2874
+ # the base darkness of a shadow right under its caster.
2875
+ shadow_hit_strength = 0.503
2876
+ # How fast that contribution decays per unit of caster/receiver depth
2877
+ # gap: higher = deep stacks fade their shadows out sooner (clamped at
2878
+ # 0 in-shader, never lightens).
2879
+ shadow_hit_falloff = 69.627
2880
+ # Max fraction of light a deep stack of casters can block inside
2881
+ # ShadowCast (the light-transmission model's ceiling).
2882
+ shadow_strength = 0.683
2883
+ # Composite-time darkening: how far shadowed pixels mix toward
2884
+ # shadow_color (scales the ShadowCast intensity at the final blend).
2885
+ shadow_opacity = 0.684
2886
+ # What shadows mix TOWARD - a slightly blue gray by default.
2887
+ shadow_color = (0.0, 0.02, 0.05)
2888
+ # Specular highlight on the LIT edge of raised backgrounds — the edge
2889
+ # facing the light source (top-left when the shadow falls down-right;
2890
+ # direction derives from shadow_light_dir so the two always agree).
2891
+ # Value = bevel radius in px: the width of the highlight rim and the
2892
+ # apparent roundness of the edge. 0 disables the pass.
2893
+ # [tint=(0.85, 0.75, 0.05), show_tint=True]
2894
+ specular_bevel = 1.243
2895
+
2896
+ # Global surface roughness for the specular rim, (0, 1]: low = tight
2897
+ # bright crest line at the edge, high = broad dim sheen at the bevel.
2898
+ specular_roughness = 0.094
2899
+ # Peak brightness of the highlight (white light added at composite).
2900
+ specular_opacity = 0.286
2901
+ # Fade of the highlight ALONG the lit edges, in px: brightest at the
2902
+ # lit corner (top-left when the shadow falls down-right), dying out
2903
+ # over this distance scanning down the left edge / across the top
2904
+ # edge. Distances come from edge walks (smooth-min over silhouette edge
2905
+ # tests on an absolute sample grid), so the gradient is smooth - no
2906
+ # dashes or stair steps. 0 = uniform rim, no fade.
2907
+ specular_fade = 3280.821
2908
+ # Size-relative cap on the fade: per axis the fade length becomes
2909
+ # min(specular_fade, rel * edge_extent), the extent being the soft
2910
+ # forward+backward distances. Large windows keep the fixed
2911
+ # specular_fade look; small widgets fade out within their own edge
2912
+ # instead of holding a uniform bright rim. 0 = pure fixed fade.
2913
+ specular_fade_rel = 0
2914
+
2915
+ # Depth falloff: specular intensity decays as exp(-depth * rate), so
2916
+ # surfaces near the floor catch the full highlight and high-stacked
2917
+ # windows progressively lose it. One layer slot is ~0.3 depth steps
2918
+ # at the 64x32 layer/depth config; 0 = depth-independent.
2919
+ specular_depth_falloff = 0.0
2920
+ # Slope tolerance for the bevel edge march, in depth units per px: a
2921
+ # sample only counts as a silhouette edge when it drops more than
2922
+ # eps + slope*distance below the start depth. Backgrounds interpolate
2923
+ # depth across their quad, so without this a tilted surface can read
2924
+ # as a phantom edge.
2925
+ specular_slope_tol = 0.003
2926
+
2927
+ # Glow Settings - add_glow() marks rendered as light sources in the
2928
+ # shadow composite (blit_offscreen PASS 6 stamps the low-res light
2929
+ # buffer; ShadowComposite adds it and cuts shadow under it).
2930
+ glow = True
2931
+ # Resolution divisor for the glow light buffer. The falloff is smooth by
2932
+ # construction, so it survives aggressive downscaling; the composite's
2933
+ # bilinear fetch upsamples for free.
2934
+ glow_downscale = 1
2935
+ # Master strength of the glow light at composite time.
2936
+ glow_strength = 0.091
2937
+ # How strongly glow luminance cancels shadow beneath it (0 = shadows
2938
+ # ignore glows, >1 = a full lit glow erases the shadow under it).
2939
+ # Keep MODEST: shadows are cast relative from the casters (light_dir),
2940
+ # so a strong cut brightens a band-shaped region DISPLACED from the
2941
+ # glow - it reads as a second copy of the glow drawn over itself, and
2942
+ # it shifts between live-rendered and blit-served frames because the
2943
+ # depth detail under the band differs subtly between those paths.
2944
+ # (Confirmed by glow_debug_log: dups=0 = one stamping, one composite
2945
+ # - the "double" is this cut, not a second glow rendering.)
2946
+ glow_shadow_cut = -0.291
2947
+
2948
+ # Downward AREA-LIGHT glow mode. Off = the omnidirectional
2949
+ # inverse-square skirt. On = each glow rect reads as a downward-facing
2950
+ # area l
2951
+ # ight: no light above or beside the source, a lit trapezoid below
2952
+ # it that widens by glow_area_spread px per px of drop, brightness held
2953
+ # flat for the first glow_area_hold fraction of the falloff radius and
2954
+ # then cut off with a sharp smoothstep - a much harder transition than
2955
+ # the inverse-square tail.
2956
+ glow_area_light = False
2957
+ # Fraction of the falloff radius over which the area light holds full
2958
+ # brightness before the gradient cutoff begins (0 = fade from the edge,
2959
+ # 0.9 = bright almost all the way down, then a hard stop).
2960
+ glow_area_hold = -1.119
2961
+ # Lateral widening of the lit trapezoid, in px per px of drop below
2962
+ # the rect (tan of the light cone's half-angle; 0 = straight down).
2963
+ glow_area_spread = 0.647
2964
+ # Falloff curve exponent past the hold point: brightness falls as
2965
+ # smoothstep^exponent, smooth at BOTH ends so there is no hard edge at
2966
+ # the far extent. 1 = plain smoothstep; higher = the light dies faster
2967
+ # near the source and trails out longer - a more prominent gradient.
2968
+ glow_area_falloff = 2.25
2969
+ # Fan-edge penumbra: the side edges of the light cone blur by this many
2970
+ # px per px of drop (symmetric about the trapezoid center) - razor-sharp
2971
+ # at the source and progressively softer with distance, like a real
2972
+ # area-light penumbra. 0 = hard fan edges all the way down.
2973
+ glow_area_edge_blur = 9.113
2974
+ # Tilt of the light, in degrees from straight down (clamped to +/-80).
2975
+ # Positive shears the fan toward screen-right as it drops; the whole
2976
+ # fan (center, edges, penumbra) shifts by tan(angle) px per px of drop.
2977
+ glow_area_angle = 0.00
2978
+ # Which edge of the emitting rect the light hangs from. True = the TOP
2979
+ # edge: the fan starts there and washes down THROUGH the rect and past
2980
+ # it (the rect interior gets the gradient too). False = the BOTTOM
2981
+ # edge: the rect interior stays fully lit and the fan starts under it.
2982
+ glow_area_top_edge = False
2983
+ # Emit from the token background's LEFT, RIGHT and BOTTOM edges
2984
+ # instead of a single downward fan (ignore glow_area_top_edge while
2985
+ # on). The hold/falloff profile runs on the distance from the rounded
2986
+ # rect itself, sheared sideways by glow_area_angle as it drops below
2987
+ # the top edge, and the skirt tapers to nothing approaching the top
2988
+ # rect so the top edge remains dark. glow_area_spread /
2989
+ # glow_area_edge_blur are fan-only and ignored here.
2990
+ glow_area_edges = False
2991
+
2992
+ # Band offsets for the glow receiver mask, applied live in PASS 6 (no
2993
+ # re-render needed to tune). Lower bound is relative to the emitter's
2994
+ # ROOT WINDOW surface (negative reaches below the window, positive
2995
+ # trims up into it); upper bound is relative to the EMITTER's own
2996
+ # surface (how far above it a receiver may sit and still catch light).
2997
+ # Units: one shallow depth step (~one Melty.shadow_depth increment
2998
+ # near depth 0). Applied LINEARLY in rank space - shadow_depth_at's
2999
+ # depth curve is non-monotone, so offsets never go through it, which
3000
+ # makes large values (+/-1000) genuinely open the whole band, same as
3001
+ # glow_debug_no_mask.
3002
+ glow_mask_lower_offset = 0.054
3003
+ glow_mask_upper_offset = 0.151
3004
+
3005
+ # How many consecutive EMPTY body runs (cleared without re-emitting)
3006
+ # before an emitter's retained glow drops. 1 = AUTHORITATIVE: the
3007
+ # first empty run drops the glow - live edits shed stale glow
3008
+ # instantly. Raise it if async tint recomputes (all views rebuilding
3009
+ # their tree per publish, background symbol indexing) start reading as
3010
+ # random glow loss again: each unit is another body run of grace against
3011
+ # a transient stale tint state.
3012
+ glow_clear_hold_frames = 0
3013
+
3014
+ # Automatic culling of retained glow/depth marks from views judged no
3015
+ # longer visible (territory-repaint kills: tab switches, jump-to
3016
+ # content swaps). OFF = retained marks only ever change by the
3017
+ # emitter's own re-emission or explicit clears - stale glow may linger
3018
+ # after tab switches, but if a mysterious glow LOSS stops happening
3019
+ # with this off, the culling misjudged it; if it persists, the
3020
+ # depth-mask gate or the stamp path is the culprit. A narrowing tool.
3021
+ glow_auto_cull = True
3022
+
3023
+ # --- Glow debug ---
3024
+ # Bypass the glow-mask receiver gate: light falls on EVERY pixel under
3025
+ # the quad. Glows appearing only with this on = the rank maths is broken
3026
+ # (emitter/floor vs the mask's receiver ranks), not the stamping.
3027
+ glow_debug_no_mask = False
3028
+ # Stamp hard full-intensity rects instead of the falloff: solid inside
3029
+ # the emitting quad, 30% across the skirt - shows position + radius
3030
+ # extent through the real pipeline.
3031
+ glow_debug_rects = False
3032
+ # Draw the entire low-res glow buffer over the whole frame (replaces the
3033
+ # image). Buffer has content but the normal view doesn't = composite
3034
+ # hookup broken; buffer empty = stamping broken.
3035
+ glow_debug_view = False
3036
+ # ~1/sec console print of pipeline counts (frame marks, retained
3037
+ # emitters, stamped quads, first mark's rank/floor band).
3038
+ glow_debug_log = False
3039
+
3040
+ caller_walk_steps = 7
3041
+ draw_legacy = False
3042
+ show_full_call_stack = False
3043
+
3044
+ # Screenshot output dir (screenshot.py / context menu capture)
3045
+ screenshots = "" # Default: the user's XDG cache directory.
3046
+
3047
+ debug_set_anywhere = False
3048
+ ignore_call_from = ()
3049
+
3050
+
3051
+ @window
3052
+ class Actions:
3053
+ """General-purpose stash for app triggers ("New file", "New Render
3054
+ Function", ...). Rendered by draw_actions (actions_playground.py)."""
3055
+
3056
+ @staticmethod
3057
+ def new_file(name: str):
3058
+ pass
3059
+
3060
+ @defaults(icon="")
3061
+ @staticmethod
3062
+ def new_render_func(name="draw_other"):
3063
+ pass
3064
+
3065
+ @defaults(icon="\uf030")
3066
+ @staticmethod
3067
+ def screenshot():
3068
+ """Arm the region screenshot tool (also Ctrl+Shift+3): a crosshair
3069
+ follows the cursor; click-drag a box; on release the framebuffer
3070
+ pixels inside it are saved as a PNG (Toggles.screenshots), opened in
3071
+ the code editor, and the file path is copied to the clipboard. Esc
3072
+ cancels."""
3073
+ from meltygui.core.diagnostics.screenshot_core import arm
3074
+ arm()
3075
+
3076
+ @staticmethod
3077
+ def claude_terminal():
3078
+ """Open a gnome-terminal window running `claude-d` (Claude Code in a
3079
+ studio-discoverable tmux session — see ~/bin/claude-d). The script owns
3080
+ the session lifetime; closing the window kills it."""
3081
+ import subprocess
3082
+ # Full paths + close_fds=False -> posix_spawn, not fork (forking this
3083
+ # process stalls the render thread).
3084
+ import shutil
3085
+ terminal, command = shutil.which("gnome-terminal"), shutil.which("claude-d")
3086
+ if terminal is None or command is None:
3087
+ from meltygui.core.diagnostics.notifications import notify
3088
+ notify("Install gnome-terminal and claude-d to use this action")
3089
+ return
3090
+ subprocess.Popen([terminal, "--", command], close_fds=False)
3091
+
3092
+
3093
+ @window
3094
+ class LegacyToggles:
3095
+ # All the padding settings from imgui style
3096
+ item_spacing = (3, 2)
3097
+ frame_padding = (4, 1)
3098
+ window_padding = (6, 6)
3099
+ line_height = 16
3100
+
3101
+
3102
+ # =========
3103
+ def shadow_depth_at(depth, active_layer):
3104
+ scaling = 53.42
3105
+ cap = 5.975
3106
+
3107
+ divisor = max(cap, depth - scaling)
3108
+
3109
+ depth_and_layer = active_layer * Core.melty.max_depth + (depth * (scaling / (divisor)))
3110
+ depth_and_layer *= Core.melty.layer_inc
3111
+ return depth_and_layer
3112
+
3113
+
3114
+ class WindowManager:
3115
+ excluded_windows = ["demo test", "Egg Time", "Layer 1"]