vue-kaspa-cli 0.1.8 → 0.1.9
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.
- package/package.json +1 -1
- package/templates/nuxt/app/app.vue +35 -0
- package/templates/nuxt/app/components/KaspaStatus.vue +280 -55
- package/templates/nuxt/app/pages/index.vue +1 -1
- package/templates/nuxt/public/logo.png +0 -0
- package/templates/vue/public/logo.png +0 -0
- package/templates/vue/src/components/KaspaStatus.vue +199 -143
- package/templates/vue/src/style.css +20 -20
package/package.json
CHANGED
|
@@ -1,3 +1,38 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<NuxtPage />
|
|
3
3
|
</template>
|
|
4
|
+
|
|
5
|
+
<style>
|
|
6
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
7
|
+
|
|
8
|
+
:root {
|
|
9
|
+
--ks-surface: #ffffff;
|
|
10
|
+
--ks-soft: #f4f4f5;
|
|
11
|
+
--ks-border: #e4e4e7;
|
|
12
|
+
--ks-heading: #18181b;
|
|
13
|
+
--ks-text: #3f3f46;
|
|
14
|
+
--ks-muted: #a1a1aa;
|
|
15
|
+
--ks-accent: #49c5a3;
|
|
16
|
+
--ks-shine: rgba(73, 197, 163, .55);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@media (prefers-color-scheme: dark) {
|
|
20
|
+
:root {
|
|
21
|
+
--ks-surface: #09090b;
|
|
22
|
+
--ks-soft: #18181b;
|
|
23
|
+
--ks-border: #27272a;
|
|
24
|
+
--ks-heading: #fafafa;
|
|
25
|
+
--ks-text: #a1a1aa;
|
|
26
|
+
--ks-muted: #52525b;
|
|
27
|
+
--ks-shine: rgba(73, 197, 163, .40);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
body {
|
|
32
|
+
margin: 0;
|
|
33
|
+
min-height: 100vh;
|
|
34
|
+
background: var(--ks-surface);
|
|
35
|
+
color: var(--ks-text);
|
|
36
|
+
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
37
|
+
}
|
|
38
|
+
</style>
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
+
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
2
3
|
// useKaspa, useRpc, and computed are auto-imported by Nuxt.
|
|
3
|
-
// WASM init and RPC connection are handled automatically by the vue-kaspa Nuxt module
|
|
4
|
-
// when autoConnect: true (the default). No manual connect call is needed here.
|
|
5
4
|
|
|
6
5
|
const kaspa = useKaspa()
|
|
7
6
|
const rpc = useRpc()
|
|
7
|
+
const bento = ref<HTMLElement | null>(null)
|
|
8
|
+
const donateDialog = ref<HTMLDialogElement | null>(null)
|
|
8
9
|
|
|
9
10
|
const stateLabel = computed(() => {
|
|
10
11
|
if (kaspa.wasmStatus.value === 'loading') return 'Loading WASM…'
|
|
@@ -19,10 +20,10 @@ const stateLabel = computed(() => {
|
|
|
19
20
|
})
|
|
20
21
|
|
|
21
22
|
const badgeColor = computed(() => {
|
|
22
|
-
if (kaspa.wasmStatus.value !== 'ready') return '
|
|
23
|
+
if (kaspa.wasmStatus.value !== 'ready') return 'var(--ks-muted)'
|
|
23
24
|
if (rpc.connectionState.value === 'connected') return '#4caf50'
|
|
24
25
|
if (rpc.connectionState.value === 'error') return '#f44336'
|
|
25
|
-
return '
|
|
26
|
+
return 'var(--ks-muted)'
|
|
26
27
|
})
|
|
27
28
|
|
|
28
29
|
const daaScore = computed(() =>
|
|
@@ -30,73 +31,297 @@ const daaScore = computed(() =>
|
|
|
30
31
|
)
|
|
31
32
|
|
|
32
33
|
const links = [
|
|
33
|
-
{ label: 'Faucet',
|
|
34
|
-
{ label: 'Faucet',
|
|
35
|
-
{ label: 'Docs',
|
|
36
|
-
{ label: 'Explorer', title: 'Testnet 10', href: 'https://tn10.kaspa.stream/' },
|
|
37
|
-
{ label: 'Explorer', title: 'Testnet 12', href: 'https://tn12.kaspa.stream/' },
|
|
38
|
-
{ label: 'Explorer', title: 'Mainnet', href: 'https://kaspa.stream/' },
|
|
34
|
+
{ label: 'Faucet', title: 'Testnet 10', desc: 'Get free test KAS', icon: '💧', href: 'https://faucet-tn10.kaspanet.io/' },
|
|
35
|
+
{ label: 'Faucet', title: 'Testnet 12', desc: 'Get free test KAS', icon: '💧', href: 'https://faucet-tn12.kaspanet.io/' },
|
|
36
|
+
{ label: 'Docs', title: 'vue-kaspa', desc: 'Read the full docs', icon: '📖', href: 'https://vue-kaspa.vercel.app/' },
|
|
37
|
+
{ label: 'Explorer', title: 'Testnet 10', desc: 'Browse transactions', icon: '🔍', href: 'https://tn10.kaspa.stream/' },
|
|
38
|
+
{ label: 'Explorer', title: 'Testnet 12', desc: 'Browse transactions', icon: '🔍', href: 'https://tn12.kaspa.stream/' },
|
|
39
|
+
{ label: 'Explorer', title: 'Mainnet', desc: 'Browse transactions', icon: '🔍', href: 'https://kaspa.stream/' },
|
|
39
40
|
]
|
|
41
|
+
|
|
42
|
+
function onMouseMove(e: MouseEvent) {
|
|
43
|
+
const cards = bento.value?.querySelectorAll<HTMLElement>('[data-shine]') ?? []
|
|
44
|
+
for (const card of cards) {
|
|
45
|
+
const r = card.getBoundingClientRect()
|
|
46
|
+
card.style.setProperty('--x', `${e.clientX - r.left}px`)
|
|
47
|
+
card.style.setProperty('--y', `${e.clientY - r.top}px`)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onMounted(() => window.addEventListener('mousemove', onMouseMove))
|
|
52
|
+
onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
40
53
|
</script>
|
|
41
54
|
|
|
42
55
|
<template>
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
56
|
+
<!-- Donation dialog -->
|
|
57
|
+
<dialog ref="donateDialog" class="ks-dialog" @click.self="donateDialog?.close()">
|
|
58
|
+
<div class="ks-dialog-inner">
|
|
59
|
+
<button class="ks-dialog-close" @click="donateDialog?.close()">✕</button>
|
|
60
|
+
<p class="ks-dialog-title">Support vue-kaspa ❤️</p>
|
|
61
|
+
<p class="ks-dialog-body">vue-kaspa is free and open-source. If it saves you time, consider sending some KAS — every bit helps keep the project alive and maintained.</p>
|
|
62
|
+
<code class="ks-dialog-addr">kaspa:qypr7ayn2g55fccyv9n6gf9zgrcnpepkfgjf9d8mtfp68ezv3mgqnggxqs902q4</code>
|
|
63
|
+
<p class="ks-dialog-thanks">Thank you for your support 🙏</p>
|
|
64
|
+
</div>
|
|
65
|
+
</dialog>
|
|
66
|
+
|
|
67
|
+
<div class="ks-root">
|
|
68
|
+
|
|
69
|
+
<!-- Header -->
|
|
70
|
+
<header class="ks-header">
|
|
71
|
+
<img src="/logo.png" alt="vue-kaspa" class="ks-header-logo" />
|
|
72
|
+
<span class="ks-header-brand">vue-kaspa</span>
|
|
73
|
+
<nav class="ks-header-nav">
|
|
74
|
+
<a
|
|
75
|
+
href="https://github.com/furatamasensei/vue-kaspa"
|
|
76
|
+
target="_blank"
|
|
77
|
+
rel="noopener"
|
|
78
|
+
class="ks-icon-btn"
|
|
79
|
+
title="GitHub"
|
|
80
|
+
>
|
|
81
|
+
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
|
|
82
|
+
<path d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.166 6.839 9.489.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.603-3.369-1.342-3.369-1.342-.454-1.155-1.11-1.463-1.11-1.463-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.114 2.504.337 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.202 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.741 0 .267.18.578.688.48C19.138 20.163 22 16.418 22 12c0-5.523-4.477-10-10-10z"/>
|
|
83
|
+
</svg>
|
|
84
|
+
</a>
|
|
85
|
+
<button class="ks-icon-btn" title="Support this project" @click="donateDialog?.showModal()">
|
|
86
|
+
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
|
|
87
|
+
<path d="M12 21.593c-.425-.396-8.8-8.044-8.8-12.593C3.2 5.796 7.192 3 12 3s8.8 2.796 8.8 6c0 4.549-8.375 12.197-8.8 12.593z"/>
|
|
88
|
+
</svg>
|
|
89
|
+
</button>
|
|
90
|
+
</nav>
|
|
91
|
+
</header>
|
|
92
|
+
|
|
93
|
+
<!-- Bento grid — Γ layout: net card spans col 1–2 × row 1–3, links fill col 3 then bottom row -->
|
|
94
|
+
<div ref="bento" class="ks-grid">
|
|
95
|
+
|
|
96
|
+
<!-- Network card -->
|
|
97
|
+
<div data-shine class="ks-shine ks-net-shine">
|
|
98
|
+
<div class="ks-card ks-net-card">
|
|
99
|
+
<div class="ks-net-top">
|
|
100
|
+
<span class="ks-net-icon">⬡</span>
|
|
101
|
+
<span
|
|
102
|
+
class="ks-badge"
|
|
103
|
+
:style="`border-color:${badgeColor};color:${badgeColor}`"
|
|
104
|
+
>{{ stateLabel }}</span>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="ks-stats">
|
|
107
|
+
<div class="ks-stat">
|
|
108
|
+
<span class="ks-stat-label">Network</span>
|
|
109
|
+
<span class="ks-stat-value">{{ rpc.networkId.value ?? '—' }}</span>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="ks-stat">
|
|
112
|
+
<span class="ks-stat-label">Server version</span>
|
|
113
|
+
<span class="ks-stat-value">{{ rpc.serverVersion.value ?? '—' }}</span>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="ks-stat">
|
|
116
|
+
<span class="ks-stat-label">DAA Score</span>
|
|
117
|
+
<span class="ks-stat-value" style="font-family:monospace">{{ daaScore }}</span>
|
|
118
|
+
</div>
|
|
119
|
+
<div class="ks-stat">
|
|
120
|
+
<span class="ks-stat-label">Synced</span>
|
|
121
|
+
<span
|
|
122
|
+
class="ks-stat-value"
|
|
123
|
+
:style="`color:${rpc.isConnected.value ? (rpc.isSynced.value ? '#4caf50' : 'var(--ks-text)') : 'var(--ks-muted)'}`"
|
|
124
|
+
>{{ rpc.isConnected.value ? (rpc.isSynced.value ? 'Yes' : 'Syncing…') : '—' }}</span>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
73
127
|
</div>
|
|
74
128
|
</div>
|
|
75
129
|
|
|
76
|
-
|
|
77
|
-
<a href="https://vue-kaspa.vercel.app" target="_blank" rel="noopener" style="color:#374151;text-decoration:none;">Docs</a>
|
|
78
|
-
<span>·</span>
|
|
79
|
-
<a href="https://github.com/furatamasensei/vue-kaspa" target="_blank" rel="noopener" style="color:#374151;text-decoration:none;">GitHub</a>
|
|
80
|
-
</footer>
|
|
81
|
-
</div>
|
|
82
|
-
|
|
83
|
-
<!-- Bento link cards -->
|
|
84
|
-
<div style="display:grid;grid-template-columns:repeat(3,1fr);gap:0.75rem;">
|
|
130
|
+
<!-- Link cards — auto-placed: first 3 fill col 3 rows 1–3, last 3 fill row 4 -->
|
|
85
131
|
<a
|
|
86
132
|
v-for="link in links"
|
|
87
133
|
:key="link.href"
|
|
134
|
+
data-shine
|
|
135
|
+
class="ks-shine ks-link-shine"
|
|
88
136
|
:href="link.href"
|
|
89
137
|
target="_blank"
|
|
90
138
|
rel="noopener"
|
|
91
|
-
style="display:flex;flex-direction:column;justify-content:space-between;gap:0.75rem;padding:1rem;border:1px solid #e2e8f0;border-radius:10px;background:#f9f9f9;text-decoration:none;cursor:pointer;"
|
|
92
139
|
>
|
|
93
|
-
<div
|
|
94
|
-
<
|
|
95
|
-
|
|
140
|
+
<div class="ks-card ks-link-card">
|
|
141
|
+
<div class="ks-link-top">
|
|
142
|
+
<span class="ks-link-icon">{{ link.icon }}</span>
|
|
143
|
+
<span class="ks-link-arrow">↗</span>
|
|
144
|
+
</div>
|
|
145
|
+
<div>
|
|
146
|
+
<div class="ks-link-label">{{ link.label }}</div>
|
|
147
|
+
<div class="ks-link-title">{{ link.title }}</div>
|
|
148
|
+
<div class="ks-link-desc">{{ link.desc }}</div>
|
|
149
|
+
</div>
|
|
96
150
|
</div>
|
|
97
|
-
<span style="font-size:.85rem;font-weight:600;color:#1a1a1a;">{{ link.title }}</span>
|
|
98
151
|
</a>
|
|
99
|
-
</div>
|
|
100
152
|
|
|
153
|
+
</div>
|
|
101
154
|
</div>
|
|
102
155
|
</template>
|
|
156
|
+
|
|
157
|
+
<style scoped>
|
|
158
|
+
/* Root */
|
|
159
|
+
.ks-root {
|
|
160
|
+
width: 100%;
|
|
161
|
+
max-width: 720px;
|
|
162
|
+
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/* Header */
|
|
166
|
+
.ks-header {
|
|
167
|
+
display: flex;
|
|
168
|
+
align-items: center;
|
|
169
|
+
gap: 0.6rem;
|
|
170
|
+
margin-bottom: 0.75rem;
|
|
171
|
+
}
|
|
172
|
+
.ks-header-logo { width: 28px; height: 28px; object-fit: contain; }
|
|
173
|
+
.ks-header-brand { font-size: 1rem; font-weight: 700; color: var(--ks-heading); flex: 1; }
|
|
174
|
+
.ks-header-nav { display: flex; gap: 0.15rem; }
|
|
175
|
+
|
|
176
|
+
.ks-icon-btn {
|
|
177
|
+
display: inline-flex;
|
|
178
|
+
align-items: center;
|
|
179
|
+
justify-content: center;
|
|
180
|
+
width: 34px;
|
|
181
|
+
height: 34px;
|
|
182
|
+
border-radius: 8px;
|
|
183
|
+
border: none;
|
|
184
|
+
background: transparent;
|
|
185
|
+
color: var(--ks-muted);
|
|
186
|
+
cursor: pointer;
|
|
187
|
+
text-decoration: none;
|
|
188
|
+
transition: color 0.15s, background 0.15s;
|
|
189
|
+
}
|
|
190
|
+
.ks-icon-btn:hover { color: var(--ks-heading); background: var(--ks-border); }
|
|
191
|
+
|
|
192
|
+
/* Grid */
|
|
193
|
+
.ks-grid {
|
|
194
|
+
display: grid;
|
|
195
|
+
grid-template-columns: repeat(3, 1fr);
|
|
196
|
+
gap: 0.75rem;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/* Shine wrapper — 1px padding reveals radial gradient as a glowing border */
|
|
200
|
+
.ks-shine {
|
|
201
|
+
padding: 1px;
|
|
202
|
+
border-radius: 14px;
|
|
203
|
+
background: radial-gradient(
|
|
204
|
+
350px circle at var(--x, -9999px) var(--y, -9999px),
|
|
205
|
+
var(--ks-shine),
|
|
206
|
+
var(--ks-border) 80%
|
|
207
|
+
);
|
|
208
|
+
display: block;
|
|
209
|
+
text-decoration: none;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/* Network card occupies col 1–2, row 1–3 */
|
|
213
|
+
.ks-net-shine {
|
|
214
|
+
grid-column: 1 / span 2;
|
|
215
|
+
grid-row: 1 / span 3;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/* Card base — solid fill sits inside the 1px shine gap */
|
|
219
|
+
.ks-card {
|
|
220
|
+
border-radius: 13px;
|
|
221
|
+
background: var(--ks-soft);
|
|
222
|
+
height: 100%;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/* Network card */
|
|
226
|
+
.ks-net-card {
|
|
227
|
+
padding: 1.75rem;
|
|
228
|
+
display: flex;
|
|
229
|
+
flex-direction: column;
|
|
230
|
+
gap: 1.5rem;
|
|
231
|
+
min-height: 320px;
|
|
232
|
+
}
|
|
233
|
+
.ks-net-top { display: flex; align-items: center; justify-content: space-between; }
|
|
234
|
+
.ks-net-icon { font-size: 2.25rem; color: var(--ks-accent); line-height: 1; }
|
|
235
|
+
.ks-badge {
|
|
236
|
+
font-size: .7rem;
|
|
237
|
+
font-weight: 500;
|
|
238
|
+
padding: .2em .6em;
|
|
239
|
+
border-radius: 999px;
|
|
240
|
+
border: 1px solid;
|
|
241
|
+
white-space: nowrap;
|
|
242
|
+
}
|
|
243
|
+
.ks-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
|
|
244
|
+
.ks-stat { display: flex; flex-direction: column; gap: .25rem; }
|
|
245
|
+
.ks-stat-label {
|
|
246
|
+
font-size: .65rem;
|
|
247
|
+
text-transform: uppercase;
|
|
248
|
+
letter-spacing: .06em;
|
|
249
|
+
color: var(--ks-muted);
|
|
250
|
+
}
|
|
251
|
+
.ks-stat-value {
|
|
252
|
+
font-size: .95rem;
|
|
253
|
+
color: var(--ks-heading);
|
|
254
|
+
overflow: hidden;
|
|
255
|
+
text-overflow: ellipsis;
|
|
256
|
+
white-space: nowrap;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/* Link cards */
|
|
260
|
+
.ks-link-card {
|
|
261
|
+
padding: 1rem;
|
|
262
|
+
display: flex;
|
|
263
|
+
flex-direction: column;
|
|
264
|
+
justify-content: space-between;
|
|
265
|
+
gap: 0.5rem;
|
|
266
|
+
min-height: 100px;
|
|
267
|
+
}
|
|
268
|
+
.ks-link-top { display: flex; justify-content: space-between; align-items: flex-start; }
|
|
269
|
+
.ks-link-icon { font-size: 1.35rem; line-height: 1; }
|
|
270
|
+
.ks-link-arrow { font-size: .75rem; color: var(--ks-muted); }
|
|
271
|
+
.ks-link-label {
|
|
272
|
+
font-size: .58rem;
|
|
273
|
+
text-transform: uppercase;
|
|
274
|
+
letter-spacing: .06em;
|
|
275
|
+
color: var(--ks-muted);
|
|
276
|
+
margin-bottom: .1rem;
|
|
277
|
+
}
|
|
278
|
+
.ks-link-title { font-size: .875rem; font-weight: 600; color: var(--ks-heading); }
|
|
279
|
+
.ks-link-desc { font-size: .75rem; color: var(--ks-muted); margin-top: .1rem; }
|
|
280
|
+
|
|
281
|
+
/* Dialog */
|
|
282
|
+
.ks-dialog {
|
|
283
|
+
border: 1px solid var(--ks-border);
|
|
284
|
+
border-radius: 16px;
|
|
285
|
+
padding: 0;
|
|
286
|
+
max-width: 420px;
|
|
287
|
+
width: calc(100vw - 2rem);
|
|
288
|
+
background: var(--ks-surface);
|
|
289
|
+
color: var(--ks-text);
|
|
290
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, .25);
|
|
291
|
+
}
|
|
292
|
+
.ks-dialog-inner { padding: 2rem; position: relative; }
|
|
293
|
+
.ks-dialog-close {
|
|
294
|
+
position: absolute;
|
|
295
|
+
top: 1rem;
|
|
296
|
+
right: 1rem;
|
|
297
|
+
background: none;
|
|
298
|
+
border: none;
|
|
299
|
+
font-size: .9rem;
|
|
300
|
+
cursor: pointer;
|
|
301
|
+
color: var(--ks-muted);
|
|
302
|
+
padding: .25rem;
|
|
303
|
+
line-height: 1;
|
|
304
|
+
}
|
|
305
|
+
.ks-dialog-close:hover { color: var(--ks-heading); }
|
|
306
|
+
.ks-dialog-title { margin: 0 0 .75rem; font-size: 1.05rem; font-weight: 700; color: var(--ks-heading); }
|
|
307
|
+
.ks-dialog-body { font-size: .875rem; color: var(--ks-muted); margin: 0 0 1.25rem; line-height: 1.65; }
|
|
308
|
+
.ks-dialog-addr {
|
|
309
|
+
display: block;
|
|
310
|
+
padding: .6em .85em;
|
|
311
|
+
border-radius: 8px;
|
|
312
|
+
background: var(--ks-soft);
|
|
313
|
+
border: 1px solid var(--ks-border);
|
|
314
|
+
font-size: .7rem;
|
|
315
|
+
word-break: break-all;
|
|
316
|
+
color: var(--ks-text);
|
|
317
|
+
}
|
|
318
|
+
.ks-dialog-thanks { font-size: .8rem; color: var(--ks-muted); margin: .75rem 0 0; text-align: center; }
|
|
319
|
+
</style>
|
|
320
|
+
|
|
321
|
+
<style>
|
|
322
|
+
/* ::backdrop can't receive scoped attribute — must be global */
|
|
323
|
+
.ks-dialog::backdrop {
|
|
324
|
+
background: rgba(0, 0, 0, .5);
|
|
325
|
+
backdrop-filter: blur(4px);
|
|
326
|
+
}
|
|
327
|
+
</style>
|
|
Binary file
|
|
Binary file
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import { computed } from 'vue'
|
|
2
|
+
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
3
3
|
import { useKaspa, useRpc } from 'vue-kaspa'
|
|
4
4
|
|
|
5
5
|
const kaspa = useKaspa()
|
|
6
6
|
const rpc = useRpc()
|
|
7
|
+
const bento = ref<HTMLElement | null>(null)
|
|
8
|
+
const donateDialog = ref<HTMLDialogElement | null>(null)
|
|
7
9
|
|
|
8
10
|
const stateLabel = computed(() => {
|
|
9
11
|
if (kaspa.wasmStatus.value === 'loading') return 'Loading WASM…'
|
|
@@ -17,11 +19,11 @@ const stateLabel = computed(() => {
|
|
|
17
19
|
} as Record<string, string>)[rpc.connectionState.value] ?? rpc.connectionState.value
|
|
18
20
|
})
|
|
19
21
|
|
|
20
|
-
const
|
|
21
|
-
if (kaspa.wasmStatus.value !== 'ready') return '
|
|
22
|
-
if (rpc.connectionState.value === 'connected') return '
|
|
23
|
-
if (rpc.connectionState.value === 'error') return '
|
|
24
|
-
return '
|
|
22
|
+
const badgeColor = computed(() => {
|
|
23
|
+
if (kaspa.wasmStatus.value !== 'ready') return 'var(--ks-muted)'
|
|
24
|
+
if (rpc.connectionState.value === 'connected') return '#4caf50'
|
|
25
|
+
if (rpc.connectionState.value === 'error') return '#f44336'
|
|
26
|
+
return 'var(--ks-muted)'
|
|
25
27
|
})
|
|
26
28
|
|
|
27
29
|
const daaScore = computed(() =>
|
|
@@ -29,170 +31,224 @@ const daaScore = computed(() =>
|
|
|
29
31
|
)
|
|
30
32
|
|
|
31
33
|
const links = [
|
|
32
|
-
{ label: 'Faucet',
|
|
33
|
-
{ label: 'Faucet',
|
|
34
|
-
{ label: 'Docs',
|
|
35
|
-
{ label: 'Explorer', title: 'Testnet 10', href: 'https://tn10.kaspa.stream/' },
|
|
36
|
-
{ label: 'Explorer', title: 'Testnet 12', href: 'https://tn12.kaspa.stream/' },
|
|
37
|
-
{ label: 'Explorer', title: 'Mainnet', href: 'https://kaspa.stream/' },
|
|
34
|
+
{ label: 'Faucet', title: 'Testnet 10', desc: 'Get free test KAS', icon: '💧', href: 'https://faucet-tn10.kaspanet.io/' },
|
|
35
|
+
{ label: 'Faucet', title: 'Testnet 12', desc: 'Get free test KAS', icon: '💧', href: 'https://faucet-tn12.kaspanet.io/' },
|
|
36
|
+
{ label: 'Docs', title: 'vue-kaspa', desc: 'Read the full docs', icon: '📖', href: 'https://vue-kaspa.vercel.app/' },
|
|
37
|
+
{ label: 'Explorer', title: 'Testnet 10', desc: 'Browse transactions', icon: '🔍', href: 'https://tn10.kaspa.stream/' },
|
|
38
|
+
{ label: 'Explorer', title: 'Testnet 12', desc: 'Browse transactions', icon: '🔍', href: 'https://tn12.kaspa.stream/' },
|
|
39
|
+
{ label: 'Explorer', title: 'Mainnet', desc: 'Browse transactions', icon: '🔍', href: 'https://kaspa.stream/' },
|
|
38
40
|
]
|
|
41
|
+
|
|
42
|
+
function onMouseMove(e: MouseEvent) {
|
|
43
|
+
const cards = bento.value?.querySelectorAll<HTMLElement>('[data-shine]') ?? []
|
|
44
|
+
for (const card of cards) {
|
|
45
|
+
const r = card.getBoundingClientRect()
|
|
46
|
+
card.style.setProperty('--x', `${e.clientX - r.left}px`)
|
|
47
|
+
card.style.setProperty('--y', `${e.clientY - r.top}px`)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
onMounted(() => window.addEventListener('mousemove', onMouseMove))
|
|
52
|
+
onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
39
53
|
</script>
|
|
40
54
|
|
|
41
55
|
<template>
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
</
|
|
56
|
+
<!-- Donation dialog -->
|
|
57
|
+
<dialog ref="donateDialog" class="dialog" @click.self="donateDialog?.close()">
|
|
58
|
+
<div class="dialog-inner">
|
|
59
|
+
<button class="dialog-close" @click="donateDialog?.close()">✕</button>
|
|
60
|
+
<p class="dialog-title">Support vue-kaspa ❤️</p>
|
|
61
|
+
<p class="dialog-body">vue-kaspa is free and open-source. If it saves you time, consider sending some KAS — every bit helps keep the project alive and maintained.</p>
|
|
62
|
+
<code class="dialog-addr">kaspa:qypr7ayn2g55fccyv9n6gf9zgrcnpepkfgjf9d8mtfp68ezv3mgqnggxqs902q4</code>
|
|
63
|
+
<p class="dialog-thanks">Thank you for your support 🙏</p>
|
|
64
|
+
</div>
|
|
65
|
+
</dialog>
|
|
66
|
+
|
|
67
|
+
<div class="root">
|
|
68
|
+
|
|
69
|
+
<!-- Header -->
|
|
70
|
+
<header class="header">
|
|
71
|
+
<img src="/logo.png" alt="vue-kaspa" class="header-logo" />
|
|
72
|
+
<span class="header-brand">vue-kaspa</span>
|
|
73
|
+
<nav class="header-nav">
|
|
74
|
+
<a
|
|
75
|
+
href="https://github.com/furatamasensei/vue-kaspa"
|
|
76
|
+
target="_blank"
|
|
77
|
+
rel="noopener"
|
|
78
|
+
class="icon-btn"
|
|
79
|
+
title="GitHub"
|
|
80
|
+
>
|
|
81
|
+
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
|
|
82
|
+
<path d="M12 2C6.477 2 2 6.477 2 12c0 4.42 2.865 8.166 6.839 9.489.5.092.682-.217.682-.482 0-.237-.008-.866-.013-1.7-2.782.603-3.369-1.342-3.369-1.342-.454-1.155-1.11-1.463-1.11-1.463-.908-.62.069-.608.069-.608 1.003.07 1.531 1.03 1.531 1.03.892 1.529 2.341 1.087 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.11-4.555-4.943 0-1.091.39-1.984 1.029-2.683-.103-.253-.446-1.27.098-2.647 0 0 .84-.269 2.75 1.025A9.578 9.578 0 0112 6.836c.85.004 1.705.114 2.504.337 1.909-1.294 2.747-1.025 2.747-1.025.546 1.377.202 2.394.1 2.647.64.699 1.028 1.592 1.028 2.683 0 3.842-2.339 4.687-4.566 4.935.359.309.678.919.678 1.852 0 1.336-.012 2.415-.012 2.741 0 .267.18.578.688.48C19.138 20.163 22 16.418 22 12c0-5.523-4.477-10-10-10z"/>
|
|
83
|
+
</svg>
|
|
84
|
+
</a>
|
|
85
|
+
<button class="icon-btn" title="Support this project" @click="donateDialog?.showModal()">
|
|
86
|
+
<svg viewBox="0 0 24 24" width="18" height="18" fill="currentColor">
|
|
87
|
+
<path d="M12 21.593c-.425-.396-8.8-8.044-8.8-12.593C3.2 5.796 7.192 3 12 3s8.8 2.796 8.8 6c0 4.549-8.375 12.197-8.8 12.593z"/>
|
|
88
|
+
</svg>
|
|
89
|
+
</button>
|
|
90
|
+
</nav>
|
|
91
|
+
</header>
|
|
92
|
+
|
|
93
|
+
<!-- Bento grid — Γ layout: net card spans col 1–2 × row 1–3, links fill col 3 then bottom row -->
|
|
94
|
+
<div ref="bento" class="grid">
|
|
95
|
+
|
|
96
|
+
<!-- Network card -->
|
|
97
|
+
<div data-shine class="shine net-shine">
|
|
98
|
+
<div class="card net-card">
|
|
99
|
+
<div class="net-top">
|
|
100
|
+
<span class="net-icon">⬡</span>
|
|
101
|
+
<span
|
|
102
|
+
class="badge"
|
|
103
|
+
:style="`border-color:${badgeColor};color:${badgeColor}`"
|
|
104
|
+
>{{ stateLabel }}</span>
|
|
105
|
+
</div>
|
|
106
|
+
<div class="stats">
|
|
107
|
+
<div class="stat">
|
|
108
|
+
<span class="stat-label">Network</span>
|
|
109
|
+
<span class="stat-value">{{ rpc.networkId.value ?? '—' }}</span>
|
|
110
|
+
</div>
|
|
111
|
+
<div class="stat">
|
|
112
|
+
<span class="stat-label">Server version</span>
|
|
113
|
+
<span class="stat-value">{{ rpc.serverVersion.value ?? '—' }}</span>
|
|
114
|
+
</div>
|
|
115
|
+
<div class="stat">
|
|
116
|
+
<span class="stat-label">DAA Score</span>
|
|
117
|
+
<span class="stat-value mono">{{ daaScore }}</span>
|
|
118
|
+
</div>
|
|
119
|
+
<div class="stat">
|
|
120
|
+
<span class="stat-label">Synced</span>
|
|
121
|
+
<span
|
|
122
|
+
class="stat-value"
|
|
123
|
+
:style="`color:${rpc.isConnected.value ? (rpc.isSynced.value ? '#4caf50' : 'var(--ks-text)') : 'var(--ks-muted)'}`"
|
|
124
|
+
>{{ rpc.isConnected.value ? (rpc.isSynced.value ? 'Yes' : 'Syncing…') : '—' }}</span>
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
70
127
|
</div>
|
|
71
128
|
</div>
|
|
72
129
|
|
|
73
|
-
|
|
74
|
-
<a href="https://vue-kaspa.vercel.app" target="_blank" rel="noopener">Docs</a>
|
|
75
|
-
<span>·</span>
|
|
76
|
-
<a href="https://github.com/furatamasensei/vue-kaspa" target="_blank" rel="noopener">GitHub</a>
|
|
77
|
-
</footer>
|
|
78
|
-
</div>
|
|
79
|
-
|
|
80
|
-
<!-- Bento link cards -->
|
|
81
|
-
<div class="link-grid">
|
|
130
|
+
<!-- Link cards — auto-placed: first 3 fill col 3 rows 1–3, last 3 fill row 4 -->
|
|
82
131
|
<a
|
|
83
132
|
v-for="link in links"
|
|
84
133
|
:key="link.href"
|
|
134
|
+
data-shine
|
|
135
|
+
class="shine link-shine"
|
|
85
136
|
:href="link.href"
|
|
86
137
|
target="_blank"
|
|
87
138
|
rel="noopener"
|
|
88
|
-
class="link-card"
|
|
89
139
|
>
|
|
90
|
-
<div class="link-
|
|
91
|
-
<
|
|
92
|
-
|
|
140
|
+
<div class="card link-card">
|
|
141
|
+
<div class="link-top">
|
|
142
|
+
<span class="link-icon">{{ link.icon }}</span>
|
|
143
|
+
<span class="link-arrow">↗</span>
|
|
144
|
+
</div>
|
|
145
|
+
<div>
|
|
146
|
+
<div class="link-label">{{ link.label }}</div>
|
|
147
|
+
<div class="link-title">{{ link.title }}</div>
|
|
148
|
+
<div class="link-desc">{{ link.desc }}</div>
|
|
149
|
+
</div>
|
|
93
150
|
</div>
|
|
94
|
-
<span class="link-card__title">{{ link.title }}</span>
|
|
95
151
|
</a>
|
|
96
|
-
</div>
|
|
97
152
|
|
|
153
|
+
</div>
|
|
98
154
|
</div>
|
|
99
155
|
</template>
|
|
100
156
|
|
|
101
157
|
<style scoped>
|
|
102
|
-
|
|
158
|
+
/* Root */
|
|
159
|
+
.root {
|
|
103
160
|
width: 100%;
|
|
104
|
-
|
|
105
|
-
display: flex;
|
|
106
|
-
flex-direction: column;
|
|
107
|
-
gap: 0.75rem;
|
|
108
|
-
}
|
|
109
|
-
.card {
|
|
110
|
-
padding: 2rem;
|
|
111
|
-
border: 1px solid var(--color-border);
|
|
112
|
-
border-radius: 10px;
|
|
113
|
-
background: var(--color-background-soft);
|
|
114
|
-
display: flex;
|
|
115
|
-
flex-direction: column;
|
|
116
|
-
gap: 1.5rem;
|
|
117
|
-
}
|
|
118
|
-
.card__header { display: flex; align-items: center; gap: .75rem; }
|
|
119
|
-
.logo { font-size: 1.75rem; color: #49c5a3; line-height: 1; }
|
|
120
|
-
.card__title { font-size: 1.25rem; font-weight: 600; color: var(--color-heading); flex: 1; margin: 0; }
|
|
121
|
-
.badge {
|
|
122
|
-
font-size: .75rem;
|
|
123
|
-
font-weight: 500;
|
|
124
|
-
padding: .2em .65em;
|
|
125
|
-
border-radius: 999px;
|
|
126
|
-
border: 1px solid currentColor;
|
|
127
|
-
white-space: nowrap;
|
|
128
|
-
}
|
|
129
|
-
.badge--ok { color: #4caf50; }
|
|
130
|
-
.badge--pending { color: var(--color-muted, #888); }
|
|
131
|
-
.badge--error { color: #f44336; }
|
|
132
|
-
.grid { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }
|
|
133
|
-
.stat { display: flex; flex-direction: column; gap: .25rem; }
|
|
134
|
-
.stat__label {
|
|
135
|
-
font-size: .7rem;
|
|
136
|
-
text-transform: uppercase;
|
|
137
|
-
letter-spacing: .05em;
|
|
138
|
-
color: var(--color-muted, #888);
|
|
161
|
+
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
139
162
|
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
.
|
|
148
|
-
|
|
149
|
-
.stat__value.muted { color: var(--color-muted, #888); }
|
|
150
|
-
.card__footer {
|
|
151
|
-
display: flex;
|
|
163
|
+
|
|
164
|
+
/* Header */
|
|
165
|
+
.header { display: flex; align-items: center; gap: .6rem; margin-bottom: .75rem; }
|
|
166
|
+
.header-logo { width: 28px; height: 28px; object-fit: contain; }
|
|
167
|
+
.header-brand { font-size: 1rem; font-weight: 700; color: var(--ks-heading); flex: 1; }
|
|
168
|
+
.header-nav { display: flex; gap: .15rem; }
|
|
169
|
+
|
|
170
|
+
.icon-btn {
|
|
171
|
+
display: inline-flex;
|
|
152
172
|
align-items: center;
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
/* Bento link grid */
|
|
162
|
-
.link-grid {
|
|
163
|
-
display: grid;
|
|
164
|
-
grid-template-columns: repeat(3, 1fr);
|
|
165
|
-
gap: 0.75rem;
|
|
166
|
-
}
|
|
167
|
-
.link-card {
|
|
168
|
-
display: flex;
|
|
169
|
-
flex-direction: column;
|
|
170
|
-
justify-content: space-between;
|
|
171
|
-
gap: 0.75rem;
|
|
172
|
-
padding: 1rem;
|
|
173
|
-
border: 1px solid var(--color-border);
|
|
174
|
-
border-radius: 10px;
|
|
175
|
-
background: var(--color-background-soft);
|
|
176
|
-
text-decoration: none;
|
|
173
|
+
justify-content: center;
|
|
174
|
+
width: 34px;
|
|
175
|
+
height: 34px;
|
|
176
|
+
border-radius: 8px;
|
|
177
|
+
border: none;
|
|
178
|
+
background: transparent;
|
|
179
|
+
color: var(--ks-muted);
|
|
177
180
|
cursor: pointer;
|
|
178
|
-
|
|
181
|
+
text-decoration: none;
|
|
182
|
+
transition: color .15s, background .15s;
|
|
179
183
|
}
|
|
180
|
-
.
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
184
|
+
.icon-btn:hover { color: var(--ks-heading); background: var(--ks-border); }
|
|
185
|
+
|
|
186
|
+
/* Grid */
|
|
187
|
+
.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: .75rem; }
|
|
188
|
+
|
|
189
|
+
/* Shine wrapper — 1px padding reveals radial gradient as a glowing border */
|
|
190
|
+
.shine {
|
|
191
|
+
padding: 1px;
|
|
192
|
+
border-radius: 14px;
|
|
193
|
+
background: radial-gradient(
|
|
194
|
+
350px circle at var(--x, -9999px) var(--y, -9999px),
|
|
195
|
+
var(--ks-shine),
|
|
196
|
+
var(--ks-border) 80%
|
|
197
|
+
);
|
|
198
|
+
display: block;
|
|
199
|
+
text-decoration: none;
|
|
185
200
|
}
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
201
|
+
|
|
202
|
+
/* Network card occupies col 1–2, row 1–3 */
|
|
203
|
+
.net-shine { grid-column: 1 / span 2; grid-row: 1 / span 3; }
|
|
204
|
+
|
|
205
|
+
/* Card base */
|
|
206
|
+
.card { border-radius: 13px; background: var(--ks-soft); height: 100%; }
|
|
207
|
+
|
|
208
|
+
/* Network card */
|
|
209
|
+
.net-card { padding: 1.75rem; display: flex; flex-direction: column; gap: 1.5rem; min-height: 320px; }
|
|
210
|
+
.net-top { display: flex; align-items: center; justify-content: space-between; }
|
|
211
|
+
.net-icon { font-size: 2.25rem; color: var(--ks-accent); line-height: 1; }
|
|
212
|
+
.badge { font-size: .7rem; font-weight: 500; padding: .2em .6em; border-radius: 999px; border: 1px solid; white-space: nowrap; }
|
|
213
|
+
.stats { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
|
|
214
|
+
.stat { display: flex; flex-direction: column; gap: .25rem; }
|
|
215
|
+
.stat-label { font-size: .65rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ks-muted); }
|
|
216
|
+
.stat-value { font-size: .95rem; color: var(--ks-heading); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
217
|
+
.mono { font-family: monospace; }
|
|
218
|
+
|
|
219
|
+
/* Link cards */
|
|
220
|
+
.link-card { padding: 1rem; display: flex; flex-direction: column; justify-content: space-between; gap: .5rem; min-height: 100px; }
|
|
221
|
+
.link-top { display: flex; justify-content: space-between; align-items: flex-start; }
|
|
222
|
+
.link-icon { font-size: 1.35rem; line-height: 1; }
|
|
223
|
+
.link-arrow { font-size: .75rem; color: var(--ks-muted); }
|
|
224
|
+
.link-label { font-size: .58rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ks-muted); margin-bottom: .1rem; }
|
|
225
|
+
.link-title { font-size: .875rem; font-weight: 600; color: var(--ks-heading); }
|
|
226
|
+
.link-desc { font-size: .75rem; color: var(--ks-muted); margin-top: .1rem; }
|
|
227
|
+
|
|
228
|
+
/* Dialog */
|
|
229
|
+
.dialog {
|
|
230
|
+
border: 1px solid var(--ks-border);
|
|
231
|
+
border-radius: 16px;
|
|
232
|
+
padding: 0;
|
|
233
|
+
max-width: 420px;
|
|
234
|
+
width: calc(100vw - 2rem);
|
|
235
|
+
background: var(--ks-surface);
|
|
236
|
+
color: var(--ks-text);
|
|
237
|
+
box-shadow: 0 20px 60px rgba(0, 0, 0, .25);
|
|
191
238
|
}
|
|
192
|
-
.
|
|
193
|
-
.
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
239
|
+
.dialog-inner { padding: 2rem; position: relative; }
|
|
240
|
+
.dialog-close { position: absolute; top: 1rem; right: 1rem; background: none; border: none; font-size: .9rem; cursor: pointer; color: var(--ks-muted); padding: .25rem; line-height: 1; }
|
|
241
|
+
.dialog-close:hover { color: var(--ks-heading); }
|
|
242
|
+
.dialog-title { margin: 0 0 .75rem; font-size: 1.05rem; font-weight: 700; color: var(--ks-heading); }
|
|
243
|
+
.dialog-body { font-size: .875rem; color: var(--ks-muted); margin: 0 0 1.25rem; line-height: 1.65; }
|
|
244
|
+
.dialog-addr { display: block; padding: .6em .85em; border-radius: 8px; background: var(--ks-soft); border: 1px solid var(--ks-border); font-size: .7rem; word-break: break-all; color: var(--ks-text); }
|
|
245
|
+
.dialog-thanks { font-size: .8rem; color: var(--ks-muted); margin: .75rem 0 0; text-align: center; }
|
|
246
|
+
</style>
|
|
247
|
+
|
|
248
|
+
<style>
|
|
249
|
+
/* ::backdrop can't receive scoped attribute — must be global */
|
|
250
|
+
.dialog::backdrop {
|
|
251
|
+
background: rgba(0, 0, 0, .5);
|
|
252
|
+
backdrop-filter: blur(4px);
|
|
197
253
|
}
|
|
198
254
|
</style>
|
|
@@ -1,24 +1,25 @@
|
|
|
1
|
-
*, *::before, *::after {
|
|
2
|
-
box-sizing: border-box;
|
|
3
|
-
}
|
|
1
|
+
*, *::before, *::after { box-sizing: border-box; }
|
|
4
2
|
|
|
5
3
|
:root {
|
|
6
|
-
--
|
|
7
|
-
--
|
|
8
|
-
--
|
|
9
|
-
--
|
|
10
|
-
--
|
|
11
|
-
--
|
|
4
|
+
--ks-surface: #ffffff;
|
|
5
|
+
--ks-soft: #f4f4f5;
|
|
6
|
+
--ks-border: #e4e4e7;
|
|
7
|
+
--ks-heading: #18181b;
|
|
8
|
+
--ks-text: #3f3f46;
|
|
9
|
+
--ks-muted: #a1a1aa;
|
|
10
|
+
--ks-accent: #49c5a3;
|
|
11
|
+
--ks-shine: rgba(73, 197, 163, .55);
|
|
12
12
|
}
|
|
13
13
|
|
|
14
14
|
@media (prefers-color-scheme: dark) {
|
|
15
15
|
:root {
|
|
16
|
-
--
|
|
17
|
-
--
|
|
18
|
-
--
|
|
19
|
-
--
|
|
20
|
-
--
|
|
21
|
-
--
|
|
16
|
+
--ks-surface: #09090b;
|
|
17
|
+
--ks-soft: #18181b;
|
|
18
|
+
--ks-border: #27272a;
|
|
19
|
+
--ks-heading: #fafafa;
|
|
20
|
+
--ks-text: #a1a1aa;
|
|
21
|
+
--ks-muted: #52525b;
|
|
22
|
+
--ks-shine: rgba(73, 197, 163, .40);
|
|
22
23
|
}
|
|
23
24
|
}
|
|
24
25
|
|
|
@@ -28,14 +29,13 @@ body {
|
|
|
28
29
|
display: flex;
|
|
29
30
|
align-items: center;
|
|
30
31
|
justify-content: center;
|
|
31
|
-
background: var(--
|
|
32
|
-
color: var(--
|
|
32
|
+
background: var(--ks-surface);
|
|
33
|
+
color: var(--ks-text);
|
|
33
34
|
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
34
|
-
|
|
35
|
+
padding: 2rem 1.5rem;
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
#app {
|
|
38
39
|
width: 100%;
|
|
39
|
-
max-width:
|
|
40
|
-
padding: 1.5rem;
|
|
40
|
+
max-width: 720px;
|
|
41
41
|
}
|