-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathui.py
More file actions
322 lines (275 loc) · 10.9 KB
/
Copy pathui.py
File metadata and controls
322 lines (275 loc) · 10.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
#!/usr/bin/env python3
"""Terminal rendering for KeySurgeon.
Rich powers the default v2 surface. --plain / --no-color falls back to this
dependency-light ANSI/ASCII renderer so automation never has to parse styled UI.
"""
import ctypes
import ctypes.wintypes as wt
import os
import sys
import textwrap
import brand
from faults import band
try:
import rich_ui
except Exception: # pragma: no cover - fallback path is intentionally broad
rich_ui = None
_RAW = {
"RST": "\x1b[0m",
"BLD": "\x1b[1m",
"ACC": "\x1b[38;5;51m", # signal cyan
"OK": "\x1b[38;5;84m", # repair green
"BAD": "\x1b[38;5;203m", # fault coral
"WRN": "\x1b[38;5;221m", # probe amber
"DIM": "\x1b[38;5;245m",
}
USE_COLOR = True
USE_GLYPHS = True
USE_RICH = False
def init(use_color=True, use_glyphs=True):
global USE_COLOR, USE_GLYPHS, USE_RICH
USE_COLOR = use_color and not os.environ.get("NO_COLOR")
USE_GLYPHS = use_glyphs
USE_RICH = bool(USE_COLOR and USE_GLYPHS and rich_ui)
if USE_COLOR:
h = ctypes.windll.kernel32.GetStdHandle(-11)
mode = wt.DWORD()
if ctypes.windll.kernel32.GetConsoleMode(h, ctypes.byref(mode)):
ctypes.windll.kernel32.SetConsoleMode(h, mode.value | 0x0004)
def _esc(name):
return _RAW[name] if USE_COLOR else ""
def c(text, *names):
if not USE_COLOR:
return str(text)
return "".join(_esc(n) for n in names) + str(text) + _esc("RST")
def g(glyph, ascii_fallback):
return glyph if USE_GLYPHS else ascii_fallback
_BAND_COLOR = {
"HEALTHY": "OK",
"WATCH": "WRN",
"DEGRADING": "WRN",
"FAILING": "BAD",
}
def score_color(score):
return _BAND_COLOR.get(band(score), "DIM")
def banner(subtitle=""):
if USE_RICH:
return rich_ui.banner(subtitle)
line = c(brand.SIGNAL_MARK + " " + brand.NAME, "BLD", "ACC")
if subtitle:
line += " " + c("/", "DIM") + " " + c(subtitle, "DIM")
print("\n " + line + "\n")
MENU_ITEMS = [
("0", "tour", "Tour", "first-run command center and proof gates"),
("1", "triage", "Triage", "something's wrong, walk me through it"),
("2", "sweep", "Sweep", "check the whole board"),
("3", "watch", "Watch", "tell me what's dying while I type"),
("4", "test", "Test", "test specific keys right now"),
("5", "report", "Report", "latest board health and trend"),
("6", "fix", "Fix", "show the repair ladder for one key"),
("7", "proof", "Proof", "full local proof and blockers"),
("8", "app", "App", "open the optional Textual command center"),
("d", "doctor", "Doctor", "check install and support facts"),
]
def menu():
if USE_RICH:
rich_ui.menu(MENU_ITEMS)
return input(" pick a number > ")
banner("pick a mode")
for num, _mode, title, blurb in MENU_ITEMS:
print(" " + c(num, "BLD", "ACC") + " "
+ c(f"{title:<8}", "BLD") + c(blurb, "DIM"))
print("\n " + c("q", "DIM") + " " + c("quit", "DIM"))
return input("\n " + c("pick a number", "ACC") + c(" > ", "DIM"))
def tour(payload):
if USE_RICH:
return rich_ui.tour(payload)
banner("first-run tour")
proof = payload["local_proof"]
summary = payload.get("proof_summary") or {}
print(" " + c("What it is: ", "BLD") + "keyboard chatter and fault diagnosis")
print(" " + c("Core loop: ", "BLD") + "watch -> test -> fix -> proof")
print(" " + c("App shell: ", "BLD", "ACC") + "keysurgeon app")
print(" " + c("Privacy: ", "BLD") + "local JSON only; no typed text exported")
print()
rows = [
("1", "keysurgeon watch --bg", "catch double-fires while you type normally"),
("2", "keysurgeon test E", "turn suspicion into a timed verdict"),
("3", "keysurgeon fix E", "show the cheapest-first repair ladder"),
("4", "keysurgeon proof --json", "see exactly what is and is not proved"),
("5", "keysurgeon issue", "write a redacted GitHub-ready packet"),
]
for num, command, why in rows:
print(" " + c(num, "ACC", "BLD") + " " + c(f"{command:<28}", "BLD") + c(why, "DIM"))
print()
gates = [
("Rich/Textual", proof["rich_textual_stack"]),
("Public assets", proof["demo_assets"]),
("Hardware smoke", proof["manual_keyboard_smoke"]),
]
print(" " + c(
f"Readiness: local {summary.get('local', 0)} "
f"command-gated {summary.get('command_gated', 0)} "
f"blocked {summary.get('blocked', 0)}",
"BLD",
))
for label, item in gates:
print(" " + c(f"{item['status']:<13}", _proof_status_style(item["status"]), "BLD")
+ f" {label:<16} {item['detail']}")
blockers = payload.get("public_blockers") or []
if blockers:
print()
print(" " + c("Not claimed yet:", "WRN", "BLD"))
for blocker in blockers:
print(" " + c("- ", "WRN") + blocker)
print()
def live_counter(label, n, expected):
if USE_RICH:
return rich_ui.live_counter(label, n, expected)
full = n >= expected
col = "OK" if full else "ACC"
filled = min(12, int((n / max(expected, 1)) * 12))
bar = "[" + ("#" * filled).ljust(12, ".") + "]"
sys.stdout.write("\r " + c(f"[{label}]", "BLD", "ACC") + " "
+ c(bar, col) + f" {n:>2}/{expected}"
+ c(" ESC skips", "DIM") + " ")
sys.stdout.flush()
def _wrap(text, width=48):
return textwrap.wrap(text, width)
def card(verdict, rungs, closer):
if USE_RICH:
return rich_ui.card(verdict, rungs, closer)
label = verdict["label"]
bnd = band(verdict["score"])
bcol = score_color(verdict["score"])
width = 54
head = f" {label} "
tag = f" {bnd} "
fill = width - len(head) - len(tag) - 2
print()
print(" " + c("+" + "-" + head, "DIM") + c("-" * fill, "DIM")
+ c(tag, "BLD", bcol) + c("-+", "DIM"))
def row(s=""):
print(" " + c("|", "DIM") + " " + s)
row(c(verdict["headline"], "BLD"))
for line in _wrap(verdict["detail"]):
row(line)
if verdict.get("confidence") == "low":
row()
for line in _wrap("Only a few presses seen. Run it again for a confident read."):
row(c(line, "WRN"))
evidence = verdict.get("evidence") or []
if evidence:
row()
row(c("evidence - ", "DIM") + c(evidence[0], "DIM"))
for item in evidence[1:]:
row(c(" - " + item, "DIM"))
if rungs:
row()
row(c("What to do, easiest first:", "BLD"))
for n, title, blurb in rungs:
row(c(f" {n} ", "ACC") + c(title, "BLD"))
for line in _wrap(blurb, 44):
row(c(" " + line, "DIM"))
if closer:
row()
row(c(closer, "OK") if verdict["score"] > 0 else c(closer, "WRN"))
print(" " + c("+" + "-" * (width - 1) + "+", "DIM"))
def heatmap(score_by_label, title="board health"):
if USE_RICH:
return rich_ui.heatmap(score_by_label, title)
banner(title)
rows = ["1234567890", "QWERTYUIOP", "ASDFGHJKL", "ZXCVBNM"]
for offset, rowkeys in enumerate(rows):
cells = []
for key in rowkeys:
if key in score_by_label:
cells.append(c(f" {key} ", "BLD", score_color(score_by_label[key])))
else:
cells.append(c(f" {key} ", "DIM"))
print(" " + (" " * offset) + " ".join(cells))
print("\n " + c("+ healthy", "OK") + " " + c("! watch", "WRN")
+ " " + c("x failing", "BAD") + " " + c(". untested", "DIM") + "\n")
def report(snapshot, board_type):
if USE_RICH:
return rich_ui.report(snapshot, board_type)
banner("board report")
if not snapshot:
print(" " + c("No saved results yet.", "DIM"))
print(" " + c("Run a sweep or test first, then check back.", "DIM") + "\n")
return
keys = snapshot["keys"]
scores = [item["score"] for item in keys] or [100]
avg = sum(scores) // len(scores)
bnd = band(avg)
bad = sorted([item for item in keys if item["score"] < 90],
key=lambda item: item["score"])
print(" " + c(f"Board: {bnd}", "BLD", score_color(avg))
+ c(f" avg {avg}/100 ({len(keys)} keys tested, {snapshot['ts']})", "DIM"))
print(" " + c(f"Keyboard type: {board_type}", "DIM") + "\n")
if not bad:
print(" " + c("Every tested key is healthy. Nothing to fix.", "OK") + "\n")
return
print(" " + c("Keys that need attention:", "BLD"))
for item in bad:
print(" " + c(f"{item['label']:<3}", "BLD", score_color(item["score"]))
+ c(f" {item['fault']:<13}", score_color(item["score"]))
+ c(f"score {item['score']}", "DIM"))
worst = bad[0]
print("\n " + c("Most important next step: ", "BLD")
+ c(f"check {worst['label']} ({worst['fault']}) - "
f"run keysurgeon fix {worst['label']}", "ACC") + "\n")
def proof_report(payload):
if USE_RICH:
return rich_ui.proof_report(payload)
banner("proof")
print(" " + c(f"{payload['tool']} {payload['version']}", "BLD", "ACC"))
print(" " + c(payload["privacy"], "DIM"))
print()
proof = payload["local_proof"]
rows = [
("Rich/Textual demos", proof["demo_assets"]),
("Manual keyboard smoke", proof["manual_keyboard_smoke"]),
("Stack proof", proof["rich_textual_stack"]),
("Claim matrix", proof["proof_matrix"]),
]
for label, item in rows:
status = item["status"]
print(" " + c(f"{status:<8}", _proof_status_style(status), "BLD")
+ f" {label:<24} {item['detail']}")
matrix = payload.get("proof_matrix") or []
if matrix:
summary = payload.get("proof_summary") or {}
print()
print(" " + c("Claim matrix:", "BLD"))
print(" " + c(
f"local {summary.get('local', 0)} "
f"command-gated {summary.get('command_gated', 0)} "
f"blocked {summary.get('blocked', 0)}",
"DIM",
))
for item in matrix[:8]:
tier_style = "WRN" if item["tier"] == "blocked" else ("ACC" if item["tier"] == "command_gated" else "OK")
print(" " + c(f"{item['tier']:<13}", tier_style, "BLD")
+ f" {item['claim']} -> {item['verifier']}")
assets = proof["demo_assets"].get("assets") or []
if assets:
print()
print(" " + c("Generated public assets:", "BLD"))
for item in assets:
status = item["status"]
print(" " + c(f"{status:<6}", _proof_status_style(status), "BLD")
+ f" {item['kind']:<28} {item['path']} ({item['bytes']} bytes)")
blockers = payload.get("public_blockers") or []
if blockers:
print()
print(" " + c("Not proved yet:", "WRN", "BLD"))
for blocker in blockers:
print(" " + c("- ", "WRN") + blocker)
print()
def _proof_status_style(status):
if status == "ok":
return "OK"
if status == "missing":
return "BAD"
return "WRN"