sediment-cli 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.
sediment_cli/ui.py ADDED
@@ -0,0 +1,123 @@
1
+ # SPDX-License-Identifier: AGPL-3.0-or-later
2
+ """
3
+ Terminal styling for the operator CLI, in the Sediment brand palette.
4
+
5
+ Stdlib-only ANSI. Styling is gated per stream: on only for a TTY, never
6
+ under ``NO_COLOR`` or ``TERM=dumb`` — piped output, tests, and CI read
7
+ exactly the bytes they read before this module existed. Truecolor when
8
+ ``COLORTERM`` advertises it, 256-color approximations otherwise.
9
+
10
+ The palette is the site design system (the ``--sds-*`` tokens): sandstone
11
+ is the one accent, phosphor means success, iron oxide means failure,
12
+ bleached is for headings, and hierarchy below that is dim — never a fourth
13
+ color. Flat color only (the brand allows no gradients), so the strata mark
14
+ is three flat bands, light to dark.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ import os
20
+ import sys
21
+ from typing import TextIO
22
+
23
+ # name -> (truecolor RGB, 256-color fallback), from the brand kit.
24
+ _PALETTE = {
25
+ "sandstone": ((196, 147, 90), 173), # --sds-sandstone · primary accent
26
+ "phosphor": ((95, 216, 149), 78), # positive / success
27
+ "iron-oxide": ((184, 73, 43), 130), # negative / errors
28
+ "bleached": ((240, 237, 232), 255), # --sds-bleached · headings
29
+ "ochre": ((139, 115, 85), 95), # --sds-ochre · muted
30
+ }
31
+ _ATTRS = {"bold": "\x1b[1m", "dim": "\x1b[2m"}
32
+ _RESET = "\x1b[0m"
33
+
34
+
35
+ def on(stream: TextIO | None = None) -> bool:
36
+ """Whether *stream* (default stdout) gets styled output."""
37
+ stream = sys.stdout if stream is None else stream
38
+ if "NO_COLOR" in os.environ or os.environ.get("TERM") == "dumb":
39
+ return False
40
+ return hasattr(stream, "isatty") and stream.isatty()
41
+
42
+
43
+ def _fg(name: str) -> str:
44
+ rgb, fallback = _PALETTE[name]
45
+ if os.environ.get("COLORTERM") in ("truecolor", "24bit"):
46
+ return "\x1b[38;2;%d;%d;%dm" % rgb
47
+ return f"\x1b[38;5;{fallback}m"
48
+
49
+
50
+ def style(text: str, *names: str, stream: TextIO | None = None) -> str:
51
+ """*text* wrapped in the named palette colors / attrs, unchanged when the
52
+ stream is not a styled TTY. Pad before styling — ANSI codes break
53
+ ``str.format`` field widths."""
54
+ if not on(stream):
55
+ return text
56
+ codes = "".join(_ATTRS.get(n) or _fg(n) for n in names)
57
+ return f"{codes}{text}{_RESET}"
58
+
59
+
60
+ def glyph(char: str, name: str, stream: TextIO | None = None) -> str:
61
+ """A styled ``✓ ``-style prefix on a styled TTY, ``""`` otherwise, so
62
+ plain output keeps its exact historical bytes."""
63
+ return f"{style(char, name, stream=stream)} " if on(stream) else ""
64
+
65
+
66
+ def error_line(msg: str) -> str:
67
+ """The CLI's one stderr error shape: ``✗ error: <msg>`` styled on a
68
+ TTY, exactly ``error: <msg>`` piped — every verb routes through this so
69
+ the vocabulary and the bytes stay uniform."""
70
+ err = sys.stderr
71
+ label = glyph("✗", "iron-oxide", err) + style("error:", "iron-oxide", stream=err)
72
+ return f"{label} {msg}"
73
+
74
+
75
+ def warn_line(msg: str) -> str:
76
+ """The matching stderr warning shape: ``⚠ warning: <msg>`` styled on a
77
+ TTY, exactly ``warning: <msg>`` piped."""
78
+ err = sys.stderr
79
+ label = glyph("⚠", "sandstone", err) + style("warning:", "sandstone", stream=err)
80
+ return f"{label} {msg}"
81
+
82
+
83
+ # Listing sections of help output whose first cell (command / flag) gets the
84
+ # accent; USAGE bodies and description text stay uncolored.
85
+ _HELP_LISTINGS = frozenset(
86
+ {"COMMANDS:", "FORMATS:", "OPTIONS:", "ARGUMENTS:", "REPORTS:"}
87
+ )
88
+
89
+
90
+ def style_help(text: str, stream: TextIO | None = None) -> str:
91
+ """TTY-gated color pass over already-formatted help text: bold
92
+ headings, accent on the invocation cell of listing rows. Plain text
93
+ passes through untouched, so goldens and pipes never see codes."""
94
+ if not on(stream):
95
+ return text
96
+ out: list[str] = []
97
+ listing = False
98
+ for line in text.splitlines():
99
+ if line and not line.startswith(" ") and line == line.upper():
100
+ listing = line.endswith(":") and line in _HELP_LISTINGS
101
+ out.append(style(line, "bleached", "bold", stream=stream))
102
+ elif listing and line.startswith(" ") and len(line) > 2 and line[2] != " ":
103
+ cell, gap, rest = line[2:].partition(" ")
104
+ out.append(" " + style(cell, "sandstone", stream=stream) + gap + rest)
105
+ else:
106
+ out.append(line)
107
+ return "\n".join(out) + ("\n" if text.endswith("\n") else "")
108
+
109
+
110
+ def banner(title: str, subtitle: str) -> None:
111
+ """The strata mark: wordmark over three flat bands, light to dark.
112
+ TTY-only — a piped invocation prints no banner at all."""
113
+ if not on():
114
+ return
115
+ bands = (
116
+ style("▬" * 10, "bleached")
117
+ + style("▬" * 10, "sandstone")
118
+ + style("▬" * 10, "ochre")
119
+ )
120
+ print()
121
+ print(f" {style(title, 'bleached', 'bold')} {style(subtitle, 'dim')}")
122
+ print(f" {bands}")
123
+ print()
@@ -0,0 +1,16 @@
1
+ Metadata-Version: 2.5
2
+ Name: sediment-cli
3
+ Version: 0.1.0
4
+ Summary: The sediment command: login, install, server, facts, exports
5
+ Project-URL: Source, https://github.com/sediment-ai/sediment
6
+ Project-URL: Documentation, https://github.com/sediment-ai/sediment/tree/main/docs
7
+ Project-URL: Issues, https://github.com/sediment-ai/sediment/issues
8
+ Project-URL: Changelog, https://github.com/sediment-ai/sediment/blob/main/CHANGELOG.md
9
+ License-Expression: AGPL-3.0-or-later
10
+ License-File: LICENSE
11
+ Requires-Python: >=3.12
12
+ Requires-Dist: httpx>=0.27
13
+ Requires-Dist: sediment-api==0.1.0
14
+ Requires-Dist: sediment-core==0.1.0
15
+ Requires-Dist: sediment-derive==0.1.0
16
+ Requires-Dist: sediment-export==0.1.0
@@ -0,0 +1,13 @@
1
+ sediment_cli/__init__.py,sha256=6gb2sotSZC-wSD3UBl6h-9c5qPw8Df0UnBKLkmwdzVk,782
2
+ sediment_cli/attribution.py,sha256=saR1WOKd9IAQp-EKMGWKgttdUF6vzjTqFhSZbsXLZpk,148644
3
+ sediment_cli/cli.py,sha256=ixAiPhZjIt8NNZtyLZ9-U_ocV7Qs_ul_3ea0PF_j4j0,74974
4
+ sediment_cli/client.py,sha256=-PSnax17b2P4FGRfY5jxXSw5tHr-htJ5VobzY7xQDDw,11337
5
+ sediment_cli/delivery.py,sha256=iNfdVXTyYvPrzUuIthomlQPU3zesw-vTaqKKKpRUzws,47874
6
+ sediment_cli/local_postgres.py,sha256=NTYo0Wz2l0BahMqjCglRufZ4z6wN6CilfYkRuAfQR-s,12922
7
+ sediment_cli/transcript.py,sha256=3rtBHhHLCVRLRJOf2TGOUypR2OOyqupP8ZOKHpE9Bh8,70158
8
+ sediment_cli/ui.py,sha256=Z3OGv4wwDz6p3T-CVtqBlFBybCVnviy_UtehBXzo2UU,4811
9
+ sediment_cli-0.1.0.dist-info/METADATA,sha256=JbOniGzESp1Hj27MMX94xGfj9J665trzSEofz2rXc1Y,683
10
+ sediment_cli-0.1.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
11
+ sediment_cli-0.1.0.dist-info/entry_points.txt,sha256=1XF4d332LhsC0uzRrbOnB3NQooeCD7XtW74ioGaUVvo,47
12
+ sediment_cli-0.1.0.dist-info/licenses/LICENSE,sha256=hIahDEOTzuHCU5J2nd07LWwkLW7Hko4UFO__ffsvB-8,34523
13
+ sediment_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ sediment = sediment_cli:main