labforge 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.
- labforge/__init__.py +17 -0
- labforge/controls.py +278 -0
- labforge/figures.py +92 -0
- labforge/lab.py +165 -0
- labforge/mathtext.py +140 -0
- labforge/pages/__init__.py +6 -0
- labforge/pages/analysis.py +99 -0
- labforge/pages/panel.py +109 -0
- labforge/pages/scroll.py +61 -0
- labforge/pages/simulation.py +71 -0
- labforge/pages/theory.py +40 -0
- labforge/pages/visualization.py +30 -0
- labforge/param.py +186 -0
- labforge/registry.py +36 -0
- labforge/scan.py +75 -0
- labforge/shell.py +220 -0
- labforge/state.py +74 -0
- labforge/theme.py +323 -0
- labforge/ui.py +167 -0
- labforge-0.1.0.dist-info/METADATA +232 -0
- labforge-0.1.0.dist-info/RECORD +23 -0
- labforge-0.1.0.dist-info/WHEEL +4 -0
- labforge-0.1.0.dist-info/licenses/LICENSE +21 -0
labforge/ui.py
ADDED
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Shared UI builders: the app's typographic and surface vocabulary.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import flet as ft
|
|
6
|
+
|
|
7
|
+
from .mathtext import equation_image
|
|
8
|
+
|
|
9
|
+
# The app's two typographic voices: a grotesque for prose and a monospace for
|
|
10
|
+
# anything that is data — parameter labels, readouts, tables, telemetry lines.
|
|
11
|
+
# Both resolve by name as macOS system fonts; bundle files via page.fonts to
|
|
12
|
+
# ship the same faces on Windows or in the browser.
|
|
13
|
+
FONT_BODY = "Helvetica Neue"
|
|
14
|
+
FONT_MONO = "Menlo"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def image(data, width=None, height=None):
|
|
18
|
+
"""
|
|
19
|
+
Display a raw base64 PNG (no data: prefix), as produced by figures/mathtext,
|
|
20
|
+
at the given logical-pixel size (None keeps the intrinsic size).
|
|
21
|
+
"""
|
|
22
|
+
# Flet 0.86 Image takes base64 directly on src (there is no src_base64).
|
|
23
|
+
return ft.Image(src=data, width=width, height=height)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def equation(latex, fontsize=18):
|
|
27
|
+
"""
|
|
28
|
+
Render displayed math — content without surrounding dollar signs, e.g.
|
|
29
|
+
r"f(x)=\\lambda" — as a LaTeX image sized to the measured equation.
|
|
30
|
+
"""
|
|
31
|
+
img = equation_image(latex, fontsize=fontsize)
|
|
32
|
+
return image(img["src"], width=img["width"], height=img["height"])
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def heading(text):
|
|
36
|
+
"""Section heading: tracked display caps for the top of a page or section."""
|
|
37
|
+
return ft.Text(
|
|
38
|
+
text.upper(),
|
|
39
|
+
style=ft.TextStyle(size=22, weight=ft.FontWeight.W_800, letter_spacing=1.5),
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def subheading(text):
|
|
44
|
+
"""Letterspaced all-caps microlabel that marks a block within a page."""
|
|
45
|
+
return ft.Text(
|
|
46
|
+
text.upper(),
|
|
47
|
+
style=ft.TextStyle(
|
|
48
|
+
size=11,
|
|
49
|
+
weight=ft.FontWeight.W_700,
|
|
50
|
+
letter_spacing=2.5,
|
|
51
|
+
color=ft.Colors.PRIMARY,
|
|
52
|
+
font_family=FONT_MONO,
|
|
53
|
+
),
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def mono(text, size=12, color=ft.Colors.ON_SURFACE_VARIANT):
|
|
58
|
+
"""Monospace telemetry text for status lines and small readouts."""
|
|
59
|
+
return ft.Text(
|
|
60
|
+
text,
|
|
61
|
+
style=ft.TextStyle(size=size, font_family=FONT_MONO, letter_spacing=0.5, color=color),
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def body(markdown):
|
|
66
|
+
"""Prose as selectable GitHub-flavored Markdown."""
|
|
67
|
+
return ft.Markdown(
|
|
68
|
+
value=markdown,
|
|
69
|
+
selectable=True,
|
|
70
|
+
extension_set=ft.MarkdownExtensionSet.GITHUB_WEB,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
def action_button(label, on_click):
|
|
75
|
+
"""Primary action: a squared filled button with a tracked mono-caps label."""
|
|
76
|
+
return ft.FilledButton(
|
|
77
|
+
content=ft.Text(
|
|
78
|
+
label.upper(),
|
|
79
|
+
style=ft.TextStyle(
|
|
80
|
+
size=13, weight=ft.FontWeight.W_700, letter_spacing=2, font_family=FONT_MONO
|
|
81
|
+
),
|
|
82
|
+
),
|
|
83
|
+
on_click=on_click,
|
|
84
|
+
style=ft.ButtonStyle(shape=ft.RoundedRectangleBorder(radius=2)),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def card(content, padding=16):
|
|
89
|
+
"""
|
|
90
|
+
Group related controls on a card surface: a lifted tone, a hairline outline
|
|
91
|
+
and near-square corners.
|
|
92
|
+
"""
|
|
93
|
+
return ft.Container(
|
|
94
|
+
content=content,
|
|
95
|
+
padding=padding,
|
|
96
|
+
bgcolor=ft.Colors.SURFACE_CONTAINER_LOW,
|
|
97
|
+
border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT),
|
|
98
|
+
border_radius=2,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def placeholder(text):
|
|
103
|
+
"""Muted card standing in for a section the author registered nothing for."""
|
|
104
|
+
return card(ft.Text(text, color=ft.Colors.ON_SURFACE_VARIANT), padding=20)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def data_table(columns, rows):
|
|
108
|
+
"""
|
|
109
|
+
An ft.DataTable (same columns/rows arguments) with the house treatment —
|
|
110
|
+
tinted mono-caps heading row, hairline rules, monospace cells — so every
|
|
111
|
+
table in an app reads the same.
|
|
112
|
+
"""
|
|
113
|
+
return ft.DataTable(
|
|
114
|
+
columns=columns,
|
|
115
|
+
rows=rows,
|
|
116
|
+
heading_row_color=ft.Colors.SURFACE_CONTAINER_LOW,
|
|
117
|
+
heading_row_height=44,
|
|
118
|
+
heading_text_style=ft.TextStyle(
|
|
119
|
+
size=11,
|
|
120
|
+
weight=ft.FontWeight.W_700,
|
|
121
|
+
letter_spacing=2,
|
|
122
|
+
font_family=FONT_MONO,
|
|
123
|
+
color=ft.Colors.ON_SURFACE_VARIANT,
|
|
124
|
+
),
|
|
125
|
+
data_text_style=ft.TextStyle(size=13, font_family=FONT_MONO),
|
|
126
|
+
horizontal_lines=ft.BorderSide(1, ft.Colors.OUTLINE_VARIANT),
|
|
127
|
+
border=ft.Border.all(1, ft.Colors.OUTLINE_VARIANT),
|
|
128
|
+
border_radius=2,
|
|
129
|
+
column_spacing=32,
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def needs_run():
|
|
134
|
+
"""Instrument-panel status line shown before the worker has been run."""
|
|
135
|
+
return card(
|
|
136
|
+
ft.Row(
|
|
137
|
+
spacing=12,
|
|
138
|
+
vertical_alignment=ft.CrossAxisAlignment.CENTER,
|
|
139
|
+
controls=[
|
|
140
|
+
ft.Icon(ft.Icons.SCIENCE_OUTLINED, size=18, color=ft.Colors.PRIMARY),
|
|
141
|
+
ft.Text(
|
|
142
|
+
"NO DATA",
|
|
143
|
+
style=ft.TextStyle(
|
|
144
|
+
size=12,
|
|
145
|
+
weight=ft.FontWeight.W_700,
|
|
146
|
+
letter_spacing=2,
|
|
147
|
+
font_family=FONT_MONO,
|
|
148
|
+
),
|
|
149
|
+
),
|
|
150
|
+
mono("— set parameters on the Simulation page and press RUN"),
|
|
151
|
+
],
|
|
152
|
+
),
|
|
153
|
+
padding=16,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
def page_scaffold(title, controls):
|
|
158
|
+
"""
|
|
159
|
+
Scrollable page body: a heading above the supplied controls, in a column
|
|
160
|
+
that fills the content pane and scrolls when tall.
|
|
161
|
+
"""
|
|
162
|
+
return ft.Column(
|
|
163
|
+
expand=True,
|
|
164
|
+
scroll=ft.ScrollMode.AUTO,
|
|
165
|
+
spacing=16,
|
|
166
|
+
controls=[heading(title), *controls],
|
|
167
|
+
)
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: labforge
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Wrap plain Python functions into a small scientific Flet desktop app: theory, simulation, visualization, analysis.
|
|
5
|
+
Project-URL: Homepage, https://github.com/laroccod/labforge
|
|
6
|
+
Project-URL: Repository, https://github.com/laroccod/labforge
|
|
7
|
+
Project-URL: Issues, https://github.com/laroccod/labforge/issues
|
|
8
|
+
Project-URL: Changelog, https://github.com/laroccod/labforge/blob/main/CHANGELOG.md
|
|
9
|
+
Author: Daniel La Rocco
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: desktop-app,flet,gui,matplotlib,scientific-computing,simulation,visualization
|
|
13
|
+
Classifier: Development Status :: 3 - Alpha
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Operating System :: MacOS
|
|
17
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
23
|
+
Classifier: Topic :: Scientific/Engineering
|
|
24
|
+
Classifier: Topic :: Scientific/Engineering :: Visualization
|
|
25
|
+
Requires-Python: >=3.10
|
|
26
|
+
Requires-Dist: flet==0.86.0
|
|
27
|
+
Requires-Dist: matplotlib>=3.8
|
|
28
|
+
Requires-Dist: numpy>=2.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: black>=24.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
# `labforge`
|
|
35
|
+
|
|
36
|
+
*By Daniel La Rocco*
|
|
37
|
+
|
|
38
|
+
## **Turn plain Python scripts into a small scientific desktop app.**
|
|
39
|
+
|
|
40
|
+
labforge wraps a simulation you already have — a function that produces data,
|
|
41
|
+
a function that plots it, a function that summarizes it — into a polished
|
|
42
|
+
four-section app following the **theory → simulation → visualization →
|
|
43
|
+
analysis** workflow. Pure Python, rendered with [Flet](https://flet.dev); no
|
|
44
|
+
HTML, no JavaScript, no callbacks to wire up.
|
|
45
|
+
|
|
46
|
+
You provide the science and labforge supplies the app shell, the parameter controls
|
|
47
|
+
generated from your function signatures, the parameter-scan engine,
|
|
48
|
+
LaTeX rendering for your theory notes, and eight instrument-panel themes, four
|
|
49
|
+
dark and four light.
|
|
50
|
+
|
|
51
|
+

|
|
52
|
+
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
import matplotlib.pyplot as plt
|
|
57
|
+
import numpy as np
|
|
58
|
+
|
|
59
|
+
import labforge
|
|
60
|
+
from labforge import Lab, Param, ScanResult
|
|
61
|
+
|
|
62
|
+
lab = Lab("gausslab")
|
|
63
|
+
lab.set_theory("theory.md") # markdown file or string; $$...$$ becomes LaTeX
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def sample(mu=0.0, sigma=1.0, n=2000, seed=42):
|
|
67
|
+
"""Draw n Gaussian variates; reproducible for a given seed."""
|
|
68
|
+
return np.random.default_rng(seed).normal(mu, sigma, n)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
lab.add_worker(sample, {
|
|
72
|
+
"mu": Param(default=0.0, bounds=(-5, 5), scan=True),
|
|
73
|
+
"sigma": Param(default=1.0, bounds=(0.1, 4), scan=True),
|
|
74
|
+
"n": Param(kind="int", default=2000, bounds=(10, 100_000)),
|
|
75
|
+
"seed": "int",
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def histogram(data, bins=40):
|
|
80
|
+
fig, ax = plt.subplots(figsize=(7, 3.4))
|
|
81
|
+
if isinstance(data, ScanResult): # a parameter scan: one histogram per grid point
|
|
82
|
+
for params, draws in data:
|
|
83
|
+
ax.hist(draws, bins=bins, density=True, alpha=0.5,
|
|
84
|
+
label=", ".join(f"{k} = {params[k]:g}" for k in data.keys))
|
|
85
|
+
ax.legend()
|
|
86
|
+
else:
|
|
87
|
+
ax.hist(data, bins=bins, density=True, color=labforge.palette().data)
|
|
88
|
+
labforge.style(fig, ax) # optional house treatment
|
|
89
|
+
return fig, ax
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
lab.add_viz(histogram, "Histogram", "Density histogram of the draw.",
|
|
93
|
+
{"bins": Param(kind="int", default=40, bounds=(5, 200))})
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def moments(data):
|
|
97
|
+
if isinstance(data, ScanResult): # one table row per grid point
|
|
98
|
+
return [{**params, "mean": float(np.mean(d)), "std": float(np.std(d))}
|
|
99
|
+
for params, d in data]
|
|
100
|
+
return {"mean": float(np.mean(data)), "std": float(np.std(data, ddof=1))}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
lab.add_analysis(moments, "Moments", "Sample moments of the draw.")
|
|
104
|
+
|
|
105
|
+
lab.open()
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
That is the whole app. `lab.open()` opens a native window with four pages —
|
|
109
|
+
Theory, Simulation, Visualization, Analysis — a slider for every bounded
|
|
110
|
+
parameter, a Run button, and tabs for each registered visualization and
|
|
111
|
+
analysis.
|
|
112
|
+
|
|
113
|
+
An extended version of this example — same lab plus a fitted-density overlay
|
|
114
|
+
and a Q-Q plot tab — lives at [`examples/demo_lab.py`](examples/demo_lab.py):
|
|
115
|
+
|
|
116
|
+
```bash
|
|
117
|
+
python examples/demo_lab.py # native window
|
|
118
|
+
python examples/demo_lab.py --browser 8550 # serve at http://localhost:8550
|
|
119
|
+
python examples/demo_lab.py --scroll # one continuous scrolling page
|
|
120
|
+
python examples/demo_lab.py --theme lavender
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Concepts
|
|
124
|
+
|
|
125
|
+
**Worker.** One function produces the data. Each keyword argument gets a UI
|
|
126
|
+
control from its `Param` spec — or from the signature default alone, if you
|
|
127
|
+
spec nothing:
|
|
128
|
+
|
|
129
|
+
| spec | control |
|
|
130
|
+
| --- | --- |
|
|
131
|
+
| `Param(default=1.0, bounds=(0, 5))` | slider with live readout |
|
|
132
|
+
| `Param(kind="int", default=100, bounds=(10, 1000))` | integer slider |
|
|
133
|
+
| `Param(default=1.0)` / `"scalar"` / `"int"` | validated text field |
|
|
134
|
+
| `Param(..., scan=True)` / `"scalar or array"` / `"int or array"` | scannable (see below) |
|
|
135
|
+
| `"N-tuple"` (e.g. `"2-tuple"`) | one field per element |
|
|
136
|
+
|
|
137
|
+
Validation happens at registration: unknown spec keys, defaults outside
|
|
138
|
+
bounds, or a scan spec on a non-worker function raise `ValueError` when the
|
|
139
|
+
app is assembled, never mid-use.
|
|
140
|
+
|
|
141
|
+
**Parameter scans.** A kwarg declared `scan=True` gets a scan toggle (bounded)
|
|
142
|
+
or a comma-separated field (unbounded). Enter `0, 1, 2` and labforge calls the
|
|
143
|
+
worker once per point of the cartesian grid across all scanned parameters —
|
|
144
|
+
the worker itself always receives scalars. Downstream functions then receive a
|
|
145
|
+
`ScanResult`: a list of `(params, result)` records with `keys`, `values()` and
|
|
146
|
+
`axis(name)` helpers, distinguished with `isinstance(data, ScanResult)`.
|
|
147
|
+
|
|
148
|
+
**Visualizations.** Functions `viz(data, **kwargs)` returning a matplotlib
|
|
149
|
+
figure (bare or `(fig, ax)`). Each gets a tab with its own controls and a
|
|
150
|
+
Render button. Figures are yours — labforge only serializes them; the house
|
|
151
|
+
style (`labforge.style(fig, ax)`) is strictly opt-in.
|
|
152
|
+
|
|
153
|
+
**Analyses.** Functions `analysis(data, **kwargs)` — the return shape picks
|
|
154
|
+
the rendering:
|
|
155
|
+
|
|
156
|
+
| return | rendered as |
|
|
157
|
+
| --- | --- |
|
|
158
|
+
| `dict` | two-column quantity/value table |
|
|
159
|
+
| `list` of `dict`s, or a DataFrame | full table |
|
|
160
|
+
| `str` | markdown |
|
|
161
|
+
| Figure or `(fig, ax)` | image |
|
|
162
|
+
| anything else | its `repr` |
|
|
163
|
+
|
|
164
|
+
**Theory.** A markdown file or string. Displayed `$$...$$` equations render as
|
|
165
|
+
crisp images — through your system LaTeX toolchain when one is installed
|
|
166
|
+
(with the Euler math font), falling back to matplotlib's mathtext otherwise.
|
|
167
|
+
|
|
168
|
+
## Layouts, views and themes
|
|
169
|
+
|
|
170
|
+
`lab.open()` takes four independent knobs:
|
|
171
|
+
|
|
172
|
+
- `view="app"` (native window, default) or `"browser"` — serve the same app
|
|
173
|
+
and open it in your web browser; with a fixed `port` the URL is stable
|
|
174
|
+
across relaunches, and every browser tab gets its own isolated session.
|
|
175
|
+
- `layout="pages"` (rail navigation, default) or `"scroll"` — the whole lab
|
|
176
|
+
as one continuous scrolling page.
|
|
177
|
+
- `theme` — one of eight palettes, four dark and four light. A theme sets the
|
|
178
|
+
chrome, the plot palette and the equation ink together, so figures never
|
|
179
|
+
drift from the app around them. Colour your own figures with
|
|
180
|
+
`labforge.palette().data`, `.model`, `.highlight`; list the options with
|
|
181
|
+
`labforge.themes()`:
|
|
182
|
+
|
|
183
|
+
| name | mode | look |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| `paper` | light | warm paper surfaces, graphite ink, vermilion accent (default) |
|
|
186
|
+
| `mint` | light | light mint greens on white |
|
|
187
|
+
| `glacier` | light | pale glacier blues, deep slate accent |
|
|
188
|
+
| `lavender` | light | soft lavender neutrals, deep violet accent |
|
|
189
|
+
| `retro_green` | dark | matrix mood, calmer green, legible olive card |
|
|
190
|
+
| `instrument` | dark | near-black teal-tinted surfaces, one electric accent |
|
|
191
|
+
| `neon_violet` | dark | violet chrome, hot-pink data |
|
|
192
|
+
| `neon_gold` | dark | the violet palette with pale gold as the accent |
|
|
193
|
+
|
|
194
|
+
The Theory page under each theme — the equations are real LaTeX, rendered in
|
|
195
|
+
the theme's ink:
|
|
196
|
+
|
|
197
|
+
| | |
|
|
198
|
+
| --- | --- |
|
|
199
|
+
| `paper`  | `mint`  |
|
|
200
|
+
| `glacier`  | `lavender`  |
|
|
201
|
+
| `retro_green`  | `instrument`  |
|
|
202
|
+
| `neon_violet`  | `neon_gold`  |
|
|
203
|
+
|
|
204
|
+
## Install
|
|
205
|
+
|
|
206
|
+
Requires Python ≥ 3.10. Not yet on PyPI — install from a clone:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
git clone https://github.com/laroccod/labforge.git
|
|
210
|
+
cd labforge
|
|
211
|
+
pip install -e .
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
Dependencies: `flet` (pinned), `numpy`, `matplotlib`. A system LaTeX
|
|
215
|
+
toolchain (`latex` + `dvipng`) is optional and only affects equation
|
|
216
|
+
typography.
|
|
217
|
+
|
|
218
|
+
## Development
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
pip install -e ".[dev]"
|
|
222
|
+
pytest # unit + offline page-tree smoke tests
|
|
223
|
+
black --check --line-length 100 src tests examples
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The test suite builds every page and layout headlessly — no window needed —
|
|
227
|
+
and asserts that equations and figures actually rendered, so most mistakes are
|
|
228
|
+
caught without launching the app.
|
|
229
|
+
|
|
230
|
+
## License
|
|
231
|
+
|
|
232
|
+
[MIT](LICENSE)
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
labforge/__init__.py,sha256=ayUX0S7AOuaAmOo3WfRN5WSj_-EDlDagU8IbQxHw0og,671
|
|
2
|
+
labforge/controls.py,sha256=1roInLtNQIuMYG1pz9qHs6AccnHpKXQViau_TFaWMsM,8947
|
|
3
|
+
labforge/figures.py,sha256=eWAB5nJqRKTyJp2sRccj43FrtY96Oz9xno-HlN8G76Q,2977
|
|
4
|
+
labforge/lab.py,sha256=Tdh7BTS2N46p_boeIIrv5fVW-Ob6Yqo5dn_VMamuHac,5951
|
|
5
|
+
labforge/mathtext.py,sha256=PVErkvwl-S_RRTVfk90QiXo4aREc7ipvah1xJQruBy4,4972
|
|
6
|
+
labforge/param.py,sha256=WT2-H5SGvunXYiK8nmFp0ar-xhGDM8jVLy26pxnvY_4,7011
|
|
7
|
+
labforge/registry.py,sha256=kVKmeyPNQ12fCU5BY7mBFGOAfDHdLYBSSxSlDddBH0A,719
|
|
8
|
+
labforge/scan.py,sha256=VJclxRNj5f_AGeZEtRfAOOUMIS8MrxDWrut-JRmW-K0,2213
|
|
9
|
+
labforge/shell.py,sha256=KWwcKPtlgrqRNbhiXEKi4QsmAofnw-0e9u3H66qIElM,8266
|
|
10
|
+
labforge/state.py,sha256=sFibg-Z6fqsGvrX41FiUU5OqVBn9tlR-BasnfWG1ytg,2741
|
|
11
|
+
labforge/theme.py,sha256=itATAI9IU_evJnHLrpTDSkD9XIeza21tF2I_tz5HHjc,9535
|
|
12
|
+
labforge/ui.py,sha256=zkROpZiZp6EHZBQU6dB15Ms3YTSws-h0PHLpBj8yoLw,5219
|
|
13
|
+
labforge/pages/__init__.py,sha256=cHxqJmCZSDmVB_E1W0ZvagD8gnzGe39UyFIVaue4a40,204
|
|
14
|
+
labforge/pages/analysis.py,sha256=Q3ILD_hY4d9bIZITDdKxlJln_tAyQXgu7yTnpTdjBg4,3097
|
|
15
|
+
labforge/pages/panel.py,sha256=zhV1wylMKjZBZeF1VaM-qnkQyXRwUld7yQ74F6EhO9k,3752
|
|
16
|
+
labforge/pages/scroll.py,sha256=IwJvry9ZBRE-2N9gfsWxcyFWC7hnfIrV0rKk9cXYM2Q,2011
|
|
17
|
+
labforge/pages/simulation.py,sha256=pIQd8JK0QATlFIUmZ9Tt8nuwTHGh0xWOXXHR-8cyH74,2417
|
|
18
|
+
labforge/pages/theory.py,sha256=dZbdMBEZnPSDnBFycSkTqxgUfbZUbDtl-pYcYx9BR9g,1373
|
|
19
|
+
labforge/pages/visualization.py,sha256=vGTad6ofAVfN1F6SwD9VO4csUbfhYbJf8YwEtCLbw34,927
|
|
20
|
+
labforge-0.1.0.dist-info/METADATA,sha256=HTPFcB48jO7Iqj5_f0CYeiRItRp9V5Mt8ayhhPMU9mQ,9790
|
|
21
|
+
labforge-0.1.0.dist-info/WHEEL,sha256=lCkmxWfQsSc9CfIClYeavTdQeEX2toPqufh9gI35EQA,87
|
|
22
|
+
labforge-0.1.0.dist-info/licenses/LICENSE,sha256=jL8VIVG60zlXtgzTZRgrwChwHG8s0ZSPxYyIm84GwSY,1072
|
|
23
|
+
labforge-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daniel La Rocco
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|