haybale-share 0.0.39__tar.gz
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.
- haybale_share-0.0.39/.gitignore +225 -0
- haybale_share-0.0.39/PKG-INFO +8 -0
- haybale_share-0.0.39/haybale_share/__init__.py +45 -0
- haybale_share-0.0.39/haybale_share/_flow/__init__.py +0 -0
- haybale_share-0.0.39/haybale_share/_flow/__main__.py +174 -0
- haybale_share-0.0.39/haybale_share/_flow/_harness.py +491 -0
- haybale_share-0.0.39/haybale_share/_flow/_state.py +342 -0
- haybale_share-0.0.39/haybale_share/_flow/chrome.py +58 -0
- haybale_share-0.0.39/haybale_share/_flow/copy.py +81 -0
- haybale_share-0.0.39/haybale_share/_flow/panels.py +590 -0
- haybale_share-0.0.39/haybale_share/editors/__init__.py +0 -0
- haybale_share-0.0.39/haybale_share/editors/share_editor.py +117 -0
- haybale_share-0.0.39/haybale_share/py.typed +0 -0
- haybale_share-0.0.39/pyproject.toml +26 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# C extensions
|
|
7
|
+
*.so
|
|
8
|
+
|
|
9
|
+
# Distribution / packaging
|
|
10
|
+
.Python
|
|
11
|
+
build/
|
|
12
|
+
develop-eggs/
|
|
13
|
+
dist/
|
|
14
|
+
downloads/
|
|
15
|
+
eggs/
|
|
16
|
+
.eggs/
|
|
17
|
+
lib/
|
|
18
|
+
lib64/
|
|
19
|
+
parts/
|
|
20
|
+
sdist/
|
|
21
|
+
var/
|
|
22
|
+
wheels/
|
|
23
|
+
share/python-wheels/
|
|
24
|
+
*.egg-info/
|
|
25
|
+
.installed.cfg
|
|
26
|
+
*.egg
|
|
27
|
+
MANIFEST
|
|
28
|
+
|
|
29
|
+
# PyInstaller
|
|
30
|
+
# Usually these files are written by a python script from a template
|
|
31
|
+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
|
32
|
+
*.manifest
|
|
33
|
+
*.spec
|
|
34
|
+
|
|
35
|
+
# The MANIFEST/*.manifest packaging-artifact rules above are unanchored and
|
|
36
|
+
# (on a case-insensitive filesystem) also match our real, tracked
|
|
37
|
+
# haywire_studio/share/manifest/ package — un-ignore it explicitly.
|
|
38
|
+
!packages/haywire-studio/src/haywire_studio/share/manifest/
|
|
39
|
+
|
|
40
|
+
# Installer logs
|
|
41
|
+
pip-log.txt
|
|
42
|
+
pip-delete-this-directory.txt
|
|
43
|
+
|
|
44
|
+
# Unit test / coverage reports
|
|
45
|
+
htmlcov/
|
|
46
|
+
.tox/
|
|
47
|
+
.nox/
|
|
48
|
+
.coverage
|
|
49
|
+
.coverage.*
|
|
50
|
+
.cache
|
|
51
|
+
nosetests.xml
|
|
52
|
+
coverage.xml
|
|
53
|
+
*.cover
|
|
54
|
+
*.py,cover
|
|
55
|
+
.hypothesis/
|
|
56
|
+
.pytest_cache/
|
|
57
|
+
cover/
|
|
58
|
+
|
|
59
|
+
# Translations
|
|
60
|
+
*.mo
|
|
61
|
+
*.pot
|
|
62
|
+
|
|
63
|
+
# Django stuff:
|
|
64
|
+
*.log
|
|
65
|
+
local_settings.py
|
|
66
|
+
db.sqlite3
|
|
67
|
+
db.sqlite3-journal
|
|
68
|
+
|
|
69
|
+
# Flask stuff:
|
|
70
|
+
instance/
|
|
71
|
+
.webassets-cache
|
|
72
|
+
|
|
73
|
+
# Scrapy stuff:
|
|
74
|
+
.scrapy
|
|
75
|
+
|
|
76
|
+
# Sphinx documentation
|
|
77
|
+
docs/_build/
|
|
78
|
+
|
|
79
|
+
# PyBuilder
|
|
80
|
+
.pybuilder/
|
|
81
|
+
target/
|
|
82
|
+
|
|
83
|
+
# Jupyter Notebook
|
|
84
|
+
.ipynb_checkpoints
|
|
85
|
+
|
|
86
|
+
# IPython
|
|
87
|
+
profile_default/
|
|
88
|
+
ipython_config.py
|
|
89
|
+
|
|
90
|
+
# pyenv
|
|
91
|
+
# For a library or package, you might want to ignore these files since the code is
|
|
92
|
+
# intended to run in multiple environments; otherwise, check them in:
|
|
93
|
+
# .python-version
|
|
94
|
+
|
|
95
|
+
# pipenv
|
|
96
|
+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
|
97
|
+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
|
98
|
+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
|
99
|
+
# install all needed dependencies.
|
|
100
|
+
#Pipfile.lock
|
|
101
|
+
|
|
102
|
+
# UV
|
|
103
|
+
# Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
|
|
104
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
105
|
+
# commonly ignored for libraries.
|
|
106
|
+
#uv.lock
|
|
107
|
+
|
|
108
|
+
# poetry
|
|
109
|
+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
|
|
110
|
+
# This is especially recommended for binary packages to ensure reproducibility, and is more
|
|
111
|
+
# commonly ignored for libraries.
|
|
112
|
+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
|
|
113
|
+
#poetry.lock
|
|
114
|
+
|
|
115
|
+
# pdm
|
|
116
|
+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
|
|
117
|
+
#pdm.lock
|
|
118
|
+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
|
|
119
|
+
# in version control.
|
|
120
|
+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
|
|
121
|
+
.pdm.toml
|
|
122
|
+
.pdm-python
|
|
123
|
+
.pdm-build/
|
|
124
|
+
|
|
125
|
+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
|
|
126
|
+
__pypackages__/
|
|
127
|
+
|
|
128
|
+
# Celery stuff
|
|
129
|
+
celerybeat-schedule
|
|
130
|
+
celerybeat.pid
|
|
131
|
+
|
|
132
|
+
# SageMath parsed files
|
|
133
|
+
*.sage.py
|
|
134
|
+
|
|
135
|
+
# Environments
|
|
136
|
+
**/.env
|
|
137
|
+
.venv
|
|
138
|
+
env/
|
|
139
|
+
venv/
|
|
140
|
+
ENV/
|
|
141
|
+
env.bak/
|
|
142
|
+
venv.bak/
|
|
143
|
+
|
|
144
|
+
# Spyder project settings
|
|
145
|
+
.spyderproject
|
|
146
|
+
.spyproject
|
|
147
|
+
|
|
148
|
+
# Rope project settings
|
|
149
|
+
.ropeproject
|
|
150
|
+
|
|
151
|
+
# mkdocs documentation
|
|
152
|
+
/site
|
|
153
|
+
|
|
154
|
+
# mypy
|
|
155
|
+
.mypy_cache/
|
|
156
|
+
.dmypy.json
|
|
157
|
+
dmypy.json
|
|
158
|
+
|
|
159
|
+
# Pyre type checker
|
|
160
|
+
.pyre/
|
|
161
|
+
|
|
162
|
+
# pytype static type analyzer
|
|
163
|
+
.pytype/
|
|
164
|
+
|
|
165
|
+
# Cython debug symbols
|
|
166
|
+
cython_debug/
|
|
167
|
+
|
|
168
|
+
# PyCharm
|
|
169
|
+
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
170
|
+
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
171
|
+
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
172
|
+
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
173
|
+
.idea/
|
|
174
|
+
|
|
175
|
+
# Abstra
|
|
176
|
+
# Abstra is an AI-powered process automation framework.
|
|
177
|
+
# Ignore directories containing user credentials, local state, and settings.
|
|
178
|
+
# Learn more at https://abstra.io/docs
|
|
179
|
+
.abstra/
|
|
180
|
+
|
|
181
|
+
# Visual Studio Code
|
|
182
|
+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
|
|
183
|
+
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
|
|
184
|
+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
|
|
185
|
+
# you could uncomment the following to ignore the enitre vscode folder
|
|
186
|
+
.vscode/
|
|
187
|
+
|
|
188
|
+
# Ruff stuff:
|
|
189
|
+
.ruff_cache/
|
|
190
|
+
|
|
191
|
+
# PyPI configuration file
|
|
192
|
+
.pypirc
|
|
193
|
+
|
|
194
|
+
# Cursor
|
|
195
|
+
# Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
|
|
196
|
+
# exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
|
|
197
|
+
# refer to https://docs.cursor.com/context/ignore-files
|
|
198
|
+
.cursorignore
|
|
199
|
+
.cursorindexingignore
|
|
200
|
+
|
|
201
|
+
.DS_Store
|
|
202
|
+
saves/*.*
|
|
203
|
+
|
|
204
|
+
# Dev-mode graphs (local testing only, not part of the repo)
|
|
205
|
+
graphs/*.json
|
|
206
|
+
# MkDocs build output
|
|
207
|
+
site/
|
|
208
|
+
|
|
209
|
+
# Baked docs for the haywire-core wheel (generated by scripts/bake_docs.py;
|
|
210
|
+
# force-included into the wheel at build time). The *contents* are never
|
|
211
|
+
# committed, but the directory itself is kept via a committed .gitkeep so the
|
|
212
|
+
# hatchling force-include target exists in a fresh checkout — otherwise every
|
|
213
|
+
# editable install (`uv sync`) fails the build on a missing force-include path.
|
|
214
|
+
packages/haywire-core/src/haywire/_baked_docs/*
|
|
215
|
+
!packages/haywire-core/src/haywire/_baked_docs/.gitkeep
|
|
216
|
+
|
|
217
|
+
# Local git worktrees (claude-code workflow)
|
|
218
|
+
.worktrees/
|
|
219
|
+
|
|
220
|
+
# Haywire project config (user-local, not repo state)
|
|
221
|
+
.haywire/
|
|
222
|
+
|
|
223
|
+
# Local symlink to external dev project (machine-specific path)
|
|
224
|
+
barn/haybale-visiongraph
|
|
225
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Project publishing — the Share editor and its flow.
|
|
2
|
+
|
|
3
|
+
Publishing is PROJECT-scoped, not library-scoped (ADR 0023): a haywire project
|
|
4
|
+
is one uv workspace root with one marketstall feed and one git remote, and
|
|
5
|
+
every ``barn/*`` library versions in lockstep. That is why this is its own
|
|
6
|
+
library rather than an editor inside haybale-marketplace, whose job is
|
|
7
|
+
*consuming* feeds — its own architecture doc says it is "not a publisher".
|
|
8
|
+
|
|
9
|
+
The engine lives in :mod:`haywire.core.publishing`; everything here is UI.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from importlib.metadata import version as _pkg_version
|
|
13
|
+
from pathlib import Path
|
|
14
|
+
|
|
15
|
+
from haywire.core.library.base import BaseLibrary
|
|
16
|
+
from haywire.core.library.decorator import library
|
|
17
|
+
from haywire.ui.editor.registry import EditorTypeRegistry
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@library(
|
|
21
|
+
label="Share",
|
|
22
|
+
id="share",
|
|
23
|
+
version=_pkg_version("haybale-share"),
|
|
24
|
+
description="Publish this project: dependencies, version, docs, commit, tag, push",
|
|
25
|
+
url="",
|
|
26
|
+
help_url="",
|
|
27
|
+
author="",
|
|
28
|
+
author_url="",
|
|
29
|
+
dependencies=[],
|
|
30
|
+
tags=["publishing"],
|
|
31
|
+
file_watcher=True,
|
|
32
|
+
)
|
|
33
|
+
class Library(BaseLibrary):
|
|
34
|
+
"""Project publishing library — the Share editor."""
|
|
35
|
+
|
|
36
|
+
def register_components(self):
|
|
37
|
+
base_path = Path(__file__).parent
|
|
38
|
+
|
|
39
|
+
self.add_folder_to_registry(
|
|
40
|
+
folder_path=str(base_path / "editors"),
|
|
41
|
+
registry_cls=EditorTypeRegistry,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def validate(self) -> bool:
|
|
45
|
+
return True
|
|
File without changes
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
"""Runnable panel harness for the Share flow.
|
|
2
|
+
|
|
3
|
+
uv run python -m haybale_share._flow
|
|
4
|
+
|
|
5
|
+
Opens http://localhost:8091 with every screen in every state, side by side
|
|
6
|
+
with the scenario list. Nothing writes: each scenario hand-builds flow state
|
|
7
|
+
and calls the panel directly, never a pipeline step. See ``_harness.py`` for
|
|
8
|
+
why fixtures beat driving a real ``SharePipeline`` here.
|
|
9
|
+
|
|
10
|
+
Boots a real library system so the themes and ``hui`` elements resolve — the
|
|
11
|
+
point is to look at the panels as they will actually appear, not at unstyled
|
|
12
|
+
approximations. Mirrors ``tests/ui/harness/app.py``.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import os
|
|
18
|
+
import sys
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from typing import Callable
|
|
22
|
+
|
|
23
|
+
from nicegui import app, ui
|
|
24
|
+
|
|
25
|
+
from haywire.core.di.config import (
|
|
26
|
+
create_library_system_service,
|
|
27
|
+
set_global_injector,
|
|
28
|
+
set_library_system,
|
|
29
|
+
)
|
|
30
|
+
from haywire.core.di.context import set_workspace_root
|
|
31
|
+
|
|
32
|
+
from ._harness import Panel
|
|
33
|
+
from ._state import ShareFlow
|
|
34
|
+
|
|
35
|
+
_HERE = Path(__file__).resolve()
|
|
36
|
+
# _flow/ → haybale_share/ → haybale-share/ → barn/ → repo root
|
|
37
|
+
_REPO_ROOT = _HERE.parent.parent.parent.parent
|
|
38
|
+
_BARN = str(_REPO_ROOT / "barn")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _render_scenario(container: ui.element, label: str, build, panel) -> None:
|
|
42
|
+
"""Render one scenario into *container*, inside the flow's real chrome.
|
|
43
|
+
|
|
44
|
+
The progress bar, warning rows and error banner come from
|
|
45
|
+
``haywire.ui.components.stepper``, exactly as ``show_step_flow`` composes
|
|
46
|
+
them — a panel judged without its chrome is judged in the wrong context.
|
|
47
|
+
"""
|
|
48
|
+
from haywire.ui.components.stepper import render_error, render_progress, render_warnings
|
|
49
|
+
|
|
50
|
+
from .panels import suppress_duplicate_error
|
|
51
|
+
|
|
52
|
+
container.clear()
|
|
53
|
+
flow = build()
|
|
54
|
+
|
|
55
|
+
def _rerender() -> None:
|
|
56
|
+
_render_scenario(container, label, build, panel)
|
|
57
|
+
|
|
58
|
+
with container:
|
|
59
|
+
ui.label(label).classes("text-sm font-medium")
|
|
60
|
+
ui.label(f"step = {flow.step!r}").classes("text-xs font-mono hw-text-muted")
|
|
61
|
+
|
|
62
|
+
# 640px: the width show_share_flow() opens the real popup at.
|
|
63
|
+
#
|
|
64
|
+
# `hw-panel` is not decoration — nearly every Quasar field rule in
|
|
65
|
+
# STATIC_CSS is scoped to it (the field label colour, the underline
|
|
66
|
+
# colours, the focus background). Popup applies it to its own content
|
|
67
|
+
# column, so a harness that omits it renders every select and input
|
|
68
|
+
# with browser defaults and misreports what the panel looks like.
|
|
69
|
+
with (
|
|
70
|
+
ui.column()
|
|
71
|
+
.classes("gap-2 p-3 rounded hw-panel")
|
|
72
|
+
.style("width: 640px; border: 1px solid var(--hw-border); background: var(--hw-bg-elevated);")
|
|
73
|
+
):
|
|
74
|
+
render_progress(flow)
|
|
75
|
+
render_warnings(flow)
|
|
76
|
+
# The same error_detail show_share_flow passes. Rendering the banner
|
|
77
|
+
# without it is how this harness first showed a duplicate the real
|
|
78
|
+
# flow does not have — a harness that diverges from production
|
|
79
|
+
# reports bugs that are its own.
|
|
80
|
+
render_error(flow, _rerender, suppress_duplicate_error)
|
|
81
|
+
panel(flow, _rerender)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _inject_theme() -> None:
|
|
85
|
+
"""Put the workbench theme's --hw-* variables on :root.
|
|
86
|
+
|
|
87
|
+
Without this every token resolves to empty and the panels render with
|
|
88
|
+
browser defaults — borders vanish, `hw-text-dim` is plain black. The app
|
|
89
|
+
shell normally does this (`shell.py`'s `_theme_css`), but the harness has
|
|
90
|
+
no shell, so it did what a harness must never do: showed the panels
|
|
91
|
+
looking materially different from production, and specifically hid the
|
|
92
|
+
boundaries this screen relies on.
|
|
93
|
+
"""
|
|
94
|
+
from haywire.core.di.config import get_library_system
|
|
95
|
+
from haywire.ui.app.shell import STATIC_CSS
|
|
96
|
+
|
|
97
|
+
try:
|
|
98
|
+
registry = get_library_system().get_theme_registry()
|
|
99
|
+
keys = list(registry.list_workbench_keys())
|
|
100
|
+
if not keys:
|
|
101
|
+
return
|
|
102
|
+
theme = registry.get_workbench(keys[0])
|
|
103
|
+
css = " ".join(f"{k}: {v};" for k, v in theme.to_css_vars().items())
|
|
104
|
+
# The page background too: the workbench themes are dark, and a dark
|
|
105
|
+
# panel floating on a white page misrepresents every contrast
|
|
106
|
+
# judgement this harness exists to support.
|
|
107
|
+
body_css = "body { background: var(--hw-bg-page); color: var(--hw-text-body); }"
|
|
108
|
+
ui.add_head_html(f"<style>:root {{ {css} }} {body_css}</style>")
|
|
109
|
+
# The shell's own static CSS, verbatim — it is what styles Quasar
|
|
110
|
+
# fields and (globally, since they portal to <body>) the .q-menu
|
|
111
|
+
# dropdowns. Without it a select opens as a white menu with white
|
|
112
|
+
# text, which is a harness artefact and not something the panel does.
|
|
113
|
+
ui.add_css(STATIC_CSS)
|
|
114
|
+
except Exception as exc: # noqa: BLE001 — a themeless harness still renders
|
|
115
|
+
print(f"harness: could not inject a workbench theme ({exc})")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _page() -> None:
|
|
119
|
+
from ._harness import SCENARIOS
|
|
120
|
+
|
|
121
|
+
_inject_theme()
|
|
122
|
+
ui.query("body").classes("hw-app")
|
|
123
|
+
|
|
124
|
+
with ui.row().classes("w-full h-screen gap-0 items-stretch"):
|
|
125
|
+
with (
|
|
126
|
+
ui.column()
|
|
127
|
+
.classes("gap-1 p-3 h-full overflow-auto")
|
|
128
|
+
.style("width: 300px; border-right: 1px solid var(--hw-border);")
|
|
129
|
+
):
|
|
130
|
+
ui.label("Share flow — panels").classes("text-sm font-medium")
|
|
131
|
+
ui.label("Fixture state. Nothing writes.").classes("text-xs hw-text-muted")
|
|
132
|
+
buttons: list[tuple[ui.button, str, Callable[[], ShareFlow], Panel]] = []
|
|
133
|
+
|
|
134
|
+
with ui.scroll_area().classes("flex-1 w-full"):
|
|
135
|
+
for group, entries in SCENARIOS.items():
|
|
136
|
+
ui.label(group).classes("text-xs hw-text-dim mt-2")
|
|
137
|
+
for label, build, panel in entries:
|
|
138
|
+
button = (
|
|
139
|
+
ui.button(label).props("flat dense align=left no-caps").classes("w-full text-xs")
|
|
140
|
+
)
|
|
141
|
+
buttons.append((button, label, build, panel))
|
|
142
|
+
|
|
143
|
+
with ui.scroll_area().classes("flex-1 h-full"):
|
|
144
|
+
stage = ui.column().classes("p-4 gap-2")
|
|
145
|
+
|
|
146
|
+
for button, label, build, panel in buttons:
|
|
147
|
+
button.on_click(lambda _e=None, la=label, b=build, p=panel: _render_scenario(stage, la, b, p))
|
|
148
|
+
|
|
149
|
+
first_label, first_build, first_panel = next(iter(SCENARIOS.values()))[0]
|
|
150
|
+
_render_scenario(stage, first_label, first_build, first_panel)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def main() -> None:
|
|
154
|
+
library_paths = [_BARN] if os.path.isdir(_BARN) else []
|
|
155
|
+
service = create_library_system_service(
|
|
156
|
+
workspace_root=str(_REPO_ROOT),
|
|
157
|
+
library_paths=library_paths,
|
|
158
|
+
enable_file_watching=False,
|
|
159
|
+
watch_settings=False,
|
|
160
|
+
)
|
|
161
|
+
set_library_system(service)
|
|
162
|
+
set_global_injector(service.injector)
|
|
163
|
+
set_workspace_root(str(_REPO_ROOT))
|
|
164
|
+
|
|
165
|
+
ui.page("/")(_page)
|
|
166
|
+
app.on_shutdown(lambda: service.cleanup() if hasattr(service, "cleanup") else None)
|
|
167
|
+
|
|
168
|
+
ui.run(port=8091, show=False, title="Share flow — panel harness", reload=False)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
if __name__ in {"__main__", "__mp_main__"}:
|
|
172
|
+
if str(_REPO_ROOT) not in sys.path:
|
|
173
|
+
sys.path.insert(0, str(_REPO_ROOT))
|
|
174
|
+
main()
|