@miller-tech/uap 1.134.0 → 1.135.2

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 (39) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/cli/deliver.js +11 -2
  3. package/dist/cli/deliver.js.map +1 -1
  4. package/dist/dashboard/controls.d.ts +66 -0
  5. package/dist/dashboard/controls.d.ts.map +1 -0
  6. package/dist/dashboard/controls.js +188 -0
  7. package/dist/dashboard/controls.js.map +1 -0
  8. package/dist/dashboard/data-service.d.ts +20 -0
  9. package/dist/dashboard/data-service.d.ts.map +1 -1
  10. package/dist/dashboard/data-service.js +47 -0
  11. package/dist/dashboard/data-service.js.map +1 -1
  12. package/dist/dashboard/server.d.ts.map +1 -1
  13. package/dist/dashboard/server.js +81 -5
  14. package/dist/dashboard/server.js.map +1 -1
  15. package/dist/delivery/acceptance-judge.d.ts +1 -1
  16. package/dist/delivery/acceptance-judge.d.ts.map +1 -1
  17. package/dist/delivery/acceptance-judge.js +78 -5
  18. package/dist/delivery/acceptance-judge.js.map +1 -1
  19. package/dist/delivery/convergence-loop.d.ts +5 -0
  20. package/dist/delivery/convergence-loop.d.ts.map +1 -1
  21. package/dist/delivery/convergence-loop.js +11 -0
  22. package/dist/delivery/convergence-loop.js.map +1 -1
  23. package/dist/delivery/run-state.d.ts +12 -0
  24. package/dist/delivery/run-state.d.ts.map +1 -1
  25. package/dist/delivery/run-state.js +58 -1
  26. package/dist/delivery/run-state.js.map +1 -1
  27. package/docs/DASHBOARD_UPLIFT_SPEC.md +171 -0
  28. package/package.json +1 -1
  29. package/src/policies/enforcers/__pycache__/_common.cpython-312.pyc +0 -0
  30. package/tools/agents/scripts/__pycache__/toolcall_path_normalizer.cpython-312.pyc +0 -0
  31. package/tools/agents/scripts/anthropic_proxy.py +27 -0
  32. package/web/dash/core.js +398 -0
  33. package/web/dash/styles.css +307 -0
  34. package/web/dash/tab-deliver.js +12 -0
  35. package/web/dash/tab-memory.js +12 -0
  36. package/web/dash/tab-models.js +12 -0
  37. package/web/dash/tab-policies.js +12 -0
  38. package/web/dash/tabs.js +581 -0
  39. package/web/dashboard.html +12 -1303
@@ -0,0 +1,307 @@
1
+ /* UAP Console dashboard — design tokens only (see DESIGN.md). */
2
+ :root {
3
+ --bg: #0d1117;
4
+ --bg2: #161b22; /* surface */
5
+ --bg3: #21262d; /* surface-raised */
6
+ --fg: #c9d1d9; /* text */
7
+ --fg2: #8b949e; /* text-muted */
8
+ --fg3: #484f58; /* text-subtle */
9
+ --cyan: #58a6ff; /* primary */
10
+ --green: #3fb950;
11
+ --yellow: #d29922;
12
+ --red: #f85149;
13
+ --purple: #bc8cff;
14
+ --orange: #d18616;
15
+ --border: #30363d;
16
+ --radius: 8px;
17
+ }
18
+ * { margin: 0; padding: 0; box-sizing: border-box; }
19
+ body {
20
+ font-family: 'SF Mono', 'Fira Code', 'Cascadia Code', monospace;
21
+ background: var(--bg);
22
+ color: var(--fg);
23
+ font-size: 13px;
24
+ line-height: 1.5;
25
+ }
26
+ .container { max-width: 1600px; margin: 0 auto; padding: 16px; }
27
+
28
+ /* ── Header ── */
29
+ header {
30
+ display: flex;
31
+ justify-content: space-between;
32
+ align-items: center;
33
+ padding: 12px 0;
34
+ border-bottom: 1px solid var(--border);
35
+ margin-bottom: 12px;
36
+ flex-wrap: wrap;
37
+ gap: 8px;
38
+ }
39
+ header h1 { font-size: 18px; color: var(--cyan); font-weight: 600; }
40
+ .header-center { display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--fg3); flex-wrap: wrap; }
41
+ .sys-badge { padding: 2px 8px; border-radius: 4px; background: var(--bg3); border: 1px solid var(--border); font-size: 11px; }
42
+ .sys-badge .label { color: var(--fg3); }
43
+ .sys-badge .val { color: var(--fg); font-weight: 500; }
44
+ .sys-badge .val.cyan { color: var(--cyan); }
45
+ .sys-badge .val.green { color: var(--green); }
46
+ .sys-badge .val.yellow { color: var(--yellow); }
47
+ .header-right { display: flex; align-items: center; gap: 12px; }
48
+ .header-ts { color: var(--fg3); font-size: 11px; }
49
+ .status-dot { display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; }
50
+ .status-dot.connected { background: var(--green); box-shadow: 0 0 4px var(--green); }
51
+ .status-dot.disconnected { background: var(--red); }
52
+ .status-dot.polling { background: var(--yellow); }
53
+ .refresh-info { color: var(--fg3); font-size: 11px; }
54
+
55
+ /* ── Tab bar ── */
56
+ .tabbar { display: flex; gap: 4px; border-bottom: 1px solid var(--border); margin-bottom: 16px; overflow-x: auto; flex-wrap: wrap; }
57
+ .tab {
58
+ background: transparent; border: none; border-bottom: 2px solid transparent;
59
+ color: var(--fg2); padding: 8px 14px; cursor: pointer; font-size: 13px;
60
+ font-family: inherit; display: flex; align-items: center; gap: 6px; white-space: nowrap;
61
+ }
62
+ .tab:hover { color: var(--fg); }
63
+ .tab.active { color: var(--cyan); border-bottom-color: var(--cyan); font-weight: 600; }
64
+ .tab:focus-visible { outline: 2px solid var(--cyan); outline-offset: -2px; }
65
+ .tab .tab-badge {
66
+ background: var(--bg3); color: var(--fg2); border-radius: 10px;
67
+ padding: 0 6px; font-size: 10px; font-weight: 600; min-width: 16px; text-align: center;
68
+ }
69
+ .tab.active .tab-badge { background: rgba(88,166,255,0.15); color: var(--cyan); }
70
+ .tab .tab-badge.alert { background: rgba(248,81,73,0.15); color: var(--red); }
71
+
72
+ /* ── Layout ── */
73
+ .hero-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
74
+ .metrics-row { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 16px; }
75
+ .split-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-bottom: 16px; }
76
+ .full-row { margin-bottom: 16px; }
77
+ @media (max-width: 1000px) {
78
+ .hero-row, .metrics-row, .split-row { grid-template-columns: 1fr; }
79
+ }
80
+
81
+ /* ── Tiles (KPI) ── */
82
+ .tile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px; }
83
+ .tile {
84
+ background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius);
85
+ padding: 12px 16px; cursor: pointer; transition: border-color 0.2s, transform 0.2s;
86
+ }
87
+ .tile:hover { border-color: var(--cyan); transform: translateY(-1px); }
88
+ .tile:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
89
+ .tile .tile-label { color: var(--fg2); font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; }
90
+ .tile .tile-value { font-size: 22px; font-weight: 700; color: var(--fg); margin-top: 4px; }
91
+ .tile .tile-value.green { color: var(--green); }
92
+ .tile .tile-value.cyan { color: var(--cyan); }
93
+ .tile .tile-value.yellow { color: var(--yellow); }
94
+ .tile .tile-value.red { color: var(--red); }
95
+ .tile .tile-value.purple { color: var(--purple); }
96
+ .tile .tile-sub { color: var(--fg3); font-size: 11px; margin-top: 2px; }
97
+
98
+ /* ── Panels ── */
99
+ .panel { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px; overflow: hidden; margin-bottom: 16px; }
100
+ .panel h2 { font-size: 14px; color: var(--cyan); margin-bottom: 12px; font-weight: 600; border-bottom: 1px solid var(--border); padding-bottom: 8px; display: flex; align-items: center; gap: 8px; }
101
+ .panel h2 .h2-actions { margin-left: auto; display: flex; gap: 6px; }
102
+ .panel h3 { font-size: 12px; color: var(--fg2); margin: 12px 0 6px; text-transform: uppercase; letter-spacing: 0.5px; }
103
+ .panel h3:first-child { margin-top: 0; }
104
+ .kv { display: flex; justify-content: space-between; padding: 3px 0; gap: 8px; }
105
+ .kv .label { color: var(--fg2); }
106
+ .kv .value { color: var(--fg); font-weight: 500; }
107
+ .kv .value.green { color: var(--green); }
108
+ .kv .value.yellow { color: var(--yellow); }
109
+ .kv .value.red { color: var(--red); }
110
+ .kv .value.cyan { color: var(--cyan); }
111
+ .kv .value.purple { color: var(--purple); }
112
+ .kv .value.orange { color: var(--orange); }
113
+ .table-wrap { overflow-x: auto; }
114
+ table { width: 100%; border-collapse: collapse; font-size: 12px; }
115
+ th { text-align: left; color: var(--fg2); font-weight: 500; padding: 4px 8px; border-bottom: 1px solid var(--border); white-space: nowrap; }
116
+ td { padding: 4px 8px; border-bottom: 1px solid var(--bg3); }
117
+ tr.clickable { cursor: pointer; }
118
+ tr.clickable:hover td { background: var(--bg3); }
119
+ .badge { display: inline-block; padding: 1px 6px; border-radius: 3px; font-size: 11px; font-weight: 600; }
120
+ .badge.required { background: rgba(248,81,73,0.15); color: var(--red); }
121
+ .badge.recommended { background: rgba(210,153,34,0.15); color: var(--yellow); }
122
+ .badge.optional { background: rgba(72,79,88,0.15); color: var(--fg3); }
123
+ .badge.on { background: rgba(63,185,80,0.15); color: var(--green); }
124
+ .badge.off { background: rgba(248,81,73,0.15); color: var(--red); }
125
+ .badge.active { background: rgba(88,166,255,0.15); color: var(--cyan); }
126
+ .badge.idle { background: rgba(72,79,88,0.15); color: var(--fg3); }
127
+ .badge.running { background: rgba(88,166,255,0.15); color: var(--cyan); }
128
+ .badge.delivered { background: rgba(63,185,80,0.15); color: var(--green); }
129
+ .badge.failed { background: rgba(248,81,73,0.15); color: var(--red); }
130
+ .badge.interrupted { background: rgba(210,153,34,0.15); color: var(--yellow); }
131
+ .badge.droid { background: rgba(188,140,255,0.15); color: var(--purple); }
132
+ .badge.subagent { background: rgba(209,134,22,0.15); color: var(--orange); }
133
+ .badge.main { background: rgba(88,166,255,0.15); color: var(--cyan); }
134
+ .badge.completed { background: rgba(63,185,80,0.15); color: var(--green); }
135
+ .badge.file-only { background: rgba(72,79,88,0.15); color: var(--fg3); }
136
+
137
+ /* ── Bars ── */
138
+ .bar-container { display: flex; align-items: center; gap: 8px; margin: 3px 0; }
139
+ .bar-label { width: 80px; color: var(--fg2); font-size: 12px; }
140
+ .bar { height: 14px; border-radius: 3px; min-width: 2px; transition: width 0.3s; }
141
+ .bar.cyan { background: var(--cyan); }
142
+ .bar.green { background: var(--green); }
143
+ .bar.yellow { background: var(--yellow); }
144
+ .bar.red { background: var(--red); }
145
+ .bar.purple { background: var(--purple); }
146
+ .bar-value { color: var(--fg2); font-size: 11px; min-width: 20px; }
147
+
148
+ /* ── Charts ── */
149
+ .chart-container { position: relative; width: 100%; }
150
+ .chart-container .uplot { width: 100% !important; }
151
+ .chart-hero { height: 280px; }
152
+ .chart-spark { height: 80px; margin: 8px 0; }
153
+ .uplot .u-title { color: var(--fg2) !important; font-size: 11px !important; }
154
+ .uplot .u-legend { font-size: 11px !important; }
155
+ .uplot .u-legend th { color: var(--fg2) !important; }
156
+ .uplot .u-legend td { color: var(--fg) !important; }
157
+ .uplot .u-series > * { padding: 2px 6px !important; }
158
+ .u-tooltip { position: absolute; z-index: 100; background: var(--bg2); border: 1px solid var(--border); border-radius: 6px; padding: 8px 12px; font-size: 11px; pointer-events: none; box-shadow: 0 4px 12px rgba(0,0,0,0.4); white-space: nowrap; }
159
+ .u-tooltip .tt-time { color: var(--fg3); margin-bottom: 4px; font-size: 10px; }
160
+ .u-tooltip .tt-row { display: flex; gap: 8px; align-items: center; padding: 1px 0; }
161
+ .u-tooltip .tt-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
162
+ .u-tooltip .tt-label { color: var(--fg2); }
163
+ .u-tooltip .tt-val { color: var(--fg); font-weight: 600; margin-left: auto; }
164
+
165
+ /* ── Buttons / inputs ── */
166
+ .btn { background: var(--bg3); border: 1px solid var(--border); color: var(--fg); padding: 3px 8px; border-radius: 4px; cursor: pointer; font-size: 11px; font-family: inherit; }
167
+ .btn:hover { background: var(--border); }
168
+ .btn:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
169
+ .btn.btn-primary { background: rgba(88,166,255,0.15); border-color: var(--cyan); color: var(--cyan); }
170
+ .btn.btn-primary:hover { background: rgba(88,166,255,0.25); }
171
+ .btn.btn-danger { background: rgba(248,81,73,0.12); border-color: var(--red); color: var(--red); }
172
+ .btn.btn-danger:hover { background: rgba(248,81,73,0.22); }
173
+ .btn.btn-lg { padding: 6px 14px; font-size: 13px; }
174
+ .btn:disabled { opacity: 0.4; cursor: not-allowed; }
175
+ select, input, textarea { background: var(--bg3); border: 1px solid var(--border); color: var(--fg); padding: 4px 6px; border-radius: 4px; font-size: 11px; font-family: inherit; }
176
+ select:focus-visible, input:focus-visible, textarea:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
177
+ textarea { width: 100%; resize: vertical; min-height: 60px; }
178
+ label.field { display: flex; flex-direction: column; gap: 4px; font-size: 11px; color: var(--fg2); }
179
+ .toolbar { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 12px; }
180
+ .toolbar .spacer { flex: 1; }
181
+
182
+ /* ── Audit / Events ── */
183
+ .audit-row { display: flex; gap: 8px; padding: 3px 0; font-size: 12px; border-bottom: 1px solid var(--bg3); }
184
+ .audit-time { color: var(--fg3); width: 80px; flex-shrink: 0; font-size: 11px; }
185
+ .audit-icon { width: 40px; flex-shrink: 0; font-weight: 600; }
186
+ .audit-icon.pass { color: var(--green); }
187
+ .audit-icon.block { color: var(--red); }
188
+ .audit-op { color: var(--fg); flex: 1; }
189
+ .audit-policy { color: var(--fg3); width: 80px; flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; }
190
+ .audit-reason { color: var(--fg3); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
191
+ .event-feed { max-height: 260px; overflow-y: auto; }
192
+ .event-row { display: flex; gap: 6px; padding: 2px 0; font-size: 11px; border-bottom: 1px solid var(--bg3); animation: event-in 0.2s ease-out; }
193
+ .event-row .event-cat { padding: 0 4px; border-radius: 3px; font-weight: 600; font-size: 10px; text-transform: uppercase; }
194
+ .event-row .event-time { color: var(--fg3); min-width: 60px; }
195
+ .event-row .event-msg { color: var(--fg2); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
196
+ @keyframes event-in { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: translateY(0); } }
197
+ .cat-policy { background: rgba(248,81,73,0.15); color: var(--red); }
198
+ .cat-memory { background: rgba(188,140,255,0.15); color: var(--purple); }
199
+ .cat-deploy { background: rgba(88,166,255,0.15); color: var(--cyan); }
200
+ .cat-agent { background: rgba(209,134,22,0.15); color: var(--orange); }
201
+ .cat-task { background: rgba(63,185,80,0.15); color: var(--green); }
202
+ .cat-skill { background: rgba(210,153,34,0.15); color: var(--yellow); }
203
+ .cat-pattern { background: rgba(188,140,255,0.15); color: var(--purple); }
204
+ .cat-cost { background: rgba(63,185,80,0.15); color: var(--green); }
205
+ .cat-system { background: rgba(72,79,88,0.3); color: var(--fg2); }
206
+
207
+ /* ── Gauge / progress / cost ── */
208
+ .gauge-track { background: var(--bg3); border-radius: 3px; height: 8px; margin: 6px 0; overflow: hidden; }
209
+ .gauge-fill { height: 100%; border-radius: 3px; transition: width 0.3s; }
210
+ .progress-track { background: var(--bg3); border-radius: 3px; height: 6px; margin: 6px 0; overflow: hidden; }
211
+ .progress-fill { height: 100%; border-radius: 3px; background: var(--cyan); transition: width 0.3s; }
212
+ .cost-savings { background: rgba(63,185,80,0.08); border: 1px solid rgba(63,185,80,0.2); border-radius: var(--radius); padding: 8px 12px; margin-top: 8px; }
213
+ .cost-savings .big-number { font-size: 20px; font-weight: 700; color: var(--green); }
214
+ .cost-savings .sub { font-size: 11px; color: var(--fg2); }
215
+
216
+ /* ── Agent rows / cards ── */
217
+ .agent-row { display: flex; align-items: center; gap: 8px; padding: 4px 0; border-bottom: 1px solid var(--bg3); font-size: 12px; }
218
+ .agent-name { color: var(--fg); font-weight: 500; min-width: 100px; }
219
+ .agent-task { color: var(--fg2); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
220
+ .agent-tokens { color: var(--fg3); font-size: 11px; min-width: 60px; text-align: right; }
221
+ .card-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
222
+ .entity-card { background: var(--bg); border: 1px solid var(--border); border-radius: var(--radius); padding: 12px; cursor: pointer; transition: border-color 0.2s, transform 0.2s; }
223
+ .entity-card:hover { border-color: var(--cyan); transform: translateY(-1px); }
224
+ .entity-card:focus-visible { outline: 2px solid var(--cyan); outline-offset: 1px; }
225
+ .entity-card .ec-head { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
226
+ .entity-card .ec-name { font-weight: 600; color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
227
+ .entity-card .ec-task { color: var(--fg2); font-size: 11px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; margin-bottom: 6px; }
228
+ .entity-card .ec-metrics { display: flex; gap: 12px; font-size: 11px; color: var(--fg3); }
229
+
230
+ /* ── Chips ── */
231
+ .chip-list { display: flex; flex-wrap: wrap; gap: 4px; margin: 4px 0; }
232
+ .chip { display: inline-block; padding: 2px 8px; border-radius: 10px; font-size: 11px; border: 1px solid var(--border); color: var(--fg2); }
233
+ .chip.active { border-color: var(--cyan); color: var(--cyan); background: rgba(88,166,255,0.08); }
234
+ .chip.inactive { opacity: 0.5; }
235
+
236
+ /* ── Kanban ── */
237
+ .kanban { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 8px; min-height: 200px; }
238
+ .kanban-col { flex: 1; min-width: 180px; max-width: 300px; display: flex; flex-direction: column; gap: 8px; }
239
+ .kanban-col-header { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-radius: 6px; font-size: 12px; font-weight: 600; background: var(--bg3); }
240
+ .kanban-col-header .col-count { margin-left: auto; font-weight: 400; color: var(--fg3); font-size: 11px; }
241
+ .kanban-col-header.open { border-left: 3px solid var(--fg2); }
242
+ .kanban-col-header.in_progress { border-left: 3px solid var(--cyan); color: var(--cyan); }
243
+ .kanban-col-header.blocked { border-left: 3px solid var(--red); color: var(--red); }
244
+ .kanban-col-header.done { border-left: 3px solid var(--green); color: var(--green); }
245
+ .kanban-col-header.wont_do { border-left: 3px solid var(--fg3); color: var(--fg3); }
246
+ .kanban-cards { display: flex; flex-direction: column; gap: 6px; flex: 1; min-height: 40px; }
247
+ .kanban-group { font-size: 10px; color: var(--fg2); text-transform: uppercase; letter-spacing: 0.5px; padding: 4px 4px 0; border-top: 1px dashed var(--border); margin-top: 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
248
+ .kanban-group:first-child { border-top: none; margin-top: 0; }
249
+ .kanban-card.card-child { margin-left: 8px; border-left: 2px solid var(--border); }
250
+ .kanban-card .card-crumb { color: var(--fg3); font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
251
+ .kanban-card { background: var(--bg); border: 1px solid var(--border); border-radius: 6px; padding: 8px 10px; cursor: pointer; transition: transform 0.2s ease, opacity 0.3s ease, box-shadow 0.2s ease; animation: card-enter 0.35s ease-out; }
252
+ .kanban-card:hover { border-color: var(--cyan); box-shadow: 0 0 8px rgba(88,166,255,0.15); transform: translateY(-1px); }
253
+ .kanban-card.card-exit { animation: card-exit 0.3s ease-in forwards; }
254
+ .kanban-card .card-id { font-size: 10px; color: var(--cyan); font-weight: 600; }
255
+ .kanban-card .card-title { font-size: 12px; color: var(--fg); margin: 3px 0; line-height: 1.3; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
256
+ .kanban-card .card-meta { display: flex; align-items: center; gap: 6px; font-size: 10px; color: var(--fg3); }
257
+ .kanban-card .card-priority { padding: 0 4px; border-radius: 3px; font-weight: 600; font-size: 10px; }
258
+ .card-priority.p0 { background: rgba(248,81,73,0.2); color: var(--red); }
259
+ .card-priority.p1 { background: rgba(210,153,34,0.2); color: var(--yellow); }
260
+ .card-priority.p2 { background: rgba(88,166,255,0.15); color: var(--cyan); }
261
+ .card-priority.p3, .card-priority.p4 { background: rgba(72,79,88,0.2); color: var(--fg3); }
262
+ .kanban-empty { color: var(--fg3); font-size: 11px; font-style: italic; text-align: center; padding: 16px 4px; border: 1px dashed var(--border); border-radius: 6px; }
263
+ @keyframes card-enter { 0% { opacity: 0; transform: translateY(-12px) scale(0.95); } 100% { opacity: 1; transform: translateY(0) scale(1); } }
264
+ @keyframes card-exit { 0% { opacity: 1; transform: translateX(0) scale(1); } 100% { opacity: 0; transform: translateX(30px) scale(0.9); } }
265
+
266
+ /* ── Toasts ── */
267
+ .toast-container { position: fixed; top: 16px; right: 16px; z-index: 1000; display: flex; flex-direction: column; gap: 8px; }
268
+ .toast { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 8px 14px; font-size: 12px; color: var(--fg); animation: toast-in 0.2s ease-out; max-width: 320px; }
269
+ .toast.ok { border-color: var(--green); color: var(--green); }
270
+ .toast.err { border-color: var(--red); color: var(--red); }
271
+ .toast.warn { border-color: var(--yellow); color: var(--yellow); }
272
+ @keyframes toast-in { from { opacity: 0; transform: translateX(20px); } to { opacity: 1; transform: translateX(0); } }
273
+
274
+ /* ── Collapsible ── */
275
+ .collapsible { border: 1px solid var(--border); border-radius: 6px; margin: 8px 0; overflow: hidden; }
276
+ .collapsible-header { cursor: pointer; user-select: none; display: flex; align-items: center; gap: 6px; padding: 8px 12px; background: var(--bg3); font-weight: 600; font-size: 12px; }
277
+ .collapsible-header::before { content: '\25B6'; font-size: 10px; color: var(--fg3); transition: transform 0.2s; }
278
+ .collapsible-header.open::before { transform: rotate(90deg); }
279
+ .collapsible-body { max-height: 0; overflow: hidden; transition: max-height 0.3s ease; }
280
+ .collapsible-body.open { max-height: 3000px; }
281
+ .collapsible-body .cb-inner { padding: 12px; }
282
+
283
+ /* ── Drawer (drill-in) ── */
284
+ .drawer-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.5); z-index: 900; opacity: 0; pointer-events: none; transition: opacity 0.2s; }
285
+ .drawer-backdrop.open { opacity: 1; pointer-events: auto; }
286
+ .drawer { position: fixed; top: 0; right: 0; bottom: 0; width: 520px; max-width: 92vw; background: var(--bg2); border-left: 1px solid var(--border); z-index: 901; transform: translateX(100%); transition: transform 0.25s ease; display: flex; flex-direction: column; }
287
+ .drawer.open { transform: translateX(0); }
288
+ .drawer-head { display: flex; align-items: center; gap: 8px; padding: 14px 16px; border-bottom: 1px solid var(--border); }
289
+ .drawer-head h3 { font-size: 14px; color: var(--cyan); font-weight: 600; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
290
+ .drawer-body { padding: 16px; overflow-y: auto; flex: 1; }
291
+ .drawer-close { background: none; border: none; color: var(--fg2); cursor: pointer; font-size: 18px; line-height: 1; padding: 0 4px; }
292
+ .drawer-close:hover { color: var(--fg); }
293
+
294
+ /* ── Modal (confirm / forms) ── */
295
+ .modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.6); z-index: 1100; display: flex; align-items: center; justify-content: center; }
296
+ .modal { background: var(--bg2); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; width: 420px; max-width: 92vw; box-shadow: 0 8px 32px rgba(0,0,0,0.5); }
297
+ .modal h3 { font-size: 15px; color: var(--fg); margin-bottom: 12px; }
298
+ .modal .modal-msg { color: var(--fg2); font-size: 13px; margin-bottom: 16px; white-space: pre-wrap; }
299
+ .modal .modal-fields { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
300
+ .modal .modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
301
+
302
+ .empty { color: var(--fg3); font-style: italic; padding: 8px 0; }
303
+ .muted { color: var(--fg3); }
304
+ .mono-sm { font-family: inherit; font-size: 11px; color: var(--fg3); }
305
+ .section-note { color: var(--fg3); font-size: 11px; margin-bottom: 8px; }
306
+ .grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
307
+ @media (max-width: 800px) { .grid-2 { grid-template-columns: 1fr; } }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * tab-deliver.js — Deliver tab stub
3
+ * Registers with UAP.registerTab and renders a "coming soon" message
4
+ * (textContent only, per the module contract — no innerHTML).
5
+ */
6
+
7
+ UAP.registerTab('deliver', {
8
+ label: 'Deliver',
9
+ render(root, state) {
10
+ root.textContent = 'Deliver — coming soon';
11
+ }
12
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * tab-memory.js — Memory tab stub
3
+ * Registers with UAP.registerTab and renders a "coming soon" message
4
+ * (textContent only, per the module contract — no innerHTML).
5
+ */
6
+
7
+ UAP.registerTab('memory', {
8
+ label: 'Memory',
9
+ render(root, state) {
10
+ root.textContent = 'Memory — coming soon';
11
+ }
12
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * tab-models.js — Models tab stub
3
+ * Registers with UAP.registerTab and renders a "coming soon" message
4
+ * (textContent only, per the module contract — no innerHTML).
5
+ */
6
+
7
+ UAP.registerTab('models', {
8
+ label: 'Models',
9
+ render(root, state) {
10
+ root.textContent = 'Models — coming soon';
11
+ }
12
+ });
@@ -0,0 +1,12 @@
1
+ /**
2
+ * tab-policies.js — Policies tab stub
3
+ * Registers with UAP.registerTab and renders a "coming soon" message
4
+ * (textContent only, per the module contract — no innerHTML).
5
+ */
6
+
7
+ UAP.registerTab('policies', {
8
+ label: 'Policies',
9
+ render(root, state) {
10
+ root.textContent = 'Policies — coming soon';
11
+ }
12
+ });