rastack 0.0.43 → 0.0.45

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 (41) hide show
  1. package/CHANGELOG.md +14 -0
  2. package/admin/AdminApp.tsx +461 -0
  3. package/admin/adapters.ts +346 -0
  4. package/admin/httpEngine.ts +56 -0
  5. package/admin/index.tsx +140 -0
  6. package/admin/ui/FilterBar.tsx +231 -0
  7. package/admin/ui/Popover.tsx +54 -0
  8. package/admin/ui/Sidebar.tsx +86 -0
  9. package/admin/ui/icons.tsx +146 -0
  10. package/admin/ui/styles.ts +398 -0
  11. package/admin/useAdmin.ts +434 -0
  12. package/dist/admin.js +459 -0
  13. package/dist/dev/harness.d.ts +70 -0
  14. package/dist/dev/harness.js +189 -0
  15. package/dist/rastack-admin.d.ts +14 -0
  16. package/dist/rastack-admin.js +128 -0
  17. package/dist/rastack-dev.d.ts +18 -0
  18. package/dist/rastack-dev.js +197 -0
  19. package/dist/rastack-wasm-build.d.ts +9 -4
  20. package/dist/rastack-wasm-build.js +44 -33
  21. package/dist/rastack.d.ts +2 -3
  22. package/dist/rastack.js +13 -32
  23. package/dist/wasm/package.json +4 -0
  24. package/dist/wasm/rastack_wasm.d.ts +100 -0
  25. package/dist/wasm/rastack_wasm.js +745 -0
  26. package/dist/wasm/rastack_wasm_bg.wasm +0 -0
  27. package/dist/wasm/rastack_wasm_bg.wasm.d.ts +28 -0
  28. package/package.json +6 -2
  29. package/scripts/build-admin.js +48 -0
  30. package/scripts/copy-wasm.js +35 -0
  31. package/src/dev/harness.ts +174 -0
  32. package/src/rastack-admin.ts +112 -0
  33. package/src/rastack-dev.ts +197 -0
  34. package/src/rastack-wasm-build.ts +52 -41
  35. package/src/rastack.ts +14 -37
  36. package/test/dev.spec.ts +373 -0
  37. package/wasm/package.json +4 -0
  38. package/wasm/rastack_wasm.d.ts +100 -0
  39. package/wasm/rastack_wasm.js +745 -0
  40. package/wasm/rastack_wasm_bg.wasm +0 -0
  41. package/wasm/rastack_wasm_bg.wasm.d.ts +28 -0
@@ -0,0 +1,398 @@
1
+ /**
2
+ * The admin's chrome stylesheet — injected once. Every value is a design-token
3
+ * CSS variable (`var(--color-*)`, `var(--space-*)`, `var(--radius-*)`, emitted
4
+ * by `<ThemeProvider>` with no prefix) or an admin-local `--ras-*` derived from
5
+ * them, so the whole surface re-themes (light/dark included) with the tokens.
6
+ * The shared `rastack/components` paint themselves the same way — this only
7
+ * styles the structural chrome the component kit doesn't ship (sidebar,
8
+ * top bar, filter popovers, chips).
9
+ */
10
+ import React from "react";
11
+
12
+ const CSS = String.raw`
13
+ :root {
14
+ --ras-sidebar-w: 274px;
15
+ --ras-shadow-sm: 0 1px 2px rgba(11, 16, 32, 0.05), 0 1px 3px rgba(11, 16, 32, 0.04);
16
+ --ras-shadow-md: 0 6px 16px rgba(11, 16, 32, 0.08), 0 18px 40px rgba(11, 16, 32, 0.10);
17
+ --ras-shadow-pop: 0 10px 30px rgba(11, 16, 32, 0.16), 0 2px 6px rgba(11, 16, 32, 0.08);
18
+ --ras-hover: color-mix(in srgb, var(--color-text) 6%, transparent);
19
+ --ras-active: color-mix(in srgb, var(--color-accent) 12%, transparent);
20
+ --ras-active-line: var(--color-accent);
21
+ --ras-ring: 0 0 0 3px color-mix(in srgb, var(--color-accent) 22%, transparent);
22
+ --ras-ease: cubic-bezier(0.22, 0.7, 0.24, 1);
23
+ }
24
+ [data-theme="dark"] {
25
+ --ras-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.5);
26
+ --ras-shadow-md: 0 8px 24px rgba(0, 0, 0, 0.5), 0 2px 8px rgba(0, 0, 0, 0.4);
27
+ --ras-shadow-pop: 0 14px 40px rgba(0, 0, 0, 0.6), 0 2px 8px rgba(0, 0, 0, 0.5);
28
+ --ras-hover: color-mix(in srgb, var(--color-text) 9%, transparent);
29
+ --ras-active: color-mix(in srgb, var(--color-accent) 20%, transparent);
30
+ }
31
+
32
+ * { box-sizing: border-box; }
33
+ html, body { margin: 0; height: 100%; }
34
+ body {
35
+ background: var(--color-bg);
36
+ color: var(--color-text);
37
+ font-family: var(--font-family-sans, "Inter", system-ui, sans-serif);
38
+ font-size: 14px;
39
+ line-height: 1.5;
40
+ -webkit-font-smoothing: antialiased;
41
+ text-rendering: optimizeLegibility;
42
+ }
43
+ .ras-boot,
44
+ .rastack-boot {
45
+ min-height: 100vh;
46
+ display: flex;
47
+ align-items: center;
48
+ justify-content: center;
49
+ gap: 10px;
50
+ color: var(--color-text-muted);
51
+ font-family: var(--font-family-sans, system-ui, sans-serif);
52
+ }
53
+ a { color: inherit; text-decoration: none; }
54
+ :focus-visible { outline: none; box-shadow: var(--ras-ring); border-radius: var(--radius-sm); }
55
+
56
+ /* ---- layout -------------------------------------------------------------- */
57
+ .ras-root {
58
+ display: grid;
59
+ grid-template-columns: var(--ras-sidebar-w) minmax(0, 1fr);
60
+ min-height: 100vh;
61
+ }
62
+ .ras-main { min-width: 0; display: flex; flex-direction: column; }
63
+
64
+ /* ---- sidebar ------------------------------------------------------------- */
65
+ .ras-sidebar {
66
+ position: sticky;
67
+ top: 0;
68
+ align-self: start;
69
+ height: 100vh;
70
+ display: flex;
71
+ flex-direction: column;
72
+ gap: 4px;
73
+ padding: 20px 14px 14px;
74
+ background: var(--color-surface-alt);
75
+ border-right: 1px solid var(--color-border);
76
+ overflow-y: auto;
77
+ }
78
+ .ras-brand {
79
+ display: flex;
80
+ align-items: center;
81
+ gap: 11px;
82
+ padding: 6px 8px 18px;
83
+ }
84
+ .ras-brand-name {
85
+ font-weight: 700;
86
+ font-size: 15px;
87
+ letter-spacing: -0.01em;
88
+ line-height: 1.1;
89
+ }
90
+ .ras-brand-sub {
91
+ font-size: 11px;
92
+ color: var(--color-text-muted);
93
+ letter-spacing: 0.04em;
94
+ text-transform: uppercase;
95
+ margin-top: 1px;
96
+ }
97
+ .ras-nav { display: flex; flex-direction: column; gap: 18px; padding-top: 2px; }
98
+ .ras-nav-section { display: flex; flex-direction: column; gap: 2px; }
99
+ .ras-nav-label {
100
+ font-size: 11px;
101
+ font-weight: 600;
102
+ letter-spacing: 0.07em;
103
+ text-transform: uppercase;
104
+ color: var(--color-text-muted);
105
+ padding: 4px 10px;
106
+ }
107
+ .ras-nav-link {
108
+ position: relative;
109
+ display: flex;
110
+ align-items: center;
111
+ gap: 10px;
112
+ padding: 8px 10px;
113
+ border-radius: var(--radius-md);
114
+ color: var(--color-text-muted);
115
+ font-weight: 500;
116
+ cursor: pointer;
117
+ transition: background 0.14s var(--ras-ease), color 0.14s var(--ras-ease);
118
+ }
119
+ .ras-nav-link:hover { background: var(--ras-hover); color: var(--color-text); }
120
+ .ras-nav-link .ras-ico { color: var(--color-text-muted); transition: color 0.14s; }
121
+ .ras-nav-link.is-active {
122
+ background: var(--ras-active);
123
+ color: var(--color-accent-strong);
124
+ font-weight: 600;
125
+ }
126
+ .ras-nav-link.is-active .ras-ico { color: var(--color-accent); }
127
+ .ras-nav-link.is-active::before {
128
+ content: "";
129
+ position: absolute;
130
+ left: -14px;
131
+ top: 8px;
132
+ bottom: 8px;
133
+ width: 3px;
134
+ border-radius: 0 3px 3px 0;
135
+ background: var(--ras-active-line);
136
+ }
137
+ .ras-sidebar-foot {
138
+ margin-top: auto;
139
+ padding: 12px 8px 2px;
140
+ display: flex;
141
+ align-items: center;
142
+ justify-content: space-between;
143
+ gap: 8px;
144
+ border-top: 1px solid var(--color-border);
145
+ }
146
+ .ras-foot-meta { font-size: 11px; color: var(--color-text-muted); letter-spacing: 0.02em; }
147
+ .ras-icon-btn {
148
+ display: inline-flex;
149
+ align-items: center;
150
+ justify-content: center;
151
+ width: 32px;
152
+ height: 32px;
153
+ border-radius: var(--radius-md);
154
+ border: 1px solid var(--color-border);
155
+ background: var(--color-surface);
156
+ color: var(--color-text-muted);
157
+ cursor: pointer;
158
+ transition: background 0.14s, color 0.14s, border-color 0.14s;
159
+ }
160
+ .ras-icon-btn:hover { color: var(--color-text); border-color: color-mix(in srgb, var(--color-text) 22%, transparent); }
161
+
162
+ /* ---- top bar ------------------------------------------------------------- */
163
+ .ras-topbar {
164
+ position: sticky;
165
+ top: 0;
166
+ z-index: 20;
167
+ display: flex;
168
+ align-items: center;
169
+ gap: 8px;
170
+ min-height: 56px;
171
+ padding: 10px 28px;
172
+ background: color-mix(in srgb, var(--color-bg) 82%, transparent);
173
+ border-bottom: 1px solid var(--color-border);
174
+ backdrop-filter: saturate(1.4) blur(10px);
175
+ }
176
+ .ras-crumbs { display: flex; align-items: center; gap: 6px; font-size: 13px; color: var(--color-text-muted); flex-wrap: wrap; }
177
+ .ras-crumbs a { cursor: pointer; }
178
+ .ras-crumbs a:hover { color: var(--color-text); }
179
+ .ras-crumb-sep { color: color-mix(in srgb, var(--color-text-muted) 55%, transparent); }
180
+ .ras-crumb-current { color: var(--color-text); font-weight: 600; }
181
+
182
+ .ras-content { padding: 26px 28px 64px; max-width: 1240px; width: 100%; }
183
+
184
+ /* ---- page head ----------------------------------------------------------- */
185
+ .ras-pagehead {
186
+ display: flex;
187
+ align-items: flex-end;
188
+ justify-content: space-between;
189
+ gap: 16px;
190
+ margin-bottom: 20px;
191
+ }
192
+ .ras-title { font-size: 26px; font-weight: 700; letter-spacing: -0.02em; margin: 0; line-height: 1.1; }
193
+ .ras-subtitle { color: var(--color-text-muted); font-size: 13px; margin-top: 5px; }
194
+ .ras-eyebrow {
195
+ font-size: 11px; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase;
196
+ color: var(--color-accent); margin-bottom: 8px;
197
+ }
198
+
199
+ /* ---- filter bar (Linear-style) ------------------------------------------- */
200
+ .ras-filterbar {
201
+ display: flex;
202
+ align-items: center;
203
+ gap: 8px;
204
+ flex-wrap: wrap;
205
+ margin-bottom: 16px;
206
+ }
207
+ .ras-filterbar .ras-grow { flex: 1 1 auto; }
208
+ .ras-chip {
209
+ display: inline-flex;
210
+ align-items: stretch;
211
+ height: 30px;
212
+ border: 1px solid var(--color-border);
213
+ border-radius: var(--radius-pill);
214
+ background: var(--color-surface);
215
+ box-shadow: var(--ras-shadow-sm);
216
+ overflow: hidden;
217
+ font-size: 12.5px;
218
+ }
219
+ .ras-chip-key { display: inline-flex; align-items: center; gap: 6px; padding: 0 4px 0 11px; color: var(--color-text-muted); }
220
+ .ras-chip-op { color: color-mix(in srgb, var(--color-text-muted) 75%, transparent); padding: 0 2px; }
221
+ .ras-chip-val {
222
+ display: inline-flex; align-items: center; gap: 5px; padding: 0 9px;
223
+ font-weight: 600; color: var(--color-text); cursor: pointer;
224
+ border-left: 1px solid transparent; transition: background 0.14s;
225
+ }
226
+ .ras-chip-val:hover { background: var(--ras-hover); }
227
+ .ras-chip-x {
228
+ display: inline-flex; align-items: center; justify-content: center;
229
+ padding: 0 9px; color: var(--color-text-muted); cursor: pointer;
230
+ border-left: 1px solid var(--color-border); transition: background 0.14s, color 0.14s;
231
+ }
232
+ .ras-chip-x:hover { background: color-mix(in srgb, var(--color-danger) 14%, transparent); color: var(--color-danger); }
233
+ .ras-addfilter {
234
+ display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 12px;
235
+ border: 1px dashed color-mix(in srgb, var(--color-text-muted) 45%, transparent);
236
+ border-radius: var(--radius-pill); background: transparent;
237
+ color: var(--color-text-muted); font-weight: 500; font-size: 12.5px; cursor: pointer;
238
+ transition: background 0.14s, color 0.14s, border-color 0.14s;
239
+ }
240
+ .ras-addfilter:hover { color: var(--color-text); border-color: var(--color-accent); background: var(--ras-hover); }
241
+ .ras-clearfilter {
242
+ background: none; border: none; color: var(--color-text-muted); cursor: pointer;
243
+ font-size: 12.5px; padding: 0 8px; height: 30px; border-radius: var(--radius-sm);
244
+ }
245
+ .ras-clearfilter:hover { color: var(--color-danger); }
246
+
247
+ /* ---- search -------------------------------------------------------------- */
248
+ .ras-search { position: relative; display: inline-flex; align-items: center; min-width: 220px; }
249
+ .ras-search .ras-search-ico { position: absolute; left: 10px; color: var(--color-text-muted); pointer-events: none; }
250
+ .ras-search input {
251
+ width: 100%; height: 32px; padding: 0 12px 0 32px;
252
+ border: 1px solid var(--color-border); border-radius: var(--radius-md);
253
+ background: var(--color-surface); color: var(--color-text);
254
+ font: inherit; font-size: 13px; transition: border-color 0.14s, box-shadow 0.14s;
255
+ }
256
+ .ras-search input::placeholder { color: var(--color-text-muted); }
257
+ .ras-search input:focus { outline: none; border-color: var(--color-accent); box-shadow: var(--ras-ring); }
258
+
259
+ /* ---- popover ------------------------------------------------------------- */
260
+ .ras-pop-wrap { position: relative; display: inline-flex; }
261
+ .ras-pop {
262
+ position: absolute;
263
+ top: calc(100% + 6px);
264
+ left: 0;
265
+ z-index: 50;
266
+ min-width: 236px;
267
+ max-width: 300px;
268
+ padding: 6px;
269
+ background: var(--color-surface);
270
+ border: 1px solid var(--color-border);
271
+ border-radius: var(--radius-lg);
272
+ box-shadow: var(--ras-shadow-pop);
273
+ animation: ras-pop-in 0.13s var(--ras-ease);
274
+ }
275
+ .ras-pop.align-right { left: auto; right: 0; }
276
+ @keyframes ras-pop-in { from { opacity: 0; transform: translateY(-4px) scale(0.98); } to { opacity: 1; transform: none; } }
277
+ .ras-pop-search {
278
+ width: 100%; height: 32px; padding: 0 10px; margin-bottom: 4px;
279
+ border: none; border-bottom: 1px solid var(--color-border); border-radius: 0;
280
+ background: transparent; color: var(--color-text); font: inherit; font-size: 13px;
281
+ }
282
+ .ras-pop-search:focus { outline: none; box-shadow: none; border-bottom-color: var(--color-accent); }
283
+ .ras-pop-head {
284
+ display: flex; align-items: center; gap: 8px; padding: 6px 8px; margin-bottom: 2px;
285
+ color: var(--color-text-muted); font-weight: 600; font-size: 12px; cursor: pointer;
286
+ border-radius: var(--radius-sm);
287
+ }
288
+ .ras-pop-head:hover { background: var(--ras-hover); color: var(--color-text); }
289
+ .ras-pop-list { max-height: 280px; overflow-y: auto; display: flex; flex-direction: column; gap: 1px; }
290
+ .ras-pop-item {
291
+ display: flex; align-items: center; gap: 9px;
292
+ padding: 7px 9px; border-radius: var(--radius-sm);
293
+ color: var(--color-text); font-size: 13px; cursor: pointer; text-align: left;
294
+ border: none; background: none; width: 100%;
295
+ transition: background 0.1s;
296
+ }
297
+ .ras-pop-item:hover { background: var(--ras-hover); }
298
+ .ras-pop-item .ras-ico { color: var(--color-text-muted); }
299
+ .ras-pop-item .ras-pop-item-chev { margin-left: auto; color: var(--color-text-muted); }
300
+ .ras-pop-item.is-selected { color: var(--color-accent-strong); font-weight: 600; }
301
+ .ras-pop-empty { padding: 12px 10px; color: var(--color-text-muted); font-size: 12.5px; text-align: center; }
302
+
303
+ /* ---- table wrapper ------------------------------------------------------- */
304
+ .ras-tablecard { overflow: hidden; }
305
+ .ras-tablecard table { font-size: 13.5px; }
306
+ .ras-tablecard thead th {
307
+ text-transform: uppercase;
308
+ letter-spacing: 0.05em;
309
+ font-size: 11px !important;
310
+ color: var(--color-text-muted) !important;
311
+ background: var(--color-surface-alt) !important;
312
+ font-weight: 600 !important;
313
+ }
314
+ .ras-tablecard tbody tr { transition: background 0.1s; }
315
+ .ras-id { font-family: var(--font-family-mono, ui-monospace, monospace); font-size: 12.5px; color: var(--color-text-muted); }
316
+ .ras-cell-link {
317
+ display: inline-flex; align-items: center; gap: 6px;
318
+ color: var(--color-accent-strong); font-weight: 500; cursor: pointer;
319
+ }
320
+ .ras-cell-link:hover { text-decoration: underline; }
321
+ .ras-fk {
322
+ display: inline-flex; align-items: center; gap: 6px;
323
+ padding: 2px 9px 2px 8px; border-radius: var(--radius-pill);
324
+ border: 1px solid var(--color-border);
325
+ background: color-mix(in srgb, var(--color-accent) 8%, transparent);
326
+ color: var(--color-accent-strong); font-weight: 500; font-size: 12px; cursor: pointer;
327
+ }
328
+ .ras-fk:hover { border-color: var(--color-accent); }
329
+ .ras-muted { color: var(--color-text-muted); }
330
+ .ras-bool {
331
+ display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 500;
332
+ }
333
+ .ras-dot { width: 7px; height: 7px; border-radius: 999px; }
334
+
335
+ /* ---- empty state --------------------------------------------------------- */
336
+ .ras-empty {
337
+ display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
338
+ padding: 64px 24px; text-align: center; color: var(--color-text-muted);
339
+ }
340
+ .ras-empty .ras-empty-ico {
341
+ display: inline-flex; align-items: center; justify-content: center;
342
+ width: 52px; height: 52px; border-radius: var(--radius-lg);
343
+ background: var(--color-surface-alt); color: var(--color-text-muted);
344
+ }
345
+ .ras-empty h3 { margin: 4px 0 0; color: var(--color-text); font-size: 16px; font-weight: 600; }
346
+
347
+ /* ---- forms --------------------------------------------------------------- */
348
+ .ras-formgrid { max-width: 620px; }
349
+ .ras-danger-zone {
350
+ display: flex; align-items: center; justify-content: space-between; gap: 16px;
351
+ margin-top: 22px; padding: 16px 18px;
352
+ border: 1px solid color-mix(in srgb, var(--color-danger) 30%, var(--color-border));
353
+ border-radius: var(--radius-lg); background: color-mix(in srgb, var(--color-danger) 5%, transparent);
354
+ }
355
+ .ras-danger-zone .ras-dz-title { font-weight: 600; color: var(--color-text); }
356
+ .ras-danger-zone .ras-dz-sub { font-size: 12.5px; color: var(--color-text-muted); margin-top: 2px; }
357
+
358
+ /* ---- index / overview ---------------------------------------------------- */
359
+ .ras-cardgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 14px; }
360
+ .ras-modelcard {
361
+ display: flex; flex-direction: column; gap: 10px; padding: 18px;
362
+ cursor: pointer; transition: transform 0.14s var(--ras-ease), box-shadow 0.14s, border-color 0.14s;
363
+ }
364
+ .ras-modelcard:hover { transform: translateY(-2px); box-shadow: var(--ras-shadow-md); border-color: color-mix(in srgb, var(--color-accent) 40%, var(--color-border)); }
365
+ .ras-modelcard .ras-mc-top { display: flex; align-items: center; justify-content: space-between; }
366
+ .ras-modelcard .ras-mc-name { font-weight: 600; font-size: 15px; }
367
+ .ras-modelcard .ras-mc-go { color: var(--color-accent); display: inline-flex; align-items: center; gap: 4px; font-size: 12.5px; font-weight: 500; }
368
+ .ras-mc-icon { display:inline-flex; align-items:center; justify-content:center; width:36px; height:36px; border-radius: var(--radius-md); background: var(--color-surface-alt); color: var(--color-accent); }
369
+
370
+ /* ---- scrollbars ---------------------------------------------------------- */
371
+ * { scrollbar-width: thin; scrollbar-color: color-mix(in srgb, var(--color-text) 20%, transparent) transparent; }
372
+ *::-webkit-scrollbar { width: 10px; height: 10px; }
373
+ *::-webkit-scrollbar-thumb { background: color-mix(in srgb, var(--color-text) 18%, transparent); border-radius: 999px; border: 3px solid transparent; background-clip: content-box; }
374
+ *::-webkit-scrollbar-thumb:hover { background: color-mix(in srgb, var(--color-text) 30%, transparent); background-clip: content-box; }
375
+
376
+ /* ---- responsive ---------------------------------------------------------- */
377
+ @media (max-width: 860px) {
378
+ .ras-root { grid-template-columns: 1fr; }
379
+ .ras-sidebar {
380
+ position: static; height: auto; flex-direction: row; align-items: center;
381
+ gap: 10px; overflow-x: auto; border-right: none; border-bottom: 1px solid var(--color-border);
382
+ padding: 10px 14px;
383
+ }
384
+ .ras-brand { padding: 0 8px 0 0; }
385
+ .ras-nav { flex-direction: row; gap: 8px; }
386
+ .ras-nav-label, .ras-sidebar-foot, .ras-brand-sub { display: none; }
387
+ .ras-nav-link.is-active::before { display: none; }
388
+ .ras-topbar, .ras-content { padding-left: 16px; padding-right: 16px; }
389
+ }
390
+ `;
391
+
392
+ /** Inject the admin chrome stylesheet once. */
393
+ export function AdminStyles(): React.JSX.Element {
394
+ return React.createElement("style", {
395
+ "data-rastack-admin": "",
396
+ dangerouslySetInnerHTML: { __html: CSS },
397
+ });
398
+ }