nucli 0.3.0__tar.gz → 0.5.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: nucli
3
- Version: 0.3.0
3
+ Version: 0.5.0
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.3.0
19
+ Requires-Dist: nustd>=0.5.0
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 ships `nu._cli` into the `nu.` namespace and depends on
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 `nu.ui` and `nu.kv` at runtime.
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 ships `nu._cli` into the `nu.` namespace and depends on
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 `nu.ui` and `nu.kv` at runtime.
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.3.0"
7
+ version = "0.5.0"
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
- # `nu._cli` into the `nu.` namespace the kernel owns. Depends on `nustd` rather
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
- # `nu.ui` and `nu.kv` at runtime.
16
+ # `nustd.ui` and `nustd.kv` at runtime.
17
17
  dependencies = [
18
- "nustd>=0.3.0",
18
+ "nustd>=0.5.0",
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 = "nu._cli:main"
55
+ nu = "nucli:main"
56
56
 
57
57
  [tool.hatch.build.targets.wheel]
58
- packages = ["src/nu"]
59
- # No `nu/__init__.py` here on purpose: the kernel wheel owns the package root
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`.
@@ -1,6 +1,6 @@
1
1
  """The `nu` command — entrypoint package."""
2
2
 
3
- from nu._cli.main import cli, main
3
+ from nucli.main import cli, main
4
4
 
5
5
 
6
6
  __all__ = ["cli", "main"]
@@ -0,0 +1,7 @@
1
+ """Enables `python -m nucli`."""
2
+
3
+ from nucli.main import main
4
+
5
+
6
+ if __name__ == "__main__":
7
+ main()
@@ -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 nu._cli.demos)."""
19
- return Path(str(resources.files("nu._cli").joinpath("demos")))
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]:
@@ -12,8 +12,8 @@ from rich.console import Console
12
12
  from rich.table import Table
13
13
  from rich.text import Text
14
14
 
15
- from nu._cli._meta import demos
16
15
  from nu._config.branding import BLUE, PURPLE
16
+ from nucli._meta import demos
17
17
 
18
18
 
19
19
  if TYPE_CHECKING:
@@ -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: nu.kv.IntRef
16
+ value: nustd.kv.IntRef
16
17
 
17
18
 
18
- class Links(nu.ui.Row):
19
- docs = nu.ui.LinkRef.slot(
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 = nu.ui.LinkRef.slot(
23
+ github = nustd.ui.LinkRef.slot(
23
24
  label="Star on GitHub", href="https://github.com/nustackdev/nu", target="_blank"
24
25
  )
25
- examples = nu.ui.LinkRef.slot(
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(nu.ui.Page):
33
+ class Dashboard(nustd.ui.Page):
33
34
  """Live counter page with description and source."""
34
35
 
35
- heading = nu.ui.HeadingRef.slot(label="Persistent counter, live")
36
- count = nu.ui.StatRef.slot(label="Count")
37
- about_heading = nu.ui.HeadingRef.slot(label="How it works")
38
- about = nu.ui.MarkdownRef.slot(
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 = nu.ui.HeadingRef.slot(label="Try Nu yourself")
48
- links_intro = nu.ui.TextRef.slot(
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 = nu.ui.HeadingRef.slot(label="Source")
53
- source_intro = nu.ui.TextRef.slot(
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 = nu.ui.CodeBlockRef.slot(
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(nu.ui.Index):
63
+ class App(nustd.ui.Index):
63
64
  """UI index with one page."""
64
65
 
65
- pages = nu.ui.Pages({"/": Dashboard})
66
+ home = Dashboard.slot("/")
66
67
 
67
68
 
68
69
  app = nu.With(
69
- nu.kv.rocksdb_navigator(str(_DB)),
70
- nu.ui.server(
71
- nu.kv.auto_flow_atomic(
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
- Dashboard.count.set_value(nu.str(Counter.value)),
75
+ App.home.count.set_value(nu.str(Counter.value)),
75
76
  ),
76
77
  ),
77
78
  ),
78
- body=nu.kv.auto_flow_atomic(
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(nu.ui.Field):
27
- input = nu.ui.InputRef.slot(placeholder="e.g. Arrival")
27
+ class TitleField(nustd.ui.Field):
28
+ input = nustd.ui.InputRef.slot(placeholder="e.g. Arrival")
28
29
 
29
30
 
30
- class TextAreaField(nu.ui.Field):
31
- input = nu.ui.TextAreaRef.slot(placeholder="Quick thoughts...", rows=2)
31
+ class TextAreaField(nustd.ui.Field):
32
+ input = nustd.ui.TextAreaRef.slot(placeholder="Quick thoughts...", rows=2)
32
33
 
33
34
 
34
- class YearField(nu.ui.Field):
35
- input = nu.ui.NumberInputRef.slot(
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(nu.ui.Field):
44
- input = nu.ui.NumberInputRef.slot(
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(nu.ui.Field):
53
- input = nu.ui.SelectRef.slot(options=_GENRES, selected="drama")
53
+ class GenreField(nustd.ui.Field):
54
+ input = nustd.ui.SelectRef.slot(options=_GENRES, selected="drama")
54
55
 
55
56
 
56
- class SwitchField(nu.ui.Field):
57
- input = nu.ui.SwitchRef.slot(default=True)
57
+ class SwitchField(nustd.ui.Field):
58
+ input = nustd.ui.SwitchRef.slot(default=True)
58
59
 
59
60
 
60
- class DetailsFieldset(nu.ui.Fieldset):
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(nu.ui.Fieldset):
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(nu.ui.Form):
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 = nu.ui.ButtonRef.slot(label="Log it", variant="primary")
76
- feedback = nu.ui.AlertRef.slot(variant="ok", dismissible=True)
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(nu.ui.Field):
80
- input = nu.ui.NumberInputRef.slot(min=1.0, max=10.0, step=0.5, default=1.0)
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(nu.ui.Field):
84
- input = nu.ui.SelectRef.slot(options=_GENRES_WITH_ANY, selected="")
84
+ class FilterGenreField(nustd.ui.Field):
85
+ input = nustd.ui.SelectRef.slot(options=_GENRES_WITH_ANY, selected="")
85
86
 
86
87
 
87
- class WatchedOnlyField(nu.ui.Field):
88
- input = nu.ui.SwitchRef.slot(default=False)
88
+ class WatchedOnlyField(nustd.ui.Field):
89
+ input = nustd.ui.SwitchRef.slot(default=False)
89
90
 
90
91
 
91
- class FilterRow(nu.ui.Row):
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 = nu.ui.ButtonRef.slot(label="Apply", variant="secondary")
96
- clear = nu.ui.ButtonRef.slot(label="Clear", variant="ghost")
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(nu.ui.Card):
100
+ class FilterCard(nustd.ui.Card):
100
101
  body = FilterRow.slot(gap=3, align="center", wrap=True)
101
102
 
102
103
 
103
- class StatsRow(nu.ui.Row):
104
- total = nu.ui.StatRef.slot(label="Total")
105
- watched = nu.ui.StatRef.slot(label="Watched")
106
- unseen = nu.ui.StatRef.slot(label="Unseen")
107
- latest = nu.ui.TextRef.slot()
108
- health = nu.ui.BadgeRef.slot(label="Fresh", variant="ok")
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(nu.ui.Card):
112
+ class StatsCard(nustd.ui.Card):
112
113
  body = StatsRow.slot(gap=6, align="center", wrap=True)
113
114
 
114
115
 
115
- class TableBody(nu.ui.Column):
116
- table = nu.ui.TableRef.slot(
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 = nu.ui.AlertRef.slot(
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(nu.ui.Card):
131
+ class TableCard(nustd.ui.Card):
131
132
  body = TableBody.slot(gap=3)
132
133
 
133
134
 
134
- class Links(nu.ui.Row):
135
- docs = nu.ui.LinkRef.slot(
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 = nu.ui.LinkRef.slot(
139
+ github = nustd.ui.LinkRef.slot(
139
140
  label="Star on GitHub", href="https://github.com/nustackdev/nu", target="_blank"
140
141
  )
141
- examples = nu.ui.LinkRef.slot(
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(nu.ui.Row):
152
- about = nu.ui.ButtonRef.slot(label="About this demo", variant="ghost")
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(nu.ui.Page):
156
- heading = nu.ui.HeadingRef.slot(label="Your movies")
157
- intro = nu.ui.TextRef.slot(
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(nu.ui.Row):
169
- back = nu.ui.ButtonRef.slot(label="Back to app", variant="ghost")
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(nu.ui.Page):
173
- heading = nu.ui.HeadingRef.slot(label="How it works")
174
- about = nu.ui.MarkdownRef.slot(
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,73 +180,75 @@ class About(nu.ui.Page):
179
180
  "- Same Interactions handle add, delete, filter, and page routing.\n"
180
181
  ),
181
182
  )
182
- links_heading = nu.ui.HeadingRef.slot(label="Try Nu yourself")
183
- links_intro = nu.ui.TextRef.slot(
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 = nu.ui.HeadingRef.slot(label="Source")
188
- source_intro = nu.ui.TextRef.slot(
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 = nu.ui.CodeBlockRef.slot(
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(nu.ui.Row):
199
- year = nu.ui.StatRef.slot(label="Year")
200
- genre = nu.ui.StatRef.slot(label="Genre")
201
- rating = nu.ui.StatRef.slot(label="Rating")
202
- watched = nu.ui.BadgeRef.slot(label="Watched", variant="ok")
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(nu.ui.Card):
206
+ class MetaCard(nustd.ui.Card):
206
207
  meta = DetailRow.slot(gap=6, align="center", wrap=True)
207
208
 
208
209
 
209
- class NotesCard(nu.ui.Card):
210
- body = nu.ui.MarkdownRef.slot()
210
+ class NotesCard(nustd.ui.Card):
211
+ body = nustd.ui.MarkdownRef.slot()
211
212
 
212
213
 
213
- class DetailActions(nu.ui.Row):
214
- back = nu.ui.ButtonRef.slot(label="Back", variant="ghost")
215
- remove = nu.ui.ButtonRef.slot(label="Delete", variant="danger")
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(nu.ui.Page):
219
- heading = nu.ui.HeadingRef.slot()
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(nu.ui.Index):
226
- title: nu.ui.TitleRef
227
- nav: nu.ui.NavRef
228
- pages = nu.ui.Pages({"/": Movies, "/detail": MovieDetail, "/about": About})
226
+ class App(nustd.ui.Index):
227
+ title: nustd.ui.TitleRef
228
+ nav: nustd.ui.NavRef
229
+ movies = Movies.slot("/")
230
+ detail = MovieDetail.slot("/detail")
231
+ about = About.slot("/about")
229
232
 
230
233
 
231
234
  # ---- State ------------------------------------------------------------------
232
235
 
233
236
 
234
237
  class Movie(nu.Shape):
235
- title = nu.kv.StrRef.slot()
236
- year = nu.kv.IntRef.slot()
237
- genre = nu.kv.StrRef.slot()
238
- rating = nu.kv.FloatRef.slot()
239
- watched = nu.kv.BoolRef.slot()
240
- notes = nu.kv.StrRef.slot()
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()
241
244
 
242
245
 
243
246
  class State(nu.Shape):
244
- movies = nu.kv.ShapesListRef.slot(Movie)
245
- total = nu.kv.IntRef.slot()
246
- watched = nu.kv.IntRef.slot()
247
- latest_title = nu.kv.StrRef.slot()
248
- selected = nu.kv.IntRef.slot() # index of the movie open in MovieDetail
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
249
252
 
250
253
 
251
254
  # ---- Seed ------------------------------------------------------------------
@@ -310,9 +313,9 @@ def _rows_form() -> nu.Nu:
310
313
 
311
314
  def _rows_filtered() -> nu.Nu:
312
315
  """Same shape as _rows_form, but honors the current FilterRow inputs."""
313
- min_r = nu.Float(FilterRow.min_rating.input)
314
- genre = nu.Str(FilterRow.genre.input)
315
- watched_only = nu.Bool(FilterRow.watched_only.input)
316
+ min_r = nu.Float(App.movies.filters.body.min_rating.input)
317
+ genre = nu.Str(App.movies.filters.body.genre.input)
318
+ watched_only = nu.Bool(App.movies.filters.body.watched_only.input)
316
319
  predicate = nu.And(
317
320
  nu.Ge(nu.DictAttrRef("r")["rating"], min_r),
318
321
  nu.Or(nu.Eq(genre, ""), nu.Eq(nu.DictAttrRef("r")["genre"], genre)),
@@ -332,7 +335,7 @@ def _rows_filtered() -> nu.Nu:
332
335
  # Seed once, on a store that has never been written. A restart then keeps what
333
336
  # the user logged instead of replacing the shelf with the samples again.
334
337
  # `selected` stays unconditional: it is a cursor into the detail page, not data.
335
- init = nu.kv.Transaction(
338
+ init = nustd.kv.Transaction(
336
339
  nu.IfDo(
337
340
  State.total.missing(),
338
341
  State.total.set(len(_SEED_MOVIES))
@@ -344,62 +347,62 @@ init = nu.kv.Transaction(
344
347
  )
345
348
 
346
349
 
347
- hydrate = nu.kv.Snapshot(
348
- Movies.stats.body.total.set_value(nu.str(State.total))
349
- | Movies.stats.body.watched.set_value(nu.str(State.watched))
350
- | Movies.stats.body.unseen.set_value(nu.str(State.total - State.watched))
351
- | Movies.stats.body.latest.set(State.latest_title)
352
- | Movies.shelf.body.table.set(_rows_form())
350
+ hydrate = nustd.kv.Snapshot(
351
+ App.movies.stats.body.total.set_value(nu.str(State.total))
352
+ | App.movies.stats.body.watched.set_value(nu.str(State.watched))
353
+ | App.movies.stats.body.unseen.set_value(nu.str(State.total - State.watched))
354
+ | App.movies.stats.body.latest.set(State.latest_title)
355
+ | App.movies.shelf.body.table.set(_rows_form())
353
356
  )
354
357
 
355
358
 
356
359
  on_add = nu.ReactForever(
357
- AddMovieForm.submit.clicked(),
358
- nu.kv.Transaction(
360
+ App.movies.form.submit.on_click(),
361
+ nustd.kv.Transaction(
359
362
  State.movies.append(
360
363
  nu.Dict.of(
361
- title=nu.Str(AddMovieForm.details.title.input),
362
- year=nu.Int(AddMovieForm.details.year.input),
363
- genre=nu.Str(AddMovieForm.details.genre.input),
364
- rating=nu.Float(AddMovieForm.score.rating.input),
365
- watched=nu.Bool(AddMovieForm.score.watched.input),
366
- notes=nu.Str(AddMovieForm.score.notes.input),
364
+ title=nu.Str(App.movies.form.details.title.input),
365
+ year=nu.Int(App.movies.form.details.year.input),
366
+ genre=nu.Str(App.movies.form.details.genre.input),
367
+ rating=nu.Float(App.movies.form.score.rating.input),
368
+ watched=nu.Bool(App.movies.form.score.watched.input),
369
+ notes=nu.Str(App.movies.form.score.notes.input),
367
370
  ),
368
371
  )
369
372
  | State.total.set(State.total + 1)
370
373
  | State.watched.set(
371
- State.watched + nu.If(nu.Bool(AddMovieForm.score.watched.input), 1, 0),
374
+ State.watched + nu.If(nu.Bool(App.movies.form.score.watched.input), 1, 0),
372
375
  )
373
- | State.latest_title.set(nu.Str(AddMovieForm.details.title.input)),
376
+ | State.latest_title.set(nu.Str(App.movies.form.details.title.input)),
374
377
  )
375
- >> nu.kv.Snapshot(
376
- Movies.shelf.body.table.set(_rows_form())
377
- | Movies.stats.body.total.set_value(nu.str(State.total))
378
- | Movies.stats.body.watched.set_value(nu.str(State.watched))
379
- | Movies.stats.body.unseen.set_value(nu.str(State.total - State.watched))
380
- | Movies.stats.body.latest.set(State.latest_title)
381
- | Movies.form.feedback.set(
378
+ >> nustd.kv.Snapshot(
379
+ App.movies.shelf.body.table.set(_rows_form())
380
+ | App.movies.stats.body.total.set_value(nu.str(State.total))
381
+ | App.movies.stats.body.watched.set_value(nu.str(State.watched))
382
+ | App.movies.stats.body.unseen.set_value(nu.str(State.total - State.watched))
383
+ | App.movies.stats.body.latest.set(State.latest_title)
384
+ | App.movies.form.feedback.set(
382
385
  title="Logged",
383
- body="Added " + nu.Str(AddMovieForm.details.title.input),
386
+ body="Added " + nu.Str(App.movies.form.details.title.input),
384
387
  )
385
388
  ),
386
389
  )
387
390
 
388
391
 
389
392
  on_row_click = nu.ReactForever(
390
- Movies.shelf.body.table.row_clicked(),
393
+ App.movies.shelf.body.table.on_row_click(),
391
394
  nu.IfDo(
392
395
  nu.Contains(nu.DictAttrRef("row_click"), "row_index"),
393
- nu.kv.Transaction(State.selected.set(nu.DictAttrRef("row_click")["row_index"]))
394
- >> nu.kv.Snapshot(
395
- MovieDetail.heading.set(State.movies[State.selected].title)
396
- | MovieDetail.meta.meta.year.set_value(nu.str(State.movies[State.selected].year))
397
- | MovieDetail.meta.meta.genre.set_value(State.movies[State.selected].genre)
398
- | MovieDetail.meta.meta.rating.set_value(nu.str(State.movies[State.selected].rating))
399
- | MovieDetail.meta.meta.watched.set(
396
+ nustd.kv.Transaction(State.selected.set(nu.DictAttrRef("row_click")["row_index"]))
397
+ >> nustd.kv.Snapshot(
398
+ App.detail.heading.set(State.movies[State.selected].title)
399
+ | App.detail.meta.meta.year.set_value(nu.str(State.movies[State.selected].year))
400
+ | App.detail.meta.meta.genre.set_value(State.movies[State.selected].genre)
401
+ | App.detail.meta.meta.rating.set_value(nu.str(State.movies[State.selected].rating))
402
+ | App.detail.meta.meta.watched.set(
400
403
  label=nu.If(State.movies[State.selected].watched, "Watched", "Unseen"),
401
404
  )
402
- | MovieDetail.notes.body.set(State.movies[State.selected].notes)
405
+ | App.detail.notes.body.set(State.movies[State.selected].notes)
403
406
  )
404
407
  >> App.nav.set("/detail"),
405
408
  ),
@@ -408,41 +411,41 @@ on_row_click = nu.ReactForever(
408
411
 
409
412
 
410
413
  on_delete = nu.ReactForever(
411
- MovieDetail.actions.remove.clicked(),
412
- nu.kv.Transaction(
414
+ App.detail.actions.remove.on_click(),
415
+ nustd.kv.Transaction(
413
416
  State.movies.del_at(State.selected) >> State.total.set(nu.Len(State.movies)),
414
417
  )
415
- >> nu.kv.Snapshot(
416
- Movies.shelf.body.table.set(_rows_form())
417
- | Movies.stats.body.total.set_value(nu.str(State.total))
418
- | Movies.stats.body.unseen.set_value(nu.str(State.total - State.watched))
418
+ >> nustd.kv.Snapshot(
419
+ App.movies.shelf.body.table.set(_rows_form())
420
+ | App.movies.stats.body.total.set_value(nu.str(State.total))
421
+ | App.movies.stats.body.unseen.set_value(nu.str(State.total - State.watched))
419
422
  )
420
423
  >> App.nav.set("/"),
421
424
  )
422
425
 
423
426
 
424
- on_back = nu.ReactForever(MovieDetail.actions.back.clicked(), App.nav.set("/"))
427
+ on_back = nu.ReactForever(App.detail.actions.back.on_click(), App.nav.set("/"))
425
428
 
426
429
 
427
- on_about_open = nu.ReactForever(Movies.topbar.about.clicked(), App.nav.set("/about"))
430
+ on_about_open = nu.ReactForever(App.movies.topbar.about.on_click(), App.nav.set("/about"))
428
431
 
429
432
 
430
- on_about_back = nu.ReactForever(About.actions.back.clicked(), App.nav.set("/"))
433
+ on_about_back = nu.ReactForever(App.about.actions.back.on_click(), App.nav.set("/"))
431
434
 
432
435
 
433
436
  on_filter_apply = nu.ReactForever(
434
- FilterRow.apply.clicked(),
435
- nu.kv.Snapshot(Movies.shelf.body.table.set(_rows_filtered())),
437
+ App.movies.filters.body.apply.on_click(),
438
+ nustd.kv.Snapshot(App.movies.shelf.body.table.set(_rows_filtered())),
436
439
  )
437
440
 
438
441
 
439
442
  on_filter_clear = nu.ReactForever(
440
- FilterRow.clear.clicked(),
441
- nu.kv.Snapshot(
442
- FilterRow.min_rating.input.set(1.0)
443
- | FilterRow.genre.input.set("")
444
- | FilterRow.watched_only.input.set(False)
445
- | Movies.shelf.body.table.set(_rows_form())
443
+ App.movies.filters.body.clear.on_click(),
444
+ nustd.kv.Snapshot(
445
+ App.movies.filters.body.min_rating.input.set(1.0)
446
+ | App.movies.filters.body.genre.input.set("")
447
+ | App.movies.filters.body.watched_only.input.set(False)
448
+ | App.movies.shelf.body.table.set(_rows_form())
446
449
  ),
447
450
  )
448
451
 
@@ -464,9 +467,9 @@ ui = (
464
467
 
465
468
 
466
469
  app = nu.With(
467
- nu.kv.rocksdb_navigator(str(_DB)),
468
- nu.ui.server(nu.kv.auto_flow_atomic(ui)),
469
- body=nu.kv.auto_flow_atomic(init >> nu.ForeverDo(nu.Delay(3600))),
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))),
470
473
  )
471
474
 
472
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 = nu.kv.Kh57Ref.slot(int)
16
- cursor = nu.kv.IntRef.slot()
16
+ nums = nustd.kv.Kh57Ref.slot(int)
17
+ cursor = nustd.kv.IntRef.slot()
17
18
 
18
19
 
19
- class Links(nu.ui.Row):
20
- docs = nu.ui.LinkRef.slot(
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 = nu.ui.LinkRef.slot(
24
+ github = nustd.ui.LinkRef.slot(
24
25
  label="Star on GitHub", href="https://github.com/nustackdev/nu", target="_blank"
25
26
  )
26
- examples = nu.ui.LinkRef.slot(
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(nu.ui.Page):
34
+ class Dashboard(nustd.ui.Page):
34
35
  """Live chart page with description and source."""
35
36
 
36
- heading = nu.ui.HeadingRef.slot(label="Unbounded series, instant chart")
37
- chart = nu.ui.LineChart.slot()
38
- about_heading = nu.ui.HeadingRef.slot(label="How it works")
39
- about = nu.ui.MarkdownRef.slot(
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,31 +46,31 @@ 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 = nu.ui.HeadingRef.slot(label="Try Nu yourself")
49
- links_intro = nu.ui.TextRef.slot(
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 = nu.ui.HeadingRef.slot(label="Source")
54
- source_intro = nu.ui.TextRef.slot(
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 = nu.ui.CodeBlockRef.slot(
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(nu.ui.Index):
64
+ class App(nustd.ui.Index):
64
65
  """UI index with one page."""
65
66
 
66
- pages = nu.ui.Pages({"/": Dashboard})
67
+ home = Dashboard.slot("/")
67
68
 
68
69
 
69
70
  # reactive wire: repaint the chart on every write to `nums`
70
71
  ui = nu.ReactForever(
71
72
  State.nums.on_change(),
72
- Dashboard.chart.set_points(
73
+ App.home.chart.set_points(
73
74
  nu.Collect(nu.Sorted(nu.Iter(State.nums.sample(200, 0, State.cursor)))),
74
75
  ),
75
76
  )
@@ -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
- nu.kv.rocksdb_navigator(str(_DB)),
85
- nu.ui.server(nu.kv.auto_flow_atomic(ui)),
86
- body=nu.kv.auto_flow_atomic(feed),
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.
@@ -1,7 +0,0 @@
1
- """Enables `python -m nu._cli`."""
2
-
3
- from nu._cli.main import main
4
-
5
-
6
- if __name__ == "__main__":
7
- main()
File without changes
File without changes