tux-review 0.1.0

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.
Files changed (60) hide show
  1. package/README.md +67 -0
  2. package/bin/tux.js +17 -0
  3. package/client/tux-review.css +155 -0
  4. package/client/tux-review.js +586 -0
  5. package/package.json +59 -0
  6. package/skills/tux-design-create.md +50 -0
  7. package/skills/tux-design-incorporate.md +61 -0
  8. package/skills/tux-design-install.md +56 -0
  9. package/skills/tux-design-start-review.md +51 -0
  10. package/skills/tux-design-stop-review.md +44 -0
  11. package/skills/tux-feedback-delete.md +45 -0
  12. package/skills/tux-feedback-export.md +47 -0
  13. package/skills/tux-feedback-show.md +55 -0
  14. package/skills/tux-live-create.md +50 -0
  15. package/skills/tux-live-incorporate.md +62 -0
  16. package/skills/tux-live-install.md +63 -0
  17. package/skills/tux-live-start-review.md +47 -0
  18. package/skills/tux-live-stop-review.md +46 -0
  19. package/src/canonical.js +28 -0
  20. package/src/cli.js +229 -0
  21. package/src/config.js +72 -0
  22. package/src/design.js +140 -0
  23. package/src/errors.js +28 -0
  24. package/src/feedback.js +289 -0
  25. package/src/ids.js +57 -0
  26. package/src/index.js +17 -0
  27. package/src/live.js +261 -0
  28. package/src/schema.js +77 -0
  29. package/src/serve-main.js +42 -0
  30. package/src/server.js +347 -0
  31. package/src/store.js +72 -0
  32. package/src/templates.js +44 -0
  33. package/templates/angular/README.md +19 -0
  34. package/templates/angular/angular.json +49 -0
  35. package/templates/angular/package.json +25 -0
  36. package/templates/angular/src/app/app.component.html +40 -0
  37. package/templates/angular/src/app/app.component.ts +30 -0
  38. package/templates/angular/src/index.html +11 -0
  39. package/templates/angular/src/main.ts +4 -0
  40. package/templates/angular/src/styles.css +11 -0
  41. package/templates/angular/tsconfig.app.json +8 -0
  42. package/templates/angular/tsconfig.json +11 -0
  43. package/templates/react/README.md +18 -0
  44. package/templates/react/index.html +12 -0
  45. package/templates/react/package.json +18 -0
  46. package/templates/react/src/App.jsx +80 -0
  47. package/templates/react/src/main.jsx +6 -0
  48. package/templates/react/src/styles.css +11 -0
  49. package/templates/vanilla/README.md +22 -0
  50. package/templates/vanilla/index.html +18 -0
  51. package/templates/vanilla/package.json +6 -0
  52. package/templates/vanilla/src/app.js +79 -0
  53. package/templates/vanilla/src/styles.css +11 -0
  54. package/templates/vue/README.md +18 -0
  55. package/templates/vue/index.html +12 -0
  56. package/templates/vue/package.json +17 -0
  57. package/templates/vue/src/App.vue +61 -0
  58. package/templates/vue/src/main.js +5 -0
  59. package/templates/vue/src/styles.css +11 -0
  60. package/templates/vue/vite.config.js +6 -0
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # TUX Review
2
+
3
+ TUX turns a running web UI into a review artifact. Reviewers can attach
4
+ structured, machine-readable feedback to pages, routes, components, element
5
+ instances, and UI states directly in the browser.
6
+
7
+ This is the **Node.js/npm variant** of TUX. It includes the `tux` CLI, a
8
+ local review server, framework templates, and the framework-agnostic Review
9
+ Client. It has no runtime dependencies and requires Node.js 20 or newer.
10
+
11
+ Choose this package if your project uses npm/JavaScript or you need the
12
+ `tux-review/client` JavaScript export. If you only want a stdlib-only CLI and
13
+ server, install the separate but command-compatible Python package from PyPI
14
+ instead. Do not install both for one project.
15
+
16
+ ## Install
17
+
18
+ Run the CLI without a global installation:
19
+
20
+ ```bash
21
+ npx --yes --package=tux-review tux --help
22
+ ```
23
+
24
+ Or install it in the project (recommended):
25
+
26
+ ```bash
27
+ npm install --save-dev tux-review
28
+ npx tux --version
29
+ ```
30
+
31
+ ## Typical workflow
32
+
33
+ After installation, run every CLI command through `npx tux …` (or `tux …` if
34
+ you installed it globally). For a clickable design:
35
+
36
+ ```bash
37
+ npx tux design install --framework vanilla
38
+ npx tux design create --framework vanilla --name checkout
39
+ npx tux design start-review
40
+ ```
41
+
42
+ For an existing application, use the live-review workflow:
43
+
44
+ ```bash
45
+ npx tux live install
46
+ npx tux live start-review --url http://localhost:3000
47
+ ```
48
+
49
+ Feedback is stored in `.tux/feedback.json` by default and can be inspected or
50
+ exported with `tux feedback show` and `tux feedback export`.
51
+
52
+ ## What is included
53
+
54
+ - A deterministic CLI for design and live UI reviews
55
+ - A plain-JavaScript browser Review Client (`tux-review/client`)
56
+ - Vanilla, React, Vue, and Angular design templates
57
+ - Canonical JSON feedback and agent skills
58
+
59
+ ## Documentation and support
60
+
61
+ The complete guide, examples, and normative specification are in the
62
+ [GitHub repository](https://github.com/sommelo1/tux). Report issues at
63
+ [github.com/sommelo1/tux/issues](https://github.com/sommelo1/tux/issues).
64
+
65
+ ## License
66
+
67
+ [MIT](https://github.com/sommelo1/tux/blob/main/LICENSE)
package/bin/tux.js ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /** Entry point for the `tux` binary. */
3
+ import { run } from '../src/cli.js';
4
+
5
+ const isTTY = process.stdin.isTTY ?? false;
6
+ const result = await run(process.argv.slice(2), { isTTY });
7
+ if (result.stdout) process.stdout.write(result.stdout);
8
+ if (result.stderr) process.stderr.write(result.stderr);
9
+ if (result.keepAlive) {
10
+ // keep the process alive while a foreground server runs
11
+ process.on('SIGINT', () => {
12
+ result.keepAlive.close();
13
+ process.exit(0);
14
+ });
15
+ } else {
16
+ process.exitCode = result.exit;
17
+ }
@@ -0,0 +1,155 @@
1
+ /* TUX Review Client styles — tux-owned classes only; host element
2
+ * selectors are never touched (Application Isolation, SPC section 61).
3
+ * Themeable via --tux-* tokens; values reference host variables, so host
4
+ * values win. Tokens live on :root because tux surfaces render in three
5
+ * places: the overlay (#tux-root), the top-layer dialog (portaled editor,
6
+ * markers, reparented float/sidebar/toast), and — for the hover outline —
7
+ * directly on host elements. */
8
+
9
+ :root {
10
+ --tux-primary: var(--primary, #569cd6);
11
+ --tux-surface: var(--card-bg, #fff);
12
+ --tux-surface2: var(--chip-bg, #f0f2f5);
13
+ --tux-border: var(--card-border, rgba(0, 0, 0, .12));
14
+ --tux-text: var(--on-surface, #1e1e1e);
15
+ --tux-muted: var(--outline, #6e6e6e);
16
+ --tux-danger: #c62828;
17
+ --tux-rec: #e53935;
18
+ --tux-green: #2e7d32;
19
+ --tux-yellow: #f9a825;
20
+ --tux-radius: 12px;
21
+ --tux-shadow: 0 4px 16px rgba(0, 0, 0, .16);
22
+ }
23
+
24
+ #tux-root {
25
+ font-family: system-ui, sans-serif;
26
+ font-size: 14px;
27
+ color: var(--tux-text);
28
+ }
29
+
30
+ /* Floating activation surface (launcher + state label). While a native
31
+ <dialog> is open, the client reparents this element into the dialog so
32
+ it stays clickable above the backdrop. */
33
+ #tux-root .tux-float,
34
+ dialog[open] .tux-float {
35
+ position: fixed; right: 14px; bottom: 14px; z-index: 2147483000;
36
+ display: flex; align-items: center; gap: 10px;
37
+ font-family: system-ui, sans-serif;
38
+ font-size: 14px;
39
+ color: var(--tux-text);
40
+ }
41
+
42
+ /* Launcher: light gray when off, recording red (pulsing) when commenting. */
43
+ .tux-float .tux-launcher {
44
+ width: 44px; height: 44px; border-radius: 50%;
45
+ border: 1px solid var(--tux-border); cursor: pointer;
46
+ background: var(--tux-surface2); color: var(--tux-muted);
47
+ font-size: 20px; box-shadow: var(--tux-shadow);
48
+ }
49
+ .tux-float .tux-launcher.tux-active {
50
+ background: var(--tux-rec); color: #fff; border-color: var(--tux-rec);
51
+ animation: tux-rec-pulse 1.6s ease-out infinite;
52
+ }
53
+ .tux-float .tux-launcher.tux-invite {
54
+ animation: tux-invite-pulse 2s ease-in-out infinite;
55
+ }
56
+ @keyframes tux-rec-pulse {
57
+ 0% { box-shadow: 0 0 0 0 rgba(229, 57, 53, .45), var(--tux-shadow); }
58
+ 70% { box-shadow: 0 0 0 14px rgba(229, 57, 53, 0), var(--tux-shadow); }
59
+ 100% { box-shadow: 0 0 0 0 rgba(229, 57, 53, 0), var(--tux-shadow); }
60
+ }
61
+ @keyframes tux-invite-pulse {
62
+ 0%, 100% { box-shadow: 0 0 0 0 rgba(86, 156, 214, .40), var(--tux-shadow); }
63
+ 50% { box-shadow: 0 0 0 12px rgba(86, 156, 214, 0), var(--tux-shadow); }
64
+ }
65
+
66
+ /* Persistent state label next to the launcher. */
67
+ .tux-float .tux-launcher-hint {
68
+ height: 44px; display: flex; align-items: center;
69
+ padding: 0 14px; border-radius: 999px;
70
+ background: var(--tux-surface); border: 1px solid var(--tux-border);
71
+ box-shadow: var(--tux-shadow); color: var(--tux-text);
72
+ font-size: 12.5px; font-weight: 600; white-space: nowrap;
73
+ }
74
+ .tux-float .tux-launcher-hint::before {
75
+ content: ""; width: 8px; height: 8px; border-radius: 50%;
76
+ background: var(--tux-muted); margin-right: 8px; flex: none;
77
+ }
78
+ .tux-float .tux-launcher-hint.tux-hint-on::before { background: var(--tux-rec); }
79
+ .tux-float .tux-launcher-hint.tux-hint-invite::before {
80
+ background: var(--tux-primary);
81
+ animation: tux-invite-pulse 2s ease-in-out infinite;
82
+ }
83
+
84
+ body.tux-mode * { cursor: crosshair !important; }
85
+
86
+ #tux-root .tux-layer { position: absolute; left: 0; top: 0; }
87
+
88
+ /* Hover outline — lands on host elements, so this rule is intentionally
89
+ unscoped: the class name is tux-owned and the outline must work both on
90
+ plain page elements and inside top-layer dialogs. .tux-editing marks the
91
+ target of the currently open editor with the same outline. */
92
+ .tux-hover, .tux-editing { outline: 2px dashed var(--tux-primary) !important; outline-offset: -2px; }
93
+
94
+ /* Markers and the editor portal into native dialogs when the target lives
95
+ in the top layer — the dialog[open] alternative keeps them styled there. */
96
+ :is(#tux-root, dialog[open]) .tux-marker {
97
+ position: absolute; z-index: 2147483001;
98
+ min-width: 22px; height: 22px; padding: 0 4px; border-radius: 11px;
99
+ border: 2px solid var(--tux-surface); background: var(--tux-yellow);
100
+ color: #000; font-size: 11px; font-weight: 700; cursor: pointer;
101
+ box-shadow: var(--tux-shadow);
102
+ }
103
+ :is(#tux-root, dialog[open]) .tux-marker.tux-resolved { background: var(--tux-green); color: #fff; }
104
+
105
+ :is(#tux-root, dialog[open]) .tux-editor {
106
+ display: none; position: absolute; z-index: 2147483002;
107
+ width: 300px; padding: 12px; border-radius: var(--tux-radius);
108
+ border: 1px solid var(--tux-border); background: var(--tux-surface);
109
+ box-shadow: var(--tux-shadow); text-align: left;
110
+ }
111
+ :is(#tux-root, dialog[open]) .tux-editor.tux-open { display: block; }
112
+ :is(#tux-root, dialog[open]) .tux-ed-head { display: flex; justify-content: space-between; font-size: 12px; color: var(--tux-muted); margin-bottom: 6px; }
113
+ :is(#tux-root, dialog[open]) .tux-ed-target { font-size: 11px; color: var(--tux-muted); background: var(--tux-surface2); border-radius: 8px; padding: 6px 8px; margin-bottom: 8px; word-break: break-all; }
114
+ :is(#tux-root, dialog[open]) .tux-ed-row { display: flex; gap: 8px; align-items: center; font-size: 12px; margin-bottom: 8px; color: var(--tux-muted); }
115
+ :is(#tux-root, dialog[open]) .tux-editor select, :is(#tux-root, dialog[open]) .tux-editor textarea {
116
+ font: inherit; color: var(--tux-text); background: var(--tux-surface);
117
+ border: 1px solid var(--tux-border); border-radius: 8px; padding: 6px 8px;
118
+ }
119
+ :is(#tux-root, dialog[open]) .tux-editor textarea { width: 100%; min-height: 70px; box-sizing: border-box; resize: vertical; }
120
+ :is(#tux-root, dialog[open]) .tux-ed-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 8px; }
121
+ :is(#tux-root, dialog[open]) .tux-btn-primary { background: var(--tux-primary); color: #fff; border: none; border-radius: 999px; padding: 6px 14px; cursor: pointer; }
122
+ :is(#tux-root, dialog[open]) .tux-btn-ghost { background: none; border: 1px solid var(--tux-border); color: var(--tux-muted); border-radius: 999px; padding: 6px 14px; cursor: pointer; }
123
+ :is(#tux-root, dialog[open]) .tux-btn-danger { background: none; border: 1px solid var(--tux-danger); color: var(--tux-danger); border-radius: 999px; padding: 6px 14px; cursor: pointer; }
124
+ :is(#tux-root, dialog[open]) .tux-x { background: none; border: none; color: var(--tux-muted); cursor: pointer; }
125
+
126
+ /* Sidebar and toast can be reparented into an open dialog together with
127
+ the float — the dialog[open] alternative keeps them styled there. */
128
+ :is(#tux-root, dialog[open]) .tux-sidebar {
129
+ position: fixed; right: 0; top: 0; bottom: 0; z-index: 2147482999;
130
+ width: 300px; background: var(--tux-surface); border-left: 1px solid var(--tux-border);
131
+ box-shadow: var(--tux-shadow); transform: translateX(100%); transition: transform .18s ease;
132
+ display: flex; flex-direction: column;
133
+ }
134
+ :is(#tux-root, dialog[open]) .tux-sidebar.tux-open { transform: translateX(0); }
135
+ :is(#tux-root, dialog[open]) .tux-sb-head { display: flex; justify-content: space-between; align-items: center; padding: 12px 14px; font-weight: 600; border-bottom: 1px solid var(--tux-border); }
136
+ :is(#tux-root, dialog[open]) .tux-sb-close { background: none; border: none; color: var(--tux-muted); cursor: pointer; }
137
+ :is(#tux-root, dialog[open]) .tux-sb-meta { padding: 8px 14px; font-size: 11px; color: var(--tux-muted); border-bottom: 1px solid var(--tux-border); }
138
+ :is(#tux-root, dialog[open]) .tux-sb-list { overflow: auto; flex: 1; padding: 8px; }
139
+ :is(#tux-root, dialog[open]) .tux-sb-item { display: grid; grid-template-columns: auto 1fr; gap: 2px 8px; padding: 8px; border-radius: 10px; background: var(--tux-surface2); margin-bottom: 8px; cursor: pointer; }
140
+ :is(#tux-root, dialog[open]) .tux-sb-item.tux-done { opacity: .55; }
141
+ :is(#tux-root, dialog[open]) .tux-sb-type { grid-row: span 2; align-self: center; font-size: 10px; text-transform: uppercase; padding: 3px 7px; border-radius: 999px; background: var(--tux-yellow); }
142
+ :is(#tux-root, dialog[open]) .tux-type-approval { background: var(--tux-green); color: #fff; }
143
+ :is(#tux-root, dialog[open]) .tux-type-question { background: var(--tux-primary); color: #fff; }
144
+ :is(#tux-root, dialog[open]) .tux-type-issue { background: var(--tux-danger); color: #fff; }
145
+ :is(#tux-root, dialog[open]) .tux-sb-text { font-size: 12px; line-height: 1.4; word-break: break-word; }
146
+ :is(#tux-root, dialog[open]) .tux-sb-route { font-size: 10px; color: var(--tux-muted); }
147
+ :is(#tux-root, dialog[open]) .tux-sb-empty { color: var(--tux-muted); font-size: 12px; padding: 12px; }
148
+
149
+ :is(#tux-root, dialog[open]) .tux-toast {
150
+ position: fixed; left: 50%; bottom: 20px; transform: translateX(-50%) translateY(20px);
151
+ z-index: 2147483003; background: var(--tux-text); color: var(--tux-surface);
152
+ padding: 8px 16px; border-radius: 999px; font-size: 12px; opacity: 0;
153
+ transition: opacity .15s ease, transform .15s ease; pointer-events: none;
154
+ }
155
+ :is(#tux-root, dialog[open]) .tux-toast.tux-visible { opacity: 1; transform: translateX(-50%) translateY(0); }