nucli 0.4.0__tar.gz → 0.5.1__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.
- {nucli-0.4.0 → nucli-0.5.1}/PKG-INFO +4 -4
- {nucli-0.4.0 → nucli-0.5.1}/README.md +2 -2
- {nucli-0.4.0 → nucli-0.5.1}/pyproject.toml +7 -8
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/__init__.py +1 -1
- nucli-0.5.1/src/nucli/__main__.py +7 -0
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/_meta.py +2 -2
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/commands/demo.py +1 -1
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/commands/doctor.py +1 -1
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/demos/counter.py +21 -20
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/demos/movies.py +98 -97
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/demos/sampled.py +21 -20
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/main.py +4 -4
- nucli-0.4.0/src/nu/_cli/__main__.py +0 -7
- {nucli-0.4.0 → nucli-0.5.1}/.gitignore +0 -0
- {nucli-0.4.0 → nucli-0.5.1}/LICENSE.md +0 -0
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/commands/__init__.py +0 -0
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/commands/telemetry.py +0 -0
- {nucli-0.4.0/src/nu/_cli → nucli-0.5.1/src/nucli}/demos/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: nucli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.5.1
|
|
4
4
|
Summary: The `nu` command line: doctor, demos, telemetry. Ships alongside the nucore kernel and the nustd fabrics.
|
|
5
5
|
Project-URL: Repository, https://github.com/nustackdev/nu
|
|
6
6
|
Author-email: Gor Arakelyan <gorarkln@gmail.com>
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
17
|
Requires-Python: >=3.10
|
|
18
18
|
Requires-Dist: click>=8.0
|
|
19
|
-
Requires-Dist: nustd>=0.
|
|
19
|
+
Requires-Dist: nustd>=0.5.1
|
|
20
20
|
Requires-Dist: rich-click>=1.6
|
|
21
21
|
Requires-Dist: rich>=10.7
|
|
22
22
|
Description-Content-Type: text/markdown
|
|
@@ -34,10 +34,10 @@ nu demo movies # run a packaged demo app
|
|
|
34
34
|
nu telemetry # see / change telemetry settings
|
|
35
35
|
```
|
|
36
36
|
|
|
37
|
-
It
|
|
37
|
+
It imports as `nucli`, installs the `nu` command, and depends on
|
|
38
38
|
[`nustd`](https://pypi.org/project/nustd/) (which pulls the
|
|
39
39
|
[`nucore`](https://pypi.org/project/nucore/) kernel) because the
|
|
40
|
-
packaged demos run on `
|
|
40
|
+
packaged demos run on `nustd.ui` and `nustd.kv` at runtime.
|
|
41
41
|
|
|
42
42
|
You normally do not install this directly - `pip install "nustd[all]" nucli`
|
|
43
43
|
brings it along. Released in lockstep with the kernel. Docs at
|
|
@@ -11,10 +11,10 @@ nu demo movies # run a packaged demo app
|
|
|
11
11
|
nu telemetry # see / change telemetry settings
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
It
|
|
14
|
+
It imports as `nucli`, installs the `nu` command, and depends on
|
|
15
15
|
[`nustd`](https://pypi.org/project/nustd/) (which pulls the
|
|
16
16
|
[`nucore`](https://pypi.org/project/nucore/) kernel) because the
|
|
17
|
-
packaged demos run on `
|
|
17
|
+
packaged demos run on `nustd.ui` and `nustd.kv` at runtime.
|
|
18
18
|
|
|
19
19
|
You normally do not install this directly - `pip install "nustd[all]" nucli`
|
|
20
20
|
brings it along. Released in lockstep with the kernel. Docs at
|
|
@@ -4,18 +4,18 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nucli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.5.1"
|
|
8
8
|
description = "The `nu` command line: doctor, demos, telemetry. Ships alongside the nucore kernel and the nustd fabrics."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = "Apache-2.0"
|
|
11
11
|
authors = [{ name = "Gor Arakelyan", email = "gorarkln@gmail.com" }]
|
|
12
12
|
requires-python = ">=3.10"
|
|
13
13
|
# Released in lockstep with the kernel. Carries no `nu/__init__.py` -- it drops
|
|
14
|
-
# `
|
|
14
|
+
# `nucli` into the `nu.` namespace the kernel owns. Depends on `nustd` rather
|
|
15
15
|
# than the bare kernel because the packaged demos (`nu demo movies`) run on
|
|
16
|
-
# `
|
|
16
|
+
# `nustd.ui` and `nustd.kv` at runtime.
|
|
17
17
|
dependencies = [
|
|
18
|
-
"nustd>=0.
|
|
18
|
+
"nustd>=0.5.1",
|
|
19
19
|
"click>=8.0",
|
|
20
20
|
"rich>=10.7",
|
|
21
21
|
"rich-click>=1.6",
|
|
@@ -52,9 +52,8 @@ classifiers = [
|
|
|
52
52
|
Repository = "https://github.com/nustackdev/nu"
|
|
53
53
|
|
|
54
54
|
[project.scripts]
|
|
55
|
-
nu = "
|
|
55
|
+
nu = "nucli:main"
|
|
56
56
|
|
|
57
57
|
[tool.hatch.build.targets.wheel]
|
|
58
|
-
packages = ["src/
|
|
59
|
-
#
|
|
60
|
-
# and the installs merge into one `site-packages/nu/`.
|
|
58
|
+
packages = ["src/nucli"]
|
|
59
|
+
# The import name is `nucli`; the command it installs is `nu`.
|
|
@@ -15,8 +15,8 @@ def nu_version() -> str:
|
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
def demos_root() -> Path:
|
|
18
|
-
"""Locate packaged demo scripts (ships as
|
|
19
|
-
return Path(str(resources.files("
|
|
18
|
+
"""Locate packaged demo scripts (ships as nucli.demos)."""
|
|
19
|
+
return Path(str(resources.files("nucli").joinpath("demos")))
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
def demos() -> dict[str, Path]:
|
|
@@ -10,8 +10,8 @@ from rich.console import Console
|
|
|
10
10
|
from rich.table import Table
|
|
11
11
|
from rich.text import Text
|
|
12
12
|
|
|
13
|
-
from nu._cli._meta import nu_version
|
|
14
13
|
from nu._config.branding import BLUE, PURPLE
|
|
14
|
+
from nucli._meta import nu_version
|
|
15
15
|
|
|
16
16
|
|
|
17
17
|
# Fabric extras (as declared in packages/nustd/pyproject.toml) and the import
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
import nu
|
|
6
|
+
import nustd
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
_DB = Path.home() / ".nu" / "demos" / "counter"
|
|
@@ -12,30 +13,30 @@ _DB.parent.mkdir(parents=True, exist_ok=True)
|
|
|
12
13
|
class Counter(nu.Shape):
|
|
13
14
|
"""Persistent counter state."""
|
|
14
15
|
|
|
15
|
-
value:
|
|
16
|
+
value: nustd.kv.IntRef
|
|
16
17
|
|
|
17
18
|
|
|
18
|
-
class Links(
|
|
19
|
-
docs =
|
|
19
|
+
class Links(nustd.ui.Row):
|
|
20
|
+
docs = nustd.ui.LinkRef.slot(
|
|
20
21
|
label="Read the docs", href="https://nustack.dev/docs", target="_blank"
|
|
21
22
|
)
|
|
22
|
-
github =
|
|
23
|
+
github = nustd.ui.LinkRef.slot(
|
|
23
24
|
label="Star on GitHub", href="https://github.com/nustackdev/nu", target="_blank"
|
|
24
25
|
)
|
|
25
|
-
examples =
|
|
26
|
+
examples = nustd.ui.LinkRef.slot(
|
|
26
27
|
label="Browse more demos",
|
|
27
28
|
href="https://github.com/nustackdev/nu/tree/main/examples",
|
|
28
29
|
target="_blank",
|
|
29
30
|
)
|
|
30
31
|
|
|
31
32
|
|
|
32
|
-
class Dashboard(
|
|
33
|
+
class Dashboard(nustd.ui.Page):
|
|
33
34
|
"""Live counter page with description and source."""
|
|
34
35
|
|
|
35
|
-
heading =
|
|
36
|
-
count =
|
|
37
|
-
about_heading =
|
|
38
|
-
about =
|
|
36
|
+
heading = nustd.ui.HeadingRef.slot(label="Persistent counter, live")
|
|
37
|
+
count = nustd.ui.StatRef.slot(label="Count")
|
|
38
|
+
about_heading = nustd.ui.HeadingRef.slot(label="How it works")
|
|
39
|
+
about = nustd.ui.MarkdownRef.slot(
|
|
39
40
|
value=(
|
|
40
41
|
"- Stores counter value in rocksdb.\n"
|
|
41
42
|
"- App increments the counter once a second.\n"
|
|
@@ -44,38 +45,38 @@ class Dashboard(nu.ui.Page):
|
|
|
44
45
|
"- Same Interactions used to orchestrate storage and UI update.\n"
|
|
45
46
|
),
|
|
46
47
|
)
|
|
47
|
-
links_heading =
|
|
48
|
-
links_intro =
|
|
48
|
+
links_heading = nustd.ui.HeadingRef.slot(label="Try Nu yourself")
|
|
49
|
+
links_intro = nustd.ui.TextRef.slot(
|
|
49
50
|
value="Persistent state, live browser, no glue. See how far the primitive goes.",
|
|
50
51
|
)
|
|
51
52
|
links = Links.slot(gap=4, align="center", wrap=True)
|
|
52
|
-
source_heading =
|
|
53
|
-
source_intro =
|
|
53
|
+
source_heading = nustd.ui.HeadingRef.slot(label="Source")
|
|
54
|
+
source_intro = nustd.ui.TextRef.slot(
|
|
54
55
|
value="The whole app, one file. Storage, UI, and the wires between them.",
|
|
55
56
|
)
|
|
56
|
-
source =
|
|
57
|
+
source = nustd.ui.CodeBlockRef.slot(
|
|
57
58
|
code=Path(__file__).read_text(),
|
|
58
59
|
language="python",
|
|
59
60
|
)
|
|
60
61
|
|
|
61
62
|
|
|
62
|
-
class App(
|
|
63
|
+
class App(nustd.ui.Index):
|
|
63
64
|
"""UI index with one page."""
|
|
64
65
|
|
|
65
66
|
home = Dashboard.slot("/")
|
|
66
67
|
|
|
67
68
|
|
|
68
69
|
app = nu.With(
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
70
|
+
nustd.kv.rocksdb_navigator(str(_DB)),
|
|
71
|
+
nustd.ui.server(
|
|
72
|
+
nustd.kv.auto_flow_atomic(
|
|
72
73
|
nu.ReactForever(
|
|
73
74
|
Counter.value.on_change(),
|
|
74
75
|
App.home.count.set_value(nu.str(Counter.value)),
|
|
75
76
|
),
|
|
76
77
|
),
|
|
77
78
|
),
|
|
78
|
-
body=
|
|
79
|
+
body=nustd.kv.auto_flow_atomic(
|
|
79
80
|
nu.IfDo(Counter.value.missing(), Counter.value.set(0))
|
|
80
81
|
>> nu.ForeverDo(
|
|
81
82
|
Counter.value.inc() >> nu.Delay(1.0),
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
import nu
|
|
6
|
+
import nustd
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
_DB = Path.home() / ".nu" / "demos" / "movies"
|
|
@@ -23,16 +24,16 @@ _GENRES_WITH_ANY = [{"value": "", "label": "Any genre"}, *_GENRES]
|
|
|
23
24
|
# ---- UI ---------------------------------------------------------------------
|
|
24
25
|
|
|
25
26
|
|
|
26
|
-
class TitleField(
|
|
27
|
-
input =
|
|
27
|
+
class TitleField(nustd.ui.Field):
|
|
28
|
+
input = nustd.ui.InputRef.slot(placeholder="e.g. Arrival")
|
|
28
29
|
|
|
29
30
|
|
|
30
|
-
class TextAreaField(
|
|
31
|
-
input =
|
|
31
|
+
class TextAreaField(nustd.ui.Field):
|
|
32
|
+
input = nustd.ui.TextAreaRef.slot(placeholder="Quick thoughts...", rows=2)
|
|
32
33
|
|
|
33
34
|
|
|
34
|
-
class YearField(
|
|
35
|
-
input =
|
|
35
|
+
class YearField(nustd.ui.Field):
|
|
36
|
+
input = nustd.ui.NumberInputRef.slot(
|
|
36
37
|
min=1900.0,
|
|
37
38
|
max=2100.0,
|
|
38
39
|
step=1.0,
|
|
@@ -40,8 +41,8 @@ class YearField(nu.ui.Field):
|
|
|
40
41
|
)
|
|
41
42
|
|
|
42
43
|
|
|
43
|
-
class RatingField(
|
|
44
|
-
input =
|
|
44
|
+
class RatingField(nustd.ui.Field):
|
|
45
|
+
input = nustd.ui.NumberInputRef.slot(
|
|
45
46
|
min=1.0,
|
|
46
47
|
max=10.0,
|
|
47
48
|
step=0.5,
|
|
@@ -49,96 +50,96 @@ class RatingField(nu.ui.Field):
|
|
|
49
50
|
)
|
|
50
51
|
|
|
51
52
|
|
|
52
|
-
class GenreField(
|
|
53
|
-
input =
|
|
53
|
+
class GenreField(nustd.ui.Field):
|
|
54
|
+
input = nustd.ui.SelectRef.slot(options=_GENRES, selected="drama")
|
|
54
55
|
|
|
55
56
|
|
|
56
|
-
class SwitchField(
|
|
57
|
-
input =
|
|
57
|
+
class SwitchField(nustd.ui.Field):
|
|
58
|
+
input = nustd.ui.SwitchRef.slot(default=True)
|
|
58
59
|
|
|
59
60
|
|
|
60
|
-
class DetailsFieldset(
|
|
61
|
+
class DetailsFieldset(nustd.ui.Fieldset):
|
|
61
62
|
title = TitleField.slot(label="Title", help="Movie title", required=True)
|
|
62
63
|
year = YearField.slot(label="Year")
|
|
63
64
|
genre = GenreField.slot(label="Genre")
|
|
64
65
|
|
|
65
66
|
|
|
66
|
-
class ScoreFieldset(
|
|
67
|
+
class ScoreFieldset(nustd.ui.Fieldset):
|
|
67
68
|
rating = RatingField.slot(label="Rating", help="How much you liked it")
|
|
68
69
|
watched = SwitchField.slot(label="Watched?", help="Off means still on the pile")
|
|
69
70
|
notes = TextAreaField.slot(label="Notes")
|
|
70
71
|
|
|
71
72
|
|
|
72
|
-
class AddMovieForm(
|
|
73
|
+
class AddMovieForm(nustd.ui.Form):
|
|
73
74
|
details = DetailsFieldset.slot(legend="Movie", gap="md")
|
|
74
75
|
score = ScoreFieldset.slot(legend="Your take", gap="md")
|
|
75
|
-
submit =
|
|
76
|
-
feedback =
|
|
76
|
+
submit = nustd.ui.ButtonRef.slot(label="Log it", variant="primary")
|
|
77
|
+
feedback = nustd.ui.AlertRef.slot(variant="ok", dismissible=True)
|
|
77
78
|
|
|
78
79
|
|
|
79
|
-
class MinRatingField(
|
|
80
|
-
input =
|
|
80
|
+
class MinRatingField(nustd.ui.Field):
|
|
81
|
+
input = nustd.ui.NumberInputRef.slot(min=1.0, max=10.0, step=0.5, default=1.0)
|
|
81
82
|
|
|
82
83
|
|
|
83
|
-
class FilterGenreField(
|
|
84
|
-
input =
|
|
84
|
+
class FilterGenreField(nustd.ui.Field):
|
|
85
|
+
input = nustd.ui.SelectRef.slot(options=_GENRES_WITH_ANY, selected="")
|
|
85
86
|
|
|
86
87
|
|
|
87
|
-
class WatchedOnlyField(
|
|
88
|
-
input =
|
|
88
|
+
class WatchedOnlyField(nustd.ui.Field):
|
|
89
|
+
input = nustd.ui.SwitchRef.slot(default=False)
|
|
89
90
|
|
|
90
91
|
|
|
91
|
-
class FilterRow(
|
|
92
|
+
class FilterRow(nustd.ui.Row):
|
|
92
93
|
min_rating = MinRatingField.slot(label="Min rating")
|
|
93
94
|
genre = FilterGenreField.slot(label="Genre")
|
|
94
95
|
watched_only = WatchedOnlyField.slot(label="Already watched")
|
|
95
|
-
apply =
|
|
96
|
-
clear =
|
|
96
|
+
apply = nustd.ui.ButtonRef.slot(label="Apply", variant="secondary")
|
|
97
|
+
clear = nustd.ui.ButtonRef.slot(label="Clear", variant="ghost")
|
|
97
98
|
|
|
98
99
|
|
|
99
|
-
class FilterCard(
|
|
100
|
+
class FilterCard(nustd.ui.Card):
|
|
100
101
|
body = FilterRow.slot(gap=3, align="center", wrap=True)
|
|
101
102
|
|
|
102
103
|
|
|
103
|
-
class StatsRow(
|
|
104
|
-
total =
|
|
105
|
-
watched =
|
|
106
|
-
unseen =
|
|
107
|
-
latest =
|
|
108
|
-
health =
|
|
104
|
+
class StatsRow(nustd.ui.Row):
|
|
105
|
+
total = nustd.ui.StatRef.slot(label="Total")
|
|
106
|
+
watched = nustd.ui.StatRef.slot(label="Watched")
|
|
107
|
+
unseen = nustd.ui.StatRef.slot(label="Unseen")
|
|
108
|
+
latest = nustd.ui.TextRef.slot()
|
|
109
|
+
health = nustd.ui.BadgeRef.slot(label="Fresh", variant="ok")
|
|
109
110
|
|
|
110
111
|
|
|
111
|
-
class StatsCard(
|
|
112
|
+
class StatsCard(nustd.ui.Card):
|
|
112
113
|
body = StatsRow.slot(gap=6, align="center", wrap=True)
|
|
113
114
|
|
|
114
115
|
|
|
115
|
-
class TableBody(
|
|
116
|
-
table =
|
|
116
|
+
class TableBody(nustd.ui.Column):
|
|
117
|
+
table = nustd.ui.TableRef.slot(
|
|
117
118
|
columns=["title", "year", "genre", "rating", "watched", "notes"],
|
|
118
119
|
striped=True,
|
|
119
120
|
dense=True,
|
|
120
121
|
clickable_rows=True,
|
|
121
122
|
max_rows=200,
|
|
122
123
|
)
|
|
123
|
-
empty =
|
|
124
|
+
empty = nustd.ui.AlertRef.slot(
|
|
124
125
|
variant="info",
|
|
125
126
|
body="No movies match",
|
|
126
127
|
dismissible=False,
|
|
127
128
|
)
|
|
128
129
|
|
|
129
130
|
|
|
130
|
-
class TableCard(
|
|
131
|
+
class TableCard(nustd.ui.Card):
|
|
131
132
|
body = TableBody.slot(gap=3)
|
|
132
133
|
|
|
133
134
|
|
|
134
|
-
class Links(
|
|
135
|
-
docs =
|
|
135
|
+
class Links(nustd.ui.Row):
|
|
136
|
+
docs = nustd.ui.LinkRef.slot(
|
|
136
137
|
label="Read the docs", href="https://nustack.dev/docs", target="_blank"
|
|
137
138
|
)
|
|
138
|
-
github =
|
|
139
|
+
github = nustd.ui.LinkRef.slot(
|
|
139
140
|
label="Star on GitHub", href="https://github.com/nustackdev/nu", target="_blank"
|
|
140
141
|
)
|
|
141
|
-
examples =
|
|
142
|
+
examples = nustd.ui.LinkRef.slot(
|
|
142
143
|
label="Browse more demos",
|
|
143
144
|
href="https://github.com/nustackdev/nu/tree/main/examples",
|
|
144
145
|
target="_blank",
|
|
@@ -148,13 +149,13 @@ class Links(nu.ui.Row):
|
|
|
148
149
|
# ---- Pages ------------------------------------------------------------------
|
|
149
150
|
|
|
150
151
|
|
|
151
|
-
class TopBar(
|
|
152
|
-
about =
|
|
152
|
+
class TopBar(nustd.ui.Row):
|
|
153
|
+
about = nustd.ui.ButtonRef.slot(label="About this demo", variant="ghost")
|
|
153
154
|
|
|
154
155
|
|
|
155
|
-
class Movies(
|
|
156
|
-
heading =
|
|
157
|
-
intro =
|
|
156
|
+
class Movies(nustd.ui.Page):
|
|
157
|
+
heading = nustd.ui.HeadingRef.slot(label="Your movies")
|
|
158
|
+
intro = nustd.ui.TextRef.slot(
|
|
158
159
|
value="Log what you watch. Filter the shelf, click a row for details.",
|
|
159
160
|
)
|
|
160
161
|
topbar = TopBar.slot(gap=3, align="center")
|
|
@@ -165,13 +166,13 @@ class Movies(nu.ui.Page):
|
|
|
165
166
|
shelf = TableCard.slot(title="Movies")
|
|
166
167
|
|
|
167
168
|
|
|
168
|
-
class AboutActions(
|
|
169
|
-
back =
|
|
169
|
+
class AboutActions(nustd.ui.Row):
|
|
170
|
+
back = nustd.ui.ButtonRef.slot(label="Back to app", variant="ghost")
|
|
170
171
|
|
|
171
172
|
|
|
172
|
-
class About(
|
|
173
|
-
heading =
|
|
174
|
-
about =
|
|
173
|
+
class About(nustd.ui.Page):
|
|
174
|
+
heading = nustd.ui.HeadingRef.slot(label="How it works")
|
|
175
|
+
about = nustd.ui.MarkdownRef.slot(
|
|
175
176
|
value=(
|
|
176
177
|
"- Real app: form, filterable table, stats row, per-item detail page.\n"
|
|
177
178
|
"- Every row lives in rocksdb. Restart, everything is still there.\n"
|
|
@@ -179,52 +180,52 @@ class About(nu.ui.Page):
|
|
|
179
180
|
"- Same Interactions handle add, delete, filter, and page routing.\n"
|
|
180
181
|
),
|
|
181
182
|
)
|
|
182
|
-
links_heading =
|
|
183
|
-
links_intro =
|
|
183
|
+
links_heading = nustd.ui.HeadingRef.slot(label="Try Nu yourself")
|
|
184
|
+
links_intro = nustd.ui.TextRef.slot(
|
|
184
185
|
value="Full apps, forms, routing, no glue. See how far the primitive goes.",
|
|
185
186
|
)
|
|
186
187
|
links = Links.slot(gap=4, align="center", wrap=True)
|
|
187
|
-
source_heading =
|
|
188
|
-
source_intro =
|
|
188
|
+
source_heading = nustd.ui.HeadingRef.slot(label="Source")
|
|
189
|
+
source_intro = nustd.ui.TextRef.slot(
|
|
189
190
|
value="The whole app, one file. Storage, UI, and the wires between them.",
|
|
190
191
|
)
|
|
191
|
-
source =
|
|
192
|
+
source = nustd.ui.CodeBlockRef.slot(
|
|
192
193
|
code=Path(__file__).read_text(),
|
|
193
194
|
language="python",
|
|
194
195
|
)
|
|
195
196
|
actions = AboutActions.slot(gap=3, align="center")
|
|
196
197
|
|
|
197
198
|
|
|
198
|
-
class DetailRow(
|
|
199
|
-
year =
|
|
200
|
-
genre =
|
|
201
|
-
rating =
|
|
202
|
-
watched =
|
|
199
|
+
class DetailRow(nustd.ui.Row):
|
|
200
|
+
year = nustd.ui.StatRef.slot(label="Year")
|
|
201
|
+
genre = nustd.ui.StatRef.slot(label="Genre")
|
|
202
|
+
rating = nustd.ui.StatRef.slot(label="Rating")
|
|
203
|
+
watched = nustd.ui.BadgeRef.slot(label="Watched", variant="ok")
|
|
203
204
|
|
|
204
205
|
|
|
205
|
-
class MetaCard(
|
|
206
|
+
class MetaCard(nustd.ui.Card):
|
|
206
207
|
meta = DetailRow.slot(gap=6, align="center", wrap=True)
|
|
207
208
|
|
|
208
209
|
|
|
209
|
-
class NotesCard(
|
|
210
|
-
body =
|
|
210
|
+
class NotesCard(nustd.ui.Card):
|
|
211
|
+
body = nustd.ui.MarkdownRef.slot()
|
|
211
212
|
|
|
212
213
|
|
|
213
|
-
class DetailActions(
|
|
214
|
-
back =
|
|
215
|
-
remove =
|
|
214
|
+
class DetailActions(nustd.ui.Row):
|
|
215
|
+
back = nustd.ui.ButtonRef.slot(label="Back", variant="ghost")
|
|
216
|
+
remove = nustd.ui.ButtonRef.slot(label="Delete", variant="danger")
|
|
216
217
|
|
|
217
218
|
|
|
218
|
-
class MovieDetail(
|
|
219
|
-
heading =
|
|
219
|
+
class MovieDetail(nustd.ui.Page):
|
|
220
|
+
heading = nustd.ui.HeadingRef.slot()
|
|
220
221
|
meta = MetaCard.slot(title="Details")
|
|
221
222
|
notes = NotesCard.slot(title="Notes")
|
|
222
223
|
actions = DetailActions.slot(gap=3, align="center")
|
|
223
224
|
|
|
224
225
|
|
|
225
|
-
class App(
|
|
226
|
-
title:
|
|
227
|
-
nav:
|
|
226
|
+
class App(nustd.ui.Index):
|
|
227
|
+
title: nustd.ui.TitleRef
|
|
228
|
+
nav: nustd.ui.NavRef
|
|
228
229
|
movies = Movies.slot("/")
|
|
229
230
|
detail = MovieDetail.slot("/detail")
|
|
230
231
|
about = About.slot("/about")
|
|
@@ -234,20 +235,20 @@ class App(nu.ui.Index):
|
|
|
234
235
|
|
|
235
236
|
|
|
236
237
|
class Movie(nu.Shape):
|
|
237
|
-
title =
|
|
238
|
-
year =
|
|
239
|
-
genre =
|
|
240
|
-
rating =
|
|
241
|
-
watched =
|
|
242
|
-
notes =
|
|
238
|
+
title = nustd.kv.StrRef.slot()
|
|
239
|
+
year = nustd.kv.IntRef.slot()
|
|
240
|
+
genre = nustd.kv.StrRef.slot()
|
|
241
|
+
rating = nustd.kv.FloatRef.slot()
|
|
242
|
+
watched = nustd.kv.BoolRef.slot()
|
|
243
|
+
notes = nustd.kv.StrRef.slot()
|
|
243
244
|
|
|
244
245
|
|
|
245
246
|
class State(nu.Shape):
|
|
246
|
-
movies =
|
|
247
|
-
total =
|
|
248
|
-
watched =
|
|
249
|
-
latest_title =
|
|
250
|
-
selected =
|
|
247
|
+
movies = nustd.kv.ShapesListRef.slot(Movie)
|
|
248
|
+
total = nustd.kv.IntRef.slot()
|
|
249
|
+
watched = nustd.kv.IntRef.slot()
|
|
250
|
+
latest_title = nustd.kv.StrRef.slot()
|
|
251
|
+
selected = nustd.kv.IntRef.slot() # index of the movie open in MovieDetail
|
|
251
252
|
|
|
252
253
|
|
|
253
254
|
# ---- Seed ------------------------------------------------------------------
|
|
@@ -334,7 +335,7 @@ def _rows_filtered() -> nu.Nu:
|
|
|
334
335
|
# Seed once, on a store that has never been written. A restart then keeps what
|
|
335
336
|
# the user logged instead of replacing the shelf with the samples again.
|
|
336
337
|
# `selected` stays unconditional: it is a cursor into the detail page, not data.
|
|
337
|
-
init =
|
|
338
|
+
init = nustd.kv.Transaction(
|
|
338
339
|
nu.IfDo(
|
|
339
340
|
State.total.missing(),
|
|
340
341
|
State.total.set(len(_SEED_MOVIES))
|
|
@@ -346,7 +347,7 @@ init = nu.kv.Transaction(
|
|
|
346
347
|
)
|
|
347
348
|
|
|
348
349
|
|
|
349
|
-
hydrate =
|
|
350
|
+
hydrate = nustd.kv.Snapshot(
|
|
350
351
|
App.movies.stats.body.total.set_value(nu.str(State.total))
|
|
351
352
|
| App.movies.stats.body.watched.set_value(nu.str(State.watched))
|
|
352
353
|
| App.movies.stats.body.unseen.set_value(nu.str(State.total - State.watched))
|
|
@@ -357,7 +358,7 @@ hydrate = nu.kv.Snapshot(
|
|
|
357
358
|
|
|
358
359
|
on_add = nu.ReactForever(
|
|
359
360
|
App.movies.form.submit.on_click(),
|
|
360
|
-
|
|
361
|
+
nustd.kv.Transaction(
|
|
361
362
|
State.movies.append(
|
|
362
363
|
nu.Dict.of(
|
|
363
364
|
title=nu.Str(App.movies.form.details.title.input),
|
|
@@ -374,7 +375,7 @@ on_add = nu.ReactForever(
|
|
|
374
375
|
)
|
|
375
376
|
| State.latest_title.set(nu.Str(App.movies.form.details.title.input)),
|
|
376
377
|
)
|
|
377
|
-
>>
|
|
378
|
+
>> nustd.kv.Snapshot(
|
|
378
379
|
App.movies.shelf.body.table.set(_rows_form())
|
|
379
380
|
| App.movies.stats.body.total.set_value(nu.str(State.total))
|
|
380
381
|
| App.movies.stats.body.watched.set_value(nu.str(State.watched))
|
|
@@ -392,8 +393,8 @@ on_row_click = nu.ReactForever(
|
|
|
392
393
|
App.movies.shelf.body.table.on_row_click(),
|
|
393
394
|
nu.IfDo(
|
|
394
395
|
nu.Contains(nu.DictAttrRef("row_click"), "row_index"),
|
|
395
|
-
|
|
396
|
-
>>
|
|
396
|
+
nustd.kv.Transaction(State.selected.set(nu.DictAttrRef("row_click")["row_index"]))
|
|
397
|
+
>> nustd.kv.Snapshot(
|
|
397
398
|
App.detail.heading.set(State.movies[State.selected].title)
|
|
398
399
|
| App.detail.meta.meta.year.set_value(nu.str(State.movies[State.selected].year))
|
|
399
400
|
| App.detail.meta.meta.genre.set_value(State.movies[State.selected].genre)
|
|
@@ -411,10 +412,10 @@ on_row_click = nu.ReactForever(
|
|
|
411
412
|
|
|
412
413
|
on_delete = nu.ReactForever(
|
|
413
414
|
App.detail.actions.remove.on_click(),
|
|
414
|
-
|
|
415
|
+
nustd.kv.Transaction(
|
|
415
416
|
State.movies.del_at(State.selected) >> State.total.set(nu.Len(State.movies)),
|
|
416
417
|
)
|
|
417
|
-
>>
|
|
418
|
+
>> nustd.kv.Snapshot(
|
|
418
419
|
App.movies.shelf.body.table.set(_rows_form())
|
|
419
420
|
| App.movies.stats.body.total.set_value(nu.str(State.total))
|
|
420
421
|
| App.movies.stats.body.unseen.set_value(nu.str(State.total - State.watched))
|
|
@@ -434,13 +435,13 @@ on_about_back = nu.ReactForever(App.about.actions.back.on_click(), App.nav.set("
|
|
|
434
435
|
|
|
435
436
|
on_filter_apply = nu.ReactForever(
|
|
436
437
|
App.movies.filters.body.apply.on_click(),
|
|
437
|
-
|
|
438
|
+
nustd.kv.Snapshot(App.movies.shelf.body.table.set(_rows_filtered())),
|
|
438
439
|
)
|
|
439
440
|
|
|
440
441
|
|
|
441
442
|
on_filter_clear = nu.ReactForever(
|
|
442
443
|
App.movies.filters.body.clear.on_click(),
|
|
443
|
-
|
|
444
|
+
nustd.kv.Snapshot(
|
|
444
445
|
App.movies.filters.body.min_rating.input.set(1.0)
|
|
445
446
|
| App.movies.filters.body.genre.input.set("")
|
|
446
447
|
| App.movies.filters.body.watched_only.input.set(False)
|
|
@@ -466,9 +467,9 @@ ui = (
|
|
|
466
467
|
|
|
467
468
|
|
|
468
469
|
app = nu.With(
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
body=
|
|
470
|
+
nustd.kv.rocksdb_navigator(str(_DB)),
|
|
471
|
+
nustd.ui.server(nustd.kv.auto_flow_atomic(ui)),
|
|
472
|
+
body=nustd.kv.auto_flow_atomic(init >> nu.ForeverDo(nu.Delay(3600))),
|
|
472
473
|
)
|
|
473
474
|
|
|
474
475
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from pathlib import Path
|
|
4
4
|
|
|
5
5
|
import nu
|
|
6
|
+
import nustd
|
|
6
7
|
|
|
7
8
|
|
|
8
9
|
_DB = Path.home() / ".nu" / "demos" / "sampled"
|
|
@@ -12,31 +13,31 @@ _DB.parent.mkdir(parents=True, exist_ok=True)
|
|
|
12
13
|
class State(nu.Shape):
|
|
13
14
|
"""Persistent series and write cursor."""
|
|
14
15
|
|
|
15
|
-
nums =
|
|
16
|
-
cursor =
|
|
16
|
+
nums = nustd.kv.Kh57Ref.slot(int)
|
|
17
|
+
cursor = nustd.kv.IntRef.slot()
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
class Links(
|
|
20
|
-
docs =
|
|
20
|
+
class Links(nustd.ui.Row):
|
|
21
|
+
docs = nustd.ui.LinkRef.slot(
|
|
21
22
|
label="Read the docs", href="https://nustack.dev/docs", target="_blank"
|
|
22
23
|
)
|
|
23
|
-
github =
|
|
24
|
+
github = nustd.ui.LinkRef.slot(
|
|
24
25
|
label="Star on GitHub", href="https://github.com/nustackdev/nu", target="_blank"
|
|
25
26
|
)
|
|
26
|
-
examples =
|
|
27
|
+
examples = nustd.ui.LinkRef.slot(
|
|
27
28
|
label="Browse more demos",
|
|
28
29
|
href="https://github.com/nustackdev/nu/tree/main/examples",
|
|
29
30
|
target="_blank",
|
|
30
31
|
)
|
|
31
32
|
|
|
32
33
|
|
|
33
|
-
class Dashboard(
|
|
34
|
+
class Dashboard(nustd.ui.Page):
|
|
34
35
|
"""Live chart page with description and source."""
|
|
35
36
|
|
|
36
|
-
heading =
|
|
37
|
-
chart =
|
|
38
|
-
about_heading =
|
|
39
|
-
about =
|
|
37
|
+
heading = nustd.ui.HeadingRef.slot(label="Unbounded series, instant chart")
|
|
38
|
+
chart = nustd.ui.LineChart.slot()
|
|
39
|
+
about_heading = nustd.ui.HeadingRef.slot(label="How it works")
|
|
40
|
+
about = nustd.ui.MarkdownRef.slot(
|
|
40
41
|
value=(
|
|
41
42
|
"- Writes 50 numbers a second into a **kh57-backed** series. Grows without limit.\n"
|
|
42
43
|
"- Chart repaints from a 200-point reservoir sample. Same cost at 1k rows or 1B.\n"
|
|
@@ -45,22 +46,22 @@ class Dashboard(nu.ui.Page):
|
|
|
45
46
|
"- Same Interactions used to orchestrate the feed and the redraw.\n"
|
|
46
47
|
),
|
|
47
48
|
)
|
|
48
|
-
links_heading =
|
|
49
|
-
links_intro =
|
|
49
|
+
links_heading = nustd.ui.HeadingRef.slot(label="Try Nu yourself")
|
|
50
|
+
links_intro = nustd.ui.TextRef.slot(
|
|
50
51
|
value="Billion-row backends, live UIs, no glue. See how far the primitive goes.",
|
|
51
52
|
)
|
|
52
53
|
links = Links.slot(gap=4, align="center", wrap=True)
|
|
53
|
-
source_heading =
|
|
54
|
-
source_intro =
|
|
54
|
+
source_heading = nustd.ui.HeadingRef.slot(label="Source")
|
|
55
|
+
source_intro = nustd.ui.TextRef.slot(
|
|
55
56
|
value="The whole app, one file. Storage, UI, and the wires between them.",
|
|
56
57
|
)
|
|
57
|
-
source =
|
|
58
|
+
source = nustd.ui.CodeBlockRef.slot(
|
|
58
59
|
code=Path(__file__).read_text(),
|
|
59
60
|
language="python",
|
|
60
61
|
)
|
|
61
62
|
|
|
62
63
|
|
|
63
|
-
class App(
|
|
64
|
+
class App(nustd.ui.Index):
|
|
64
65
|
"""UI index with one page."""
|
|
65
66
|
|
|
66
67
|
home = Dashboard.slot("/")
|
|
@@ -81,9 +82,9 @@ feed = State.cursor.init(0) >> nu.ForeverDo(
|
|
|
81
82
|
|
|
82
83
|
# assemble: rocksdb-backed, served over the browser
|
|
83
84
|
app = nu.With(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
body=
|
|
85
|
+
nustd.kv.rocksdb_navigator(str(_DB)),
|
|
86
|
+
nustd.ui.server(nustd.kv.auto_flow_atomic(ui)),
|
|
87
|
+
body=nustd.kv.auto_flow_atomic(feed),
|
|
87
88
|
)
|
|
88
89
|
|
|
89
90
|
|
|
@@ -4,11 +4,11 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
import rich_click as click
|
|
6
6
|
|
|
7
|
-
from nu._cli._meta import nu_version
|
|
8
|
-
from nu._cli.commands.demo import demo
|
|
9
|
-
from nu._cli.commands.doctor import doctor
|
|
10
|
-
from nu._cli.commands.telemetry import telemetry
|
|
11
7
|
from nu._config.branding import BLUE, PURPLE, render_header
|
|
8
|
+
from nucli._meta import nu_version
|
|
9
|
+
from nucli.commands.demo import demo
|
|
10
|
+
from nucli.commands.doctor import doctor
|
|
11
|
+
from nucli.commands.telemetry import telemetry
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
# Rich-click styling: strict brand duo -- purple headers, blue values.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|