vue-kaspa-cli 0.1.9 → 0.1.11
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/_package.json +1 -0
- package/templates/nuxt/app/app.vue +6 -1
- package/templates/nuxt/app/components/KaspaStatus.vue +120 -111
- package/templates/vue/_package.json +1 -0
- package/templates/vue/src/components/KaspaStatus.vue +117 -76
- package/templates/vue/src/style.css +6 -1
package/package.json
CHANGED
|
@@ -31,7 +31,12 @@
|
|
|
31
31
|
body {
|
|
32
32
|
margin: 0;
|
|
33
33
|
min-height: 100vh;
|
|
34
|
-
background:
|
|
34
|
+
background:
|
|
35
|
+
radial-gradient(ellipse 60% 55% at 8% 100%, rgba(73, 197, 163, .32) 0%, transparent 55%),
|
|
36
|
+
radial-gradient(ellipse 60% 55% at 92% 100%, rgba(73, 197, 163, .32) 0%, transparent 55%),
|
|
37
|
+
radial-gradient(ellipse 80% 28% at 50% 100%, rgba(73, 197, 163, .18) 0%, transparent 60%),
|
|
38
|
+
var(--ks-surface);
|
|
39
|
+
background-attachment: fixed;
|
|
35
40
|
color: var(--ks-text);
|
|
36
41
|
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
37
42
|
}
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
3
|
-
|
|
3
|
+
import { Droplet, BookOpen, Search, ArrowUpRight, Heart, Copy, Check } from 'lucide-vue-next'
|
|
4
|
+
// useKaspa and useRpc are auto-imported by Nuxt.
|
|
4
5
|
|
|
5
6
|
const kaspa = useKaspa()
|
|
6
7
|
const rpc = useRpc()
|
|
7
8
|
const bento = ref<HTMLElement | null>(null)
|
|
8
9
|
const donateDialog = ref<HTMLDialogElement | null>(null)
|
|
10
|
+
const copied = ref(false)
|
|
11
|
+
|
|
12
|
+
const KASPA_ADDRESS = 'kaspa:qypr7ayn2g55fccyv9n6gf9zgrcnpepkfgjf9d8mtfp68ezv3mgqnggxqs902q4'
|
|
13
|
+
|
|
14
|
+
async function copyAddress() {
|
|
15
|
+
await navigator.clipboard.writeText(KASPA_ADDRESS)
|
|
16
|
+
copied.value = true
|
|
17
|
+
setTimeout(() => { copied.value = false }, 2000)
|
|
18
|
+
}
|
|
9
19
|
|
|
10
20
|
const stateLabel = computed(() => {
|
|
11
21
|
if (kaspa.wasmStatus.value === 'loading') return 'Loading WASM…'
|
|
@@ -31,12 +41,12 @@ const daaScore = computed(() =>
|
|
|
31
41
|
)
|
|
32
42
|
|
|
33
43
|
const links = [
|
|
34
|
-
{ label: 'Faucet', title: 'Testnet 10', desc: 'Get free test KAS', icon:
|
|
35
|
-
{ label: 'Faucet', title: 'Testnet 12', desc: 'Get free test KAS', icon:
|
|
36
|
-
{ label: 'Docs', title: 'vue-kaspa', desc: 'Read the full docs', icon:
|
|
37
|
-
{ label: 'Explorer', title: 'Testnet 10', desc: 'Browse transactions', icon:
|
|
38
|
-
{ label: 'Explorer', title: 'Testnet 12', desc: 'Browse transactions', icon:
|
|
39
|
-
{ label: 'Explorer', title: 'Mainnet', desc: 'Browse transactions', icon:
|
|
44
|
+
{ label: 'Faucet', title: 'Testnet 10', desc: 'Get free test KAS', icon: Droplet, href: 'https://faucet-tn10.kaspanet.io/' },
|
|
45
|
+
{ label: 'Faucet', title: 'Testnet 12', desc: 'Get free test KAS', icon: Droplet, href: 'https://faucet-tn12.kaspanet.io/' },
|
|
46
|
+
{ label: 'Docs', title: 'vue-kaspa', desc: 'Read the full docs', icon: BookOpen, href: 'https://vue-kaspa.vercel.app/' },
|
|
47
|
+
{ label: 'Explorer', title: 'Testnet 10', desc: 'Browse transactions', icon: Search, href: 'https://tn10.kaspa.stream/' },
|
|
48
|
+
{ label: 'Explorer', title: 'Testnet 12', desc: 'Browse transactions', icon: Search, href: 'https://tn12.kaspa.stream/' },
|
|
49
|
+
{ label: 'Explorer', title: 'Mainnet', desc: 'Browse transactions', icon: Search, href: 'https://kaspa.stream/' },
|
|
40
50
|
]
|
|
41
51
|
|
|
42
52
|
function onMouseMove(e: MouseEvent) {
|
|
@@ -59,7 +69,14 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
59
69
|
<button class="ks-dialog-close" @click="donateDialog?.close()">✕</button>
|
|
60
70
|
<p class="ks-dialog-title">Support vue-kaspa ❤️</p>
|
|
61
71
|
<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
|
-
<
|
|
72
|
+
<div class="ks-copy-wrap">
|
|
73
|
+
<code class="ks-dialog-addr">{{ KASPA_ADDRESS }}</code>
|
|
74
|
+
<button class="ks-copy-btn" :class="{ copied }" @click="copyAddress">
|
|
75
|
+
<Check v-if="copied" :size="13" />
|
|
76
|
+
<Copy v-else :size="13" />
|
|
77
|
+
{{ copied ? 'Copied!' : 'Copy' }}
|
|
78
|
+
</button>
|
|
79
|
+
</div>
|
|
63
80
|
<p class="ks-dialog-thanks">Thank you for your support 🙏</p>
|
|
64
81
|
</div>
|
|
65
82
|
</dialog>
|
|
@@ -83,74 +100,72 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
83
100
|
</svg>
|
|
84
101
|
</a>
|
|
85
102
|
<button class="ks-icon-btn" title="Support this project" @click="donateDialog?.showModal()">
|
|
86
|
-
<
|
|
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>
|
|
103
|
+
<Heart :size="18" />
|
|
89
104
|
</button>
|
|
90
105
|
</nav>
|
|
91
106
|
</header>
|
|
92
107
|
|
|
93
|
-
<!-- Bento grid
|
|
108
|
+
<!-- Bento grid -->
|
|
94
109
|
<div ref="bento" class="ks-grid">
|
|
95
110
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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>
|
|
111
|
+
<!-- Network card: col 1–2, row 1–3 -->
|
|
112
|
+
<div data-shine class="ks-shine ks-net-shine">
|
|
113
|
+
<div class="ks-card ks-net-card">
|
|
114
|
+
<div class="ks-net-top">
|
|
115
|
+
<span class="ks-net-icon">⬡</span>
|
|
121
116
|
<span
|
|
122
|
-
class="ks-
|
|
123
|
-
:style="`color:${
|
|
124
|
-
>{{
|
|
117
|
+
class="ks-badge"
|
|
118
|
+
:style="`border-color:${badgeColor};color:${badgeColor}`"
|
|
119
|
+
>{{ stateLabel }}</span>
|
|
120
|
+
</div>
|
|
121
|
+
<div class="ks-stats">
|
|
122
|
+
<div class="ks-stat">
|
|
123
|
+
<span class="ks-stat-label">Network</span>
|
|
124
|
+
<span class="ks-stat-value">{{ rpc.networkId.value ?? '—' }}</span>
|
|
125
|
+
</div>
|
|
126
|
+
<div class="ks-stat">
|
|
127
|
+
<span class="ks-stat-label">Server version</span>
|
|
128
|
+
<span class="ks-stat-value">{{ rpc.serverVersion.value ?? '—' }}</span>
|
|
129
|
+
</div>
|
|
130
|
+
<div class="ks-stat">
|
|
131
|
+
<span class="ks-stat-label">DAA Score</span>
|
|
132
|
+
<span class="ks-stat-value" style="font-family:monospace">{{ daaScore }}</span>
|
|
133
|
+
</div>
|
|
134
|
+
<div class="ks-stat">
|
|
135
|
+
<span class="ks-stat-label">Synced</span>
|
|
136
|
+
<span
|
|
137
|
+
class="ks-stat-value"
|
|
138
|
+
:style="`color:${rpc.isConnected.value ? (rpc.isSynced.value ? '#4caf50' : 'var(--ks-text)') : 'var(--ks-muted)'}`"
|
|
139
|
+
>{{ rpc.isConnected.value ? (rpc.isSynced.value ? 'Yes' : 'Syncing…') : '—' }}</span>
|
|
140
|
+
</div>
|
|
125
141
|
</div>
|
|
126
142
|
</div>
|
|
127
143
|
</div>
|
|
128
|
-
</div>
|
|
129
144
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
<!-- Link cards: auto-placed into Γ shape -->
|
|
146
|
+
<a
|
|
147
|
+
v-for="link in links"
|
|
148
|
+
:key="link.href"
|
|
149
|
+
data-shine
|
|
150
|
+
class="ks-shine ks-link-shine"
|
|
151
|
+
:href="link.href"
|
|
152
|
+
target="_blank"
|
|
153
|
+
rel="noopener"
|
|
154
|
+
>
|
|
155
|
+
<div class="ks-card ks-link-card">
|
|
156
|
+
<div class="ks-link-top">
|
|
157
|
+
<component :is="link.icon" :size="20" class="ks-link-icon" />
|
|
158
|
+
<ArrowUpRight :size="13" class="ks-link-arrow" />
|
|
159
|
+
</div>
|
|
160
|
+
<div>
|
|
161
|
+
<div class="ks-link-label">{{ link.label }}</div>
|
|
162
|
+
<div class="ks-link-title">{{ link.title }}</div>
|
|
163
|
+
<div class="ks-link-desc">{{ link.desc }}</div>
|
|
164
|
+
</div>
|
|
149
165
|
</div>
|
|
150
|
-
</
|
|
151
|
-
</a>
|
|
166
|
+
</a>
|
|
152
167
|
|
|
153
|
-
|
|
168
|
+
</div>
|
|
154
169
|
</div>
|
|
155
170
|
</template>
|
|
156
171
|
|
|
@@ -163,15 +178,10 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
163
178
|
}
|
|
164
179
|
|
|
165
180
|
/* Header */
|
|
166
|
-
.ks-header {
|
|
167
|
-
display: flex;
|
|
168
|
-
align-items: center;
|
|
169
|
-
gap: 0.6rem;
|
|
170
|
-
margin-bottom: 0.75rem;
|
|
171
|
-
}
|
|
181
|
+
.ks-header { display: flex; align-items: center; gap: .6rem; margin-bottom: .75rem; }
|
|
172
182
|
.ks-header-logo { width: 28px; height: 28px; object-fit: contain; }
|
|
173
183
|
.ks-header-brand { font-size: 1rem; font-weight: 700; color: var(--ks-heading); flex: 1; }
|
|
174
|
-
.ks-header-nav { display: flex; gap:
|
|
184
|
+
.ks-header-nav { display: flex; gap: .15rem; }
|
|
175
185
|
|
|
176
186
|
.ks-icon-btn {
|
|
177
187
|
display: inline-flex;
|
|
@@ -185,7 +195,7 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
185
195
|
color: var(--ks-muted);
|
|
186
196
|
cursor: pointer;
|
|
187
197
|
text-decoration: none;
|
|
188
|
-
transition: color
|
|
198
|
+
transition: color .15s, background .15s;
|
|
189
199
|
}
|
|
190
200
|
.ks-icon-btn:hover { color: var(--ks-heading); background: var(--ks-border); }
|
|
191
201
|
|
|
@@ -193,10 +203,10 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
193
203
|
.ks-grid {
|
|
194
204
|
display: grid;
|
|
195
205
|
grid-template-columns: repeat(3, 1fr);
|
|
196
|
-
gap:
|
|
206
|
+
gap: .75rem;
|
|
197
207
|
}
|
|
198
208
|
|
|
199
|
-
/* Shine wrapper
|
|
209
|
+
/* Shine wrapper */
|
|
200
210
|
.ks-shine {
|
|
201
211
|
padding: 1px;
|
|
202
212
|
border-radius: 14px;
|
|
@@ -208,19 +218,10 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
208
218
|
display: block;
|
|
209
219
|
text-decoration: none;
|
|
210
220
|
}
|
|
221
|
+
.ks-net-shine { grid-column: 1 / span 2; grid-row: 1 / span 3; }
|
|
211
222
|
|
|
212
|
-
/*
|
|
213
|
-
.ks-
|
|
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
|
-
}
|
|
223
|
+
/* Card base */
|
|
224
|
+
.ks-card { border-radius: 13px; background: var(--ks-soft); height: 100%; }
|
|
224
225
|
|
|
225
226
|
/* Network card */
|
|
226
227
|
.ks-net-card {
|
|
@@ -242,19 +243,8 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
242
243
|
}
|
|
243
244
|
.ks-stats { display: grid; grid-template-columns: 1fr 1fr; gap: 1.1rem; }
|
|
244
245
|
.ks-stat { display: flex; flex-direction: column; gap: .25rem; }
|
|
245
|
-
.ks-stat-label {
|
|
246
|
-
|
|
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
|
-
}
|
|
246
|
+
.ks-stat-label { font-size: .65rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ks-muted); }
|
|
247
|
+
.ks-stat-value { font-size: .95rem; color: var(--ks-heading); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
258
248
|
|
|
259
249
|
/* Link cards */
|
|
260
250
|
.ks-link-card {
|
|
@@ -262,19 +252,13 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
262
252
|
display: flex;
|
|
263
253
|
flex-direction: column;
|
|
264
254
|
justify-content: space-between;
|
|
265
|
-
gap:
|
|
255
|
+
gap: .5rem;
|
|
266
256
|
min-height: 100px;
|
|
267
257
|
}
|
|
268
258
|
.ks-link-top { display: flex; justify-content: space-between; align-items: flex-start; }
|
|
269
|
-
.ks-link-icon {
|
|
270
|
-
.ks-link-arrow {
|
|
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
|
-
}
|
|
259
|
+
.ks-link-icon { color: var(--ks-accent); }
|
|
260
|
+
.ks-link-arrow { color: var(--ks-muted); }
|
|
261
|
+
.ks-link-label { font-size: .58rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ks-muted); margin-bottom: .1rem; }
|
|
278
262
|
.ks-link-title { font-size: .875rem; font-weight: 600; color: var(--ks-heading); }
|
|
279
263
|
.ks-link-desc { font-size: .75rem; color: var(--ks-muted); margin-top: .1rem; }
|
|
280
264
|
|
|
@@ -283,8 +267,9 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
283
267
|
border: 1px solid var(--ks-border);
|
|
284
268
|
border-radius: 16px;
|
|
285
269
|
padding: 0;
|
|
286
|
-
|
|
287
|
-
width:
|
|
270
|
+
width: min(440px, calc(100vw - 2rem));
|
|
271
|
+
max-width: none;
|
|
272
|
+
overflow: hidden;
|
|
288
273
|
background: var(--ks-surface);
|
|
289
274
|
color: var(--ks-text);
|
|
290
275
|
box-shadow: 0 20px 60px rgba(0, 0, 0, .25);
|
|
@@ -305,6 +290,12 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
305
290
|
.ks-dialog-close:hover { color: var(--ks-heading); }
|
|
306
291
|
.ks-dialog-title { margin: 0 0 .75rem; font-size: 1.05rem; font-weight: 700; color: var(--ks-heading); }
|
|
307
292
|
.ks-dialog-body { font-size: .875rem; color: var(--ks-muted); margin: 0 0 1.25rem; line-height: 1.65; }
|
|
293
|
+
|
|
294
|
+
.ks-copy-wrap {
|
|
295
|
+
display: flex;
|
|
296
|
+
flex-direction: column;
|
|
297
|
+
gap: .5rem;
|
|
298
|
+
}
|
|
308
299
|
.ks-dialog-addr {
|
|
309
300
|
display: block;
|
|
310
301
|
padding: .6em .85em;
|
|
@@ -313,13 +304,31 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
313
304
|
border: 1px solid var(--ks-border);
|
|
314
305
|
font-size: .7rem;
|
|
315
306
|
word-break: break-all;
|
|
307
|
+
overflow-wrap: anywhere;
|
|
308
|
+
white-space: normal;
|
|
316
309
|
color: var(--ks-text);
|
|
317
310
|
}
|
|
311
|
+
.ks-copy-btn {
|
|
312
|
+
display: inline-flex;
|
|
313
|
+
align-items: center;
|
|
314
|
+
gap: .3rem;
|
|
315
|
+
align-self: flex-end;
|
|
316
|
+
padding: .35em .75em;
|
|
317
|
+
border-radius: 6px;
|
|
318
|
+
border: 1px solid var(--ks-border);
|
|
319
|
+
background: var(--ks-soft);
|
|
320
|
+
color: var(--ks-muted);
|
|
321
|
+
font-size: .75rem;
|
|
322
|
+
cursor: pointer;
|
|
323
|
+
transition: color .15s, border-color .15s;
|
|
324
|
+
}
|
|
325
|
+
.ks-copy-btn:hover { color: var(--ks-heading); border-color: var(--ks-heading); }
|
|
326
|
+
.ks-copy-btn.copied { color: #4caf50; border-color: #4caf50; }
|
|
327
|
+
|
|
318
328
|
.ks-dialog-thanks { font-size: .8rem; color: var(--ks-muted); margin: .75rem 0 0; text-align: center; }
|
|
319
329
|
</style>
|
|
320
330
|
|
|
321
331
|
<style>
|
|
322
|
-
/* ::backdrop can't receive scoped attribute — must be global */
|
|
323
332
|
.ks-dialog::backdrop {
|
|
324
333
|
background: rgba(0, 0, 0, .5);
|
|
325
334
|
backdrop-filter: blur(4px);
|
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
|
3
3
|
import { useKaspa, useRpc } from 'vue-kaspa'
|
|
4
|
+
import { Droplet, BookOpen, Search, ArrowUpRight, Heart, Copy, Check } from 'lucide-vue-next'
|
|
4
5
|
|
|
5
6
|
const kaspa = useKaspa()
|
|
6
7
|
const rpc = useRpc()
|
|
7
8
|
const bento = ref<HTMLElement | null>(null)
|
|
8
9
|
const donateDialog = ref<HTMLDialogElement | null>(null)
|
|
10
|
+
const copied = ref(false)
|
|
11
|
+
|
|
12
|
+
const KASPA_ADDRESS = 'kaspa:qypr7ayn2g55fccyv9n6gf9zgrcnpepkfgjf9d8mtfp68ezv3mgqnggxqs902q4'
|
|
13
|
+
|
|
14
|
+
async function copyAddress() {
|
|
15
|
+
await navigator.clipboard.writeText(KASPA_ADDRESS)
|
|
16
|
+
copied.value = true
|
|
17
|
+
setTimeout(() => { copied.value = false }, 2000)
|
|
18
|
+
}
|
|
9
19
|
|
|
10
20
|
const stateLabel = computed(() => {
|
|
11
21
|
if (kaspa.wasmStatus.value === 'loading') return 'Loading WASM…'
|
|
@@ -31,12 +41,12 @@ const daaScore = computed(() =>
|
|
|
31
41
|
)
|
|
32
42
|
|
|
33
43
|
const links = [
|
|
34
|
-
{ label: 'Faucet', title: 'Testnet 10', desc: 'Get free test KAS', icon:
|
|
35
|
-
{ label: 'Faucet', title: 'Testnet 12', desc: 'Get free test KAS', icon:
|
|
36
|
-
{ label: 'Docs', title: 'vue-kaspa', desc: 'Read the full docs', icon:
|
|
37
|
-
{ label: 'Explorer', title: 'Testnet 10', desc: 'Browse transactions', icon:
|
|
38
|
-
{ label: 'Explorer', title: 'Testnet 12', desc: 'Browse transactions', icon:
|
|
39
|
-
{ label: 'Explorer', title: 'Mainnet', desc: 'Browse transactions', icon:
|
|
44
|
+
{ label: 'Faucet', title: 'Testnet 10', desc: 'Get free test KAS', icon: Droplet, href: 'https://faucet-tn10.kaspanet.io/' },
|
|
45
|
+
{ label: 'Faucet', title: 'Testnet 12', desc: 'Get free test KAS', icon: Droplet, href: 'https://faucet-tn12.kaspanet.io/' },
|
|
46
|
+
{ label: 'Docs', title: 'vue-kaspa', desc: 'Read the full docs', icon: BookOpen, href: 'https://vue-kaspa.vercel.app/' },
|
|
47
|
+
{ label: 'Explorer', title: 'Testnet 10', desc: 'Browse transactions', icon: Search, href: 'https://tn10.kaspa.stream/' },
|
|
48
|
+
{ label: 'Explorer', title: 'Testnet 12', desc: 'Browse transactions', icon: Search, href: 'https://tn12.kaspa.stream/' },
|
|
49
|
+
{ label: 'Explorer', title: 'Mainnet', desc: 'Browse transactions', icon: Search, href: 'https://kaspa.stream/' },
|
|
40
50
|
]
|
|
41
51
|
|
|
42
52
|
function onMouseMove(e: MouseEvent) {
|
|
@@ -59,7 +69,14 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
59
69
|
<button class="dialog-close" @click="donateDialog?.close()">✕</button>
|
|
60
70
|
<p class="dialog-title">Support vue-kaspa ❤️</p>
|
|
61
71
|
<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
|
-
<
|
|
72
|
+
<div class="copy-wrap">
|
|
73
|
+
<code class="dialog-addr">{{ KASPA_ADDRESS }}</code>
|
|
74
|
+
<button class="copy-btn" :class="{ copied }" @click="copyAddress">
|
|
75
|
+
<Check v-if="copied" :size="13" />
|
|
76
|
+
<Copy v-else :size="13" />
|
|
77
|
+
{{ copied ? 'Copied!' : 'Copy' }}
|
|
78
|
+
</button>
|
|
79
|
+
</div>
|
|
63
80
|
<p class="dialog-thanks">Thank you for your support 🙏</p>
|
|
64
81
|
</div>
|
|
65
82
|
</dialog>
|
|
@@ -83,83 +100,75 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
83
100
|
</svg>
|
|
84
101
|
</a>
|
|
85
102
|
<button class="icon-btn" title="Support this project" @click="donateDialog?.showModal()">
|
|
86
|
-
<
|
|
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>
|
|
103
|
+
<Heart :size="18" />
|
|
89
104
|
</button>
|
|
90
105
|
</nav>
|
|
91
106
|
</header>
|
|
92
107
|
|
|
93
|
-
<!-- Bento grid
|
|
108
|
+
<!-- Bento grid -->
|
|
94
109
|
<div ref="bento" class="grid">
|
|
95
110
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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>
|
|
111
|
+
<!-- Network card: col 1–2, row 1–3 -->
|
|
112
|
+
<div data-shine class="shine net-shine">
|
|
113
|
+
<div class="card net-card">
|
|
114
|
+
<div class="net-top">
|
|
115
|
+
<span class="net-icon">⬡</span>
|
|
116
|
+
<span class="badge" :style="`border-color:${badgeColor};color:${badgeColor}`">{{ stateLabel }}</span>
|
|
118
117
|
</div>
|
|
119
|
-
<div class="
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
class="stat-value"
|
|
123
|
-
|
|
124
|
-
|
|
118
|
+
<div class="stats">
|
|
119
|
+
<div class="stat">
|
|
120
|
+
<span class="stat-label">Network</span>
|
|
121
|
+
<span class="stat-value">{{ rpc.networkId.value ?? '—' }}</span>
|
|
122
|
+
</div>
|
|
123
|
+
<div class="stat">
|
|
124
|
+
<span class="stat-label">Server version</span>
|
|
125
|
+
<span class="stat-value">{{ rpc.serverVersion.value ?? '—' }}</span>
|
|
126
|
+
</div>
|
|
127
|
+
<div class="stat">
|
|
128
|
+
<span class="stat-label">DAA Score</span>
|
|
129
|
+
<span class="stat-value mono">{{ daaScore }}</span>
|
|
130
|
+
</div>
|
|
131
|
+
<div class="stat">
|
|
132
|
+
<span class="stat-label">Synced</span>
|
|
133
|
+
<span
|
|
134
|
+
class="stat-value"
|
|
135
|
+
:style="`color:${rpc.isConnected.value ? (rpc.isSynced.value ? '#4caf50' : 'var(--ks-text)') : 'var(--ks-muted)'}`"
|
|
136
|
+
>{{ rpc.isConnected.value ? (rpc.isSynced.value ? 'Yes' : 'Syncing…') : '—' }}</span>
|
|
137
|
+
</div>
|
|
125
138
|
</div>
|
|
126
139
|
</div>
|
|
127
140
|
</div>
|
|
128
|
-
</div>
|
|
129
141
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
142
|
+
<!-- Link cards: auto-placed into Γ shape -->
|
|
143
|
+
<a
|
|
144
|
+
v-for="link in links"
|
|
145
|
+
:key="link.href"
|
|
146
|
+
data-shine
|
|
147
|
+
class="shine link-shine"
|
|
148
|
+
:href="link.href"
|
|
149
|
+
target="_blank"
|
|
150
|
+
rel="noopener"
|
|
151
|
+
>
|
|
152
|
+
<div class="card link-card">
|
|
153
|
+
<div class="link-top">
|
|
154
|
+
<component :is="link.icon" :size="20" class="link-icon" />
|
|
155
|
+
<ArrowUpRight :size="13" class="link-arrow" />
|
|
156
|
+
</div>
|
|
157
|
+
<div>
|
|
158
|
+
<div class="link-label">{{ link.label }}</div>
|
|
159
|
+
<div class="link-title">{{ link.title }}</div>
|
|
160
|
+
<div class="link-desc">{{ link.desc }}</div>
|
|
161
|
+
</div>
|
|
149
162
|
</div>
|
|
150
|
-
</
|
|
151
|
-
</a>
|
|
163
|
+
</a>
|
|
152
164
|
|
|
153
|
-
|
|
165
|
+
</div>
|
|
154
166
|
</div>
|
|
155
167
|
</template>
|
|
156
168
|
|
|
157
169
|
<style scoped>
|
|
158
170
|
/* Root */
|
|
159
|
-
.root {
|
|
160
|
-
width: 100%;
|
|
161
|
-
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
162
|
-
}
|
|
171
|
+
.root { width: 100%; font-family: Inter, system-ui, -apple-system, sans-serif; }
|
|
163
172
|
|
|
164
173
|
/* Header */
|
|
165
174
|
.header { display: flex; align-items: center; gap: .6rem; margin-bottom: .75rem; }
|
|
@@ -184,9 +193,13 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
184
193
|
.icon-btn:hover { color: var(--ks-heading); background: var(--ks-border); }
|
|
185
194
|
|
|
186
195
|
/* Grid */
|
|
187
|
-
.grid {
|
|
196
|
+
.grid {
|
|
197
|
+
display: grid;
|
|
198
|
+
grid-template-columns: repeat(3, 1fr);
|
|
199
|
+
gap: .75rem;
|
|
200
|
+
}
|
|
188
201
|
|
|
189
|
-
/* Shine wrapper
|
|
202
|
+
/* Shine wrapper */
|
|
190
203
|
.shine {
|
|
191
204
|
padding: 1px;
|
|
192
205
|
border-radius: 14px;
|
|
@@ -198,8 +211,6 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
198
211
|
display: block;
|
|
199
212
|
text-decoration: none;
|
|
200
213
|
}
|
|
201
|
-
|
|
202
|
-
/* Network card occupies col 1–2, row 1–3 */
|
|
203
214
|
.net-shine { grid-column: 1 / span 2; grid-row: 1 / span 3; }
|
|
204
215
|
|
|
205
216
|
/* Card base */
|
|
@@ -219,8 +230,8 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
219
230
|
/* Link cards */
|
|
220
231
|
.link-card { padding: 1rem; display: flex; flex-direction: column; justify-content: space-between; gap: .5rem; min-height: 100px; }
|
|
221
232
|
.link-top { display: flex; justify-content: space-between; align-items: flex-start; }
|
|
222
|
-
.link-icon {
|
|
223
|
-
.link-arrow {
|
|
233
|
+
.link-icon { color: var(--ks-accent); }
|
|
234
|
+
.link-arrow { color: var(--ks-muted); }
|
|
224
235
|
.link-label { font-size: .58rem; text-transform: uppercase; letter-spacing: .06em; color: var(--ks-muted); margin-bottom: .1rem; }
|
|
225
236
|
.link-title { font-size: .875rem; font-weight: 600; color: var(--ks-heading); }
|
|
226
237
|
.link-desc { font-size: .75rem; color: var(--ks-muted); margin-top: .1rem; }
|
|
@@ -230,8 +241,9 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
230
241
|
border: 1px solid var(--ks-border);
|
|
231
242
|
border-radius: 16px;
|
|
232
243
|
padding: 0;
|
|
233
|
-
|
|
234
|
-
width:
|
|
244
|
+
width: min(440px, calc(100vw - 2rem));
|
|
245
|
+
max-width: none;
|
|
246
|
+
overflow: hidden;
|
|
235
247
|
background: var(--ks-surface);
|
|
236
248
|
color: var(--ks-text);
|
|
237
249
|
box-shadow: 0 20px 60px rgba(0, 0, 0, .25);
|
|
@@ -241,12 +253,41 @@ onUnmounted(() => window.removeEventListener('mousemove', onMouseMove))
|
|
|
241
253
|
.dialog-close:hover { color: var(--ks-heading); }
|
|
242
254
|
.dialog-title { margin: 0 0 .75rem; font-size: 1.05rem; font-weight: 700; color: var(--ks-heading); }
|
|
243
255
|
.dialog-body { font-size: .875rem; color: var(--ks-muted); margin: 0 0 1.25rem; line-height: 1.65; }
|
|
244
|
-
|
|
256
|
+
|
|
257
|
+
.copy-wrap { display: flex; flex-direction: column; gap: .5rem; }
|
|
258
|
+
.dialog-addr {
|
|
259
|
+
display: block;
|
|
260
|
+
padding: .6em .85em;
|
|
261
|
+
border-radius: 8px;
|
|
262
|
+
background: var(--ks-soft);
|
|
263
|
+
border: 1px solid var(--ks-border);
|
|
264
|
+
font-size: .7rem;
|
|
265
|
+
word-break: break-all;
|
|
266
|
+
overflow-wrap: anywhere;
|
|
267
|
+
white-space: normal;
|
|
268
|
+
color: var(--ks-text);
|
|
269
|
+
}
|
|
270
|
+
.copy-btn {
|
|
271
|
+
display: inline-flex;
|
|
272
|
+
align-items: center;
|
|
273
|
+
gap: .3rem;
|
|
274
|
+
align-self: flex-end;
|
|
275
|
+
padding: .35em .75em;
|
|
276
|
+
border-radius: 6px;
|
|
277
|
+
border: 1px solid var(--ks-border);
|
|
278
|
+
background: var(--ks-soft);
|
|
279
|
+
color: var(--ks-muted);
|
|
280
|
+
font-size: .75rem;
|
|
281
|
+
cursor: pointer;
|
|
282
|
+
transition: color .15s, border-color .15s;
|
|
283
|
+
}
|
|
284
|
+
.copy-btn:hover { color: var(--ks-heading); border-color: var(--ks-heading); }
|
|
285
|
+
.copy-btn.copied { color: #4caf50; border-color: #4caf50; }
|
|
286
|
+
|
|
245
287
|
.dialog-thanks { font-size: .8rem; color: var(--ks-muted); margin: .75rem 0 0; text-align: center; }
|
|
246
288
|
</style>
|
|
247
289
|
|
|
248
290
|
<style>
|
|
249
|
-
/* ::backdrop can't receive scoped attribute — must be global */
|
|
250
291
|
.dialog::backdrop {
|
|
251
292
|
background: rgba(0, 0, 0, .5);
|
|
252
293
|
backdrop-filter: blur(4px);
|
|
@@ -29,7 +29,12 @@ body {
|
|
|
29
29
|
display: flex;
|
|
30
30
|
align-items: center;
|
|
31
31
|
justify-content: center;
|
|
32
|
-
background:
|
|
32
|
+
background:
|
|
33
|
+
radial-gradient(ellipse 60% 55% at 8% 100%, rgba(73, 197, 163, .32) 0%, transparent 55%),
|
|
34
|
+
radial-gradient(ellipse 60% 55% at 92% 100%, rgba(73, 197, 163, .32) 0%, transparent 55%),
|
|
35
|
+
radial-gradient(ellipse 80% 28% at 50% 100%, rgba(73, 197, 163, .18) 0%, transparent 60%),
|
|
36
|
+
var(--ks-surface);
|
|
37
|
+
background-attachment: fixed;
|
|
33
38
|
color: var(--ks-text);
|
|
34
39
|
font-family: Inter, system-ui, -apple-system, sans-serif;
|
|
35
40
|
padding: 2rem 1.5rem;
|