py-data-engine 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.
- data_engine/__init__.py +37 -0
- data_engine/application/__init__.py +39 -0
- data_engine/application/actions.py +42 -0
- data_engine/application/catalog.py +151 -0
- data_engine/application/control.py +213 -0
- data_engine/application/details.py +73 -0
- data_engine/application/runtime.py +449 -0
- data_engine/application/workspace.py +62 -0
- data_engine/authoring/__init__.py +14 -0
- data_engine/authoring/builder.py +31 -0
- data_engine/authoring/execution/__init__.py +6 -0
- data_engine/authoring/execution/app.py +6 -0
- data_engine/authoring/execution/context.py +82 -0
- data_engine/authoring/execution/continuous.py +176 -0
- data_engine/authoring/execution/grouped.py +106 -0
- data_engine/authoring/execution/logging.py +83 -0
- data_engine/authoring/execution/polling.py +135 -0
- data_engine/authoring/execution/runner.py +210 -0
- data_engine/authoring/execution/single.py +171 -0
- data_engine/authoring/flow.py +361 -0
- data_engine/authoring/helpers.py +160 -0
- data_engine/authoring/model.py +59 -0
- data_engine/authoring/primitives.py +430 -0
- data_engine/authoring/services.py +42 -0
- data_engine/devtools/__init__.py +3 -0
- data_engine/devtools/project_ast_map.py +503 -0
- data_engine/docs/__init__.py +1 -0
- data_engine/docs/sphinx_source/_static/custom.css +13 -0
- data_engine/docs/sphinx_source/api.rst +42 -0
- data_engine/docs/sphinx_source/conf.py +37 -0
- data_engine/docs/sphinx_source/guides/app-runtime-and-workspaces.md +397 -0
- data_engine/docs/sphinx_source/guides/authoring-flow-modules.md +215 -0
- data_engine/docs/sphinx_source/guides/configuring-flows.md +185 -0
- data_engine/docs/sphinx_source/guides/core-concepts.md +208 -0
- data_engine/docs/sphinx_source/guides/database-methods.md +107 -0
- data_engine/docs/sphinx_source/guides/duckdb-helpers.md +462 -0
- data_engine/docs/sphinx_source/guides/flow-context.md +538 -0
- data_engine/docs/sphinx_source/guides/flow-methods.md +206 -0
- data_engine/docs/sphinx_source/guides/getting-started.md +271 -0
- data_engine/docs/sphinx_source/guides/project-inventory.md +5683 -0
- data_engine/docs/sphinx_source/guides/project-map.md +118 -0
- data_engine/docs/sphinx_source/guides/recipes.md +268 -0
- data_engine/docs/sphinx_source/index.rst +22 -0
- data_engine/domain/__init__.py +92 -0
- data_engine/domain/actions.py +69 -0
- data_engine/domain/catalog.py +128 -0
- data_engine/domain/details.py +214 -0
- data_engine/domain/diagnostics.py +56 -0
- data_engine/domain/errors.py +104 -0
- data_engine/domain/inspection.py +99 -0
- data_engine/domain/logs.py +118 -0
- data_engine/domain/operations.py +172 -0
- data_engine/domain/operator.py +72 -0
- data_engine/domain/runs.py +155 -0
- data_engine/domain/runtime.py +279 -0
- data_engine/domain/source_state.py +17 -0
- data_engine/domain/support.py +54 -0
- data_engine/domain/time.py +23 -0
- data_engine/domain/workspace.py +159 -0
- data_engine/flow_modules/__init__.py +1 -0
- data_engine/flow_modules/flow_module_compiler.py +179 -0
- data_engine/flow_modules/flow_module_loader.py +201 -0
- data_engine/helpers/__init__.py +25 -0
- data_engine/helpers/duckdb.py +705 -0
- data_engine/hosts/__init__.py +1 -0
- data_engine/hosts/daemon/__init__.py +23 -0
- data_engine/hosts/daemon/app.py +221 -0
- data_engine/hosts/daemon/bootstrap.py +69 -0
- data_engine/hosts/daemon/client.py +465 -0
- data_engine/hosts/daemon/commands.py +64 -0
- data_engine/hosts/daemon/composition.py +310 -0
- data_engine/hosts/daemon/constants.py +15 -0
- data_engine/hosts/daemon/entrypoints.py +97 -0
- data_engine/hosts/daemon/lifecycle.py +191 -0
- data_engine/hosts/daemon/manager.py +272 -0
- data_engine/hosts/daemon/ownership.py +126 -0
- data_engine/hosts/daemon/runtime_commands.py +188 -0
- data_engine/hosts/daemon/runtime_control.py +31 -0
- data_engine/hosts/daemon/server.py +84 -0
- data_engine/hosts/daemon/shared_state.py +147 -0
- data_engine/hosts/daemon/state_sync.py +101 -0
- data_engine/platform/__init__.py +1 -0
- data_engine/platform/identity.py +35 -0
- data_engine/platform/local_settings.py +146 -0
- data_engine/platform/theme.py +259 -0
- data_engine/platform/workspace_models.py +190 -0
- data_engine/platform/workspace_policy.py +333 -0
- data_engine/runtime/__init__.py +1 -0
- data_engine/runtime/file_watch.py +185 -0
- data_engine/runtime/ledger_models.py +116 -0
- data_engine/runtime/runtime_db.py +938 -0
- data_engine/runtime/shared_state.py +523 -0
- data_engine/services/__init__.py +49 -0
- data_engine/services/daemon.py +64 -0
- data_engine/services/daemon_state.py +40 -0
- data_engine/services/flow_catalog.py +102 -0
- data_engine/services/flow_execution.py +48 -0
- data_engine/services/ledger.py +85 -0
- data_engine/services/logs.py +65 -0
- data_engine/services/runtime_binding.py +105 -0
- data_engine/services/runtime_execution.py +126 -0
- data_engine/services/runtime_history.py +62 -0
- data_engine/services/settings.py +58 -0
- data_engine/services/shared_state.py +28 -0
- data_engine/services/theme.py +59 -0
- data_engine/services/workspace_provisioning.py +224 -0
- data_engine/services/workspaces.py +74 -0
- data_engine/ui/__init__.py +3 -0
- data_engine/ui/cli/__init__.py +19 -0
- data_engine/ui/cli/app.py +161 -0
- data_engine/ui/cli/commands_doctor.py +178 -0
- data_engine/ui/cli/commands_run.py +80 -0
- data_engine/ui/cli/commands_start.py +100 -0
- data_engine/ui/cli/commands_workspace.py +97 -0
- data_engine/ui/cli/dependencies.py +44 -0
- data_engine/ui/cli/parser.py +56 -0
- data_engine/ui/gui/__init__.py +25 -0
- data_engine/ui/gui/app.py +116 -0
- data_engine/ui/gui/bootstrap.py +487 -0
- data_engine/ui/gui/bootstrapper.py +140 -0
- data_engine/ui/gui/cache_models.py +23 -0
- data_engine/ui/gui/control_support.py +185 -0
- data_engine/ui/gui/controllers/__init__.py +6 -0
- data_engine/ui/gui/controllers/flows.py +439 -0
- data_engine/ui/gui/controllers/runtime.py +245 -0
- data_engine/ui/gui/dialogs/__init__.py +12 -0
- data_engine/ui/gui/dialogs/messages.py +88 -0
- data_engine/ui/gui/dialogs/previews.py +222 -0
- data_engine/ui/gui/helpers/__init__.py +62 -0
- data_engine/ui/gui/helpers/inspection.py +81 -0
- data_engine/ui/gui/helpers/lifecycle.py +112 -0
- data_engine/ui/gui/helpers/scroll.py +28 -0
- data_engine/ui/gui/helpers/theming.py +87 -0
- data_engine/ui/gui/icons/dark_light.svg +12 -0
- data_engine/ui/gui/icons/documentation.svg +1 -0
- data_engine/ui/gui/icons/failed.svg +3 -0
- data_engine/ui/gui/icons/group.svg +4 -0
- data_engine/ui/gui/icons/home.svg +2 -0
- data_engine/ui/gui/icons/manual.svg +2 -0
- data_engine/ui/gui/icons/poll.svg +2 -0
- data_engine/ui/gui/icons/schedule.svg +4 -0
- data_engine/ui/gui/icons/settings.svg +2 -0
- data_engine/ui/gui/icons/started.svg +3 -0
- data_engine/ui/gui/icons/success.svg +3 -0
- data_engine/ui/gui/icons/view-log.svg +3 -0
- data_engine/ui/gui/icons.py +50 -0
- data_engine/ui/gui/launcher.py +48 -0
- data_engine/ui/gui/presenters/__init__.py +72 -0
- data_engine/ui/gui/presenters/docs.py +140 -0
- data_engine/ui/gui/presenters/logs.py +58 -0
- data_engine/ui/gui/presenters/runtime_projection.py +29 -0
- data_engine/ui/gui/presenters/sidebar.py +88 -0
- data_engine/ui/gui/presenters/steps.py +148 -0
- data_engine/ui/gui/presenters/workspace.py +39 -0
- data_engine/ui/gui/presenters/workspace_binding.py +75 -0
- data_engine/ui/gui/presenters/workspace_settings.py +182 -0
- data_engine/ui/gui/preview_models.py +37 -0
- data_engine/ui/gui/render_support.py +241 -0
- data_engine/ui/gui/rendering/__init__.py +12 -0
- data_engine/ui/gui/rendering/artifacts.py +95 -0
- data_engine/ui/gui/rendering/icons.py +50 -0
- data_engine/ui/gui/runtime.py +47 -0
- data_engine/ui/gui/state_support.py +193 -0
- data_engine/ui/gui/support.py +214 -0
- data_engine/ui/gui/surface.py +209 -0
- data_engine/ui/gui/theme.py +720 -0
- data_engine/ui/gui/widgets/__init__.py +34 -0
- data_engine/ui/gui/widgets/config.py +41 -0
- data_engine/ui/gui/widgets/logs.py +62 -0
- data_engine/ui/gui/widgets/panels.py +507 -0
- data_engine/ui/gui/widgets/sidebar.py +130 -0
- data_engine/ui/gui/widgets/steps.py +84 -0
- data_engine/ui/tui/__init__.py +5 -0
- data_engine/ui/tui/app.py +222 -0
- data_engine/ui/tui/bootstrap.py +475 -0
- data_engine/ui/tui/bootstrapper.py +117 -0
- data_engine/ui/tui/controllers/__init__.py +6 -0
- data_engine/ui/tui/controllers/flows.py +349 -0
- data_engine/ui/tui/controllers/runtime.py +167 -0
- data_engine/ui/tui/runtime.py +34 -0
- data_engine/ui/tui/state_support.py +141 -0
- data_engine/ui/tui/support.py +63 -0
- data_engine/ui/tui/theme.py +204 -0
- data_engine/ui/tui/widgets.py +123 -0
- data_engine/views/__init__.py +109 -0
- data_engine/views/actions.py +80 -0
- data_engine/views/artifacts.py +58 -0
- data_engine/views/flow_display.py +69 -0
- data_engine/views/logs.py +54 -0
- data_engine/views/models.py +96 -0
- data_engine/views/presentation.py +133 -0
- data_engine/views/runs.py +62 -0
- data_engine/views/state.py +39 -0
- data_engine/views/status.py +13 -0
- data_engine/views/text.py +109 -0
- py_data_engine-0.1.0.dist-info/METADATA +330 -0
- py_data_engine-0.1.0.dist-info/RECORD +200 -0
- py_data_engine-0.1.0.dist-info/WHEEL +5 -0
- py_data_engine-0.1.0.dist-info/entry_points.txt +2 -0
- py_data_engine-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"""Theme and icon helper functions for the desktop GUI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from PySide6.QtGui import QColor, QIcon, QPixmap
|
|
8
|
+
from PySide6.QtWidgets import QApplication
|
|
9
|
+
|
|
10
|
+
from data_engine.ui.gui.rendering import render_svg_icon_pixmap as render_svg_icon_pixmap_helper
|
|
11
|
+
from data_engine.ui.gui.theme import stylesheet
|
|
12
|
+
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from data_engine.ui.gui.app import DataEngineWindow
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def group_icon(window: "DataEngineWindow", group_name: str) -> QIcon:
|
|
18
|
+
del group_name
|
|
19
|
+
return QIcon(window._render_svg_icon_pixmap("group", 18))
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def group_icon_color(window: "DataEngineWindow") -> QColor:
|
|
23
|
+
return QColor(window.theme_service.palette(window.theme_name).text)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def render_svg_icon_pixmap(
|
|
27
|
+
window: "DataEngineWindow",
|
|
28
|
+
icon_name: str,
|
|
29
|
+
size: int,
|
|
30
|
+
*,
|
|
31
|
+
fill_color: str | None = None,
|
|
32
|
+
) -> QPixmap:
|
|
33
|
+
return render_svg_icon_pixmap_helper(
|
|
34
|
+
icon_name=icon_name,
|
|
35
|
+
size=size,
|
|
36
|
+
device_pixel_ratio=window.devicePixelRatioF(),
|
|
37
|
+
fill_color=fill_color,
|
|
38
|
+
default_fill_color=window._group_icon_color(),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def view_rail_icon(window: "DataEngineWindow", view_name: str) -> QIcon:
|
|
43
|
+
icon_name = window._VIEW_RAIL_ICON_NAMES[view_name]
|
|
44
|
+
return QIcon(window._render_svg_icon_pixmap(icon_name, 18))
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def action_bar_icon(window: "DataEngineWindow", action_name: str) -> QIcon:
|
|
48
|
+
icon_name = window._ACTION_ICON_NAMES[action_name]
|
|
49
|
+
return QIcon(window._render_svg_icon_pixmap(icon_name, 16))
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def log_icon(window: "DataEngineWindow", icon_name: str, size: int = 16) -> QIcon:
|
|
53
|
+
resolved_name = window._LOG_ICON_NAMES[icon_name]
|
|
54
|
+
fill_color = window._LOG_ICON_COLORS.get(icon_name)
|
|
55
|
+
return QIcon(window._render_svg_icon_pixmap(resolved_name, size, fill_color=fill_color))
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def render_group_icon_pixmap(window: "DataEngineWindow", group_name: str, size: int) -> QPixmap:
|
|
59
|
+
del group_name
|
|
60
|
+
return window._render_svg_icon_pixmap("group", size)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def toggle_theme(window: "DataEngineWindow") -> None:
|
|
64
|
+
window.theme_name = window.theme_service.toggle_name(window.theme_name)
|
|
65
|
+
window._apply_theme()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def sync_theme_to_system(window: "DataEngineWindow", *args) -> None:
|
|
69
|
+
del args
|
|
70
|
+
window.theme_name = window.theme_service.system_name()
|
|
71
|
+
window._apply_theme()
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def apply_theme(window: "DataEngineWindow") -> None:
|
|
75
|
+
app = QApplication.instance()
|
|
76
|
+
if app is not None:
|
|
77
|
+
app.setStyleSheet(stylesheet(window.theme_name))
|
|
78
|
+
window.theme_toggle_button.setIcon(window._action_bar_icon("theme_toggle"))
|
|
79
|
+
window.refresh_button.setIcon(window._action_bar_icon("refresh"))
|
|
80
|
+
for button in (window.home_button, window.docs_button, window.settings_button):
|
|
81
|
+
icon_name = button.property("viewIconName")
|
|
82
|
+
if isinstance(icon_name, str):
|
|
83
|
+
button.setIcon(window._view_rail_icon(icon_name))
|
|
84
|
+
if window.flow_cards:
|
|
85
|
+
window._populate_flow_tree()
|
|
86
|
+
window._refresh_action_buttons()
|
|
87
|
+
window._refresh_log_view()
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg width="800px" height="800px" viewBox="0 0 24 24" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
|
|
3
|
+
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
|
|
4
|
+
<title>ic_fluent_dark_theme_24_filled</title>
|
|
5
|
+
<desc>Created with Sketch.</desc>
|
|
6
|
+
<g id="🔍-Product-Icons" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
|
7
|
+
<g id="ic_fluent_dark_theme_24_filled" fill="#212121" fill-rule="nonzero">
|
|
8
|
+
<path d="M12,22 C17.5228475,22 22,17.5228475 22,12 C22,6.4771525 17.5228475,2 12,2 C6.4771525,2 2,6.4771525 2,12 C2,17.5228475 6.4771525,22 12,22 Z M12,20 L12,4 C16.418278,4 20,7.581722 20,12 C20,16.418278 16.418278,20 12,20 Z" id="🎨-Color">
|
|
9
|
+
|
|
10
|
+
</g>
|
|
11
|
+
</g>
|
|
12
|
+
</svg>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg id="Layer_1" height="512" viewBox="0 0 24 24" width="512" xmlns="http://www.w3.org/2000/svg" data-name="Layer 1"><path d="m17 14a1 1 0 0 1 -1 1h-8a1 1 0 0 1 0-2h8a1 1 0 0 1 1 1zm-4 3h-5a1 1 0 0 0 0 2h5a1 1 0 0 0 0-2zm9-6.515v8.515a5.006 5.006 0 0 1 -5 5h-10a5.006 5.006 0 0 1 -5-5v-14a5.006 5.006 0 0 1 5-5h4.515a6.958 6.958 0 0 1 4.95 2.05l3.484 3.486a6.951 6.951 0 0 1 2.051 4.949zm-6.949-7.021a5.01 5.01 0 0 0 -1.051-.78v4.316a1 1 0 0 0 1 1h4.316a4.983 4.983 0 0 0 -.781-1.05zm4.949 7.021c0-.165-.032-.323-.047-.485h-4.953a3 3 0 0 1 -3-3v-4.953c-.162-.015-.321-.047-.485-.047h-4.515a3 3 0 0 0 -3 3v14a3 3 0 0 0 3 3h10a3 3 0 0 0 3-3z"/></svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
2
|
+
<path d="M23.64 18.1 14.24 2.28C13.77 1.48 12.94 1 12 1s-1.77.48-2.23 1.28L.36 18.1h0c-.47.82-.47 1.79 0 2.6S1.67 22 2.6 22h18.81c.94 0 1.78-.49 2.24-1.3s.46-1.78-.01-2.6ZM13 18h-2v-2h2v2Zm0-4h-2V8h2v6Z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24">
|
|
3
|
+
<path d="M1.001,10.983c.062,.011,.122,.017,.182,.017,.474,0,.895-.337,.982-.82,.162-.881,.443-1.729,.833-2.532,.457,.219,.962,.352,1.502,.352,1.93,0,3.5-1.57,3.5-3.5,0-.54-.133-1.046-.353-1.503,.803-.389,1.652-.671,2.533-.833,.543-.1,.902-.621,.803-1.164-.1-.542-.61-.903-1.164-.803-1.258,.231-2.459,.668-3.58,1.284-.515-.298-1.104-.481-1.74-.481-1.93,0-3.5,1.57-3.5,3.5,0,.636,.183,1.225,.481,1.74-.615,1.121-1.052,2.321-1.283,3.58-.1,.543,.26,1.064,.803,1.164ZM4.5,3c.827,0,1.5,.673,1.5,1.5s-.673,1.5-1.5,1.5-1.5-.673-1.5-1.5,.673-1.5,1.5-1.5Zm9.328-.852c.915,.159,1.793,.447,2.625,.85-.219,.457-.352,.962-.352,1.501,0,1.93,1.57,3.5,3.5,3.5,.539,0,1.045-.133,1.502-.352,.389,.803,.672,1.65,.833,2.533,.089,.482,.509,.819,.982,.819,.06,0,.121-.005,.182-.017,.543-.1,.902-.621,.803-1.164-.232-1.26-.668-2.461-1.283-3.58,.298-.514,.481-1.103,.481-1.739,0-1.93-1.57-3.5-3.5-3.5-.636,0-1.226,.184-1.74,.482-1.154-.633-2.391-1.078-3.689-1.304-.551-.093-1.062,.27-1.157,.814-.095,.544,.27,1.062,.813,1.157Zm7.273,2.352c0,.827-.673,1.5-1.5,1.5s-1.5-.673-1.5-1.5,.673-1.5,1.5-1.5,1.5,.673,1.5,1.5ZM10.181,21.835c-.882-.162-1.73-.443-2.533-.833,.219-.457,.353-.963,.353-1.503,0-1.93-1.57-3.5-3.5-3.5-.54,0-1.045,.133-1.502,.352-.39-.803-.671-1.651-.833-2.532-.099-.543-.611-.904-1.164-.803-.543,.1-.902,.621-.803,1.164,.231,1.259,.668,2.459,1.283,3.58-.298,.515-.481,1.104-.481,1.74,0,1.93,1.57,3.5,3.5,3.5,.636,0,1.225-.183,1.74-.481,1.121,.615,2.322,1.053,3.58,1.284,.062,.011,.122,.017,.182,.017,.474,0,.894-.337,.982-.819,.1-.543-.26-1.064-.803-1.164Zm-7.181-2.335c0-.827,.673-1.5,1.5-1.5s1.5,.673,1.5,1.5-.673,1.5-1.5,1.5-1.5-.673-1.5-1.5Zm20.101-6.483c-.543-.101-1.063,.26-1.164,.803-.162,.882-.444,1.73-.833,2.533-.457-.219-.962-.352-1.502-.352-1.93,0-3.5,1.57-3.5,3.5,0,.539,.133,1.045,.352,1.501-.832,.403-1.711,.691-2.625,.85-.544,.095-.908,.613-.813,1.157,.085,.486,.507,.829,.984,.829,.057,0,.114-.005,.173-.015,1.298-.226,2.535-.671,3.689-1.304,.515,.298,1.104,.482,1.74,.482,1.93,0,3.5-1.57,3.5-3.5,0-.636-.183-1.224-.481-1.739,.615-1.12,1.051-2.32,1.283-3.58,.1-.543-.26-1.064-.803-1.164Zm-3.499,7.983c-.827,0-1.5-.673-1.5-1.5s.673-1.5,1.5-1.5,1.5,.673,1.5,1.5-.673,1.5-1.5,1.5Zm-7.602-3v.993c0,.583-.688,.893-1.125,.508l-2.01-1.773c-.487-.42-.487-1.155,0-1.575l2.01-1.773c.437-.385,1.125-.075,1.125,.508v1.112c2.206,0,4-1.794,4-4,0-.557-.112-1.095-.332-1.6-.222-.506,.01-1.096,.516-1.317,.508-.221,1.097,.011,1.316,.516,.332,.759,.5,1.566,.5,2.4,0,3.309-2.691,6-6,6Zm0-12v-.993c0-.583,.687-.893,1.125-.508l2.01,1.773c.487,.42,.487,1.155,0,1.575l-2.01,1.773c-.437,.385-1.125,.075-1.125-.508v-1.111c-2.206,0-4,1.794-4,4,0,.557,.112,1.095,.332,1.6,.222,.506-.01,1.096-.516,1.317-.13,.057-.267,.084-.399,.084-.386,0-.753-.224-.917-.6-.332-.759-.5-1.566-.5-2.4,0-3.309,2.691-6,6-6Z"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="512" height="512"><path d="M23.121,9.069,15.536,1.483a5.008,5.008,0,0,0-7.072,0L.879,9.069A2.978,2.978,0,0,0,0,11.19v9.817a3,3,0,0,0,3,3H21a3,3,0,0,0,3-3V11.19A2.978,2.978,0,0,0,23.121,9.069ZM15,22.007H9V18.073a3,3,0,0,1,6,0Zm7-1a1,1,0,0,1-1,1H17V18.073a5,5,0,0,0-10,0v3.934H3a1,1,0,0,1-1-1V11.19a1.008,1.008,0,0,1,.293-.707L9.878,2.9a3.008,3.008,0,0,1,4.244,0l7.585,7.586A1.008,1.008,0,0,1,22,11.19Z"/></svg>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" id="Bold" viewBox="0 0 24 24" width="512" height="512"><path d="M20.463,7.713l-9.1-6.677A5.317,5.317,0,0,0,2.9,5.323V18.677a5.311,5.311,0,0,0,8.46,4.287l9.105-6.677a5.315,5.315,0,0,0,0-8.574Zm-1.774,6.155-9.1,6.677A2.317,2.317,0,0,1,5.9,18.677V5.323a2.276,2.276,0,0,1,1.27-2.066A2.328,2.328,0,0,1,8.223,3a2.3,2.3,0,0,1,1.362.455l9.1,6.677a2.316,2.316,0,0,1,0,3.736Z"/></svg>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="512" height="512"><path d="M7,0h-3C1.79,0,0,1.79,0,4v3c0,2.21,1.79,4,4,4h3c2.21,0,4-1.79,4-4v-3C11,1.79,9.21,0,7,0Zm2,7c0,1.1-.9,2-2,2h-3c-1.1,0-2-.9-2-2v-3c0-1.1,.9-2,2-2h3c1.1,0,2,.9,2,2v3Zm11,6h-3c-2.21,0-4,1.79-4,4v3c0,2.21,1.79,4,4,4h3c2.21,0,4-1.79,4-4v-3c0-2.21-1.79-4-4-4Zm2,7c0,1.1-.9,2-2,2h-3c-1.1,0-2-.9-2-2v-3c0-1.1,.9-2,2-2h3c1.1,0,2,.9,2,2v3ZM13,5c0-1.06,.44-2.05,1.25-2.78l2.07-1.95c.4-.38,1.03-.36,1.41,.04,.38,.4,.36,1.04-.04,1.41l-2.08,1.95c-.11,.1-.2,.21-.28,.32h3.67c2.21,0,4,1.79,4,4v2c0,.55-.45,1-1,1s-1-.45-1-1v-2c0-1.1-.9-2-2-2h-3.67c.08,.11,.17,.21,.27,.31l2.09,1.96c.4,.38,.42,1.01,.04,1.41-.2,.21-.46,.31-.73,.31-.25,0-.49-.09-.69-.27l-2.08-1.95c-.79-.73-1.24-1.72-1.24-2.77Zm-2,14c0,1.06-.44,2.04-1.25,2.78l-2.07,1.95c-.19,.18-.44,.27-.69,.27-.27,0-.53-.11-.73-.31-.38-.4-.36-1.04,.04-1.41l2.08-1.95c.11-.1,.2-.21,.28-.32h-3.67c-2.21,0-4-1.79-4-4v-2c0-.55,.45-1,1-1s1,.45,1,1v2c0,1.1,.9,2,2,2h3.67c-.08-.11-.17-.21-.27-.31l-2.09-1.96c-.4-.38-.42-1.01-.04-1.41,.38-.4,1.01-.42,1.41-.04l2.08,1.95c.79,.73,1.24,1.72,1.24,2.77Z"/></svg>
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24">
|
|
3
|
+
<path d="M19.5,2h-1.5V.5c0-.276-.224-.5-.5-.5s-.5,.224-.5,.5v1.5H7V.5c0-.276-.224-.5-.5-.5s-.5,.224-.5,.5v1.5h-1.5C2.019,2,0,4.019,0,6.5v13c0,2.481,2.019,4.5,4.5,4.5h15c2.481,0,4.5-2.019,4.5-4.5V6.5c0-2.481-2.019-4.5-4.5-4.5ZM4.5,3h15c1.93,0,3.5,1.57,3.5,3.5v1.5H1v-1.5c0-1.93,1.57-3.5,3.5-3.5Zm15,20H4.5c-1.93,0-3.5-1.57-3.5-3.5V9H23v10.5c0,1.93-1.57,3.5-3.5,3.5Zm-2.5-11.5v2.5c0,.552-.448,1-1,1h-2.5c-.276,0-.5-.224-.5-.5s.224-.5,.5-.5h1.957c-.706-1.217-2.015-2-3.457-2-1.692,0-3.209,1.072-3.772,2.667-.093,.262-.375,.398-.639,.305-.26-.092-.396-.377-.305-.638,.705-1.994,2.601-3.333,4.716-3.333,1.6,0,3.072,.768,4,2.004v-1.504c0-.276,.224-.5,.5-.5s.5,.224,.5,.5Zm-.284,6.167c-.705,1.994-2.601,3.333-4.716,3.333-1.6,0-3.072-.768-4-2.004v1.504c0,.276-.224,.5-.5,.5s-.5-.224-.5-.5v-2.5c0-.552,.448-1,1-1h2.5c.276,0,.5,.224,.5,.5s-.224,.5-.5,.5h-1.957c.706,1.217,2.015,2,3.457,2,1.692,0,3.209-1.072,3.772-2.667,.093-.261,.377-.396,.639-.305,.26,.092,.396,.377,.305,.638Z"/>
|
|
4
|
+
</svg>
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<svg xmlns="http://www.w3.org/2000/svg" id="Outline" viewBox="0 0 24 24" width="512" height="512"><path d="M12,8a4,4,0,1,0,4,4A4,4,0,0,0,12,8Zm0,6a2,2,0,1,1,2-2A2,2,0,0,1,12,14Z"/><path d="M21.294,13.9l-.444-.256a9.1,9.1,0,0,0,0-3.29l.444-.256a3,3,0,1,0-3-5.2l-.445.257A8.977,8.977,0,0,0,15,3.513V3A3,3,0,0,0,9,3v.513A8.977,8.977,0,0,0,6.152,5.159L5.705,4.9a3,3,0,0,0-3,5.2l.444.256a9.1,9.1,0,0,0,0,3.29l-.444.256a3,3,0,1,0,3,5.2l.445-.257A8.977,8.977,0,0,0,9,20.487V21a3,3,0,0,0,6,0v-.513a8.977,8.977,0,0,0,2.848-1.646l.447.258a3,3,0,0,0,3-5.2Zm-2.548-3.776a7.048,7.048,0,0,1,0,3.75,1,1,0,0,0,.464,1.133l1.084.626a1,1,0,0,1-1,1.733l-1.086-.628a1,1,0,0,0-1.215.165,6.984,6.984,0,0,1-3.243,1.875,1,1,0,0,0-.751.969V21a1,1,0,0,1-2,0V19.748a1,1,0,0,0-.751-.969A6.984,6.984,0,0,1,7.006,16.9a1,1,0,0,0-1.215-.165l-1.084.627a1,1,0,1,1-1-1.732l1.084-.626a1,1,0,0,0,.464-1.133,7.048,7.048,0,0,1,0-3.75A1,1,0,0,0,4.79,8.992L3.706,8.366a1,1,0,0,1,1-1.733l1.086.628A1,1,0,0,0,7.006,7.1a6.984,6.984,0,0,1,3.243-1.875A1,1,0,0,0,11,4.252V3a1,1,0,0,1,2,0V4.252a1,1,0,0,0,.751.969A6.984,6.984,0,0,1,16.994,7.1a1,1,0,0,0,1.215.165l1.084-.627a1,1,0,1,1,1,1.732l-1.084.626A1,1,0,0,0,18.746,10.125Z"/></svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 513.806 513.806">
|
|
2
|
+
<path d="M66.074 228.731C81.577 123.379 179.549 50.542 284.901 66.045c35.944 5.289 69.662 20.626 97.27 44.244l-24.853 24.853c-8.33 8.332-8.328 21.84.005 30.17 3.999 3.998 9.423 6.245 15.078 6.246h97.835c11.782 0 21.333-9.551 21.333-21.333V52.39c-.003-11.782-9.556-21.331-21.338-21.329-5.655.001-11.079 2.248-15.078 6.246L427.418 65.04C321.658-29.235 159.497-19.925 65.222 85.835c-33.399 37.467-55.073 83.909-62.337 133.573-2.864 17.607 9.087 34.202 26.693 37.066 1.586.258 3.188.397 4.795.417 16.108-.174 29.629-12.185 31.701-28.16ZM479.429 256.891c-16.108.174-29.629 12.185-31.701 28.16C432.225 390.403 334.253 463.24 228.901 447.738c-35.944-5.289-69.662-20.626-97.27-44.244l24.853-24.853c8.33-8.332 8.328-21.84-.005-30.17-3.999-3.998-9.423-6.245-15.078-6.246H43.568c-11.782 0-21.333 9.551-21.333 21.333v97.835c.003 11.782 9.556 21.331 21.338 21.329 5.655-.001 11.079-2.248 15.078-6.246l27.733-27.733c105.735 94.285 267.884 85.004 362.17-20.732 33.417-37.475 55.101-83.933 62.363-133.615 2.876-17.605-9.064-34.208-26.668-37.084-1.592-.26-3.203-.401-4.818-.42Z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
2
|
+
<path d="m12 1c-7.71 0-11 3.29-11 11s3.29 11 11 11 11-3.29 11-11-3.29-11-11-11zm5.305 8.539c-1.312 2.053-3.18 4.626-6.001 6.319-.324.195-.731.19-1.05-.013-1.52-.963-2.661-1.995-3.594-3.248-.33-.443-.238-1.069.205-1.399.442-.33 1.069-.237 1.398.205.674.905 1.488 1.679 2.536 2.405 2.16-1.46 3.644-3.507 4.819-5.346.299-.466.917-.602 1.381-.304.466.298.602.916.305 1.381z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
2
|
+
<path d="m14 7v-6.54c.913.346 1.753.879 2.465 1.59l3.484 3.486c.712.711 1.245 1.551 1.591 2.464h-6.54c-.552 0-1-.449-1-1zm-1.771 7.985c0-.394-.342-.726-.75-.733-.402.007-.725.333-.724.736 0 .095.008 1.886.008 2.024 0 .403.322.729.724.736.408-.007.75-.338.75-.733l-.008-2.015zm9.747-4.985h-6.976c-1.654 0-3-1.346-3-3v-6.976c-.161-.011-.322-.024-.485-.024h-4.515c-2.757 0-5 2.243-5 5v14c0 2.757 2.243 5 5 5h10c2.757 0 5-2.243 5-5v-8.515c0-.163-.013-.324-.024-.485zm-13.254 8.368c0 .346-.28.627-.626.627l-1.468.003c-.347 0-.629-.28-.629-.627l-.005-4.744c0-.347.281-.628.628-.628s.627.28.627.626c.001 1.249.003 2.869.004 4.118.71-.001.592-.003.837-.004.348-.002.631.28.631.628zm4.765-1.353c0 1.094-.902 1.985-2.012 1.985-1.086 0-1.967-.881-1.967-1.967l-.008-2.066c0-1.086.881-1.967 1.967-1.967 1.109 0 2.012.891 2.012 1.985v.015l.008 2zm5 0c0 1.094-.902 1.985-2.012 1.985-1.086 0-1.967-.881-1.967-1.967l-.008-2.066c0-1.086.881-1.967 1.967-1.967.772 0 1.445.432 1.782 1.064.223.417-.08.921-.553.921-.228 0-.441-.122-.548-.324-.125-.237-.38-.404-.669-.409-.402.007-.725.333-.724.736 0 .095.008 1.886.008 2.024 0 .403.322.729.724.736.32-.005.593-.212.698-.491-.036 0-.063 0-.085 0-.349.004-.633-.278-.633-.627 0-.346.28-.626.625-.627l.766-.002c.346-.001.627.278.628.624v.374s.001.015.001.015z"/>
|
|
3
|
+
</svg>
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"""UI icon registry and loading helpers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from dataclasses import dataclass
|
|
6
|
+
from importlib import resources
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True)
|
|
10
|
+
class SvgIconAsset:
|
|
11
|
+
"""Describe one SVG icon source."""
|
|
12
|
+
|
|
13
|
+
file_name: str | None = None
|
|
14
|
+
svg_text: str | None = None
|
|
15
|
+
|
|
16
|
+
def read_text(self) -> str:
|
|
17
|
+
"""Return raw SVG text for this asset."""
|
|
18
|
+
if self.svg_text is not None:
|
|
19
|
+
return self.svg_text
|
|
20
|
+
if self.file_name is None:
|
|
21
|
+
raise ValueError("SVG icon asset has no source configured.")
|
|
22
|
+
return resources.files("data_engine.ui.gui").joinpath("icons", self.file_name).read_text(encoding="utf-8")
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
ICON_ASSETS: dict[str, SvgIconAsset] = {
|
|
26
|
+
"dark_light": SvgIconAsset(file_name="dark_light.svg"),
|
|
27
|
+
"documentation": SvgIconAsset(file_name="documentation.svg"),
|
|
28
|
+
"failed": SvgIconAsset(file_name="failed.svg"),
|
|
29
|
+
"group": SvgIconAsset(file_name="group.svg"),
|
|
30
|
+
"home": SvgIconAsset(file_name="home.svg"),
|
|
31
|
+
"manual": SvgIconAsset(file_name="manual.svg"),
|
|
32
|
+
"poll": SvgIconAsset(file_name="poll.svg"),
|
|
33
|
+
"schedule": SvgIconAsset(file_name="schedule.svg"),
|
|
34
|
+
"settings": SvgIconAsset(file_name="settings.svg"),
|
|
35
|
+
"started": SvgIconAsset(file_name="started.svg"),
|
|
36
|
+
"success": SvgIconAsset(file_name="success.svg"),
|
|
37
|
+
"view-log": SvgIconAsset(file_name="view-log.svg"),
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def load_svg_icon_text(icon_name: str) -> str:
|
|
42
|
+
"""Return SVG text for one registered icon."""
|
|
43
|
+
try:
|
|
44
|
+
asset = ICON_ASSETS[icon_name]
|
|
45
|
+
except KeyError as exc:
|
|
46
|
+
raise KeyError(f"Unknown UI icon: {icon_name}") from exc
|
|
47
|
+
return asset.read_text()
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
__all__ = ["ICON_ASSETS", "SvgIconAsset", "load_svg_icon_text"]
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""Launcher entrypoints for the Data Engine PySide6 UI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
from PySide6.QtWidgets import QApplication
|
|
8
|
+
|
|
9
|
+
from data_engine.platform.identity import APP_DISPLAY_NAME
|
|
10
|
+
from data_engine.ui.gui.bootstrap import build_gui_services
|
|
11
|
+
from data_engine.ui.gui.app import DataEngineWindow
|
|
12
|
+
from data_engine.ui.gui.theme import DEFAULT_THEME, resolve_theme_name, stylesheet
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def _configure_qt_webengine_environment() -> None:
|
|
16
|
+
"""Set stable Chromium flags before the UI spins up embedded docs."""
|
|
17
|
+
existing = os.environ.get("QTWEBENGINE_CHROMIUM_FLAGS", "").strip()
|
|
18
|
+
flags = [flag for flag in existing.split() if flag]
|
|
19
|
+
if "--disable-skia-graphite" not in flags:
|
|
20
|
+
flags.append("--disable-skia-graphite")
|
|
21
|
+
os.environ["QTWEBENGINE_CHROMIUM_FLAGS"] = " ".join(flags)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def launch(theme_name: str = DEFAULT_THEME) -> None:
|
|
25
|
+
"""Create and run the PySide6 application."""
|
|
26
|
+
_configure_qt_webengine_environment()
|
|
27
|
+
services = build_gui_services()
|
|
28
|
+
app = QApplication.instance() or QApplication([])
|
|
29
|
+
app.setApplicationName(APP_DISPLAY_NAME)
|
|
30
|
+
app.setStyle("Fusion")
|
|
31
|
+
resolved_theme = services.theme_service.resolve_name(theme_name)
|
|
32
|
+
app.setStyleSheet(stylesheet(resolved_theme))
|
|
33
|
+
window = DataEngineWindow(theme_name=resolved_theme, services=services)
|
|
34
|
+
window.show()
|
|
35
|
+
if QApplication.instance() is app:
|
|
36
|
+
app.exec()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def main() -> None:
|
|
40
|
+
"""Console entrypoint for the desktop UI."""
|
|
41
|
+
launch()
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
if __name__ == "__main__":
|
|
45
|
+
main()
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
__all__ = ["launch", "main"]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""Presenter helpers for the desktop UI."""
|
|
2
|
+
|
|
3
|
+
from data_engine.ui.gui.presenters.logs import add_log_run_item, format_raw_log_message, refresh_log_view
|
|
4
|
+
from data_engine.ui.gui.presenters.docs import (
|
|
5
|
+
create_docs_browser,
|
|
6
|
+
docs_build_dir,
|
|
7
|
+
finish_docs_build,
|
|
8
|
+
initialize_docs_view,
|
|
9
|
+
load_docs_page,
|
|
10
|
+
run_docs_build_worker,
|
|
11
|
+
start_docs_build,
|
|
12
|
+
)
|
|
13
|
+
from data_engine.ui.gui.presenters.runtime_projection import (
|
|
14
|
+
apply_daemon_snapshot,
|
|
15
|
+
finish_daemon_startup,
|
|
16
|
+
)
|
|
17
|
+
from data_engine.ui.gui.presenters.sidebar import refresh_sidebar_selection, refresh_sidebar_state_views, repolish_widget_tree, set_hovered
|
|
18
|
+
from data_engine.ui.gui.presenters.steps import (
|
|
19
|
+
apply_runtime_event,
|
|
20
|
+
duration_text,
|
|
21
|
+
format_seconds,
|
|
22
|
+
normalize_completed_operation_rows,
|
|
23
|
+
refresh_live_operation_durations,
|
|
24
|
+
render_operation_durations,
|
|
25
|
+
reset_operation_state,
|
|
26
|
+
)
|
|
27
|
+
from data_engine.ui.gui.presenters.workspace_binding import rebind_workspace_context
|
|
28
|
+
from data_engine.ui.gui.presenters.workspace_settings import (
|
|
29
|
+
browse_workspace_collection_root_override,
|
|
30
|
+
force_shutdown_daemon,
|
|
31
|
+
provision_selected_workspace,
|
|
32
|
+
refresh_workspace_provisioning_controls,
|
|
33
|
+
refresh_workspace_visibility_panel,
|
|
34
|
+
refresh_workspace_root_controls,
|
|
35
|
+
reset_workspace_collection_root_override,
|
|
36
|
+
save_workspace_collection_root_override,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
__all__ = [
|
|
40
|
+
"add_log_run_item",
|
|
41
|
+
"apply_runtime_event",
|
|
42
|
+
"apply_daemon_snapshot",
|
|
43
|
+
"browse_workspace_collection_root_override",
|
|
44
|
+
"create_docs_browser",
|
|
45
|
+
"docs_build_dir",
|
|
46
|
+
"duration_text",
|
|
47
|
+
"finish_daemon_startup",
|
|
48
|
+
"finish_docs_build",
|
|
49
|
+
"format_raw_log_message",
|
|
50
|
+
"format_seconds",
|
|
51
|
+
"force_shutdown_daemon",
|
|
52
|
+
"initialize_docs_view",
|
|
53
|
+
"load_docs_page",
|
|
54
|
+
"normalize_completed_operation_rows",
|
|
55
|
+
"rebind_workspace_context",
|
|
56
|
+
"provision_selected_workspace",
|
|
57
|
+
"refresh_log_view",
|
|
58
|
+
"refresh_workspace_provisioning_controls",
|
|
59
|
+
"refresh_workspace_visibility_panel",
|
|
60
|
+
"refresh_live_operation_durations",
|
|
61
|
+
"refresh_workspace_root_controls",
|
|
62
|
+
"refresh_sidebar_selection",
|
|
63
|
+
"refresh_sidebar_state_views",
|
|
64
|
+
"reset_workspace_collection_root_override",
|
|
65
|
+
"render_operation_durations",
|
|
66
|
+
"repolish_widget_tree",
|
|
67
|
+
"reset_operation_state",
|
|
68
|
+
"run_docs_build_worker",
|
|
69
|
+
"save_workspace_collection_root_override",
|
|
70
|
+
"set_hovered",
|
|
71
|
+
"start_docs_build",
|
|
72
|
+
]
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
"""Documentation browser/build helpers for the desktop UI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import subprocess
|
|
8
|
+
import sys
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from PySide6.QtCore import QUrl
|
|
12
|
+
from PySide6.QtWidgets import QFrame, QTextBrowser
|
|
13
|
+
from PySide6.QtWebEngineWidgets import QWebEngineView
|
|
14
|
+
|
|
15
|
+
from data_engine.ui.gui.helpers import start_worker_thread
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from data_engine.ui.gui.app import DataEngineWindow
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _explicit_missing_error_detail(subject: str) -> str:
|
|
22
|
+
return f"{subject} did not provide any additional error details."
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def create_docs_browser(window: "DataEngineWindow"):
|
|
26
|
+
if os.environ.get("QT_QPA_PLATFORM") != "offscreen":
|
|
27
|
+
window.docs_uses_webengine = True
|
|
28
|
+
browser = QWebEngineView()
|
|
29
|
+
browser.setStyleSheet("background: #ffffff;")
|
|
30
|
+
return browser
|
|
31
|
+
window.docs_uses_webengine = False
|
|
32
|
+
browser = QTextBrowser()
|
|
33
|
+
browser.setOpenExternalLinks(True)
|
|
34
|
+
browser.setFrameShape(QFrame.Shape.NoFrame)
|
|
35
|
+
return browser
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def docs_build_dir(window: "DataEngineWindow") -> Path:
|
|
39
|
+
return window.workspace_paths.documentation_dir / "_build" / "html"
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def initialize_docs_view(window: "DataEngineWindow") -> None:
|
|
43
|
+
docs_root = docs_build_dir(window)
|
|
44
|
+
window.docs_root_dir = docs_root if docs_root.is_dir() else None
|
|
45
|
+
if window.docs_root_dir is None:
|
|
46
|
+
window.docs_status_label.setText("Built documentation is not available yet.")
|
|
47
|
+
window.docs_generate_button.setVisible(True)
|
|
48
|
+
window.docs_generate_button.setEnabled(not window.docs_build_running)
|
|
49
|
+
if window.docs_build_running:
|
|
50
|
+
window.docs_browser.setHtml("<h2>Building documentation</h2><p>Generating the in-app docs site...</p>")
|
|
51
|
+
else:
|
|
52
|
+
window.docs_browser.setHtml(
|
|
53
|
+
"<h2>Documentation unavailable</h2><p>Generate the Sphinx site to populate the in-app docs view.</p>"
|
|
54
|
+
)
|
|
55
|
+
return
|
|
56
|
+
|
|
57
|
+
window.docs_generate_button.setVisible(False)
|
|
58
|
+
window.docs_generate_button.setEnabled(True)
|
|
59
|
+
load_docs_page(window, window._DOCS_HOME_PAGE)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def start_docs_build(window: "DataEngineWindow") -> None:
|
|
63
|
+
if window.docs_build_running:
|
|
64
|
+
return
|
|
65
|
+
window.docs_build_running = True
|
|
66
|
+
initialize_docs_view(window)
|
|
67
|
+
start_worker_thread(window, target=window._run_docs_build_worker)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def run_docs_build_worker(window: "DataEngineWindow") -> None:
|
|
71
|
+
build_dir = docs_build_dir(window)
|
|
72
|
+
try:
|
|
73
|
+
build_dir.parent.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
completed = subprocess.run(
|
|
75
|
+
[
|
|
76
|
+
sys.executable,
|
|
77
|
+
"-m",
|
|
78
|
+
"sphinx",
|
|
79
|
+
"-b",
|
|
80
|
+
"html",
|
|
81
|
+
str(window.workspace_paths.sphinx_source_dir),
|
|
82
|
+
str(build_dir),
|
|
83
|
+
],
|
|
84
|
+
capture_output=True,
|
|
85
|
+
text=True,
|
|
86
|
+
check=True,
|
|
87
|
+
)
|
|
88
|
+
message = completed.stdout.strip() or "Documentation build completed."
|
|
89
|
+
window.signals.docs_build_finished.emit(True, message)
|
|
90
|
+
except Exception as exc:
|
|
91
|
+
detail = str(exc)
|
|
92
|
+
if isinstance(exc, subprocess.CalledProcessError):
|
|
93
|
+
detail = (exc.stderr or exc.stdout or str(exc)).strip()
|
|
94
|
+
window.signals.docs_build_finished.emit(
|
|
95
|
+
False,
|
|
96
|
+
detail or _explicit_missing_error_detail("Documentation build"),
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def finish_docs_build(window: "DataEngineWindow", succeeded: bool, message: str) -> None:
|
|
101
|
+
window.docs_build_running = False
|
|
102
|
+
if succeeded:
|
|
103
|
+
initialize_docs_view(window)
|
|
104
|
+
window.docs_status_label.setText("index.html")
|
|
105
|
+
return
|
|
106
|
+
initialize_docs_view(window)
|
|
107
|
+
if not message:
|
|
108
|
+
message = _explicit_missing_error_detail("Documentation build")
|
|
109
|
+
window.docs_status_label.setText(message)
|
|
110
|
+
window._show_message_box_later(
|
|
111
|
+
title=window.windowTitle(),
|
|
112
|
+
text=f"Documentation build failed.\n\n{message}",
|
|
113
|
+
tone="error",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def load_docs_page(window: "DataEngineWindow", file_name: str) -> None:
|
|
118
|
+
if window.docs_root_dir is None:
|
|
119
|
+
return
|
|
120
|
+
target = window.docs_root_dir / file_name
|
|
121
|
+
if not target.exists():
|
|
122
|
+
window.docs_status_label.setText(f"Missing page: {file_name}")
|
|
123
|
+
window.docs_browser.setHtml(f"<h2>Missing documentation page</h2><p>{file_name}</p>")
|
|
124
|
+
return
|
|
125
|
+
window.docs_status_label.setText(target.name)
|
|
126
|
+
if window.docs_uses_webengine:
|
|
127
|
+
window.docs_browser.setUrl(QUrl.fromLocalFile(str(target)))
|
|
128
|
+
else:
|
|
129
|
+
window.docs_browser.setSource(QUrl.fromLocalFile(str(target)))
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
__all__ = [
|
|
133
|
+
"create_docs_browser",
|
|
134
|
+
"docs_build_dir",
|
|
135
|
+
"finish_docs_build",
|
|
136
|
+
"initialize_docs_view",
|
|
137
|
+
"load_docs_page",
|
|
138
|
+
"run_docs_build_worker",
|
|
139
|
+
"start_docs_build",
|
|
140
|
+
]
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
"""Log-list presentation helpers for the desktop UI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from PySide6.QtWidgets import QListWidgetItem
|
|
8
|
+
|
|
9
|
+
from data_engine.views import format_raw_log_message as shared_format_raw_log_message
|
|
10
|
+
from data_engine.ui.gui.widgets.logs import build_log_run_widget
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from data_engine.domain import FlowLogEntry, FlowRunState
|
|
14
|
+
from data_engine.ui.gui.app import DataEngineWindow
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def refresh_log_view(window: "DataEngineWindow", *, force_scroll_to_bottom: bool = False) -> None:
|
|
18
|
+
scrollbar = window.log_view.verticalScrollBar()
|
|
19
|
+
previous_value = scrollbar.value()
|
|
20
|
+
previous_maximum = scrollbar.maximum()
|
|
21
|
+
should_follow_tail = force_scroll_to_bottom or previous_value >= max(previous_maximum - 1, 0)
|
|
22
|
+
|
|
23
|
+
window.log_view.setUpdatesEnabled(False)
|
|
24
|
+
window.log_view.clear()
|
|
25
|
+
card = window.flow_cards.get(window.selected_flow_name or "")
|
|
26
|
+
run_groups = window.log_service.runs_for_flow(window.runtime_binding.log_store, card.name) if card is not None else ()
|
|
27
|
+
presentation = window.detail_application.build_selected_flow_presentation(
|
|
28
|
+
card=card,
|
|
29
|
+
tracker=window.operation_tracker,
|
|
30
|
+
flow_states=window.flow_states,
|
|
31
|
+
run_groups=tuple(run_groups),
|
|
32
|
+
selected_run_key=None,
|
|
33
|
+
max_visible_runs=window._MAX_VISIBLE_LOG_RUNS,
|
|
34
|
+
)
|
|
35
|
+
for run_group in presentation.visible_run_groups:
|
|
36
|
+
add_log_run_item(window, run_group)
|
|
37
|
+
window.log_view.setUpdatesEnabled(True)
|
|
38
|
+
|
|
39
|
+
scrollbar = window.log_view.verticalScrollBar()
|
|
40
|
+
if should_follow_tail:
|
|
41
|
+
scrollbar.setValue(scrollbar.maximum())
|
|
42
|
+
else:
|
|
43
|
+
scrollbar.setValue(min(previous_value, scrollbar.maximum()))
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def add_log_run_item(window: "DataEngineWindow", run_group: "FlowRunState") -> None:
|
|
47
|
+
item = QListWidgetItem(run_group.display_label)
|
|
48
|
+
widget = build_log_run_widget(window, run_group)
|
|
49
|
+
item.setSizeHint(widget.sizeHint())
|
|
50
|
+
window.log_view.addItem(item)
|
|
51
|
+
window.log_view.setItemWidget(item, widget)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def format_raw_log_message(entry: "FlowLogEntry") -> str:
|
|
55
|
+
return shared_format_raw_log_message(entry)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
__all__ = ["add_log_run_item", "format_raw_log_message", "refresh_log_view"]
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Daemon/runtime projection helpers for the desktop UI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from data_engine.domain import RuntimeSessionState
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from data_engine.ui.gui.app import DataEngineWindow
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def finish_daemon_startup(window: "DataEngineWindow", success: bool, error_text: str) -> None:
|
|
14
|
+
window._daemon_startup_in_progress = False
|
|
15
|
+
if not success and not error_text:
|
|
16
|
+
error_text = "Daemon startup did not provide any additional error details."
|
|
17
|
+
if not success and error_text and window.isVisible():
|
|
18
|
+
window._append_log_line(f"Daemon startup failed: {error_text}")
|
|
19
|
+
window._sync_from_daemon()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def apply_daemon_snapshot(window: "DataEngineWindow", snapshot) -> None:
|
|
23
|
+
window.runtime_session = RuntimeSessionState.from_daemon_snapshot(snapshot, window.flow_cards.values())
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
__all__ = [
|
|
27
|
+
"apply_daemon_snapshot",
|
|
28
|
+
"finish_daemon_startup",
|
|
29
|
+
]
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Sidebar presentation helpers for the desktop UI."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
|
|
7
|
+
from PySide6.QtWidgets import QLabel, QFrame, QWidget
|
|
8
|
+
|
|
9
|
+
from data_engine.views.flow_display import FlowRowDisplay, GroupRowDisplay
|
|
10
|
+
from data_engine.views.presentation import flow_group_name
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from data_engine.ui.gui.app import DataEngineWindow
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def refresh_sidebar_selection(window: "DataEngineWindow") -> None:
|
|
17
|
+
for flow_name, widget in window.sidebar_flow_widgets.items():
|
|
18
|
+
selected = flow_name == window.selected_flow_name
|
|
19
|
+
flow_index = widget.property("flowIndex")
|
|
20
|
+
for label in widget.findChildren(QLabel, "sidebarFlowNumber"):
|
|
21
|
+
if isinstance(flow_index, int):
|
|
22
|
+
label.setText(f"{flow_index:02d}")
|
|
23
|
+
if widget.property("selected") == selected:
|
|
24
|
+
continue
|
|
25
|
+
widget.setProperty("selected", selected)
|
|
26
|
+
repolish_widget_tree(widget)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def refresh_sidebar_state_views(window: "DataEngineWindow", changed_flow_names: set[str]) -> bool:
|
|
30
|
+
"""Refresh sidebar labels/colors in place for state-only changes.
|
|
31
|
+
|
|
32
|
+
Return True when the caller should fall back to a full sidebar rebuild.
|
|
33
|
+
"""
|
|
34
|
+
if not changed_flow_names or not window.sidebar_flow_widgets:
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
affected_groups: set[str] = set()
|
|
38
|
+
for flow_name in changed_flow_names:
|
|
39
|
+
card = window.flow_cards.get(flow_name)
|
|
40
|
+
widget = window.sidebar_flow_widgets.get(flow_name)
|
|
41
|
+
if card is None or widget is None:
|
|
42
|
+
return True
|
|
43
|
+
affected_groups.add(flow_group_name(card))
|
|
44
|
+
state = window.flow_states.get(card.name, card.state)
|
|
45
|
+
flow_display = FlowRowDisplay.from_card(card, state, primary="name")
|
|
46
|
+
widget.setToolTip(flow_display.tooltip)
|
|
47
|
+
for label in widget.findChildren(QLabel, "sidebarFlowMeta"):
|
|
48
|
+
label.setText(flow_display.secondary)
|
|
49
|
+
label.setProperty("stateColor", flow_display.state_color)
|
|
50
|
+
repolish_widget_tree(label)
|
|
51
|
+
for label in widget.findChildren(QLabel, "sidebarStateDot"):
|
|
52
|
+
label.setProperty("stateColor", flow_display.state_color)
|
|
53
|
+
repolish_widget_tree(label)
|
|
54
|
+
|
|
55
|
+
for group_name in affected_groups:
|
|
56
|
+
group_widget = window.sidebar_group_widgets.get(group_name)
|
|
57
|
+
if group_widget is None:
|
|
58
|
+
return True
|
|
59
|
+
entries = [card for card in window.flow_cards.values() if flow_group_name(card) == group_name]
|
|
60
|
+
group_display = GroupRowDisplay.from_group(group_name, entries, window.flow_states)
|
|
61
|
+
for label in group_widget.findChildren(QLabel, "sidebarGroupMeta"):
|
|
62
|
+
label.setText(group_display.secondary)
|
|
63
|
+
|
|
64
|
+
return False
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def set_hovered(widget: QFrame, hovered: bool) -> None:
|
|
68
|
+
"""Update one sidebar row hover property and repolish it."""
|
|
69
|
+
if widget.property("hovered") == hovered:
|
|
70
|
+
return
|
|
71
|
+
widget.setProperty("hovered", hovered)
|
|
72
|
+
repolish_widget_tree(widget)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def repolish_widget_tree(widget: QWidget) -> None:
|
|
76
|
+
"""Reapply stylesheet state to one widget and its child widgets."""
|
|
77
|
+
style = widget.style()
|
|
78
|
+
style.unpolish(widget)
|
|
79
|
+
style.polish(widget)
|
|
80
|
+
for child in widget.findChildren(QWidget):
|
|
81
|
+
child_style = child.style()
|
|
82
|
+
child_style.unpolish(child)
|
|
83
|
+
child_style.polish(child)
|
|
84
|
+
child.update()
|
|
85
|
+
widget.update()
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
__all__ = ["refresh_sidebar_selection", "refresh_sidebar_state_views", "repolish_widget_tree", "set_hovered"]
|