flock-core 0.5.0b28__py3-none-any.whl → 0.5.56b0__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.
Potentially problematic release.
This version of flock-core might be problematic. Click here for more details.
- flock/__init__.py +12 -217
- flock/agent.py +678 -0
- flock/api/themes.py +71 -0
- flock/artifacts.py +79 -0
- flock/cli.py +75 -0
- flock/components.py +173 -0
- flock/dashboard/__init__.py +28 -0
- flock/dashboard/collector.py +283 -0
- flock/dashboard/events.py +182 -0
- flock/dashboard/launcher.py +230 -0
- flock/dashboard/service.py +537 -0
- flock/dashboard/websocket.py +235 -0
- flock/engines/__init__.py +6 -0
- flock/engines/dspy_engine.py +856 -0
- flock/examples.py +128 -0
- flock/{core/util → helper}/cli_helper.py +4 -3
- flock/{core/logging → logging}/__init__.py +2 -3
- flock/{core/logging → logging}/formatters/enum_builder.py +3 -4
- flock/{core/logging → logging}/formatters/theme_builder.py +19 -44
- flock/{core/logging → logging}/formatters/themed_formatter.py +69 -115
- flock/{core/logging → logging}/logging.py +77 -61
- flock/{core/logging → logging}/telemetry.py +20 -26
- flock/{core/logging → logging}/telemetry_exporter/base_exporter.py +2 -2
- flock/{core/logging → logging}/telemetry_exporter/file_exporter.py +6 -9
- flock/{core/logging → logging}/telemetry_exporter/sqlite_exporter.py +2 -3
- flock/{core/logging → logging}/trace_and_logged.py +20 -24
- flock/mcp/__init__.py +91 -0
- flock/{core/mcp/mcp_client.py → mcp/client.py} +103 -154
- flock/{core/mcp/mcp_config.py → mcp/config.py} +62 -117
- flock/mcp/manager.py +255 -0
- flock/mcp/servers/sse/__init__.py +1 -1
- flock/mcp/servers/sse/flock_sse_server.py +11 -53
- flock/mcp/servers/stdio/__init__.py +1 -1
- flock/mcp/servers/stdio/flock_stdio_server.py +8 -48
- flock/mcp/servers/streamable_http/flock_streamable_http_server.py +17 -62
- flock/mcp/servers/websockets/flock_websocket_server.py +7 -40
- flock/{core/mcp/flock_mcp_tool.py → mcp/tool.py} +16 -26
- flock/mcp/types/__init__.py +42 -0
- flock/{core/mcp → mcp}/types/callbacks.py +9 -15
- flock/{core/mcp → mcp}/types/factories.py +7 -6
- flock/{core/mcp → mcp}/types/handlers.py +13 -18
- flock/{core/mcp → mcp}/types/types.py +70 -74
- flock/{core/mcp → mcp}/util/helpers.py +1 -1
- flock/orchestrator.py +645 -0
- flock/registry.py +148 -0
- flock/runtime.py +262 -0
- flock/service.py +140 -0
- flock/store.py +69 -0
- flock/subscription.py +111 -0
- flock/themes/andromeda.toml +1 -1
- flock/themes/apple-system-colors.toml +1 -1
- flock/themes/arcoiris.toml +1 -1
- flock/themes/atomonelight.toml +1 -1
- flock/themes/ayu copy.toml +1 -1
- flock/themes/ayu-light.toml +1 -1
- flock/themes/belafonte-day.toml +1 -1
- flock/themes/belafonte-night.toml +1 -1
- flock/themes/blulocodark.toml +1 -1
- flock/themes/breeze.toml +1 -1
- flock/themes/broadcast.toml +1 -1
- flock/themes/brogrammer.toml +1 -1
- flock/themes/builtin-dark.toml +1 -1
- flock/themes/builtin-pastel-dark.toml +1 -1
- flock/themes/catppuccin-latte.toml +1 -1
- flock/themes/catppuccin-macchiato.toml +1 -1
- flock/themes/catppuccin-mocha.toml +1 -1
- flock/themes/cga.toml +1 -1
- flock/themes/chalk.toml +1 -1
- flock/themes/ciapre.toml +1 -1
- flock/themes/coffee-theme.toml +1 -1
- flock/themes/cyberpunkscarletprotocol.toml +1 -1
- flock/themes/dark+.toml +1 -1
- flock/themes/darkermatrix.toml +1 -1
- flock/themes/darkside.toml +1 -1
- flock/themes/desert.toml +1 -1
- flock/themes/django.toml +1 -1
- flock/themes/djangosmooth.toml +1 -1
- flock/themes/doomone.toml +1 -1
- flock/themes/dotgov.toml +1 -1
- flock/themes/dracula+.toml +1 -1
- flock/themes/duckbones.toml +1 -1
- flock/themes/encom.toml +1 -1
- flock/themes/espresso.toml +1 -1
- flock/themes/everblush.toml +1 -1
- flock/themes/fairyfloss.toml +1 -1
- flock/themes/fideloper.toml +1 -1
- flock/themes/fishtank.toml +1 -1
- flock/themes/flexoki-light.toml +1 -1
- flock/themes/floraverse.toml +1 -1
- flock/themes/framer.toml +1 -1
- flock/themes/galizur.toml +1 -1
- flock/themes/github.toml +1 -1
- flock/themes/grass.toml +1 -1
- flock/themes/grey-green.toml +1 -1
- flock/themes/gruvboxlight.toml +1 -1
- flock/themes/guezwhoz.toml +1 -1
- flock/themes/harper.toml +1 -1
- flock/themes/hax0r-blue.toml +1 -1
- flock/themes/hopscotch.256.toml +1 -1
- flock/themes/ic-green-ppl.toml +1 -1
- flock/themes/iceberg-dark.toml +1 -1
- flock/themes/japanesque.toml +1 -1
- flock/themes/jubi.toml +1 -1
- flock/themes/kibble.toml +1 -1
- flock/themes/kolorit.toml +1 -1
- flock/themes/kurokula.toml +1 -1
- flock/themes/materialdesigncolors.toml +1 -1
- flock/themes/matrix.toml +1 -1
- flock/themes/mellifluous.toml +1 -1
- flock/themes/midnight-in-mojave.toml +1 -1
- flock/themes/monokai-remastered.toml +1 -1
- flock/themes/monokai-soda.toml +1 -1
- flock/themes/neon.toml +1 -1
- flock/themes/neopolitan.toml +1 -1
- flock/themes/nord-light.toml +1 -1
- flock/themes/ocean.toml +1 -1
- flock/themes/onehalfdark.toml +1 -1
- flock/themes/onehalflight.toml +1 -1
- flock/themes/palenighthc.toml +1 -1
- flock/themes/paulmillr.toml +1 -1
- flock/themes/pencildark.toml +1 -1
- flock/themes/pnevma.toml +1 -1
- flock/themes/purple-rain.toml +1 -1
- flock/themes/purplepeter.toml +1 -1
- flock/themes/raycast-dark.toml +1 -1
- flock/themes/red-sands.toml +1 -1
- flock/themes/relaxed.toml +1 -1
- flock/themes/retro.toml +1 -1
- flock/themes/rose-pine.toml +1 -1
- flock/themes/royal.toml +1 -1
- flock/themes/ryuuko.toml +1 -1
- flock/themes/sakura.toml +1 -1
- flock/themes/scarlet-protocol.toml +1 -1
- flock/themes/seoulbones-dark.toml +1 -1
- flock/themes/shades-of-purple.toml +1 -1
- flock/themes/smyck.toml +1 -1
- flock/themes/softserver.toml +1 -1
- flock/themes/solarized-darcula.toml +1 -1
- flock/themes/square.toml +1 -1
- flock/themes/sugarplum.toml +1 -1
- flock/themes/thayer-bright.toml +1 -1
- flock/themes/tokyonight.toml +1 -1
- flock/themes/tomorrow.toml +1 -1
- flock/themes/ubuntu.toml +1 -1
- flock/themes/ultradark.toml +1 -1
- flock/themes/ultraviolent.toml +1 -1
- flock/themes/unikitty.toml +1 -1
- flock/themes/urple.toml +1 -1
- flock/themes/vesper.toml +1 -1
- flock/themes/vimbones.toml +1 -1
- flock/themes/wildcherry.toml +1 -1
- flock/themes/wilmersdorf.toml +1 -1
- flock/themes/wryan.toml +1 -1
- flock/themes/xcodedarkhc.toml +1 -1
- flock/themes/xcodelight.toml +1 -1
- flock/themes/zenbones-light.toml +1 -1
- flock/themes/zenwritten-dark.toml +1 -1
- flock/utilities.py +301 -0
- flock/{components/utility → utility}/output_utility_component.py +68 -53
- flock/visibility.py +107 -0
- flock_core-0.5.56b0.dist-info/METADATA +747 -0
- flock_core-0.5.56b0.dist-info/RECORD +398 -0
- flock_core-0.5.56b0.dist-info/entry_points.txt +2 -0
- {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/licenses/LICENSE +1 -1
- flock/adapter/__init__.py +0 -14
- flock/adapter/azure_adapter.py +0 -68
- flock/adapter/chroma_adapter.py +0 -73
- flock/adapter/faiss_adapter.py +0 -97
- flock/adapter/pinecone_adapter.py +0 -51
- flock/adapter/vector_base.py +0 -47
- flock/cli/assets/release_notes.md +0 -140
- flock/cli/config.py +0 -8
- flock/cli/constants.py +0 -36
- flock/cli/create_agent.py +0 -1
- flock/cli/create_flock.py +0 -280
- flock/cli/execute_flock.py +0 -620
- flock/cli/load_agent.py +0 -1
- flock/cli/load_examples.py +0 -1
- flock/cli/load_flock.py +0 -192
- flock/cli/load_release_notes.py +0 -20
- flock/cli/loaded_flock_cli.py +0 -254
- flock/cli/manage_agents.py +0 -459
- flock/cli/registry_management.py +0 -889
- flock/cli/runner.py +0 -41
- flock/cli/settings.py +0 -857
- flock/cli/utils.py +0 -135
- flock/cli/view_results.py +0 -29
- flock/cli/yaml_editor.py +0 -396
- flock/components/__init__.py +0 -30
- flock/components/evaluation/__init__.py +0 -9
- flock/components/evaluation/declarative_evaluation_component.py +0 -606
- flock/components/routing/__init__.py +0 -15
- flock/components/routing/conditional_routing_component.py +0 -494
- flock/components/routing/default_routing_component.py +0 -103
- flock/components/routing/llm_routing_component.py +0 -206
- flock/components/utility/__init__.py +0 -22
- flock/components/utility/example_utility_component.py +0 -250
- flock/components/utility/feedback_utility_component.py +0 -206
- flock/components/utility/memory_utility_component.py +0 -550
- flock/components/utility/metrics_utility_component.py +0 -700
- flock/config.py +0 -61
- flock/core/__init__.py +0 -110
- flock/core/agent/__init__.py +0 -16
- flock/core/agent/default_agent.py +0 -216
- flock/core/agent/flock_agent_components.py +0 -104
- flock/core/agent/flock_agent_execution.py +0 -101
- flock/core/agent/flock_agent_integration.py +0 -260
- flock/core/agent/flock_agent_lifecycle.py +0 -186
- flock/core/agent/flock_agent_serialization.py +0 -381
- flock/core/api/__init__.py +0 -10
- flock/core/api/custom_endpoint.py +0 -45
- flock/core/api/endpoints.py +0 -254
- flock/core/api/main.py +0 -162
- flock/core/api/models.py +0 -97
- flock/core/api/run_store.py +0 -224
- flock/core/api/runner.py +0 -44
- flock/core/api/service.py +0 -214
- flock/core/component/__init__.py +0 -15
- flock/core/component/agent_component_base.py +0 -309
- flock/core/component/evaluation_component.py +0 -62
- flock/core/component/routing_component.py +0 -74
- flock/core/component/utility_component.py +0 -69
- flock/core/config/flock_agent_config.py +0 -58
- flock/core/config/scheduled_agent_config.py +0 -40
- flock/core/context/context.py +0 -213
- flock/core/context/context_manager.py +0 -37
- flock/core/context/context_vars.py +0 -10
- flock/core/evaluation/utils.py +0 -396
- flock/core/execution/batch_executor.py +0 -369
- flock/core/execution/evaluation_executor.py +0 -438
- flock/core/execution/local_executor.py +0 -31
- flock/core/execution/opik_executor.py +0 -103
- flock/core/execution/temporal_executor.py +0 -164
- flock/core/flock.py +0 -634
- flock/core/flock_agent.py +0 -336
- flock/core/flock_factory.py +0 -613
- flock/core/flock_scheduler.py +0 -166
- flock/core/flock_server_manager.py +0 -136
- flock/core/interpreter/python_interpreter.py +0 -689
- flock/core/mcp/__init__.py +0 -1
- flock/core/mcp/flock_mcp_server.py +0 -680
- flock/core/mcp/mcp_client_manager.py +0 -201
- flock/core/mcp/types/__init__.py +0 -1
- flock/core/mixin/dspy_integration.py +0 -403
- flock/core/mixin/prompt_parser.py +0 -125
- flock/core/orchestration/__init__.py +0 -15
- flock/core/orchestration/flock_batch_processor.py +0 -94
- flock/core/orchestration/flock_evaluator.py +0 -113
- flock/core/orchestration/flock_execution.py +0 -295
- flock/core/orchestration/flock_initialization.py +0 -149
- flock/core/orchestration/flock_server_manager.py +0 -67
- flock/core/orchestration/flock_web_server.py +0 -117
- flock/core/registry/__init__.py +0 -45
- flock/core/registry/agent_registry.py +0 -69
- flock/core/registry/callable_registry.py +0 -139
- flock/core/registry/component_discovery.py +0 -142
- flock/core/registry/component_registry.py +0 -64
- flock/core/registry/config_mapping.py +0 -64
- flock/core/registry/decorators.py +0 -137
- flock/core/registry/registry_hub.py +0 -205
- flock/core/registry/server_registry.py +0 -57
- flock/core/registry/type_registry.py +0 -86
- flock/core/serialization/__init__.py +0 -13
- flock/core/serialization/callable_registry.py +0 -52
- flock/core/serialization/flock_serializer.py +0 -832
- flock/core/serialization/json_encoder.py +0 -41
- flock/core/serialization/secure_serializer.py +0 -175
- flock/core/serialization/serializable.py +0 -342
- flock/core/serialization/serialization_utils.py +0 -412
- flock/core/util/file_path_utils.py +0 -223
- flock/core/util/hydrator.py +0 -309
- flock/core/util/input_resolver.py +0 -164
- flock/core/util/loader.py +0 -59
- flock/core/util/splitter.py +0 -219
- flock/di.py +0 -27
- flock/platform/docker_tools.py +0 -49
- flock/platform/jaeger_install.py +0 -86
- flock/webapp/__init__.py +0 -1
- flock/webapp/app/__init__.py +0 -0
- flock/webapp/app/api/__init__.py +0 -0
- flock/webapp/app/api/agent_management.py +0 -241
- flock/webapp/app/api/execution.py +0 -709
- flock/webapp/app/api/flock_management.py +0 -129
- flock/webapp/app/api/registry_viewer.py +0 -30
- flock/webapp/app/chat.py +0 -665
- flock/webapp/app/config.py +0 -104
- flock/webapp/app/dependencies.py +0 -117
- flock/webapp/app/main.py +0 -1070
- flock/webapp/app/middleware.py +0 -113
- flock/webapp/app/models_ui.py +0 -7
- flock/webapp/app/services/__init__.py +0 -0
- flock/webapp/app/services/feedback_file_service.py +0 -363
- flock/webapp/app/services/flock_service.py +0 -337
- flock/webapp/app/services/sharing_models.py +0 -81
- flock/webapp/app/services/sharing_store.py +0 -762
- flock/webapp/app/templates/theme_mapper.html +0 -326
- flock/webapp/app/theme_mapper.py +0 -812
- flock/webapp/app/utils.py +0 -85
- flock/webapp/run.py +0 -215
- flock/webapp/static/css/chat.css +0 -301
- flock/webapp/static/css/components.css +0 -167
- flock/webapp/static/css/header.css +0 -39
- flock/webapp/static/css/layout.css +0 -46
- flock/webapp/static/css/sidebar.css +0 -127
- flock/webapp/static/css/two-pane.css +0 -48
- flock/webapp/templates/base.html +0 -200
- flock/webapp/templates/chat.html +0 -152
- flock/webapp/templates/chat_settings.html +0 -19
- flock/webapp/templates/flock_editor.html +0 -16
- flock/webapp/templates/index.html +0 -12
- flock/webapp/templates/partials/_agent_detail_form.html +0 -93
- flock/webapp/templates/partials/_agent_list.html +0 -18
- flock/webapp/templates/partials/_agent_manager_view.html +0 -51
- flock/webapp/templates/partials/_agent_tools_checklist.html +0 -14
- flock/webapp/templates/partials/_chat_container.html +0 -15
- flock/webapp/templates/partials/_chat_messages.html +0 -57
- flock/webapp/templates/partials/_chat_settings_form.html +0 -85
- flock/webapp/templates/partials/_create_flock_form.html +0 -50
- flock/webapp/templates/partials/_dashboard_flock_detail.html +0 -17
- flock/webapp/templates/partials/_dashboard_flock_file_list.html +0 -16
- flock/webapp/templates/partials/_dashboard_flock_properties_preview.html +0 -28
- flock/webapp/templates/partials/_dashboard_upload_flock_form.html +0 -16
- flock/webapp/templates/partials/_dynamic_input_form_content.html +0 -22
- flock/webapp/templates/partials/_env_vars_table.html +0 -23
- flock/webapp/templates/partials/_execution_form.html +0 -118
- flock/webapp/templates/partials/_execution_view_container.html +0 -28
- flock/webapp/templates/partials/_flock_file_list.html +0 -23
- flock/webapp/templates/partials/_flock_properties_form.html +0 -52
- flock/webapp/templates/partials/_flock_upload_form.html +0 -16
- flock/webapp/templates/partials/_header_flock_status.html +0 -5
- flock/webapp/templates/partials/_load_manager_view.html +0 -49
- flock/webapp/templates/partials/_registry_table.html +0 -25
- flock/webapp/templates/partials/_registry_viewer_content.html +0 -70
- flock/webapp/templates/partials/_results_display.html +0 -78
- flock/webapp/templates/partials/_settings_env_content.html +0 -9
- flock/webapp/templates/partials/_settings_theme_content.html +0 -14
- flock/webapp/templates/partials/_settings_view.html +0 -36
- flock/webapp/templates/partials/_share_chat_link_snippet.html +0 -11
- flock/webapp/templates/partials/_share_link_snippet.html +0 -35
- flock/webapp/templates/partials/_sidebar.html +0 -74
- flock/webapp/templates/partials/_streaming_results_container.html +0 -195
- flock/webapp/templates/partials/_structured_data_view.html +0 -40
- flock/webapp/templates/partials/_theme_preview.html +0 -36
- flock/webapp/templates/registry_viewer.html +0 -84
- flock/webapp/templates/shared_run_page.html +0 -140
- flock/workflow/__init__.py +0 -0
- flock/workflow/activities.py +0 -196
- flock/workflow/agent_activities.py +0 -24
- flock/workflow/agent_execution_activity.py +0 -202
- flock/workflow/flock_workflow.py +0 -214
- flock/workflow/temporal_config.py +0 -96
- flock/workflow/temporal_setup.py +0 -68
- flock_core-0.5.0b28.dist-info/METADATA +0 -274
- flock_core-0.5.0b28.dist-info/RECORD +0 -561
- flock_core-0.5.0b28.dist-info/entry_points.txt +0 -2
- /flock/{core/logging → logging}/formatters/themes.py +0 -0
- /flock/{core/logging → logging}/span_middleware/baggage_span_processor.py +0 -0
- /flock/{core/mcp → mcp}/util/__init__.py +0 -0
- {flock_core-0.5.0b28.dist-info → flock_core-0.5.56b0.dist-info}/WHEEL +0 -0
flock/webapp/app/theme_mapper.py
DELETED
|
@@ -1,812 +0,0 @@
|
|
|
1
|
-
from __future__ import annotations
|
|
2
|
-
|
|
3
|
-
import re
|
|
4
|
-
|
|
5
|
-
###########################
|
|
6
|
-
# Low–level colour utils #
|
|
7
|
-
###########################
|
|
8
|
-
|
|
9
|
-
_HEX_RE = re.compile(r"^#?([0-9a-f]{6})$", re.I)
|
|
10
|
-
|
|
11
|
-
def _hex_to_rgb(hex_str: str) -> tuple[int, int, int] | None:
|
|
12
|
-
if hex_str is None:
|
|
13
|
-
return None
|
|
14
|
-
# Clean the string - remove any leading/trailing whitespace
|
|
15
|
-
hex_str = hex_str.strip()
|
|
16
|
-
m = _HEX_RE.match(hex_str)
|
|
17
|
-
if not m:
|
|
18
|
-
return None
|
|
19
|
-
h = m.group(1)
|
|
20
|
-
return tuple(int(h[i : i + 2], 16) for i in (0, 2, 4)) # type: ignore[misc]
|
|
21
|
-
|
|
22
|
-
def _rgb_to_hex(rgb: tuple[int, int, int]) -> str:
|
|
23
|
-
return f"#{rgb[0]:02x}{rgb[1]:02x}{rgb[2]:02x}"
|
|
24
|
-
|
|
25
|
-
def _rel_lum(rgb: tuple[int, int, int]) -> float:
|
|
26
|
-
def channel(c: int) -> float:
|
|
27
|
-
c = c / 255.0
|
|
28
|
-
return c / 12.92 if c <= 0.03928 else ((c + 0.055) / 1.055) ** 2.4
|
|
29
|
-
r, g, b = map(channel, rgb)
|
|
30
|
-
return 0.2126 * r + 0.7152 * g + 0.0722 * b
|
|
31
|
-
|
|
32
|
-
def _contrast(a: tuple[int, int, int], b: tuple[int, int, int]) -> float:
|
|
33
|
-
if a is None or b is None:
|
|
34
|
-
return 1.0 # Default value if invalid colors provided
|
|
35
|
-
try:
|
|
36
|
-
la, lb = _rel_lum(a), _rel_lum(b)
|
|
37
|
-
darker, lighter = sorted((la, lb))
|
|
38
|
-
return (lighter + 0.05) / (darker + 0.05)
|
|
39
|
-
except (TypeError, ValueError, ZeroDivisionError) as e:
|
|
40
|
-
print(f"Error calculating contrast: {e}")
|
|
41
|
-
return 1.0 # Default value on error
|
|
42
|
-
|
|
43
|
-
def _mix(rgb: tuple[int, int, int], other: tuple[int, int, int], pct: float) -> tuple[int, int, int]:
|
|
44
|
-
if rgb is None or other is None:
|
|
45
|
-
# Return a fallback color if either input is None
|
|
46
|
-
return (128, 128, 128) # Default to gray
|
|
47
|
-
try:
|
|
48
|
-
return tuple(int(rgb[i] * (1 - pct) + other[i] * pct) for i in range(3))
|
|
49
|
-
except (TypeError, IndexError) as e:
|
|
50
|
-
print(f"Error mixing colors: {e}")
|
|
51
|
-
return (128, 128, 128) # Default to gray
|
|
52
|
-
|
|
53
|
-
def _rgba(rgb: tuple[int, int, int], alpha: float) -> str:
|
|
54
|
-
if rgb is None:
|
|
55
|
-
rgb = (128, 128, 128) # Default gray if rgb is None
|
|
56
|
-
return f"rgba({rgb[0]}, {rgb[1]}, {rgb[2]}, {alpha})"
|
|
57
|
-
|
|
58
|
-
########################################
|
|
59
|
-
# Theme → Pico-CSS conversion engine #
|
|
60
|
-
########################################
|
|
61
|
-
|
|
62
|
-
AccentOrder = ["blue", "cyan", "magenta", "green", "yellow", "red"]
|
|
63
|
-
|
|
64
|
-
def _theme_color(theme: dict, group: str, name: str) -> tuple[int, int, int] | None:
|
|
65
|
-
try:
|
|
66
|
-
raw = (
|
|
67
|
-
theme.get("colors", {})
|
|
68
|
-
.get(group, {},)
|
|
69
|
-
.get(name)
|
|
70
|
-
)
|
|
71
|
-
return _hex_to_rgb(raw) if raw else None
|
|
72
|
-
except (TypeError, AttributeError) as e:
|
|
73
|
-
print(f"Error extracting color {group}.{name}: {e}")
|
|
74
|
-
return None
|
|
75
|
-
|
|
76
|
-
def _order_colors(theme: dict, color_to_compare_to: tuple[int, int, int]) -> list[tuple[int, int, int]]:
|
|
77
|
-
colors = []
|
|
78
|
-
|
|
79
|
-
# Extract all colors from the theme recursively
|
|
80
|
-
def extract_colors(data):
|
|
81
|
-
if isinstance(data, dict):
|
|
82
|
-
for key, value in data.items():
|
|
83
|
-
if isinstance(value, str) and value.startswith('#'):
|
|
84
|
-
rgb = _hex_to_rgb(value)
|
|
85
|
-
if rgb:
|
|
86
|
-
colors.append(rgb)
|
|
87
|
-
else:
|
|
88
|
-
extract_colors(value)
|
|
89
|
-
elif isinstance(data, list):
|
|
90
|
-
for item in data:
|
|
91
|
-
extract_colors(item)
|
|
92
|
-
|
|
93
|
-
# Start extraction from the root of the theme
|
|
94
|
-
extract_colors(theme)
|
|
95
|
-
|
|
96
|
-
# Sort colors by contrast with color_to_compare_to, highest contrast first
|
|
97
|
-
colors.sort(key=lambda c: _contrast(c, color_to_compare_to), reverse=True)
|
|
98
|
-
|
|
99
|
-
return colors
|
|
100
|
-
|
|
101
|
-
def _best_contrast(
|
|
102
|
-
candidates: list[tuple[int, int, int]],
|
|
103
|
-
bg: tuple[int, int, int],
|
|
104
|
-
min_ratio: float = 4.5,
|
|
105
|
-
) -> tuple[int, int, int]:
|
|
106
|
-
if not candidates:
|
|
107
|
-
# Instead of raising an exception, return a fallback color
|
|
108
|
-
# Use white or black depending on the background
|
|
109
|
-
return (255, 255, 255) if sum(bg) < 384 else (0, 0, 0)
|
|
110
|
-
best = max(candidates, key=lambda c: _contrast(c, bg))
|
|
111
|
-
if _contrast(best, bg) >= min_ratio:
|
|
112
|
-
return best
|
|
113
|
-
return best # return highest available even if below threshold
|
|
114
|
-
|
|
115
|
-
def alacritty_to_pico(theme: dict) -> dict[str, str]:
|
|
116
|
-
css: dict[str, str] = {}
|
|
117
|
-
|
|
118
|
-
# 1 Main surface colours
|
|
119
|
-
fixed_background = _theme_color(theme, "primary", "background") or (0, 0, 0)
|
|
120
|
-
fixed_foreground = _theme_color(theme, "primary", "foreground") or (255, 255, 255)
|
|
121
|
-
|
|
122
|
-
fixed_background2 = _theme_color(theme, "selection", "background") or (0, 0, 0)
|
|
123
|
-
# Try theme's own foreground first, then fall back to palette extremes
|
|
124
|
-
# fg_candidates = [
|
|
125
|
-
# _theme_color(theme, "primary", "foreground"),
|
|
126
|
-
# _theme_color(theme, "normal", "white"),
|
|
127
|
-
# _theme_color(theme, "bright", "white"),
|
|
128
|
-
# _theme_color(theme, "normal", "black"),
|
|
129
|
-
# _theme_color(theme, "bright", "black"),
|
|
130
|
-
# ]
|
|
131
|
-
# fg = _best_contrast([c for c in fg_candidates if c], bg)
|
|
132
|
-
|
|
133
|
-
css["--pico-background-color"] = _rgb_to_hex(fixed_background)
|
|
134
|
-
css["--pico-color"] = _rgb_to_hex(fixed_foreground)
|
|
135
|
-
|
|
136
|
-
# 2 Pick an accent colour that stands out
|
|
137
|
-
accent = None
|
|
138
|
-
for shade in ("normal", "bright"):
|
|
139
|
-
for name in AccentOrder:
|
|
140
|
-
c = _theme_color(theme, shade, name)
|
|
141
|
-
if c and _contrast(c, fixed_background) >= 3:
|
|
142
|
-
accent = c
|
|
143
|
-
break
|
|
144
|
-
if accent:
|
|
145
|
-
break
|
|
146
|
-
accent = accent or fixed_foreground # worst case
|
|
147
|
-
|
|
148
|
-
accent_hover = _mix(accent, (255, 255, 255), 0.15) # lighten 15 %
|
|
149
|
-
accent_active = _mix(accent, (0, 0, 0), 0.15) # darken 15 %
|
|
150
|
-
accent_focus = _rgba(accent, 0.25) # 25 % overlay
|
|
151
|
-
accent_inv = _best_contrast([fixed_foreground, fixed_background, (255, 255, 255), (0, 0, 0)], accent, 4.5)
|
|
152
|
-
|
|
153
|
-
css["--pico-primary"] = _rgb_to_hex(accent)
|
|
154
|
-
css["--pico-primary-hover"] = _rgb_to_hex(accent_hover)
|
|
155
|
-
css["--pico-primary-active"] = _rgb_to_hex(accent_active)
|
|
156
|
-
css["--pico-primary-focus"] = accent_focus
|
|
157
|
-
css["--pico-primary-inverse"] = _rgb_to_hex(accent_inv)
|
|
158
|
-
|
|
159
|
-
# 3 Secondary accent = next colour in queue with sufficient contrast
|
|
160
|
-
sec = None
|
|
161
|
-
for name in AccentOrder:
|
|
162
|
-
if sec is None and _hex_to_rgb(css["--pico-primary"]) != _theme_color(theme, "normal", name):
|
|
163
|
-
for shade in ("normal", "bright"):
|
|
164
|
-
c = _theme_color(theme, shade, name)
|
|
165
|
-
if c and _contrast(c, fixed_background) >= 3:
|
|
166
|
-
sec = c
|
|
167
|
-
break
|
|
168
|
-
sec = sec or _mix(accent, (255, 255, 255), 0.25)
|
|
169
|
-
|
|
170
|
-
css["--pico-secondary"] = _rgb_to_hex(sec)
|
|
171
|
-
css["--pico-secondary-hover"] = _rgb_to_hex(_mix(sec, (255, 255, 255), 0.15))
|
|
172
|
-
css["--pico-secondary-focus"] = _rgba(sec, 0.25)
|
|
173
|
-
css["--pico-secondary-active"] = _rgb_to_hex(sec)
|
|
174
|
-
css["--pico-secondary-inverse"] = _rgb_to_hex(_best_contrast([fixed_foreground, fixed_background], sec, 4.5))
|
|
175
|
-
|
|
176
|
-
# 4 Headings inherit the accent spectrum
|
|
177
|
-
css["--pico-h1-color"] = css["--pico-primary"]
|
|
178
|
-
css["--pico-h2-color"] = css["--pico-secondary"]
|
|
179
|
-
css["--pico-h3-color"] = css["--pico-color"] # body colour
|
|
180
|
-
|
|
181
|
-
# 5 Muted text & borders use the least-contrasty greys we can still read
|
|
182
|
-
grey_candidates = [
|
|
183
|
-
_theme_color(theme, "bright", "black"),
|
|
184
|
-
_theme_color(theme, "normal", "black"),
|
|
185
|
-
_theme_color(theme, "bright", "white"),
|
|
186
|
-
_theme_color(theme, "normal", "white"),
|
|
187
|
-
]
|
|
188
|
-
muted = _best_contrast([c for c in grey_candidates if c], fixed_background, 3)
|
|
189
|
-
css["--pico-muted-color"] = _rgb_to_hex(muted)
|
|
190
|
-
css["--pico-border-color"] = _rgb_to_hex(_mix(muted, fixed_background, 0.5))
|
|
191
|
-
css["--pico-muted-border-color"] = css["--pico-border-color"]
|
|
192
|
-
|
|
193
|
-
# 6 Cards, selections, cursor, code — pick safe defaults
|
|
194
|
-
css["--pico-card-background-color"] = css["--pico-background-color"]
|
|
195
|
-
css["--pico-card-sectioning-background-color"] = _rgb_to_hex(fixed_background2)
|
|
196
|
-
css["--pico-card-border-color"] = css["--pico-border-color"]
|
|
197
|
-
|
|
198
|
-
sel_bg = _theme_color(theme, "selection", "background") or _mix(fixed_background, fixed_foreground, 0.20)
|
|
199
|
-
sel_fg = _best_contrast([fixed_foreground, muted, accent, sec], sel_bg, 4.5)
|
|
200
|
-
css["--pico-selection-background-color"] = _rgb_to_hex(sel_bg)
|
|
201
|
-
css["--pico-selection-color"] = _rgb_to_hex(sel_fg)
|
|
202
|
-
|
|
203
|
-
cur_bg = _theme_color(theme, "cursor", "cursor") or sel_bg
|
|
204
|
-
|
|
205
|
-
# Try theme's own foreground first, then fall back to palette extremes
|
|
206
|
-
fg_cur_candidates = [
|
|
207
|
-
_theme_color(theme, "primary", "foreground"),
|
|
208
|
-
_theme_color(theme, "normal", "white"),
|
|
209
|
-
_theme_color(theme, "bright", "white"),
|
|
210
|
-
_theme_color(theme, "normal", "blue"),
|
|
211
|
-
_theme_color(theme, "bright", "blue"),
|
|
212
|
-
_theme_color(theme, "normal", "cyan"),
|
|
213
|
-
_theme_color(theme, "bright", "cyan"),
|
|
214
|
-
_theme_color(theme, "normal", "magenta"),
|
|
215
|
-
_theme_color(theme, "bright", "magenta"),
|
|
216
|
-
_theme_color(theme, "normal", "green"),
|
|
217
|
-
_theme_color(theme, "bright", "green"),
|
|
218
|
-
_theme_color(theme, "normal", "yellow"),
|
|
219
|
-
_theme_color(theme, "bright", "yellow"),
|
|
220
|
-
_theme_color(theme, "normal", "red"),
|
|
221
|
-
_theme_color(theme, "bright", "red"),
|
|
222
|
-
_theme_color(theme, "normal", "black"),
|
|
223
|
-
_theme_color(theme, "bright", "black"),
|
|
224
|
-
]
|
|
225
|
-
cur_fg = _best_contrast([c for c in fg_cur_candidates if c], cur_bg)
|
|
226
|
-
|
|
227
|
-
css["--pico-code-background-color"] = _rgb_to_hex(cur_bg)
|
|
228
|
-
css["--pico-code-color"] = _rgb_to_hex(cur_fg)
|
|
229
|
-
|
|
230
|
-
# 7 Form elements and buttons reuse the existing tokens
|
|
231
|
-
css["--pico-form-element-background-color"] = css["--pico-background-color"]
|
|
232
|
-
css["--pico-form-element-border-color"] = css["--pico-border-color"]
|
|
233
|
-
css["--pico-form-element-color"] = css["--pico-color"]
|
|
234
|
-
css["--pico-form-element-focus-color"] = css["--pico-primary-hover"]
|
|
235
|
-
css["--pico-form-element-placeholder-color"] = css["--pico-muted-color"]
|
|
236
|
-
css["--pico-form-element-active-border-color"] = css["--pico-primary"]
|
|
237
|
-
css["--pico-form-element-active-background-color"] = css["--pico-selection-background-color"]
|
|
238
|
-
css["--pico-form-element-disabled-background-color"] = _rgb_to_hex(_mix(fixed_background, fixed_foreground, 0.1))
|
|
239
|
-
css["--pico-form-element-disabled-border-color"] = css["--pico-border-color"]
|
|
240
|
-
css["--pico-form-element-invalid-border-color"] = _rgb_to_hex(_theme_color(theme, "normal", "red") or accent_active)
|
|
241
|
-
css["--pico-form-element-invalid-focus-color"] = _rgb_to_hex(_theme_color(theme, "bright", "red") or accent_hover)
|
|
242
|
-
|
|
243
|
-
# 8 Buttons follow primary palette by default
|
|
244
|
-
css["--pico-button-base-background-color"] = css["--pico-primary"]
|
|
245
|
-
css["--pico-button-base-color"] = css["--pico-primary-inverse"]
|
|
246
|
-
css["--pico-button-hover-background-color"] = css["--pico-primary-hover"]
|
|
247
|
-
css["--pico-button-hover-color"] = css["--pico-primary-inverse"]
|
|
248
|
-
|
|
249
|
-
# 9 Semantic markup helpers
|
|
250
|
-
yellow = _theme_color(theme, "normal", "yellow") or _mix(accent, (255, 255, 0), 0.5)
|
|
251
|
-
css["--pico-mark-background-color"] = _rgba(yellow, 0.2)
|
|
252
|
-
css["--pico-mark-color"] = css["--pico-color"]
|
|
253
|
-
css["--pico-ins-color"] = _rgb_to_hex(_theme_color(theme, "normal", "green") or accent)
|
|
254
|
-
css["--pico-del-color"] = _rgb_to_hex(_theme_color(theme, "normal", "red") or accent_active)
|
|
255
|
-
|
|
256
|
-
# 10 Contrast helpers
|
|
257
|
-
css["--pico-contrast"] = css["--pico-color"]
|
|
258
|
-
css["--pico-contrast-inverse"] = css["--pico-primary-inverse"]
|
|
259
|
-
|
|
260
|
-
return css
|
|
261
|
-
|
|
262
|
-
if __name__ == "__main__":
|
|
263
|
-
import pathlib
|
|
264
|
-
import sys
|
|
265
|
-
|
|
266
|
-
try:
|
|
267
|
-
import toml
|
|
268
|
-
import uvicorn
|
|
269
|
-
from fastapi import FastAPI, Request
|
|
270
|
-
from fastapi.responses import HTMLResponse
|
|
271
|
-
from fastapi.templating import Jinja2Templates
|
|
272
|
-
except ImportError as e:
|
|
273
|
-
print(f"Error: Required module not found: {e}")
|
|
274
|
-
print("Please install required packages: pip install toml fastapi uvicorn jinja2")
|
|
275
|
-
sys.exit(1)
|
|
276
|
-
|
|
277
|
-
# Find themes directory
|
|
278
|
-
current_dir = pathlib.Path(__file__).parent
|
|
279
|
-
themes_dir = current_dir.parent.parent / 'themes'
|
|
280
|
-
|
|
281
|
-
# Check if themes directory exists
|
|
282
|
-
if not themes_dir.exists():
|
|
283
|
-
print(f"Error: Themes directory not found at {themes_dir}")
|
|
284
|
-
print(f"Current directory is {current_dir}")
|
|
285
|
-
sys.exit(1)
|
|
286
|
-
|
|
287
|
-
# Get list of all theme files
|
|
288
|
-
theme_files = list(themes_dir.glob('*.toml'))
|
|
289
|
-
|
|
290
|
-
if not theme_files:
|
|
291
|
-
print(f"Error: No theme files found in {themes_dir}")
|
|
292
|
-
sys.exit(1)
|
|
293
|
-
|
|
294
|
-
# Make sure alabaster is included
|
|
295
|
-
alabaster_path = themes_dir / 'alabaster.toml'
|
|
296
|
-
if alabaster_path not in theme_files and alabaster_path.exists():
|
|
297
|
-
theme_files.append(alabaster_path)
|
|
298
|
-
|
|
299
|
-
# Load a few random themes plus alabaster
|
|
300
|
-
selected_themes = theme_files
|
|
301
|
-
if alabaster_path.exists() and alabaster_path not in selected_themes:
|
|
302
|
-
selected_themes.append(alabaster_path)
|
|
303
|
-
|
|
304
|
-
# Dictionary to store theme data
|
|
305
|
-
themes = {}
|
|
306
|
-
|
|
307
|
-
# Load each theme
|
|
308
|
-
for theme_path in selected_themes:
|
|
309
|
-
try:
|
|
310
|
-
# Read the file as text to handle whitespace issues
|
|
311
|
-
with open(theme_path) as f:
|
|
312
|
-
content = f.read()
|
|
313
|
-
|
|
314
|
-
# Remove leading/trailing whitespace from each line
|
|
315
|
-
cleaned_content = '\n'.join(line.rstrip() for line in content.splitlines())
|
|
316
|
-
|
|
317
|
-
# Parse using StringIO
|
|
318
|
-
from io import StringIO
|
|
319
|
-
theme_data = toml.load(StringIO(cleaned_content))
|
|
320
|
-
themes[theme_path.stem] = theme_data
|
|
321
|
-
except Exception as e:
|
|
322
|
-
print(f"Error loading {theme_path.name}: {e}")
|
|
323
|
-
|
|
324
|
-
# Check if any themes were successfully loaded
|
|
325
|
-
if not themes:
|
|
326
|
-
print("Error: No themes could be loaded successfully.")
|
|
327
|
-
sys.exit(1)
|
|
328
|
-
|
|
329
|
-
# Create a FastAPI application
|
|
330
|
-
app = FastAPI(title="Theme Mapper")
|
|
331
|
-
|
|
332
|
-
# Create template directory
|
|
333
|
-
template_dir = current_dir / "templates"
|
|
334
|
-
try:
|
|
335
|
-
template_dir.mkdir(exist_ok=True)
|
|
336
|
-
except Exception as e:
|
|
337
|
-
print(f"Error creating template directory: {e}")
|
|
338
|
-
sys.exit(1)
|
|
339
|
-
|
|
340
|
-
# Create and write template file
|
|
341
|
-
template_path = template_dir / "theme_mapper.html"
|
|
342
|
-
template_content = '''
|
|
343
|
-
<!DOCTYPE html>
|
|
344
|
-
<html lang="en" data-theme="light">
|
|
345
|
-
<head>
|
|
346
|
-
<meta charset="UTF-8">
|
|
347
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
348
|
-
<title>Theme Mapper</title>
|
|
349
|
-
<!-- Use Pico CSS only -->
|
|
350
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@picocss/pico@1/css/pico.min.css">
|
|
351
|
-
<style>
|
|
352
|
-
{{ css_vars | safe }}
|
|
353
|
-
|
|
354
|
-
/* Only use Pico CSS classes and variables */
|
|
355
|
-
|
|
356
|
-
.color-sample {
|
|
357
|
-
height: 20px;
|
|
358
|
-
width: 100%;
|
|
359
|
-
border-radius: 4px;
|
|
360
|
-
margin-bottom: 5px;
|
|
361
|
-
}
|
|
362
|
-
.theme-selector {
|
|
363
|
-
position: fixed;
|
|
364
|
-
top: 10px;
|
|
365
|
-
right: 10px;
|
|
366
|
-
z-index: 100;
|
|
367
|
-
background-color: var(--pico-card-background-color);
|
|
368
|
-
padding: 10px;
|
|
369
|
-
border-radius: 8px;
|
|
370
|
-
border: 1px solid var(--pico-border-color);
|
|
371
|
-
}
|
|
372
|
-
article {
|
|
373
|
-
margin-bottom: 1rem;
|
|
374
|
-
}
|
|
375
|
-
.contrast-table td {
|
|
376
|
-
padding: 5px;
|
|
377
|
-
text-align: center;
|
|
378
|
-
}
|
|
379
|
-
.good-contrast {
|
|
380
|
-
background-color: var(--pico-ins-color);
|
|
381
|
-
color: var(--pico-background-color);
|
|
382
|
-
}
|
|
383
|
-
.bad-contrast {
|
|
384
|
-
background-color: var(--pico-del-color);
|
|
385
|
-
color: var(--pico-background-color);
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
/* New: give demo content cards a themed background so text always contrasts */
|
|
389
|
-
article {
|
|
390
|
-
background-color: var(--pico-card-sectioning-background-color);
|
|
391
|
-
border: 1px solid var(--pico-card-border-color);
|
|
392
|
-
padding: 1rem;
|
|
393
|
-
border-radius: 8px;
|
|
394
|
-
}
|
|
395
|
-
/* New: background for the two grid columns */
|
|
396
|
-
.grid > div {
|
|
397
|
-
background-color: var(--pico-card-background-color);
|
|
398
|
-
padding: 1rem;
|
|
399
|
-
border-radius: 8px;
|
|
400
|
-
}
|
|
401
|
-
|
|
402
|
-
/* Override any non-pico CSS variables */
|
|
403
|
-
body {
|
|
404
|
-
background-color: var(--pico-background-color);
|
|
405
|
-
color: var(--pico-color);
|
|
406
|
-
}
|
|
407
|
-
a {
|
|
408
|
-
color: var(--pico-primary);
|
|
409
|
-
}
|
|
410
|
-
a:hover {
|
|
411
|
-
color: var(--pico-primary-hover);
|
|
412
|
-
}
|
|
413
|
-
h1 {
|
|
414
|
-
color: var(--pico-h1-color);
|
|
415
|
-
}
|
|
416
|
-
h2 {
|
|
417
|
-
color: var(--pico-h2-color);
|
|
418
|
-
}
|
|
419
|
-
h3 {
|
|
420
|
-
color: var(--pico-h3-color);
|
|
421
|
-
}
|
|
422
|
-
mark {
|
|
423
|
-
background-color: var(--pico-mark-background-color);
|
|
424
|
-
color: var(--pico-mark-color);
|
|
425
|
-
}
|
|
426
|
-
ins {
|
|
427
|
-
color: var(--pico-ins-color);
|
|
428
|
-
}
|
|
429
|
-
del {
|
|
430
|
-
color: var(--pico-del-color);
|
|
431
|
-
}
|
|
432
|
-
code {
|
|
433
|
-
background-color: var(--pico-code-background-color);
|
|
434
|
-
color: var(--pico-code-color);
|
|
435
|
-
}
|
|
436
|
-
button, input[type="submit"], input[type="button"] {
|
|
437
|
-
background-color: var(--pico-button-base-background-color);
|
|
438
|
-
color: var(--pico-button-base-color);
|
|
439
|
-
border-color: var(--pico-button-base-background-color);
|
|
440
|
-
}
|
|
441
|
-
button:hover, input[type="submit"]:hover, input[type="button"]:hover {
|
|
442
|
-
background-color: var(--pico-button-hover-background-color);
|
|
443
|
-
color: var(--pico-button-hover-color);
|
|
444
|
-
border-color: var(--pico-button-hover-background-color);
|
|
445
|
-
}
|
|
446
|
-
button.secondary, input[type="submit"].secondary, input[type="button"].secondary {
|
|
447
|
-
background-color: var(--pico-secondary);
|
|
448
|
-
color: var(--pico-secondary-inverse);
|
|
449
|
-
border-color: var(--pico-secondary);
|
|
450
|
-
}
|
|
451
|
-
button.secondary:hover, input[type="submit"].secondary:hover, input[type="button"].secondary:hover {
|
|
452
|
-
background-color: var(--pico-secondary-hover);
|
|
453
|
-
color: var(--pico-secondary-inverse);
|
|
454
|
-
border-color: var(--pico-secondary-hover);
|
|
455
|
-
}
|
|
456
|
-
button.contrast, input[type="submit"].contrast, input[type="button"].contrast {
|
|
457
|
-
background-color: var(--pico-contrast);
|
|
458
|
-
color: var(--pico-contrast-inverse);
|
|
459
|
-
border-color: var(--pico-contrast);
|
|
460
|
-
}
|
|
461
|
-
/* Improve grid columns on wider screens */
|
|
462
|
-
@media (min-width: 768px) {
|
|
463
|
-
.grid {
|
|
464
|
-
display: grid;
|
|
465
|
-
grid-template-columns: repeat(auto-fit, minmax(380px, 1fr));
|
|
466
|
-
gap: 2rem;
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
/* Ensure container can grow a little wider than Pico default */
|
|
470
|
-
.container {
|
|
471
|
-
max-width: 90rem; /* ~1440px */
|
|
472
|
-
}
|
|
473
|
-
/* Ensure tables use full-strength text colour */
|
|
474
|
-
table th,
|
|
475
|
-
table td {
|
|
476
|
-
color: var(--pico-color);
|
|
477
|
-
opacity: 1; /* override Pico's default fade */
|
|
478
|
-
}
|
|
479
|
-
</style>
|
|
480
|
-
</head>
|
|
481
|
-
<body>
|
|
482
|
-
<div class="container">
|
|
483
|
-
<div class="theme-selector">
|
|
484
|
-
<label for="theme-select">Select Theme:</label>
|
|
485
|
-
<select id="theme-select" onchange="window.location.href='/?theme=' + this.value">
|
|
486
|
-
{% for theme_name in themes %}
|
|
487
|
-
<option value="{{ theme_name }}" {% if theme_name == current_theme %}selected{% endif %}>{{ theme_name }}</option>
|
|
488
|
-
{% endfor %}
|
|
489
|
-
</select>
|
|
490
|
-
</div>
|
|
491
|
-
|
|
492
|
-
<h1>Theme Mapper: {{ current_theme }}</h1>
|
|
493
|
-
|
|
494
|
-
<div class="grid">
|
|
495
|
-
<div>
|
|
496
|
-
<h2>UI Elements</h2>
|
|
497
|
-
<article>
|
|
498
|
-
<h3>Headings and Text</h3>
|
|
499
|
-
<h1>Heading 1</h1>
|
|
500
|
-
<h2>Heading 2</h2>
|
|
501
|
-
<h3>Heading 3</h3>
|
|
502
|
-
<p>Normal paragraph text. <a href="#">This is a link</a>. <mark>This is marked text</mark>.</p>
|
|
503
|
-
<p><small>This is small text</small></p>
|
|
504
|
-
<p><ins>This is inserted text</ins> and <del>this is deleted text</del>.</p>
|
|
505
|
-
<blockquote>
|
|
506
|
-
This is a blockquote with <cite>a citation</cite>.
|
|
507
|
-
</blockquote>
|
|
508
|
-
<code>This is inline code</code>
|
|
509
|
-
<pre><code>// This is a code block
|
|
510
|
-
function example() {
|
|
511
|
-
return "Hello World";
|
|
512
|
-
}</code></pre>
|
|
513
|
-
</article>
|
|
514
|
-
|
|
515
|
-
<article>
|
|
516
|
-
<h3>Buttons</h3>
|
|
517
|
-
<button>Default Button</button>
|
|
518
|
-
<button class="secondary">Secondary Button</button>
|
|
519
|
-
<button class="contrast">Contrast Button</button>
|
|
520
|
-
</article>
|
|
521
|
-
|
|
522
|
-
<article>
|
|
523
|
-
<h3>Form Elements</h3>
|
|
524
|
-
<form>
|
|
525
|
-
<label for="text">Text Input</label>
|
|
526
|
-
<input type="text" id="text" placeholder="Text input">
|
|
527
|
-
|
|
528
|
-
<label for="select">Select</label>
|
|
529
|
-
<select id="select">
|
|
530
|
-
<option>Option 1</option>
|
|
531
|
-
<option>Option 2</option>
|
|
532
|
-
</select>
|
|
533
|
-
|
|
534
|
-
<label for="textarea">Textarea</label>
|
|
535
|
-
<textarea id="textarea" placeholder="Textarea"></textarea>
|
|
536
|
-
|
|
537
|
-
<label for="invalid" aria-invalid="true">Invalid Input</label>
|
|
538
|
-
<input type="text" id="invalid" aria-invalid="true" placeholder="Invalid input">
|
|
539
|
-
|
|
540
|
-
<fieldset>
|
|
541
|
-
<legend>Checkboxes</legend>
|
|
542
|
-
<label>
|
|
543
|
-
<input type="checkbox" checked>
|
|
544
|
-
Checkbox 1
|
|
545
|
-
</label>
|
|
546
|
-
<label>
|
|
547
|
-
<input type="checkbox">
|
|
548
|
-
Checkbox 2
|
|
549
|
-
</label>
|
|
550
|
-
</fieldset>
|
|
551
|
-
|
|
552
|
-
<fieldset>
|
|
553
|
-
<legend>Radio Buttons</legend>
|
|
554
|
-
<label>
|
|
555
|
-
<input type="radio" name="radio" checked>
|
|
556
|
-
Radio 1
|
|
557
|
-
</label>
|
|
558
|
-
<label>
|
|
559
|
-
<input type="radio" name="radio">
|
|
560
|
-
Radio 2
|
|
561
|
-
</label>
|
|
562
|
-
</fieldset>
|
|
563
|
-
</form>
|
|
564
|
-
</article>
|
|
565
|
-
</div>
|
|
566
|
-
|
|
567
|
-
<div>
|
|
568
|
-
<h2>Theme Color Mapping</h2>
|
|
569
|
-
<article>
|
|
570
|
-
<h3>Main Colors</h3>
|
|
571
|
-
<div class="grid">
|
|
572
|
-
{% for color_name, color_value in main_colors %}
|
|
573
|
-
<div>
|
|
574
|
-
<div class="color-sample" style="background-color: {{ color_value }};"></div>
|
|
575
|
-
<small>{{ color_name }}<br>{{ color_value }}</small>
|
|
576
|
-
</div>
|
|
577
|
-
{% endfor %}
|
|
578
|
-
</div>
|
|
579
|
-
</article>
|
|
580
|
-
|
|
581
|
-
<article>
|
|
582
|
-
<h3>All Pico CSS Variables</h3>
|
|
583
|
-
<div style="max-height: 300px; overflow-y: auto;">
|
|
584
|
-
<table>
|
|
585
|
-
<thead>
|
|
586
|
-
<tr>
|
|
587
|
-
<th>Variable</th>
|
|
588
|
-
<th>Value</th>
|
|
589
|
-
<th>Sample</th>
|
|
590
|
-
</tr>
|
|
591
|
-
</thead>
|
|
592
|
-
<tbody>
|
|
593
|
-
{% for var_name, var_value in all_vars %}
|
|
594
|
-
<tr>
|
|
595
|
-
<td>{{ var_name }}</td>
|
|
596
|
-
<td>{{ var_value }}</td>
|
|
597
|
-
<td>
|
|
598
|
-
{% if var_value.startswith('#') or var_value.startswith('rgb') %}
|
|
599
|
-
<div class="color-sample" style="background-color: {{ var_value }};"></div>
|
|
600
|
-
{% endif %}
|
|
601
|
-
</td>
|
|
602
|
-
</tr>
|
|
603
|
-
{% endfor %}
|
|
604
|
-
</tbody>
|
|
605
|
-
</table>
|
|
606
|
-
</div>
|
|
607
|
-
</article>
|
|
608
|
-
|
|
609
|
-
<article>
|
|
610
|
-
<h3>Color Contrast Checks</h3>
|
|
611
|
-
<table class="contrast-table">
|
|
612
|
-
<thead>
|
|
613
|
-
<tr>
|
|
614
|
-
<th>Foreground</th>
|
|
615
|
-
<th>Background</th>
|
|
616
|
-
<th>Contrast</th>
|
|
617
|
-
<th>WCAG AA</th>
|
|
618
|
-
</tr>
|
|
619
|
-
</thead>
|
|
620
|
-
<tbody>
|
|
621
|
-
{% for check in contrast_checks %}
|
|
622
|
-
<tr>
|
|
623
|
-
<td>{{ check.fg_name }}</td>
|
|
624
|
-
<td>{{ check.bg_name }}</td>
|
|
625
|
-
<td>{{ check.contrast }}</td>
|
|
626
|
-
<td class="{% if check.passes %}good-contrast{% else %}bad-contrast{% endif %}">
|
|
627
|
-
{{ "Pass" if check.passes else "Fail" }}
|
|
628
|
-
</td>
|
|
629
|
-
</tr>
|
|
630
|
-
{% endfor %}
|
|
631
|
-
</tbody>
|
|
632
|
-
</table>
|
|
633
|
-
</article>
|
|
634
|
-
|
|
635
|
-
<article>
|
|
636
|
-
<h3>Original Theme Colors</h3>
|
|
637
|
-
<div style="max-height: 300px; overflow-y: auto;">
|
|
638
|
-
<table>
|
|
639
|
-
<thead>
|
|
640
|
-
<tr>
|
|
641
|
-
<th>Group</th>
|
|
642
|
-
<th>Name</th>
|
|
643
|
-
<th>Value</th>
|
|
644
|
-
<th>Sample</th>
|
|
645
|
-
</tr>
|
|
646
|
-
</thead>
|
|
647
|
-
<tbody>
|
|
648
|
-
{% for group, names in original_colors.items() %}
|
|
649
|
-
{% for name, value in names.items() %}
|
|
650
|
-
<tr>
|
|
651
|
-
<td>{{ group }}</td>
|
|
652
|
-
<td>{{ name }}</td>
|
|
653
|
-
<td>{{ value }}</td>
|
|
654
|
-
<td><div class="color-sample" style="background-color: {{ value }};"></div></td>
|
|
655
|
-
</tr>
|
|
656
|
-
{% endfor %}
|
|
657
|
-
{% endfor %}
|
|
658
|
-
</tbody>
|
|
659
|
-
</table>
|
|
660
|
-
</div>
|
|
661
|
-
</article>
|
|
662
|
-
</div>
|
|
663
|
-
</div>
|
|
664
|
-
</div>
|
|
665
|
-
</body>
|
|
666
|
-
</html>
|
|
667
|
-
'''
|
|
668
|
-
|
|
669
|
-
try:
|
|
670
|
-
with open(template_path, 'w') as f:
|
|
671
|
-
f.write(template_content)
|
|
672
|
-
except Exception as e:
|
|
673
|
-
print(f"Error writing template file: {e}")
|
|
674
|
-
sys.exit(1)
|
|
675
|
-
|
|
676
|
-
# Setup Jinja2 templates
|
|
677
|
-
try:
|
|
678
|
-
templates = Jinja2Templates(directory=str(template_dir))
|
|
679
|
-
except Exception as e:
|
|
680
|
-
print(f"Error setting up Jinja2 templates: {e}")
|
|
681
|
-
sys.exit(1)
|
|
682
|
-
|
|
683
|
-
@app.get("/", response_class=HTMLResponse)
|
|
684
|
-
async def index(request: Request, theme: str | None = None):
|
|
685
|
-
try:
|
|
686
|
-
# Get requested theme name from query parameter
|
|
687
|
-
if theme is None or theme not in themes:
|
|
688
|
-
# If no theme is provided or theme is invalid, default to alabaster or first theme
|
|
689
|
-
theme_name = 'alabaster' if 'alabaster' in themes else next(iter(themes))
|
|
690
|
-
else:
|
|
691
|
-
# Use the requested theme directly
|
|
692
|
-
theme_name = theme
|
|
693
|
-
|
|
694
|
-
# Get the theme data
|
|
695
|
-
theme_data = themes[theme_name]
|
|
696
|
-
|
|
697
|
-
# Convert theme to pico css variables
|
|
698
|
-
css_vars = alacritty_to_pico(theme_data)
|
|
699
|
-
|
|
700
|
-
# Format css variables for the style tag - this is important for proper application of the theme
|
|
701
|
-
css_vars_str = ":root {\n" + "\n".join([f" {k}: {v};" for k, v in css_vars.items()]) + "\n}"
|
|
702
|
-
|
|
703
|
-
# Prepare main colors for display
|
|
704
|
-
main_colors = [
|
|
705
|
-
("Background", css_vars["--pico-background-color"]),
|
|
706
|
-
("Text", css_vars["--pico-color"]),
|
|
707
|
-
("Primary", css_vars["--pico-primary"]),
|
|
708
|
-
("Secondary", css_vars["--pico-secondary"]),
|
|
709
|
-
("Muted", css_vars["--pico-muted-color"]),
|
|
710
|
-
]
|
|
711
|
-
|
|
712
|
-
# Extract original colors from theme
|
|
713
|
-
original_colors = {}
|
|
714
|
-
if "colors" in theme_data:
|
|
715
|
-
for group, colors in theme_data["colors"].items():
|
|
716
|
-
if isinstance(colors, dict):
|
|
717
|
-
group_colors = {}
|
|
718
|
-
for name, value in colors.items():
|
|
719
|
-
if isinstance(value, str):
|
|
720
|
-
group_colors[name] = value.strip()
|
|
721
|
-
if group_colors:
|
|
722
|
-
original_colors[group] = group_colors
|
|
723
|
-
|
|
724
|
-
# Check contrasts
|
|
725
|
-
contrast_checks = []
|
|
726
|
-
|
|
727
|
-
# Define relevant foreground/background variable pairs to evaluate
|
|
728
|
-
contrast_pairs = [
|
|
729
|
-
("Text", "--pico-color", "Background", "--pico-background-color"),
|
|
730
|
-
("Primary", "--pico-primary", "Background", "--pico-background-color"),
|
|
731
|
-
("Secondary", "--pico-secondary", "Background", "--pico-background-color"),
|
|
732
|
-
("Muted", "--pico-muted-color", "Background", "--pico-background-color"),
|
|
733
|
-
(
|
|
734
|
-
"Button Text",
|
|
735
|
-
"--pico-button-base-color",
|
|
736
|
-
"Button Background",
|
|
737
|
-
"--pico-button-base-background-color",
|
|
738
|
-
),
|
|
739
|
-
(
|
|
740
|
-
"Secondary Btn Text",
|
|
741
|
-
"--pico-secondary-inverse",
|
|
742
|
-
"Secondary",
|
|
743
|
-
"--pico-secondary",
|
|
744
|
-
),
|
|
745
|
-
(
|
|
746
|
-
"Contrast Text",
|
|
747
|
-
"--pico-contrast-inverse",
|
|
748
|
-
"Contrast Background",
|
|
749
|
-
"--pico-contrast",
|
|
750
|
-
),
|
|
751
|
-
(
|
|
752
|
-
"Code",
|
|
753
|
-
"--pico-code-color",
|
|
754
|
-
"Code Background",
|
|
755
|
-
"--pico-code-background-color",
|
|
756
|
-
),
|
|
757
|
-
]
|
|
758
|
-
|
|
759
|
-
for fg_name, fg_var, bg_name, bg_var in contrast_pairs:
|
|
760
|
-
fg_hex = css_vars.get(fg_var)
|
|
761
|
-
bg_hex = css_vars.get(bg_var)
|
|
762
|
-
if not fg_hex or not bg_hex:
|
|
763
|
-
continue
|
|
764
|
-
fg_rgb = _hex_to_rgb(fg_hex)
|
|
765
|
-
bg_rgb = _hex_to_rgb(bg_hex)
|
|
766
|
-
if fg_rgb is None or bg_rgb is None:
|
|
767
|
-
continue
|
|
768
|
-
ratio = _contrast(fg_rgb, bg_rgb)
|
|
769
|
-
contrast_checks.append({
|
|
770
|
-
"fg_name": fg_name,
|
|
771
|
-
"bg_name": bg_name,
|
|
772
|
-
"contrast": f"{ratio:.2f}",
|
|
773
|
-
"passes": ratio >= 4.5,
|
|
774
|
-
})
|
|
775
|
-
|
|
776
|
-
return templates.TemplateResponse(
|
|
777
|
-
"theme_mapper.html",
|
|
778
|
-
{
|
|
779
|
-
"request": request,
|
|
780
|
-
"css_vars": css_vars_str,
|
|
781
|
-
"themes": themes.keys(),
|
|
782
|
-
"current_theme": theme_name,
|
|
783
|
-
"main_colors": main_colors,
|
|
784
|
-
"all_vars": list(css_vars.items()),
|
|
785
|
-
"original_colors": original_colors,
|
|
786
|
-
"contrast_checks": contrast_checks
|
|
787
|
-
}
|
|
788
|
-
)
|
|
789
|
-
except Exception as e:
|
|
790
|
-
print(f"Error handling request: {e}")
|
|
791
|
-
from fastapi.responses import PlainTextResponse
|
|
792
|
-
return PlainTextResponse(f"Error processing theme: {e!s}", status_code=500)
|
|
793
|
-
|
|
794
|
-
# Add a more robust error handler for the application
|
|
795
|
-
@app.exception_handler(Exception)
|
|
796
|
-
async def global_exception_handler(request: Request, exc: Exception):
|
|
797
|
-
print(f"Global exception handler caught: {exc}")
|
|
798
|
-
from fastapi.responses import PlainTextResponse
|
|
799
|
-
return PlainTextResponse(f"Internal server error: {exc!s}", status_code=500)
|
|
800
|
-
|
|
801
|
-
# Run the app
|
|
802
|
-
print("Starting Theme Mapper web app...")
|
|
803
|
-
print(f"Current directory: {current_dir}")
|
|
804
|
-
print(f"Template directory: {template_dir} (exists: {template_dir.exists()})")
|
|
805
|
-
print(f"Themes directory: {themes_dir} (exists: {themes_dir.exists()})")
|
|
806
|
-
print(f"Loaded {len(themes)} themes: {', '.join(themes.keys())}")
|
|
807
|
-
|
|
808
|
-
try:
|
|
809
|
-
uvicorn.run(app, host="127.0.0.1", port=5050)
|
|
810
|
-
except Exception as e:
|
|
811
|
-
print(f"Error starting the web server: {e}")
|
|
812
|
-
sys.exit(1)
|