jskelet 0.2.2 → 0.2.4

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.
@@ -5,16 +5,19 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
6
  <meta name="robots" content="noindex, nofollow, noarchive, nosnippet" />
7
7
  <meta name="referrer" content="no-referrer" />
8
- <title>Cache</title>
8
+ <title>Cache · JSkelet</title>
9
+ <link rel="icon" href="logo.png" />
9
10
  <link rel="stylesheet" href="panel.css" />
10
11
  </head>
11
12
  <body class="login">
12
13
  <form id="form" autocomplete="off">
13
- <h1>Cache panel</h1>
14
- <p>
15
- This run's password is printed in the server log
16
- (<code>[cache-panel]</code>).
17
- </p>
14
+ <div class="brand">
15
+ <img src="logo.png" alt="" width="30" height="30" />
16
+ <span class="wordmark">JSkelet</span>
17
+ <span id="language"></span>
18
+ </div>
19
+ <h1 data-i18n="login.title">Cache panel</h1>
20
+ <p data-i18n-html="login.lead"></p>
18
21
  <input
19
22
  id="password"
20
23
  type="password"
@@ -22,18 +25,29 @@
22
25
  maxlength="64"
23
26
  spellcheck="false"
24
27
  autocomplete="off"
25
- placeholder="32-character password"
28
+ data-i18n-ph="login.placeholder"
26
29
  autofocus
27
30
  />
28
- <button class="primary" type="submit">Unlock</button>
31
+ <button class="primary" type="submit" data-i18n="login.submit">Unlock</button>
29
32
  <p class="error" id="error"></p>
33
+ <p class="hint m0" data-i18n="login.hint"></p>
30
34
  </form>
31
35
 
32
36
  <script type="module">
37
+ import { applyTranslations, languageSelect, t } from "./i18n.js";
38
+
33
39
  const form = document.getElementById("form");
34
40
  const input = document.getElementById("password");
35
41
  const error = document.getElementById("error");
36
42
 
43
+ // Giriş sayfasında da seçici var: panele girmeden önce dilini seçen biri
44
+ // içeride aynı dili bulmalı, tercih `localStorage`'da taşınıyor.
45
+ document
46
+ .getElementById("language")
47
+ .append(languageSelect(() => applyTranslations()));
48
+
49
+ applyTranslations();
50
+
37
51
  form.addEventListener("submit", async (event) => {
38
52
  event.preventDefault();
39
53
  error.textContent = "";
@@ -51,10 +65,7 @@
51
65
 
52
66
  // Yasaklandıysa sunucu artık 404 dönüyor; kalan deneme sayısı
53
67
  // bilinçli olarak bildirilmiyor.
54
- error.textContent =
55
- response.status === 404
56
- ? "Too many attempts."
57
- : "Invalid password.";
68
+ error.textContent = t(response.status === 404 ? "login.tooMany" : "login.invalid");
58
69
  input.value = "";
59
70
  input.focus();
60
71
  });
@@ -19,7 +19,8 @@
19
19
  --line: #23272f;
20
20
  --text: #e7e9ee;
21
21
  --muted: #8b93a1;
22
- --accent: #6ea8fe;
22
+ /* Pazarlama sitesiyle aynı vurgu: cyan. */
23
+ --accent: #22d3ee;
23
24
  --ok: #4ade80;
24
25
  --warn: #fbbf24;
25
26
  --bad: #f87171;
@@ -33,6 +34,8 @@ body {
33
34
  }
34
35
 
35
36
  body {
37
+ display: flex;
38
+ flex-direction: column;
36
39
  min-height: 100vh;
37
40
  background:
38
41
  radial-gradient(1200px 600px at 20% -10%, #16202f 0%, transparent 60%),
@@ -78,6 +81,10 @@ a {
78
81
  box-shadow: 0 24px 60px rgb(0 0 0 / 45%);
79
82
  }
80
83
 
84
+ .login .brand {
85
+ margin-bottom: 16px;
86
+ }
87
+
81
88
  .login h1 {
82
89
  margin: 0 0 6px;
83
90
  font-size: 18px;
@@ -134,14 +141,120 @@ header {
134
141
  z-index: 5;
135
142
  }
136
143
 
137
- header h1 {
138
- margin: 0;
144
+ header .spacer {
145
+ flex: 1;
146
+ }
147
+
148
+ /* ------------------------------------------------------------------ marka */
149
+
150
+ .brand {
151
+ display: flex;
152
+ gap: 9px;
153
+ align-items: center;
154
+ }
155
+
156
+ .brand img,
157
+ .foot-brand img {
158
+ object-fit: contain;
159
+ }
160
+
161
+ .wordmark {
139
162
  font-size: 15px;
140
- font-weight: 600;
163
+ font-weight: 700;
164
+ letter-spacing: -0.03em;
141
165
  }
142
166
 
143
- header .spacer {
144
- flex: 1;
167
+ .badge {
168
+ padding: 2px 8px;
169
+ border: 1px solid color-mix(in oklab, var(--accent) 35%, var(--line));
170
+ border-radius: 999px;
171
+ background: color-mix(in oklab, var(--accent) 12%, transparent);
172
+ color: color-mix(in oklab, var(--accent) 85%, var(--text));
173
+ font-family: ui-monospace, monospace;
174
+ font-size: 11px;
175
+ font-weight: 700;
176
+ }
177
+
178
+ .crumbs {
179
+ display: flex;
180
+ gap: 8px;
181
+ align-items: center;
182
+ padding-left: 12px;
183
+ border-left: 1px solid var(--line);
184
+ color: var(--text);
185
+ font-size: 13px;
186
+ }
187
+
188
+ /* ------------------------------------------------------------------ footer */
189
+
190
+ footer {
191
+ display: flex;
192
+ flex-wrap: wrap;
193
+ gap: 12px 20px;
194
+ align-items: center;
195
+ justify-content: space-between;
196
+ margin-top: auto;
197
+ padding: 18px 20px;
198
+ border-top: 1px solid var(--line);
199
+ background: rgb(18 20 26 / 60%);
200
+ color: var(--muted);
201
+ font-size: 12px;
202
+ }
203
+
204
+ .foot-brand {
205
+ display: flex;
206
+ gap: 8px;
207
+ align-items: center;
208
+ }
209
+
210
+ .foot-brand .wordmark {
211
+ font-size: 13px;
212
+ }
213
+
214
+ .foot-links {
215
+ display: flex;
216
+ flex-wrap: wrap;
217
+ gap: 10px;
218
+ align-items: center;
219
+ }
220
+
221
+ .foot-links a {
222
+ color: var(--muted);
223
+ text-decoration: none;
224
+ }
225
+
226
+ .foot-links a:hover {
227
+ color: var(--accent);
228
+ text-decoration: underline;
229
+ }
230
+
231
+ .dot {
232
+ color: color-mix(in oklab, var(--line) 80%, var(--muted));
233
+ }
234
+
235
+ .live {
236
+ display: inline-flex;
237
+ gap: 7px;
238
+ align-items: center;
239
+ }
240
+
241
+ .pulse {
242
+ width: 7px;
243
+ height: 7px;
244
+ border-radius: 999px;
245
+ background: var(--ok);
246
+ box-shadow: 0 0 0 0 color-mix(in oklab, var(--ok) 60%, transparent);
247
+ animation: pulse 2.4s ease-out infinite;
248
+ }
249
+
250
+ @keyframes pulse {
251
+ 70% {
252
+ box-shadow: 0 0 0 6px transparent;
253
+ }
254
+
255
+ 100% {
256
+ box-shadow: 0 0 0 0 transparent;
257
+ }
145
258
  }
146
259
 
147
260
  .chip {
@@ -322,6 +435,173 @@ label.check {
322
435
  font-size: 12px;
323
436
  }
324
437
 
438
+ .m0 {
439
+ margin: 0;
440
+ }
441
+
442
+ /* ------------------------------------------------ anahtar/değer ve ölçüler */
443
+
444
+ .kv {
445
+ display: grid;
446
+ grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
447
+ gap: 10px 20px;
448
+ margin: 0;
449
+ }
450
+
451
+ .kv > div {
452
+ display: flex;
453
+ gap: 10px;
454
+ align-items: baseline;
455
+ justify-content: space-between;
456
+ padding-bottom: 7px;
457
+ border-bottom: 1px solid color-mix(in oklab, var(--line) 60%, transparent);
458
+ }
459
+
460
+ .kv dt {
461
+ color: var(--muted);
462
+ font-size: 12px;
463
+ }
464
+
465
+ .kv dd {
466
+ margin: 0;
467
+ font-family: ui-monospace, monospace;
468
+ font-size: 12.5px;
469
+ overflow-wrap: anywhere;
470
+ text-align: right;
471
+ }
472
+
473
+ .kv dd.on {
474
+ color: var(--ok);
475
+ }
476
+
477
+ .kv dd.off {
478
+ color: var(--muted);
479
+ }
480
+
481
+ .kv dd.bad {
482
+ color: var(--bad);
483
+ }
484
+
485
+ .meter + .meter {
486
+ margin-top: 16px;
487
+ }
488
+
489
+ .meter-head {
490
+ display: flex;
491
+ gap: 12px;
492
+ align-items: baseline;
493
+ justify-content: space-between;
494
+ margin-bottom: 7px;
495
+ font-size: 12.5px;
496
+ }
497
+
498
+ .meter-head span:first-child {
499
+ color: var(--muted);
500
+ }
501
+
502
+ .bar {
503
+ height: 8px;
504
+ border-radius: 999px;
505
+ background: #0d0f14;
506
+ overflow: hidden;
507
+ }
508
+
509
+ .bar > span {
510
+ display: block;
511
+ width: 0;
512
+ height: 100%;
513
+ border-radius: 999px;
514
+ background: linear-gradient(
515
+ 90deg,
516
+ color-mix(in oklab, var(--accent) 70%, transparent),
517
+ var(--accent)
518
+ );
519
+ transition: width 0.3s ease;
520
+ }
521
+
522
+ .bar > span.warn {
523
+ background: linear-gradient(90deg, color-mix(in oklab, var(--warn) 70%, transparent), var(--warn));
524
+ }
525
+
526
+ .bar > span.bad {
527
+ background: linear-gradient(90deg, color-mix(in oklab, var(--bad) 70%, transparent), var(--bad));
528
+ }
529
+
530
+ /* ------------------------------------------------------- analitik dökümü */
531
+
532
+ .report {
533
+ margin-top: 14px;
534
+ border: 1px solid var(--line);
535
+ border-radius: var(--radius);
536
+ overflow: hidden;
537
+ }
538
+
539
+ .report table {
540
+ font-size: 12px;
541
+ }
542
+
543
+ .report .note {
544
+ padding: 9px 12px;
545
+ border-top: 1px solid var(--line);
546
+ color: var(--muted);
547
+ font-size: 11.5px;
548
+ }
549
+
550
+ /* Header'daki seçici bir form alanı gibi değil, yanındaki chip'ler gibi
551
+ görünmeli: panelin üst şeridi durum bilgisi taşıyor, kontrol değil. */
552
+ select.lang {
553
+ padding: 5px 7px;
554
+ border: 1px solid var(--line);
555
+ border-radius: 7px;
556
+ background: #0d0f14;
557
+ color: var(--muted);
558
+ font: inherit;
559
+ font-size: 11.5px;
560
+ }
561
+
562
+ select.lang:hover {
563
+ color: var(--text);
564
+ }
565
+
566
+ select {
567
+ padding: 8px 11px;
568
+ border: 1px solid var(--line);
569
+ border-radius: 8px;
570
+ background: #0d0f14;
571
+ color: var(--text);
572
+ font: inherit;
573
+ font-size: 12.5px;
574
+ }
575
+
576
+ /* --------------------------------------------------------------- öneri kutusu */
577
+
578
+ .suggest {
579
+ padding: 14px 16px;
580
+ border: 1px dashed color-mix(in oklab, var(--accent) 40%, var(--line));
581
+ border-radius: var(--radius);
582
+ background: color-mix(in oklab, var(--accent) 6%, transparent);
583
+ font-size: 12.5px;
584
+ line-height: 1.6;
585
+ }
586
+
587
+ .suggest pre {
588
+ margin: 12px 0 0;
589
+ padding: 10px 12px;
590
+ border: 1px solid var(--line);
591
+ border-radius: 8px;
592
+ background: #0d0f14;
593
+ overflow-x: auto;
594
+ font-size: 12px;
595
+ }
596
+
597
+ .suggest code {
598
+ color: color-mix(in oklab, var(--accent) 75%, var(--text));
599
+ }
600
+
601
+ .suggest .hint {
602
+ margin-top: 12px;
603
+ }
604
+
325
605
  /* -------------------------------------------------------------- sekme/tablo */
326
606
 
327
607
  .tabs {
@@ -380,6 +660,34 @@ td.key {
380
660
  overflow-wrap: anywhere;
381
661
  }
382
662
 
663
+ td.key a {
664
+ color: inherit;
665
+ text-decoration: none;
666
+ border-bottom: 1px dotted color-mix(in oklab, var(--accent) 55%, transparent);
667
+ }
668
+
669
+ td.key a:hover {
670
+ color: var(--accent);
671
+ border-bottom-color: var(--accent);
672
+ }
673
+
674
+ /* Kopyalanabilir anahtar: buton gibi davranır, metin gibi görünür. */
675
+ button.as-text {
676
+ padding: 0;
677
+ border: none;
678
+ border-bottom: 1px dotted transparent;
679
+ border-radius: 0;
680
+ background: none;
681
+ color: inherit;
682
+ font: inherit;
683
+ text-align: left;
684
+ }
685
+
686
+ button.as-text:hover:not(:disabled) {
687
+ color: var(--accent);
688
+ border-bottom-color: color-mix(in oklab, var(--accent) 55%, transparent);
689
+ }
690
+
383
691
  td.num {
384
692
  text-align: right;
385
693
  white-space: nowrap;