meltygui 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- meltygui/__init__.py +107 -0
- meltygui/accounts/__init__.py +0 -0
- meltygui/accounts/internet_accounts.py +1355 -0
- meltygui/chat/__init__.py +91 -0
- meltygui/chat/activity.py +75 -0
- meltygui/chat/backends.py +36 -0
- meltygui/chat/chat_interface.py +732 -0
- meltygui/chat/chat_proxy.py +352 -0
- meltygui/chat/codex_proxy.py +592 -0
- meltygui/chat/codex_settings.py +100 -0
- meltygui/chat/codex_transport.py +60 -0
- meltygui/chat/command_parser.py +204 -0
- meltygui/chat/images.py +227 -0
- meltygui/chat/messages.py +417 -0
- meltygui/chat/metadata.py +139 -0
- meltygui/chat/writer_locks.py +64 -0
- meltygui/code/__init__.py +0 -0
- meltygui/code/basic_converters.py +533 -0
- meltygui/code/chain_converters.py +2111 -0
- meltygui/code/code_checks.py +2209 -0
- meltygui/code/core_syntax.py +1430 -0
- meltygui/code/file_converters.py +1933 -0
- meltygui/code/fileref.py +702 -0
- meltygui/code/hotswap_guard.py +144 -0
- meltygui/code/libcst_conversion.py +9724 -0
- meltygui/code/live_instrument.py +392 -0
- meltygui/code/live_view.py +2490 -0
- meltygui/code/melty_scan.py +2684 -0
- meltygui/code/new_codecs.py +1255 -0
- meltygui/code/new_converters.py +3017 -0
- meltygui/code/project_code.py +278 -0
- meltygui/code/source_context.py +63 -0
- meltygui/code/symbol_roster.py +1588 -0
- meltygui/code/syntax_check.py +34 -0
- meltygui/code/syntax_check_worker.py +114 -0
- meltygui/completion/__init__.py +0 -0
- meltygui/completion/fim.py +1232 -0
- meltygui/completion/fim_context.py +481 -0
- meltygui/completion/providers/__init__.py +0 -0
- meltygui/completion/providers/anthropic_oauth.py +446 -0
- meltygui/completion/providers/anthropic_requests.py +69 -0
- meltygui/completion/providers/claude.py +203 -0
- meltygui/completion/providers/claude_usage.py +499 -0
- meltygui/completion/providers/codex_accounts.py +180 -0
- meltygui/completion/providers/copilot.py +617 -0
- meltygui/completion/providers/oauth_popup.py +220 -0
- meltygui/completion/providers/ollama.py +320 -0
- meltygui/completion/providers/profiles.py +23 -0
- meltygui/core/README.md +88 -0
- meltygui/core/__init__.py +1 -0
- meltygui/core/automation/__init__.py +1 -0
- meltygui/core/automation/action_core.py +153 -0
- meltygui/core/automation/collection_action.py +45 -0
- meltygui/core/automation/mcp_eval.py +167 -0
- meltygui/core/automation/mcp_hotswap.py +107 -0
- meltygui/core/automation/mcp_query.py +552 -0
- meltygui/core/automation/mcp_server.py +657 -0
- meltygui/core/automation/orchestration_core.py +2636 -0
- meltygui/core/automation/query_core.py +124 -0
- meltygui/core/automation/search_core.py +26 -0
- meltygui/core/automation/selector_core.py +340 -0
- meltygui/core/automation/value_core.py +1752 -0
- meltygui/core/cache/__init__.py +1 -0
- meltygui/core/cache/cache_diagnostics.py +0 -0
- meltygui/core/cache/invalidation_decoration.py +153 -0
- meltygui/core/cache/invalidation_tracker.py +43 -0
- meltygui/core/cache/tile_cache.py +5968 -0
- meltygui/core/conversion/__init__.py +1 -0
- meltygui/core/conversion/bubbling.py +599 -0
- meltygui/core/conversion/cache_tree.py +188 -0
- meltygui/core/conversion/chain.py +113 -0
- meltygui/core/conversion/converter_register.py +145 -0
- meltygui/core/conversion/data_decoration.py +67 -0
- meltygui/core/conversion/dict_conversion.py +1815 -0
- meltygui/core/conversion/dict_conversion_util.py +177 -0
- meltygui/core/conversion/dynamic_obj.py +89 -0
- meltygui/core/conversion/graph_compare.py +183 -0
- meltygui/core/conversion/load_save_v2.py +1032 -0
- meltygui/core/conversion/missing_saved_class.py +39 -0
- meltygui/core/conversion/path_finder.py +606 -0
- meltygui/core/conversion/render_host.py +1083 -0
- meltygui/core/core_render.py +6537 -0
- meltygui/core/definition_hotswap.py +231 -0
- meltygui/core/diagnostics/__init__.py +1 -0
- meltygui/core/diagnostics/attribute_churn.py +38 -0
- meltygui/core/diagnostics/fps_counter.py +39 -0
- meltygui/core/diagnostics/gpu_frame_timer.py +103 -0
- meltygui/core/diagnostics/inspection_core.py +169 -0
- meltygui/core/diagnostics/monitor_core.py +105 -0
- meltygui/core/diagnostics/notifications.py +706 -0
- meltygui/core/diagnostics/perf_trace.py +281 -0
- meltygui/core/diagnostics/profile_decoration.py +88 -0
- meltygui/core/diagnostics/resize_trace.py +62 -0
- meltygui/core/diagnostics/screenshot_core.py +247 -0
- meltygui/core/diagnostics/session_status.py +98 -0
- meltygui/core/diagnostics/trace_core.py +445 -0
- meltygui/core/files/__init__.py +1 -0
- meltygui/core/files/file_core.py +208 -0
- meltygui/core/files/file_explorer_core.py +104 -0
- meltygui/core/files/file_tree_core.py +198 -0
- meltygui/core/files/file_watch_core.py +43 -0
- meltygui/core/files/import_graph_core.py +43 -0
- meltygui/core/files/metadata_core.py +51 -0
- meltygui/core/graphics/__init__.py +1 -0
- meltygui/core/graphics/cuda_context_core.py +166 -0
- meltygui/core/graphics/cuda_interop_core.py +136 -0
- meltygui/core/graphics/cuda_kernel_core.py +91 -0
- meltygui/core/graphics/framebuffer_recorder.py +337 -0
- meltygui/core/graphics/gl_state.py +658 -0
- meltygui/core/graphics/lut_core.py +52 -0
- meltygui/core/graphics/overlay_renderer.py +984 -0
- meltygui/core/graphics/scene_target.py +180 -0
- meltygui/core/graphics/screenshot.py +439 -0
- meltygui/core/graphics/shader_func.py +478 -0
- meltygui/core/graphics/tensor_core.py +45 -0
- meltygui/core/graphics/text_texture.py +329 -0
- meltygui/core/graphics/wayland_color.py +635 -0
- meltygui/core/input/__init__.py +1 -0
- meltygui/core/input/collision.py +165 -0
- meltygui/core/input/drag_drop_core.py +1525 -0
- meltygui/core/input/hypr_left_drag.py +323 -0
- meltygui/core/input/input_core.py +245 -0
- meltygui/core/input/input_handler.py +1101 -0
- meltygui/core/input/mouse_cursor.py +355 -0
- meltygui/core/input/pynput_backend.py +1054 -0
- meltygui/core/input/space_mouse.py +338 -0
- meltygui/core/input/touchpad_backend.py +393 -0
- meltygui/core/input/view_selection.py +177 -0
- meltygui/core/layout/__init__.py +1 -0
- meltygui/core/layout/column_core.py +2153 -0
- meltygui/core/layout/cursor_core.py +161 -0
- meltygui/core/layout/dropdown_core.py +278 -0
- meltygui/core/layout/edge_constraints.py +155 -0
- meltygui/core/layout/grid_core.py +117 -0
- meltygui/core/layout/header_core.py +23 -0
- meltygui/core/layout/header_runtime.py +67 -0
- meltygui/core/layout/layout_core.py +87 -0
- meltygui/core/layout/tile_manager_core.py +591 -0
- meltygui/core/melty.py +6726 -0
- meltygui/core/module_map.json +896 -0
- meltygui/core/module_names.py +19 -0
- meltygui/core/rendering/__init__.py +1 -0
- meltygui/core/rendering/core_decoration.py +431 -0
- meltygui/core/rendering/core_render_helpers.py +328 -0
- meltygui/core/rendering/func_metadata.py +398 -0
- meltygui/core/rendering/mode.py +818 -0
- meltygui/core/rendering/mode_defaults.py +41 -0
- meltygui/core/rendering/modes.py +136 -0
- meltygui/core/rendering/parameter_core.py +1665 -0
- meltygui/core/rendering/render_dispatch.py +1891 -0
- meltygui/core/rendering/render_funcs.py +273 -0
- meltygui/core/rendering/shaped.py +312 -0
- meltygui/core/rendering/window_decoration.py +25 -0
- meltygui/core/runtime/__init__.py +1 -0
- meltygui/core/runtime/app.py +735 -0
- meltygui/core/runtime/app_session.py +140 -0
- meltygui/core/runtime/background.py +564 -0
- meltygui/core/runtime/extensions.py +53 -0
- meltygui/core/runtime/gc_manager.py +1163 -0
- meltygui/core/runtime/lifecycle.py +21 -0
- meltygui/core/runtime/paths.py +27 -0
- meltygui/core/runtime/settings.py +14 -0
- meltygui/core/runtime/singleton.py +16 -0
- meltygui/core/runtime/thread_safe_bool.py +24 -0
- meltygui/core/runtime/thread_signal.py +30 -0
- meltygui/core/runtime/toggles.py +3115 -0
- meltygui/core/services/__init__.py +1 -0
- meltygui/core/services/account_core.py +10 -0
- meltygui/core/services/chat_core.py +19 -0
- meltygui/core/services/claude_terminal_core.py +346 -0
- meltygui/core/services/terminal_core.py +458 -0
- meltygui/core/services/terminal_runtime.py +78 -0
- meltygui/core/styling/__init__.py +1 -0
- meltygui/core/styling/color_core.py +46 -0
- meltygui/core/styling/fonts.py +639 -0
- meltygui/core/styling/global_style.py +338 -0
- meltygui/core/styling/style.py +198 -0
- meltygui/core/styling/style_core.py +522 -0
- meltygui/core/styling/warm_start.py +149 -0
- meltygui/core/windowing/__init__.py +1 -0
- meltygui/core/windowing/backends/PYIMGUI_LICENSE +28 -0
- meltygui/core/windowing/backends/__init__.py +1 -0
- meltygui/core/windowing/backends/imgui_renderer.py +138 -0
- meltygui/core/windowing/backends/native_wayland.py +850 -0
- meltygui/core/windowing/backends/protocols/xdg-decoration-unstable-v1.xml +156 -0
- meltygui/core/windowing/backends/protocols/xdg-shell.xml +1420 -0
- meltygui/core/windowing/backends/wayland_protocol.py +101 -0
- meltygui/core/windowing/dock_core.py +182 -0
- meltygui/core/windowing/frame_geometry.py +42 -0
- meltygui/core/windowing/geometry_feed.py +864 -0
- meltygui/core/windowing/glfw_utils.py +1343 -0
- meltygui/core/windowing/os_frame.py +1552 -0
- meltygui/core/windowing/surface.py +643 -0
- meltygui/core/windowing/titlebar.py +1560 -0
- meltygui/core/windowing/titlebar_buttons.py +281 -0
- meltygui/core/windowing/wayland_move.py +932 -0
- meltygui/core/windowing/window_api.py +62 -0
- meltygui/core/windowing/window_constants.py +339 -0
- meltygui/core/windowing/window_visibility.py +162 -0
- meltygui/debug/__init__.py +0 -0
- meltygui/debug/app_view_utils.py +9 -0
- meltygui/editor/__init__.py +0 -0
- meltygui/editor/bash_syntax.py +30 -0
- meltygui/editor/code_line_fast.py +152 -0
- meltygui/editor/diff.py +139 -0
- meltygui/editor/external_changes.py +159 -0
- meltygui/editor/file_header.py +41 -0
- meltygui/editor/live_usage.py +169 -0
- meltygui/editor/live_view_views.py +1803 -0
- meltygui/editor/pending_save.py +1351 -0
- meltygui/editor/roster_tints.py +547 -0
- meltygui/editor/source_preview.py +16 -0
- meltygui/editor/source_tools.py +10 -0
- meltygui/editor/source_ui.py +70 -0
- meltygui/editor/spell_check.py +77 -0
- meltygui/editor/text_editor.py +9076 -0
- meltygui/editor/usage_picker.py +538 -0
- meltygui/events/__init__.py +0 -0
- meltygui/events/example.py +118 -0
- meltygui/examples/__init__.py +0 -0
- meltygui/examples/columns_demo.py +33 -0
- meltygui/examples/columns_window_demo.py +60 -0
- meltygui/examples/context_menu_demo.py +24 -0
- meltygui/examples/context_menu_window_demo.py +49 -0
- meltygui/examples/gui_playground.py +127 -0
- meltygui/examples/live_view_playground.py +256 -0
- meltygui/examples/lora.py +43 -0
- meltygui/examples/lora_data.py +59 -0
- meltygui/examples/lora_policies.py +67 -0
- meltygui/examples/mode_demo.py +49 -0
- meltygui/examples/modifies_demo.py +119 -0
- meltygui/examples/scalar_policies.py +60 -0
- meltygui/examples/style_layouts.py +149 -0
- meltygui/examples/tile_manager_demo.py +74 -0
- meltygui/examples/tint_demo.py +160 -0
- meltygui/examples/tint_functions.py +56 -0
- meltygui/examples/trace_demo.py +88 -0
- meltygui/examples/two_windows.py +36 -0
- meltygui/files/__init__.py +0 -0
- meltygui/files/fast_file_explorer.py +439 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/extension.js +237 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/lsd-window-geometry@latent-descent.iml +9 -0
- meltygui/gnome_extension/lsd-window-geometry@latent-descent/metadata.json +7 -0
- meltygui/graphics/__init__.py +6 -0
- meltygui/graphics/base.py +85 -0
- meltygui/graphics/examples.py +507 -0
- meltygui/graphics/executor.py +520 -0
- meltygui/graphics/filter.py +667 -0
- meltygui/graphics/filter.pyi +856 -0
- meltygui/graphics/generate_stubs.py +22 -0
- meltygui/graphics/registry.py +203 -0
- meltygui/graphics/shader_compiler.py +155 -0
- meltygui/graphics/shaders.py +1302 -0
- meltygui/graphics/stub_generator.py +250 -0
- meltygui/graphics/texture_manager.py +170 -0
- meltygui/graphics/texture_min_max.py +295 -0
- meltygui/hdr_color.py +757 -0
- meltygui/image_load.py +308 -0
- meltygui/model/__init__.py +1 -0
- meltygui/model/account_model.py +142 -0
- meltygui/model/camera_model.py +159 -0
- meltygui/model/chat_model.py +86 -0
- meltygui/model/code_model.py +62 -0
- meltygui/model/code_proxy_model.py +876 -0
- meltygui/model/collection_model.py +41 -0
- meltygui/model/color_model.py +127 -0
- meltygui/model/cuda_tensor_model.py +36 -0
- meltygui/model/cuda_texture_model.py +149 -0
- meltygui/model/dropdown_model.py +137 -0
- meltygui/model/file_metadata_model.py +73 -0
- meltygui/model/file_model.py +289 -0
- meltygui/model/format_model.py +390 -0
- meltygui/model/graph_model.py +98 -0
- meltygui/model/icon_model.py +1024 -0
- meltygui/model/import_graph_model.py +468 -0
- meltygui/model/layout_model.py +15 -0
- meltygui/model/lut_model.py +266 -0
- meltygui/model/search_model.py +173 -0
- meltygui/model/tensor_model.py +402 -0
- meltygui/model/terminal_model.py +329 -0
- meltygui/model/texture_model.py +146 -0
- meltygui/model/tile_model.py +24 -0
- meltygui/model/trace_model.py +198 -0
- meltygui/model/trace_report_model.py +146 -0
- meltygui/models/__init__.py +0 -0
- meltygui/models/file_meta.py +613 -0
- meltygui/models/function_console.py +184 -0
- meltygui/models/orchestration.py +48 -0
- meltygui/pbr.py +1576 -0
- meltygui/png_unfilter.c +49 -0
- meltygui/resources/JetBrainsMono-Regular.ttf +0 -0
- meltygui/resources/THIRD_PARTY_NOTICES.md +21 -0
- meltygui/resources/dejavu/DejaVuSans-Bold.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans-ExtraLight.ttf +0 -0
- meltygui/resources/dejavu/DejaVuSans.ttf +0 -0
- meltygui/resources/dejavu/LICENSE.txt +78 -0
- meltygui/resources/fontawesome-LICENSE.txt +121 -0
- meltygui/resources/fontawesome-webfont.ttf +0 -0
- meltygui/resources/hdri/studio_small_09_1k.hdr +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Bold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-ExtraLight.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Light.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Medium.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-SemiBold.ttf +0 -0
- meltygui/resources/jetbrains-weights/JetBrainsMono-Thin.ttf +0 -0
- meltygui/resources/jetbrains-weights/OFL.txt +93 -0
- meltygui/resources/jetbrains-weights/README.md +2 -0
- meltygui/state/__init__.py +0 -0
- meltygui/state/account_state.py +22 -0
- meltygui/state/animation_state.py +97 -0
- meltygui/state/annotation_state.py +22 -0
- meltygui/state/chat_state.py +36 -0
- meltygui/state/code_state.py +10 -0
- meltygui/state/core_enums.py +59 -0
- meltygui/state/core_markers.py +115 -0
- meltygui/state/core_undo.py +1075 -0
- meltygui/state/file_state.py +75 -0
- meltygui/state/graph_state.py +26 -0
- meltygui/state/inspection_state.py +47 -0
- meltygui/state/menu_state.py +10 -0
- meltygui/state/model_enums.py +11 -0
- meltygui/state/new_core_model.py +2394 -0
- meltygui/state/orchestration_state.py +14 -0
- meltygui/state/query_state.py +24 -0
- meltygui/state/tensor_state.py +10 -0
- meltygui/state/terminal_state.py +23 -0
- meltygui/state/trace_state.py +32 -0
- meltygui/state/voxel_state.py +12 -0
- meltygui/text_index.py +816 -0
- meltygui/utils/__init__.py +0 -0
- meltygui/utils/jump_to_code.py +344 -0
- meltygui/utils/pkl_inspect.py +90 -0
- meltygui/utils/render_utils.py +1419 -0
- meltygui/view/__init__.py +1 -0
- meltygui/view/account_view.py +524 -0
- meltygui/view/action_view.py +73 -0
- meltygui/view/chat_decoration_view.py +112 -0
- meltygui/view/chat_view.py +1703 -0
- meltygui/view/code_view.py +2677 -0
- meltygui/view/collection_view.py +1185 -0
- meltygui/view/color_view.py +824 -0
- meltygui/view/control_view.py +559 -0
- meltygui/view/decoration_view.py +439 -0
- meltygui/view/diagnostic_view.py +177 -0
- meltygui/view/dropdown_view.py +1082 -0
- meltygui/view/file_view.py +1599 -0
- meltygui/view/graph_cuda_view.py +105 -0
- meltygui/view/graph_view.py +882 -0
- meltygui/view/header_view.py +848 -0
- meltygui/view/input_view.py +238 -0
- meltygui/view/inspection_view.py +1783 -0
- meltygui/view/layout_view.py +596 -0
- meltygui/view/lut_view.py +45 -0
- meltygui/view/menu_view.py +212 -0
- meltygui/view/orchestration_view.py +658 -0
- meltygui/view/query_view.py +118 -0
- meltygui/view/search_view.py +342 -0
- meltygui/view/tab_view.py +258 -0
- meltygui/view/tensor_view.py +430 -0
- meltygui/view/terminal_view.py +355 -0
- meltygui/view/text_view.py +8031 -0
- meltygui/view/texture_view.py +480 -0
- meltygui/view/tile_view.py +38 -0
- meltygui/view/trace_view.py +923 -0
- meltygui/view/voxel_cuda_view.py +824 -0
- meltygui/view/voxel_view.py +1860 -0
- meltygui/view/window_view.py +111 -0
- meltygui-0.1.0.dist-info/METADATA +143 -0
- meltygui-0.1.0.dist-info/RECORD +372 -0
- meltygui-0.1.0.dist-info/WHEEL +4 -0
- meltygui-0.1.0.dist-info/licenses/LICENSE +202 -0
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
"""Editable palette dictionaries and integer-like LUT texture values."""
|
|
2
|
+
import math
|
|
3
|
+
import weakref
|
|
4
|
+
|
|
5
|
+
from meltygui.core.conversion.bubbling import _BubblingDictMixin, install_bubbling
|
|
6
|
+
from meltygui.model.texture_model import TextureId
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Lut(str):
|
|
10
|
+
"""A LUT NAME that is still a str everywhere it matters (dict keys,
|
|
11
|
+
comparisons, GLSL host lookups) but carries its own TYPE, so meltygui routes
|
|
12
|
+
it to its own renderer — a dropdown of the available LUTs rather than a
|
|
13
|
+
text field. Same contract as TensorDim: the renderer must return
|
|
14
|
+
Lut(...) or the first edit stores a plain str and the row falls back to
|
|
15
|
+
the generic str renderer."""
|
|
16
|
+
|
|
17
|
+
__slots__ = ()
|
|
18
|
+
|
|
19
|
+
def __repr__(self):
|
|
20
|
+
return f"Lut({str(self)!r})"
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def _bake_lut(fn, n=256, clamp=True):
|
|
24
|
+
"""Sample fn(v ∈ [0,1]) → (r, g, b) into the flat-list LUT shape.
|
|
25
|
+
Entries are EXTENDED sRGB (hdr_color.py): `clamp=False` keeps values
|
|
26
|
+
past [0, 1] — above 1 is brighter than the desktop's white, negative is
|
|
27
|
+
outside the sRGB gamut (P3) — for a table that is HDR on its own."""
|
|
28
|
+
out = []
|
|
29
|
+
for i in range(n):
|
|
30
|
+
rgb = fn(i / (n - 1))
|
|
31
|
+
if clamp:
|
|
32
|
+
rgb = (min(1.0, max(0.0, float(c))) for c in rgb)
|
|
33
|
+
out += [float(c) for c in rgb]
|
|
34
|
+
return out
|
|
35
|
+
|
|
36
|
+
def hdr_ramp(hue_at, peak=16.0, chroma=1.0, white_from=0.55, n=1024):
|
|
37
|
+
"""Build an HDR colour scale as a flat LUT list: ``n`` entries spaced
|
|
38
|
+
UNIFORMLY in Oklab lightness from black to ``peak`` × the desktop's
|
|
39
|
+
white, each the most saturated colour of hue ``hue_at(u)`` (u = the
|
|
40
|
+
lightness fraction, 0 → 1; radians) that fits the P3 box that tall,
|
|
41
|
+
times ``chroma``. That is what "a bigger LUT" means: an SDR scale
|
|
42
|
+
(``hot``: black → red → yellow → white) runs the max-chroma edges of a
|
|
43
|
+
box ONE white tall; this runs the same kind of path up a box ``peak``
|
|
44
|
+
whites tall, so the scale has log2(peak) extra stops of distinguishable
|
|
45
|
+
levels — not a brighter table, a longer one. Entries are extended sRGB,
|
|
46
|
+
unclamped (P3 reaches below 0, HDR above 1).
|
|
47
|
+
|
|
48
|
+
Whitening is a SECOND axis of information, not a side effect: past
|
|
49
|
+
``white_from`` the chroma eases to zero at the peak (smoothstep), so
|
|
50
|
+
the top of the range reads as saturated → pale → white while the
|
|
51
|
+
lightness keeps climbing — the box alone would keep a P3 yellow fully
|
|
52
|
+
saturated to within a few percent of the peak and then snap to white,
|
|
53
|
+
since a pure yellow fits a 16× box until its two channels hit 16. The
|
|
54
|
+
ease also lands the scale inside what the panel can show: a 12× white
|
|
55
|
+
saturated yellow is past any panel's peak and the compositor would
|
|
56
|
+
desaturate it anyway (Hyprland's luminance-preserving rule)."""
|
|
57
|
+
from meltygui.hdr_color import _cbrt
|
|
58
|
+
from meltygui.hdr_color import linear_to_srgb
|
|
59
|
+
from meltygui.hdr_color import oklab_max_chroma
|
|
60
|
+
from meltygui.hdr_color import oklab_to_linear
|
|
61
|
+
peak_lightness = _cbrt(peak)
|
|
62
|
+
out = []
|
|
63
|
+
for i in range(n):
|
|
64
|
+
u = i / (n - 1)
|
|
65
|
+
lightness = u * peak_lightness
|
|
66
|
+
hue = hue_at(u)
|
|
67
|
+
w = min(1.0, max(0.0, (u - white_from) / (1.0 - white_from)))
|
|
68
|
+
envelope = 1.0 - w * w * (3.0 - 2.0 * w)
|
|
69
|
+
c = chroma * envelope * oklab_max_chroma(lightness, hue, peak)
|
|
70
|
+
lin = oklab_to_linear((lightness, c * math.cos(hue), c * math.sin(hue)))
|
|
71
|
+
out += [linear_to_srgb(v) for v in lin]
|
|
72
|
+
return out
|
|
73
|
+
|
|
74
|
+
def _hot_hdr_hue(u):
|
|
75
|
+
"""`hot`'s hue path for hdr_ramp: P3 red through the lower half of the
|
|
76
|
+
lightness range, turning to P3 yellow across the middle, yellow above
|
|
77
|
+
(the box then whitens it toward the peak). The SDR `hot` is untouched:
|
|
78
|
+
an HDR scale is its own picker entry, never a scaled SDR one, so a
|
|
79
|
+
colour scale people know keeps meaning what it meant (Lukas 09-08)."""
|
|
80
|
+
from meltygui.hdr_color import linear_p3_to_srgb
|
|
81
|
+
from meltygui.hdr_color import oklab_hue
|
|
82
|
+
# [tint=(0.95, 0.35, 0.1)]
|
|
83
|
+
red_until = 0.4 # lightness fraction that stays pure red
|
|
84
|
+
# [tint=(0.95, 0.75, 0.2)]
|
|
85
|
+
yellow_from = 0.7 # ... and where it has fully turned yellow
|
|
86
|
+
red = oklab_hue(linear_p3_to_srgb((1.0, 0.0, 0.0)))
|
|
87
|
+
yellow = oklab_hue(linear_p3_to_srgb((1.0, 1.0, 0.0)))
|
|
88
|
+
t = min(1.0, max(0.0, (u - red_until) / (yellow_from - red_until)))
|
|
89
|
+
t = t * t * (3.0 - 2.0 * t) # smoothstep
|
|
90
|
+
return red + (yellow - red) * t
|
|
91
|
+
|
|
92
|
+
def _poly(coeffs):
|
|
93
|
+
"""Per-channel polynomial in t (Horner); rows are (r, g, b) coefficients
|
|
94
|
+
in ascending order — the shape of Matt Zucker's matplotlib colormap fits
|
|
95
|
+
(shadertoy WlfXRN) and of Google's turbo fit."""
|
|
96
|
+
|
|
97
|
+
def fn(t):
|
|
98
|
+
r = g = b = 0.0
|
|
99
|
+
for cr, cg, cb in reversed(coeffs):
|
|
100
|
+
r, g, b = r * t + cr, g * t + cg, b * t + cb
|
|
101
|
+
return r, g, b
|
|
102
|
+
|
|
103
|
+
return fn
|
|
104
|
+
|
|
105
|
+
def _seismic(v):
|
|
106
|
+
"""Diverging blue-white-red with dark ends (matplotlib's seismic) —
|
|
107
|
+
strong negative coverage: zero is white, sign maps to hue, magnitude
|
|
108
|
+
to saturation/darkness. Pair with the `centered` param."""
|
|
109
|
+
if v < 0.25:
|
|
110
|
+
t = v / 0.25
|
|
111
|
+
return (0.0, 0.0, 0.3 + 0.7 * t)
|
|
112
|
+
if v < 0.5:
|
|
113
|
+
t = (v - 0.25) / 0.25
|
|
114
|
+
return (t, t, 1.0)
|
|
115
|
+
if v < 0.75:
|
|
116
|
+
t = (v - 0.5) / 0.25
|
|
117
|
+
return (1.0, 1.0 - t, 1.0 - t)
|
|
118
|
+
t = (v - 0.75) / 0.25
|
|
119
|
+
return (1.0 - 0.5 * t, 0.0, 0.0)
|
|
120
|
+
|
|
121
|
+
def _coolwarm(v):
|
|
122
|
+
"""Cool-to-warm diverging ramp: blue → near-white → red."""
|
|
123
|
+
if v < 0.5:
|
|
124
|
+
t, a, b = v * 2.0, (0.230, 0.299, 0.754), (0.865, 0.865, 0.865)
|
|
125
|
+
else:
|
|
126
|
+
t, a, b = v * 2.0 - 1.0, (0.865, 0.865, 0.865), (0.706, 0.016, 0.150)
|
|
127
|
+
return tuple(x + (y - x) * t for x, y in zip(a, b))
|
|
128
|
+
|
|
129
|
+
_VIRIDIS = [
|
|
130
|
+
(0.2777273272234177, 0.005407344544966578, 0.3340998053353061),
|
|
131
|
+
(0.1050930431085774, 1.404613529898575, 1.384590162594685),
|
|
132
|
+
(-0.3308618287255563, 0.214847559468213, 0.09509516302823659),
|
|
133
|
+
(-4.634230498983486, -5.799100973351585, -19.33244095627987),
|
|
134
|
+
(6.228269936347081, 14.17993336680509, 56.69055260068105),
|
|
135
|
+
(4.776384997670288, -13.74514537774601, -65.35303263337234),
|
|
136
|
+
(-5.435455855934631, 4.645852612178535, 26.3124352495832),
|
|
137
|
+
]
|
|
138
|
+
|
|
139
|
+
_PLASMA = [
|
|
140
|
+
(0.05873234392399702, 0.02333670892565664, 0.5433401826748754),
|
|
141
|
+
(2.176514634195958, 0.2383834171260182, 0.7539604599784036),
|
|
142
|
+
(-2.689460476458034, -7.455851135738909, 3.110799939717086),
|
|
143
|
+
(6.130348345893603, 42.3461881477227, -28.51885465332158),
|
|
144
|
+
(-11.10743619062271, -82.66631109428045, 60.13984767418263),
|
|
145
|
+
(10.02306557647065, 71.41361770095349, -54.07218655560067),
|
|
146
|
+
(-3.658713842777788, -22.93153465461149, 18.19190778539828),
|
|
147
|
+
]
|
|
148
|
+
|
|
149
|
+
_MAGMA = [
|
|
150
|
+
(-0.002136485053939582, -0.000749655052795221, -0.005386127855323933),
|
|
151
|
+
(0.2516605407371642, 0.6775232436837668, 2.494026599312351),
|
|
152
|
+
(8.353717279216625, -3.577719514958484, 0.3144679030132573),
|
|
153
|
+
(-27.66873308576866, 14.26473078096533, -13.64921318813922),
|
|
154
|
+
(52.17613981234068, -27.94360607168351, 12.94416944238394),
|
|
155
|
+
(-50.76852536473588, 29.04658282127291, 4.23415299384598),
|
|
156
|
+
(18.65570506591883, -11.48977351997711, -5.601961508734096),
|
|
157
|
+
]
|
|
158
|
+
|
|
159
|
+
_INFERNO = [
|
|
160
|
+
(0.0002189403691192265, 0.001651004631001012, -0.01948089843709184),
|
|
161
|
+
(0.1065134194856116, 0.5639564367884091, 3.932712388889277),
|
|
162
|
+
(11.60249308247187, -3.972853965665698, -15.9423941062914),
|
|
163
|
+
(-41.70399613139459, 17.43639888205313, 44.35414519872813),
|
|
164
|
+
(77.162935699427, -33.40235894210092, -81.80730925738993),
|
|
165
|
+
(-71.31942824499214, 32.62606426397723, 73.20951985803202),
|
|
166
|
+
(25.13112622477341, -12.24266895238567, -23.07032500287172),
|
|
167
|
+
]
|
|
168
|
+
|
|
169
|
+
_TURBO = [
|
|
170
|
+
(0.13572138, 0.09140261, 0.10667330),
|
|
171
|
+
(4.61539260, 2.19418839, 12.64194608),
|
|
172
|
+
(-42.66032258, 4.84296658, -60.58204836),
|
|
173
|
+
(132.13108234, -14.18503333, 110.36276771),
|
|
174
|
+
(-152.94239396, 4.27729857, -89.90310912),
|
|
175
|
+
(59.28637943, 2.82956604, 27.34824973),
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def make_luts():
|
|
180
|
+
"""A fresh editable dictionary of flat RGB lists, including HDR palettes."""
|
|
181
|
+
return {
|
|
182
|
+
# the original custom jet, baked from the exact formula the shader uses
|
|
183
|
+
"jet": _bake_lut(lambda v: (1.5 - abs(4.0 * v - 3.0),
|
|
184
|
+
1.5 - abs(4.0 * v - 2.0),
|
|
185
|
+
1.5 - abs(4.0 * v - 1.0))),
|
|
186
|
+
"viridis": _bake_lut(_poly(_VIRIDIS)),
|
|
187
|
+
"plasma": _bake_lut(_poly(_PLASMA)),
|
|
188
|
+
"magma": _bake_lut(_poly(_MAGMA)),
|
|
189
|
+
"inferno": _bake_lut(_poly(_INFERNO)),
|
|
190
|
+
"turbo": _bake_lut(_poly(_TURBO)),
|
|
191
|
+
"grey": _bake_lut(lambda v: (v, v, v)),
|
|
192
|
+
"hot": _bake_lut(lambda v: (3.0 * v, 3.0 * v - 1.0, 3.0 * v - 2.0)),
|
|
193
|
+
"coolwarm": _bake_lut(_coolwarm),
|
|
194
|
+
"seismic": _bake_lut(_seismic),
|
|
195
|
+
# ── HDR ramps get their own entries (hdr_ramp - uniform Oklab lightness
|
|
196
|
+
# up a 16× box, max chroma that fits, 4 stops longer than the SDR one)
|
|
197
|
+
"hot_hdr": hdr_ramp(_hot_hdr_hue, peak=16.0),
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
def lut_values(luts, name):
|
|
202
|
+
"""Resolve a named palette from supplied data, falling back to its jet entry."""
|
|
203
|
+
return luts.get(str(name), luts.get("jet", (0.0, 0.0, 0.0, 1.0, 1.0, 1.0)))
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class LutTexture(TextureId):
|
|
207
|
+
"""A flat RGB palette that behaves as its current OpenGL texture ID."""
|
|
208
|
+
|
|
209
|
+
def __init__(self, colors):
|
|
210
|
+
from OpenGL.GL import GL_TEXTURE_1D
|
|
211
|
+
super().__init__(GL_TEXTURE_1D)
|
|
212
|
+
self.colors = colors
|
|
213
|
+
|
|
214
|
+
def _upload(self, state):
|
|
215
|
+
return state.texture1d('texture', self.colors, version=hash(tuple(self.colors)))
|
|
216
|
+
|
|
217
|
+
def cuda(self, device):
|
|
218
|
+
"""The same palette on the tensor's device, refreshed when colours change."""
|
|
219
|
+
import torch
|
|
220
|
+
return self._state().get(
|
|
221
|
+
('cuda_lut', str(device)),
|
|
222
|
+
lambda: torch.tensor(self.colors, dtype=torch.float32, device=device).reshape(-1, 3),
|
|
223
|
+
deps=tuple(self.colors))
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
class LutPalette(_BubblingDictMixin, dict):
|
|
227
|
+
"""An ordinary editable palette dictionary with stable texture proxies.
|
|
228
|
+
|
|
229
|
+
List/dictionary edits bubble through the existing mutation mechanism.
|
|
230
|
+
Subscribers are weak bound callbacks; no host, rendering loop or GL work
|
|
231
|
+
is needed to hold or edit the values.
|
|
232
|
+
"""
|
|
233
|
+
|
|
234
|
+
def __init__(self, colors=None):
|
|
235
|
+
dict.__init__(self, make_luts() if colors is None else colors)
|
|
236
|
+
self._textures = {}
|
|
237
|
+
self._callbacks = {}
|
|
238
|
+
install_bubbling(self, self)
|
|
239
|
+
|
|
240
|
+
def texture(self, name):
|
|
241
|
+
name = str(name)
|
|
242
|
+
colors = lut_values(self, name)
|
|
243
|
+
texture = self._textures.get(name)
|
|
244
|
+
if texture is None:
|
|
245
|
+
texture = LutTexture(colors)
|
|
246
|
+
self._textures[name] = texture
|
|
247
|
+
else:
|
|
248
|
+
texture.colors = colors
|
|
249
|
+
return texture
|
|
250
|
+
|
|
251
|
+
def watch(self, callback, **kwargs):
|
|
252
|
+
key = (id(callback.__self__), callback.__func__)
|
|
253
|
+
self._callbacks[key] = (weakref.WeakMethod(callback), kwargs)
|
|
254
|
+
|
|
255
|
+
def _mark_changed(self):
|
|
256
|
+
for name in list(self._textures):
|
|
257
|
+
if name not in self:
|
|
258
|
+
del self._textures[name]
|
|
259
|
+
else:
|
|
260
|
+
self._textures[name].colors = self[name]
|
|
261
|
+
for key, (reference, kwargs) in list(self._callbacks.items()):
|
|
262
|
+
callback = reference()
|
|
263
|
+
if callback is None:
|
|
264
|
+
del self._callbacks[key]
|
|
265
|
+
else:
|
|
266
|
+
callback(**kwargs)
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""Search model functions and supporting definitions."""
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def _fuzzy_substring_distance(q, k):
|
|
6
|
+
"""Min edit distance between `q` and any substring of `k` (the k-differences
|
|
7
|
+
DP: row 0 is all zeros so the match may start anywhere in k). Damerau/OSA, so
|
|
8
|
+
an adjacent transposition — the most common typo — costs 1, not 2. Both
|
|
9
|
+
lowercase."""
|
|
10
|
+
m, n = len(q), len(k)
|
|
11
|
+
if m == 0:
|
|
12
|
+
return 0
|
|
13
|
+
prev2 = None
|
|
14
|
+
prev = [0] * (n + 1)
|
|
15
|
+
for i in range(1, m + 1):
|
|
16
|
+
qi = q[i - 1]
|
|
17
|
+
cur = [i] + [0] * n
|
|
18
|
+
for j in range(1, n + 1):
|
|
19
|
+
cost = 0 if qi == k[j - 1] else 1
|
|
20
|
+
v = min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + cost)
|
|
21
|
+
if (prev2 is not None and j > 1
|
|
22
|
+
and qi == k[j - 2] and q[i - 2] == k[j - 1]):
|
|
23
|
+
v = min(v, prev2[j - 2] + 1)
|
|
24
|
+
cur[j] = v
|
|
25
|
+
prev2 = prev
|
|
26
|
+
prev = cur
|
|
27
|
+
return min(prev)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _fuzzy_key_match(q, k):
|
|
31
|
+
"""Does query `q` match candidate `k` (both lowercase), tolerating a few
|
|
32
|
+
typos? Exact substring first (fast, also covers short queries); for longer
|
|
33
|
+
queries fall back to approximate substring matching with a small edit budget
|
|
34
|
+
that scales with length (~1 typo per 4 chars). The single predicate the key
|
|
35
|
+
match's count, current index and highlight all share, so they stay in sync."""
|
|
36
|
+
if not q:
|
|
37
|
+
return False
|
|
38
|
+
if q in k:
|
|
39
|
+
return True
|
|
40
|
+
if len(q) < 4:
|
|
41
|
+
return False
|
|
42
|
+
return _fuzzy_substring_distance(q, k) <= max(1, len(q) // 4)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _split_words(s):
|
|
46
|
+
"""Lowercased word tuple of an identifier / label: separators (`_`, `.`,
|
|
47
|
+
`/`, ` `, `-`, ...) and camelCase boundaries both split; digits are their
|
|
48
|
+
own words. ("draw_any" -> ("draw", "any"), "GLState" -> ("gl", "state"),
|
|
49
|
+
"new_core_view.py" -> ("new", "core", "view", "py"))."""
|
|
50
|
+
from meltygui.core.rendering.render_dispatch import _WORD_RE
|
|
51
|
+
|
|
52
|
+
return tuple(w.lower() for w in _WORD_RE.findall(s))
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _edit_distance(a, b, cap):
|
|
56
|
+
"""Damerau/OSA edit distance, capped (returns cap + 1 once exceeded)."""
|
|
57
|
+
m, n = len(a), len(b)
|
|
58
|
+
if abs(m - n) > cap:
|
|
59
|
+
return cap + 1
|
|
60
|
+
prev2 = None
|
|
61
|
+
prev = list(range(n + 1))
|
|
62
|
+
for i in range(1, m + 1):
|
|
63
|
+
ai = a[i - 1]
|
|
64
|
+
cur = [i] + [0] * n
|
|
65
|
+
row_min = i
|
|
66
|
+
for j in range(1, n + 1):
|
|
67
|
+
cost = 0 if ai == b[j - 1] else 1
|
|
68
|
+
v = min(prev[j] + 1, cur[j - 1] + 1, prev[j - 1] + cost)
|
|
69
|
+
if (prev2 is not None and j > 1
|
|
70
|
+
and ai == b[j - 2] and a[i - 2] == b[j - 1]):
|
|
71
|
+
v = min(v, prev2[j - 2] + 1)
|
|
72
|
+
cur[j] = v
|
|
73
|
+
if v < row_min:
|
|
74
|
+
row_min = v
|
|
75
|
+
if row_min > cap:
|
|
76
|
+
return cap + 1
|
|
77
|
+
prev2 = prev
|
|
78
|
+
prev = cur
|
|
79
|
+
return prev[n]
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _word_edits(w, tw, budget):
|
|
83
|
+
"""Cost of query word `w` claiming target word `tw`: 0 for an exact
|
|
84
|
+
prefix or a >=2-char exact substring; the edit distance of a fuzzy
|
|
85
|
+
PREFIX (same first char, >=3 chars, ~1 typo per 3 chars) when within
|
|
86
|
+
`budget`; None when it can't claim it."""
|
|
87
|
+
if tw.startswith(w):
|
|
88
|
+
return 0
|
|
89
|
+
if len(w) >= 2 and w in tw:
|
|
90
|
+
return 0
|
|
91
|
+
if budget <= 0 or len(w) < 3 or w[0] != tw[0]:
|
|
92
|
+
return None
|
|
93
|
+
tol = min(budget, 1 + (len(w) - 3) // 3)
|
|
94
|
+
best = None
|
|
95
|
+
for L in (len(w) - 1, len(w), len(w) + 1):
|
|
96
|
+
if 0 < L <= len(tw):
|
|
97
|
+
d = _edit_distance(w, tw[:L], tol)
|
|
98
|
+
if d <= tol and (best is None or d < best):
|
|
99
|
+
best = d
|
|
100
|
+
return best
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _assign_words(qws, twords, budget):
|
|
104
|
+
"""Min total cost of every query word claiming a distinct target word
|
|
105
|
+
(any order), or None. Tiny backtracking -- a handful of words a side."""
|
|
106
|
+
n = len(twords)
|
|
107
|
+
used = [False] * n
|
|
108
|
+
best = [None]
|
|
109
|
+
|
|
110
|
+
def rec(i, cost):
|
|
111
|
+
if best[0] is not None and cost >= best[0]:
|
|
112
|
+
return
|
|
113
|
+
if i == len(qws):
|
|
114
|
+
best[0] = cost
|
|
115
|
+
return
|
|
116
|
+
w = qws[i]
|
|
117
|
+
for j in range(n):
|
|
118
|
+
if used[j]:
|
|
119
|
+
continue
|
|
120
|
+
e = _word_edits(w, twords[j], budget - cost)
|
|
121
|
+
if e is None:
|
|
122
|
+
continue
|
|
123
|
+
used[j] = True
|
|
124
|
+
rec(i + 1, cost + e)
|
|
125
|
+
used[j] = False
|
|
126
|
+
|
|
127
|
+
rec(0, 0)
|
|
128
|
+
return best[0]
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
def _segment_match(q, twords, budget):
|
|
132
|
+
"""Min cost of segmenting separator-less `q` into consecutive pieces
|
|
133
|
+
that each claim a distinct target word (rules of _word_edits), or None.
|
|
134
|
+
("anydraw" -> any + draw; "drawamy" -> draw + amy~any.)"""
|
|
135
|
+
n = len(twords)
|
|
136
|
+
used = [False] * n
|
|
137
|
+
best = [None]
|
|
138
|
+
L = len(q)
|
|
139
|
+
|
|
140
|
+
def rec(pos, cost):
|
|
141
|
+
if best[0] is not None and cost >= best[0]:
|
|
142
|
+
return
|
|
143
|
+
if pos == L:
|
|
144
|
+
best[0] = cost
|
|
145
|
+
return
|
|
146
|
+
for j in range(n):
|
|
147
|
+
if used[j]:
|
|
148
|
+
continue
|
|
149
|
+
tw = twords[j]
|
|
150
|
+
used[j] = True
|
|
151
|
+
for k in range(pos + 1, L + 1):
|
|
152
|
+
e = _word_edits(q[pos:k], tw, budget - cost)
|
|
153
|
+
if e is not None:
|
|
154
|
+
rec(k, cost + e)
|
|
155
|
+
used[j] = False
|
|
156
|
+
|
|
157
|
+
rec(0, 0)
|
|
158
|
+
return best[0]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _word_match(q, qws, twords, budget):
|
|
162
|
+
"""Total edit cost of query `q` (lowercased; `qws` its words) against a
|
|
163
|
+
target's words, or None. Word-form queries assign words; a single-word
|
|
164
|
+
query tries a straight word claim, then segmentation."""
|
|
165
|
+
if not twords:
|
|
166
|
+
return None
|
|
167
|
+
if len(qws) > 1:
|
|
168
|
+
return _assign_words(qws, twords, budget)
|
|
169
|
+
if len(qws) == 1:
|
|
170
|
+
e = _assign_words(qws, twords, budget)
|
|
171
|
+
if e is not None:
|
|
172
|
+
return e
|
|
173
|
+
return _segment_match(q, twords, budget)
|