loaderup 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.
@@ -0,0 +1,570 @@
1
+ :root {
2
+ --bg: #0b1118;
3
+ --surface: #121c28;
4
+ --surface-soft: #182433;
5
+ --surface-strong: #223246;
6
+ --ink: #e8f0fb;
7
+ --ink-soft: #9cb0c8;
8
+ --line: #32465f;
9
+ --brand: #18a9c2;
10
+ --brand-2: #f08d53;
11
+ --ok: #41c58e;
12
+ --warn: #f0b24f;
13
+ --bad: #ff6f7c;
14
+ --radius-lg: 20px;
15
+ --radius-md: 14px;
16
+ --shadow-soft: 0 14px 36px rgba(0, 0, 0, 0.35);
17
+ --shadow-pop: 0 24px 48px rgba(0, 0, 0, 0.45);
18
+ }
19
+
20
+ * {
21
+ box-sizing: border-box;
22
+ }
23
+
24
+ html,
25
+ body,
26
+ #root {
27
+ min-height: 100%;
28
+ margin: 0;
29
+ }
30
+
31
+ body {
32
+ font-family: "Sora", "Segoe UI", sans-serif;
33
+ color: var(--ink);
34
+ overflow-x: hidden;
35
+ background-color: var(--bg);
36
+ background-image:
37
+ radial-gradient(circle at 6% 12%, rgba(24, 169, 194, 0.24), transparent 34%),
38
+ radial-gradient(circle at 92% 4%, rgba(240, 141, 83, 0.2), transparent 30%),
39
+ radial-gradient(circle at 50% 130%, rgba(74, 101, 134, 0.25), transparent 52%),
40
+ repeating-linear-gradient(135deg, rgba(255, 255, 255, 0.03) 0 2px, rgba(12, 22, 33, 0.1) 2px 8px);
41
+ }
42
+
43
+ #root {
44
+ position: relative;
45
+ isolation: isolate;
46
+ }
47
+
48
+ #root::before {
49
+ content: "";
50
+ position: fixed;
51
+ inset: 0;
52
+ pointer-events: none;
53
+ background: radial-gradient(circle at center, transparent 0, rgba(0, 0, 0, 0.4) 100%);
54
+ z-index: -1;
55
+ }
56
+
57
+ .app-shell {
58
+ display: grid;
59
+ grid-template-columns: 280px minmax(0, 1fr);
60
+ gap: 1rem;
61
+ padding: 1rem;
62
+ }
63
+
64
+ .sidebar {
65
+ background: linear-gradient(170deg, #253545, #1c2a38 58%, #16212c);
66
+ color: #ecf2f8;
67
+ border: 1px solid rgba(255, 255, 255, 0.12);
68
+ border-radius: var(--radius-lg);
69
+ box-shadow: var(--shadow-pop);
70
+ padding: 1.15rem 0.9rem;
71
+ position: sticky;
72
+ top: 1rem;
73
+ height: fit-content;
74
+ }
75
+
76
+ .brand {
77
+ padding: 0.9rem;
78
+ border: 1px solid rgba(178, 202, 228, 0.35);
79
+ border-radius: 13px;
80
+ background: linear-gradient(145deg, rgba(255, 255, 255, 0.09), rgba(255, 255, 255, 0.02));
81
+ margin-bottom: 1rem;
82
+ }
83
+
84
+ .brand h1 {
85
+ margin: 0;
86
+ font-size: 1.2rem;
87
+ letter-spacing: 0.01em;
88
+ }
89
+
90
+ .brand p {
91
+ margin: 0.4rem 0 0;
92
+ color: #a0b9d6;
93
+ font-size: 0.82rem;
94
+ }
95
+
96
+ .nav {
97
+ display: grid;
98
+ gap: 0.45rem;
99
+ }
100
+
101
+ .nav button {
102
+ text-align: left;
103
+ border: 1px solid transparent;
104
+ background: transparent;
105
+ color: #d7e7fa;
106
+ border-radius: 12px;
107
+ padding: 0.66rem 0.74rem;
108
+ cursor: pointer;
109
+ font-weight: 600;
110
+ transition: background-color 160ms ease, border-color 160ms ease, transform 160ms ease;
111
+ }
112
+
113
+ .nav button:hover {
114
+ border-color: rgba(176, 208, 239, 0.45);
115
+ background: rgba(255, 255, 255, 0.08);
116
+ transform: translateX(2px);
117
+ }
118
+
119
+ .nav button.active {
120
+ border-color: rgba(211, 111, 63, 0.55);
121
+ background: linear-gradient(92deg, rgba(15, 124, 144, 0.34), rgba(211, 111, 63, 0.28));
122
+ color: #ffffff;
123
+ }
124
+
125
+ .sidebar-meta {
126
+ margin-top: 1rem;
127
+ border-top: 1px solid rgba(180, 205, 232, 0.28);
128
+ padding-top: 0.9rem;
129
+ display: grid;
130
+ gap: 0.5rem;
131
+ }
132
+
133
+ .meta-chip {
134
+ border: 1px solid rgba(180, 205, 232, 0.34);
135
+ border-radius: 10px;
136
+ padding: 0.45rem 0.58rem;
137
+ font-size: 0.8rem;
138
+ color: #a9c0dc;
139
+ background: rgba(0, 0, 0, 0.12);
140
+ }
141
+
142
+ .main {
143
+ padding: 0.15rem 0 0.4rem;
144
+ }
145
+
146
+ .topbar,
147
+ .kpi-card,
148
+ .card {
149
+ border: 1px solid var(--line);
150
+ border-radius: var(--radius-lg);
151
+ background: linear-gradient(165deg, rgba(18, 28, 40, 0.95), rgba(20, 33, 48, 0.9));
152
+ box-shadow: var(--shadow-soft);
153
+ backdrop-filter: blur(4px);
154
+ }
155
+
156
+ .topbar {
157
+ padding: 1rem 1.1rem;
158
+ display: flex;
159
+ align-items: center;
160
+ justify-content: space-between;
161
+ gap: 0.9rem;
162
+ animation: rise-in 420ms ease both;
163
+ }
164
+
165
+ .topbar h2 {
166
+ margin: 0;
167
+ font-size: clamp(1.1rem, 2.2vw, 1.35rem);
168
+ letter-spacing: 0.01em;
169
+ }
170
+
171
+ .topbar p {
172
+ margin: 0.26rem 0 0;
173
+ color: var(--ink-soft);
174
+ font-size: 0.88rem;
175
+ }
176
+
177
+ .topbar-right {
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 0.5rem;
181
+ flex-wrap: wrap;
182
+ }
183
+
184
+ .pill {
185
+ border: 1px solid var(--line);
186
+ border-radius: 999px;
187
+ padding: 0.31rem 0.6rem;
188
+ font-size: 0.71rem;
189
+ font-weight: 700;
190
+ text-transform: uppercase;
191
+ background: #152130;
192
+ letter-spacing: 0.08em;
193
+ }
194
+
195
+ .pill.ok,
196
+ .pill.done {
197
+ color: var(--ok);
198
+ border-color: rgba(31, 122, 89, 0.38);
199
+ }
200
+
201
+ .pill.running,
202
+ .pill.generating,
203
+ .pill.analyzing,
204
+ .pill.pending {
205
+ color: var(--warn);
206
+ border-color: rgba(155, 93, 19, 0.35);
207
+ }
208
+
209
+ .pill.failed,
210
+ .pill.offline {
211
+ color: var(--bad);
212
+ border-color: rgba(167, 51, 62, 0.36);
213
+ }
214
+
215
+ .page {
216
+ margin-top: 0.95rem;
217
+ display: grid;
218
+ gap: 0.95rem;
219
+ }
220
+
221
+ .kpi-row {
222
+ display: grid;
223
+ grid-template-columns: repeat(4, minmax(0, 1fr));
224
+ gap: 0.75rem;
225
+ }
226
+
227
+ .kpi-card {
228
+ padding: 0.9rem;
229
+ position: relative;
230
+ overflow: hidden;
231
+ animation: rise-in 520ms ease both;
232
+ }
233
+
234
+ .kpi-card::after {
235
+ content: "";
236
+ position: absolute;
237
+ left: 0;
238
+ right: 0;
239
+ bottom: 0;
240
+ height: 4px;
241
+ background: linear-gradient(90deg, var(--brand), var(--brand-2));
242
+ }
243
+
244
+ .kpi-card .label {
245
+ color: var(--ink-soft);
246
+ font-size: 0.79rem;
247
+ text-transform: uppercase;
248
+ letter-spacing: 0.08em;
249
+ }
250
+
251
+ .kpi-card .value {
252
+ margin-top: 0.34rem;
253
+ font-size: clamp(1.2rem, 2vw, 1.45rem);
254
+ font-weight: 700;
255
+ }
256
+
257
+ .card {
258
+ padding: 1rem;
259
+ animation: rise-in 620ms ease both;
260
+ }
261
+
262
+ .card h3,
263
+ .card h4 {
264
+ margin: 0 0 0.66rem;
265
+ letter-spacing: 0.01em;
266
+ }
267
+
268
+ .muted {
269
+ color: var(--ink-soft);
270
+ }
271
+
272
+ .toolbar {
273
+ display: flex;
274
+ justify-content: space-between;
275
+ gap: 0.52rem;
276
+ align-items: center;
277
+ flex-wrap: wrap;
278
+ margin-bottom: 0.62rem;
279
+ }
280
+
281
+ button,
282
+ input,
283
+ select,
284
+ textarea {
285
+ font: inherit;
286
+ }
287
+
288
+ button {
289
+ border: 1px solid var(--line);
290
+ border-radius: 12px;
291
+ background: linear-gradient(180deg, #1b2a3b, #182536);
292
+ color: var(--ink);
293
+ padding: 0.52rem 0.8rem;
294
+ cursor: pointer;
295
+ transition: transform 160ms ease, box-shadow 160ms ease, border-color 160ms ease;
296
+ }
297
+
298
+ button:hover {
299
+ transform: translateY(-1px);
300
+ border-color: #4a6f98;
301
+ box-shadow: 0 8px 18px rgba(0, 0, 0, 0.28);
302
+ }
303
+
304
+ button.primary {
305
+ border-color: transparent;
306
+ background: linear-gradient(120deg, var(--brand), var(--brand-2));
307
+ color: #0d1824;
308
+ font-weight: 700;
309
+ }
310
+
311
+ button.ghost {
312
+ background: var(--surface-soft);
313
+ }
314
+
315
+ button.danger {
316
+ color: var(--bad);
317
+ border-color: rgba(167, 51, 62, 0.3);
318
+ }
319
+
320
+ button:disabled {
321
+ opacity: 0.6;
322
+ cursor: not-allowed;
323
+ transform: none;
324
+ box-shadow: none;
325
+ }
326
+
327
+ .grid-2 {
328
+ display: grid;
329
+ grid-template-columns: 1.35fr 1fr;
330
+ gap: 0.9rem;
331
+ }
332
+
333
+ .grid-3 {
334
+ display: grid;
335
+ grid-template-columns: repeat(3, minmax(0, 1fr));
336
+ gap: 0.7rem;
337
+ }
338
+
339
+ label {
340
+ display: grid;
341
+ gap: 0.32rem;
342
+ color: var(--ink-soft);
343
+ font-size: 0.84rem;
344
+ }
345
+
346
+ .inline-actions {
347
+ display: flex;
348
+ align-items: center;
349
+ justify-content: space-between;
350
+ gap: 0.45rem;
351
+ }
352
+
353
+ .mini-btn {
354
+ padding: 0.28rem 0.48rem;
355
+ font-size: 0.72rem;
356
+ }
357
+
358
+ input,
359
+ select,
360
+ textarea {
361
+ border: 1px solid var(--line);
362
+ background: #111b28;
363
+ border-radius: 11px;
364
+ padding: 0.54rem 0.66rem;
365
+ color: var(--ink);
366
+ }
367
+
368
+ input:focus,
369
+ select:focus,
370
+ textarea:focus {
371
+ outline: 2px solid rgba(15, 124, 144, 0.22);
372
+ border-color: rgba(15, 124, 144, 0.48);
373
+ }
374
+
375
+ textarea {
376
+ min-height: 78px;
377
+ resize: vertical;
378
+ }
379
+
380
+ .targets,
381
+ .list,
382
+ .log,
383
+ .metrics {
384
+ display: grid;
385
+ gap: 0.56rem;
386
+ }
387
+
388
+ .item {
389
+ border: 1px solid var(--line);
390
+ border-radius: var(--radius-md);
391
+ padding: 0.72rem;
392
+ background: linear-gradient(180deg, #152130, #121c28);
393
+ overflow-wrap: anywhere;
394
+ transition: transform 170ms ease, border-color 170ms ease;
395
+ }
396
+
397
+ .item:hover {
398
+ transform: translateY(-1px);
399
+ border-color: rgba(15, 124, 144, 0.44);
400
+ }
401
+
402
+ .metrics {
403
+ grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
404
+ }
405
+
406
+ .metric .n {
407
+ color: var(--ink-soft);
408
+ font-size: 0.78rem;
409
+ text-transform: uppercase;
410
+ letter-spacing: 0.07em;
411
+ }
412
+
413
+ .metric .v {
414
+ margin-top: 0.22rem;
415
+ font-size: 1.11rem;
416
+ font-weight: 700;
417
+ }
418
+
419
+ .bars {
420
+ display: grid;
421
+ grid-template-columns: repeat(4, minmax(0, 1fr));
422
+ gap: 0.65rem;
423
+ align-items: end;
424
+ }
425
+
426
+ .vbar {
427
+ display: grid;
428
+ gap: 0.42rem;
429
+ }
430
+
431
+ .vbar-track {
432
+ height: 130px;
433
+ border-radius: 12px;
434
+ border: 1px solid var(--line);
435
+ background: linear-gradient(180deg, #213246, #162231);
436
+ overflow: hidden;
437
+ display: flex;
438
+ align-items: flex-end;
439
+ }
440
+
441
+ .vbar-fill {
442
+ width: 100%;
443
+ min-height: 2px;
444
+ background: linear-gradient(180deg, var(--brand), var(--brand-2));
445
+ }
446
+
447
+ .vbar-meta {
448
+ display: grid;
449
+ justify-items: center;
450
+ gap: 0.14rem;
451
+ font-size: 0.82rem;
452
+ text-align: center;
453
+ }
454
+
455
+ .vbar-meta strong {
456
+ font-size: 0.78rem;
457
+ }
458
+
459
+ pre,
460
+ code {
461
+ font-family: "IBM Plex Mono", "JetBrains Mono", monospace;
462
+ }
463
+
464
+ code {
465
+ padding: 0.08rem 0.28rem;
466
+ border-radius: 6px;
467
+ background: rgba(24, 169, 194, 0.18);
468
+ }
469
+
470
+ pre {
471
+ margin: 0;
472
+ border: 1px solid #344454;
473
+ border-radius: 13px;
474
+ background: #1a2430;
475
+ color: #dae7f2;
476
+ padding: 0.78rem;
477
+ max-height: 280px;
478
+ max-width: 100%;
479
+ overflow: auto;
480
+ white-space: pre-wrap;
481
+ overflow-wrap: anywhere;
482
+ font-size: 0.76rem;
483
+ line-height: 1.45;
484
+ }
485
+
486
+ .kpi-row .kpi-card:nth-child(2) {
487
+ animation-delay: 70ms;
488
+ }
489
+
490
+ .kpi-row .kpi-card:nth-child(3) {
491
+ animation-delay: 140ms;
492
+ }
493
+
494
+ .kpi-row .kpi-card:nth-child(4) {
495
+ animation-delay: 210ms;
496
+ }
497
+
498
+ @keyframes rise-in {
499
+ from {
500
+ opacity: 0;
501
+ transform: translateY(12px);
502
+ }
503
+
504
+ to {
505
+ opacity: 1;
506
+ transform: translateY(0);
507
+ }
508
+ }
509
+
510
+ @media (max-width: 1120px) {
511
+ .app-shell {
512
+ grid-template-columns: 1fr;
513
+ }
514
+
515
+ .sidebar {
516
+ position: static;
517
+ }
518
+
519
+ .kpi-row {
520
+ grid-template-columns: repeat(2, minmax(0, 1fr));
521
+ }
522
+
523
+ .grid-2,
524
+ .grid-3 {
525
+ grid-template-columns: 1fr;
526
+ }
527
+ }
528
+
529
+ @media (max-width: 760px) {
530
+ .app-shell {
531
+ padding: 0.7rem;
532
+ gap: 0.7rem;
533
+ }
534
+
535
+ .sidebar {
536
+ padding: 0.84rem;
537
+ }
538
+
539
+ .nav {
540
+ grid-auto-flow: column;
541
+ grid-auto-columns: minmax(135px, 1fr);
542
+ overflow-x: auto;
543
+ padding-bottom: 0.2rem;
544
+ }
545
+
546
+ .kpi-row {
547
+ grid-template-columns: 1fr;
548
+ }
549
+
550
+ .bars {
551
+ grid-template-columns: repeat(2, minmax(0, 1fr));
552
+ }
553
+
554
+ .topbar,
555
+ .card,
556
+ .kpi-card {
557
+ border-radius: 16px;
558
+ }
559
+ }
560
+
561
+ @media (prefers-reduced-motion: reduce) {
562
+ *,
563
+ *::before,
564
+ *::after {
565
+ animation-duration: 1ms !important;
566
+ animation-iteration-count: 1 !important;
567
+ transition-duration: 1ms !important;
568
+ scroll-behavior: auto !important;
569
+ }
570
+ }
loader/web/index.html ADDED
@@ -0,0 +1,16 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1">
6
+ <title>LoaderUp Control Center</title>
7
+ <link rel="preconnect" href="https://fonts.googleapis.com">
8
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
+ <link href="https://fonts.googleapis.com/css2?family=Sora:wght@400;500;600;700;800&family=IBM+Plex+Mono:wght@400;500;600&display=swap" rel="stylesheet">
10
+ <link rel="stylesheet" href="/assets/styles.css?v=20260418e">
11
+ </head>
12
+ <body>
13
+ <div id="root"></div>
14
+ <script type="module" src="/assets/app.js?v=20260418e"></script>
15
+ </body>
16
+ </html>
loaderup/__init__.py ADDED
@@ -0,0 +1,10 @@
1
+ from .decorators import load_target
2
+ from .registry import get_registry, clear_registry
3
+ from .collector import collect_load_targets
4
+
5
+ __all__ = [
6
+ "load_target",
7
+ "get_registry",
8
+ "clear_registry",
9
+ "collect_load_targets",
10
+ ]
@@ -0,0 +1,65 @@
1
+ # loaderup/autodiscover.py
2
+
3
+ from pathlib import Path
4
+ from typing import List
5
+
6
+
7
+ IGNORE_DIRS = {
8
+ ".venv",
9
+ "venv",
10
+ "__pycache__",
11
+ ".git",
12
+ "artifacts",
13
+ "node_modules",
14
+ "dist",
15
+ "build",
16
+ ".mypy_cache",
17
+ ".pytest_cache",
18
+ }
19
+
20
+
21
+ def should_ignore(path: Path) -> bool:
22
+ return any(part in IGNORE_DIRS for part in path.parts)
23
+
24
+
25
+ def looks_like_loaderup_target_file(py_file: Path) -> bool:
26
+ try:
27
+ content = py_file.read_text(encoding="utf-8", errors="ignore")
28
+ except Exception:
29
+ return False
30
+
31
+ patterns = [
32
+ "@load_target",
33
+ "from loaderup import load_target",
34
+ "import loaderup",
35
+ ]
36
+ return any(pattern in content for pattern in patterns)
37
+
38
+
39
+ def file_to_module(project_root: Path, py_file: Path) -> str:
40
+ rel = py_file.relative_to(project_root).with_suffix("")
41
+ parts = list(rel.parts)
42
+
43
+ if parts and parts[-1] == "__init__":
44
+ parts = parts[:-1]
45
+
46
+ return ".".join(parts)
47
+
48
+
49
+ def discover_target_modules(project_root: str = ".") -> List[str]:
50
+ root = Path(project_root).resolve()
51
+ modules: List[str] = []
52
+
53
+ for py_file in root.rglob("*.py"):
54
+ if should_ignore(py_file):
55
+ continue
56
+
57
+ if py_file.name.startswith("test_"):
58
+ continue
59
+
60
+ if looks_like_loaderup_target_file(py_file):
61
+ module_name = file_to_module(root, py_file)
62
+ if module_name and module_name not in modules:
63
+ modules.append(module_name)
64
+
65
+ return modules