@meddleware/token-deployer-ui 0.0.1 → 0.0.3

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.
@@ -3,8 +3,7 @@ import { computed, ref, watch } from 'vue'
3
3
  import { useWallet, useNetwork } from '@meddleware/wallet-adapter'
4
4
  import { SELECTABLE_NETWORKS } from '../config.js'
5
5
  import type { Network } from '../lib/types.js'
6
- import FormSelect from './FormSelect.vue'
7
- import AppNotice from './AppNotice.vue'
6
+ import { UiSelect, UiNotice } from '@meddleware/ui'
8
7
 
9
8
  const { wallets, currentWallet, account, connecting, error, connect, disconnect } = useWallet()
10
9
  const { network, setNetwork } = useNetwork()
@@ -42,9 +41,9 @@ watch(() => account.value, (acc) => {
42
41
  <div class="row" style="justify-content: space-between">
43
42
  <div class="row">
44
43
  <label for="network-select" style="margin: 0">Network</label>
45
- <FormSelect id="network-select" v-model="networkModel">
44
+ <UiSelect id="network-select" v-model="networkModel">
46
45
  <option v-for="n in SELECTABLE_NETWORKS" :key="n" :value="n">{{ n }}</option>
47
- </FormSelect>
46
+ </UiSelect>
48
47
  </div>
49
48
 
50
49
  <div class="row">
@@ -68,7 +67,7 @@ watch(() => account.value, (acc) => {
68
67
  </div>
69
68
  </template>
70
69
 
71
- <AppNotice v-if="error" type="error" style="margin-top: 0.75rem">{{ error }}</AppNotice>
70
+ <UiNotice v-if="error" type="error" style="margin-top: 0.75rem">{{ error }}</UiNotice>
72
71
  </div>
73
72
 
74
73
  <dialog ref="dialogEl" aria-labelledby="wallet-dialog-title" @click.self="closeDialog">
@@ -86,9 +85,9 @@ watch(() => account.value, (acc) => {
86
85
 
87
86
  <div>
88
87
  <label for="dialog-network-select">Network</label>
89
- <FormSelect id="dialog-network-select" v-model="networkModel">
88
+ <UiSelect id="dialog-network-select" v-model="networkModel">
90
89
  <option v-for="n in SELECTABLE_NETWORKS" :key="n" :value="n">{{ n }}</option>
91
- </FormSelect>
90
+ </UiSelect>
92
91
  </div>
93
92
 
94
93
  <div class="wallet-list">
@@ -4,7 +4,7 @@ import { useWallet } from '@meddleware/wallet-adapter'
4
4
  import { getReadClient } from '../lib/readClient.js'
5
5
  import type { OwnedBlob, WalrusNetwork } from '../lib/walrus.js'
6
6
  import type { Network } from '../lib/types.js'
7
- import AppNotice from './AppNotice.vue'
7
+ import { UiNotice } from '@meddleware/ui'
8
8
 
9
9
  const props = defineProps<{ network: WalrusNetwork; connected: boolean }>()
10
10
  const emit = defineEmits<{ (e: 'select', url: string): void }>()
@@ -84,7 +84,7 @@ function selectBlob(item: BlobItem): void {
84
84
  </div>
85
85
  <p class="hint">Browse Walrus blobs owned by this address. Only image blobs show a preview.</p>
86
86
 
87
- <AppNotice v-if="loadError" type="error">{{ loadError }}</AppNotice>
87
+ <UiNotice v-if="loadError" type="error">{{ loadError }}</UiNotice>
88
88
 
89
89
  <p v-if="hasLoaded && blobs.length === 0 && !loading" class="hint">
90
90
  No Walrus blobs found at this address.
@@ -7,7 +7,7 @@ import { ICON_EPOCHS } from '../lib/walrus-constants.js'
7
7
  import { ICON_MAX_BYTES, validateIconFile } from '../config.js'
8
8
  import type { WalrusNetwork } from '../lib/walrus.js'
9
9
  import type { Network } from '../lib/types.js'
10
- import AppNotice from './AppNotice.vue'
10
+ import { UiNotice } from '@meddleware/ui'
11
11
  // Vite serves the Walrus wasm as an asset URL (lightweight import — just the URL).
12
12
  import walrusWasmUrl from '@mysten/walrus-wasm/web/walrus_wasm_bg.wasm?url'
13
13
 
@@ -185,9 +185,9 @@ async function upload(): Promise<void> {
185
185
  </fieldset>
186
186
 
187
187
  <!-- Cost estimation (shown after file selection) -->
188
- <AppNotice v-if="fileName && estimatedCost" style="margin: 0.75rem 0">
188
+ <UiNotice v-if="fileName && estimatedCost" style="margin: 0.75rem 0">
189
189
  <strong>Estimated cost:</strong> {{ estimatedCost.label }}
190
- </AppNotice>
190
+ </UiNotice>
191
191
 
192
192
  <!-- Public relay notice -->
193
193
  <p v-if="showFreeRelayTip" class="hint" style="margin: 0.75rem 0">
@@ -200,6 +200,6 @@ async function upload(): Promise<void> {
200
200
  stored for ~{{ ICON_YEARS }} years.
201
201
  <span v-if="status">{{ status }}</span>
202
202
  </p>
203
- <AppNotice v-if="error" type="error">{{ error }}</AppNotice>
203
+ <UiNotice v-if="error" type="error">{{ error }}</UiNotice>
204
204
  </div>
205
205
  </template>
package/src/index.ts CHANGED
@@ -1 +1,2 @@
1
+ import './component-styles.css'
1
2
  export { default as TokenDeployerView } from './components/TokenDeployerView.vue'
package/src/main.ts CHANGED
@@ -1,8 +1,12 @@
1
+ import '@meddleware/design-tokens/tokens.css'
2
+ import '@meddleware/design-tokens/seasons.css'
3
+ import '@meddleware/ui/base.css'
1
4
  import { createApp } from 'vue'
2
5
  import App from './App.vue'
3
6
  import './styles.css'
7
+ import './component-styles.css'
4
8
  import { configureWasm } from './lib/template.js'
5
- import { useColorMode } from './composables/useColorMode.js'
9
+ import { useColorMode } from '@meddleware/ui'
6
10
 
7
11
  // Apply the colour mode before mount so there is no theme flash. Defaults to
8
12
  // `system` (follows the OS) while enabling the in-app light/dark/system control.
package/src/styles.css CHANGED
@@ -1,64 +1,7 @@
1
- /* Accessible, lightweight styling. System fonts, dark/light aware, high-contrast
2
- focus rings, and reduced-motion respect. No heavy CSS framework. */
1
+ /* SPA shell globals — imported by main.ts only (standalone SPA mode).
2
+ Token variables come from @meddleware/design-tokens, imported before this.
3
+ Component utility classes live in component-styles.css. */
3
4
 
4
- /* Oxblood / Indigo (masonic) palette — vendored mirror of the shared design
5
- system for visual parity with the dashboard. The repo is self-contained
6
- (no cross-repo links), so the token VALUES are copied here.
7
- TODO(unify): consume `@meddleware/design-tokens` once it is published.
8
- Dark mode is driven by `data-theme` (see composables/useColorMode.ts, which
9
- also enables the light/dark/system control); the `:not([data-theme])` media
10
- block preserves OS-follow before that runs (anti-flash). */
11
- :root {
12
- --bg: #f7f4f1;
13
- --surface: #ffffff;
14
- --border: #ded6cf;
15
- --text: #201b19;
16
- --muted: #6e635c;
17
- --accent: #5e1622;
18
- --accent-contrast: #ffffff;
19
- --danger: #b3261e;
20
- --ok: #1c5e4a;
21
- --radius: 10px;
22
- --lift: #efeae6;
23
- color-scheme: light;
24
- }
25
-
26
- :root[data-theme="dark"] {
27
- --bg: #120e10;
28
- --surface: #1c1618;
29
- --border: #372b2e;
30
- --text: #f2eae6;
31
- --muted: #b7a9a3;
32
- --accent: #b0465a;
33
- --accent-contrast: #ffffff;
34
- --danger: #f08a7e;
35
- --ok: #5bb392;
36
- --lift: #241c1f;
37
- color-scheme: dark;
38
- }
39
-
40
- /* Pre-JS OS-follow (only until useColorMode sets an explicit data-theme). */
41
- @media (prefers-color-scheme: dark) {
42
- :root:not([data-theme]) {
43
- --bg: #120e10;
44
- --surface: #1c1618;
45
- --border: #372b2e;
46
- --text: #f2eae6;
47
- --muted: #b7a9a3;
48
- --accent: #b0465a;
49
- --accent-contrast: #ffffff;
50
- --danger: #f08a7e;
51
- --ok: #5bb392;
52
- --lift: #241c1f;
53
- color-scheme: dark;
54
- }
55
- }
56
-
57
- * {
58
- box-sizing: border-box;
59
- }
60
-
61
- /* Top-right colour-mode control row (parity with the dashboard). */
62
5
  .theme-bar {
63
6
  display: flex;
64
7
  justify-content: flex-end;
@@ -101,284 +44,9 @@ p.lede {
101
44
  color: var(--muted);
102
45
  font-size: 1.05rem;
103
46
  }
104
- a {
105
- color: var(--accent);
106
- }
107
-
108
- .card {
109
- background: var(--surface);
110
- border: 1px solid var(--border);
111
- border-radius: var(--radius);
112
- padding: 1.25rem;
113
- margin: 1.5rem 0 1rem;
114
- border-top: 2px solid var(--border);
115
- }
116
-
117
- label {
118
- display: block;
119
- font-weight: 600;
120
- margin: 0.9rem 0 0.3rem;
121
- }
122
- .hint {
123
- color: var(--muted);
124
- font-weight: 400;
125
- font-size: 0.9rem;
126
- }
127
-
128
- input,
129
- select,
130
- textarea {
131
- width: 100%;
132
- padding: 0.6rem 0.7rem;
133
- font: inherit;
134
- color: var(--text);
135
- background: var(--bg);
136
- border: 1px solid var(--border);
137
- border-radius: 8px;
138
- }
139
- textarea {
140
- min-height: 4rem;
141
- resize: vertical;
142
- }
143
-
144
- input[aria-invalid='true'],
145
- select[aria-invalid='true'],
146
- textarea[aria-invalid='true'] {
147
- border-color: var(--danger);
148
- }
149
-
150
- .field-error {
151
- color: var(--danger);
152
- font-size: 0.88rem;
153
- margin-top: 0.3rem;
154
- }
155
-
156
- :focus-visible {
157
- outline: 3px solid var(--accent);
158
- outline-offset: 2px;
159
- }
160
-
161
- button {
162
- font: inherit;
163
- font-weight: 600;
164
- padding: 0.6rem 1rem;
165
- border-radius: 8px;
166
- border: 1px solid var(--border);
167
- background: var(--surface);
168
- color: var(--text);
169
- cursor: pointer;
170
- }
171
- button.primary {
172
- background: var(--accent);
173
- color: var(--accent-contrast);
174
- border-color: transparent;
175
- }
176
- button:disabled {
177
- opacity: 0.55;
178
- cursor: not-allowed;
179
- }
180
-
181
- .row {
182
- display: flex;
183
- gap: 0.75rem;
184
- flex-wrap: wrap;
185
- align-items: center;
186
- }
187
- .grid2 {
188
- display: grid;
189
- grid-template-columns: 1fr;
190
- gap: 0 1rem;
191
- }
192
- @media (min-width: 520px) {
193
- .grid2 {
194
- grid-template-columns: 1fr 1fr;
195
- }
196
- }
197
-
198
- fieldset {
199
- background: var(--lift);
200
- border: 1px solid var(--border);
201
- border-radius: 8px;
202
- padding: 0.5rem 1rem 1rem;
203
- margin: 1rem 0;
204
- }
205
- legend {
206
- font-weight: 700;
207
- padding: 0 0.4rem;
208
- }
209
-
210
- details {
211
- margin: 1rem 0;
212
- }
213
-
214
- details > summary {
215
- color: var(--accent);
216
- font-weight: 700;
217
- margin: 0.5rem 0;
218
- padding: 0.75rem;
219
- background: var(--lift);
220
- border-radius: 8px;
221
- border: 1px solid transparent;
222
- transition: border-color 0.2s ease;
223
- }
224
-
225
- details > summary:hover {
226
- border-color: var(--border);
227
- }
228
-
229
- details[open] > summary {
230
- border-color: var(--border);
231
- margin-bottom: 1rem;
232
- }
233
-
234
- details > fieldset {
235
- margin-top: 0;
236
- }
237
-
238
- .permanent {
239
- color: var(--danger);
240
- font-weight: 600;
241
- }
242
- .mono {
243
- font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
244
- word-break: break-all;
245
- }
246
- .badge {
247
- display: inline-block;
248
- font-size: 0.8rem;
249
- padding: 0.15rem 0.5rem;
250
- border-radius: 999px;
251
- background: var(--border);
252
- }
253
-
254
- .visually-hidden {
255
- position: absolute;
256
- width: 1px;
257
- height: 1px;
258
- padding: 0;
259
- margin: -1px;
260
- overflow: hidden;
261
- clip: rect(0 0 0 0);
262
- white-space: nowrap;
263
- border: 0;
264
- }
265
-
266
- /* Keyboard skip link: hidden until focused, then visible at the top-left. */
267
- .skip-link {
268
- position: absolute;
269
- left: 0.5rem;
270
- top: -3rem;
271
- z-index: 100;
272
- background: var(--accent);
273
- color: #fff;
274
- padding: 0.5rem 0.9rem;
275
- border-radius: 6px;
276
- text-decoration: none;
277
- transition: top 0.15s ease;
278
- }
279
- .skip-link:focus {
280
- top: 0.5rem;
281
- }
282
- @media (prefers-reduced-motion: reduce) {
283
- .skip-link {
284
- transition: none;
285
- }
286
- }
287
-
288
- .steps {
289
- list-style: none;
290
- padding: 0;
291
- }
292
- .steps li {
293
- padding: 0.35rem 0;
294
- color: var(--muted);
295
- }
296
- .steps li[data-state='active'] {
297
- color: var(--text);
298
- font-weight: 600;
299
- }
300
- .steps li[data-state='done'] {
301
- color: var(--ok);
302
- }
303
-
304
- .spinner {
305
- width: 1em;
306
- height: 1em;
307
- border: 2px solid currentColor;
308
- border-right-color: transparent;
309
- border-radius: 50%;
310
- display: inline-block;
311
- animation: spin 0.7s linear infinite;
312
- vertical-align: -0.15em;
313
- }
314
- @keyframes spin {
315
- to {
316
- transform: rotate(360deg);
317
- }
318
- }
319
- @media (prefers-reduced-motion: reduce) {
320
- .spinner {
321
- animation: none;
322
- }
323
- * {
324
- scroll-behavior: auto;
325
- }
326
- }
327
47
 
328
48
  footer {
329
49
  border-top: 1px solid var(--border);
330
50
  padding-top: 1.5rem;
331
51
  margin-top: 2rem;
332
52
  }
333
-
334
- .notice {
335
- border-left: 4px solid var(--accent);
336
- padding: 0.5rem 0.75rem;
337
- background: var(--surface);
338
- border-radius: 6px;
339
- }
340
- .notice.error {
341
- border-color: var(--danger);
342
- }
343
- .notice.ok {
344
- border-color: var(--ok);
345
- }
346
-
347
- dialog {
348
- background: var(--surface);
349
- border: 1px solid var(--border);
350
- border-radius: var(--radius);
351
- padding: 1.5rem;
352
- width: min(420px, 90vw);
353
- max-height: 90vh;
354
- overflow-y: auto;
355
- color: var(--text);
356
- }
357
- dialog::backdrop {
358
- background: rgba(0, 0, 0, 0.45);
359
- -webkit-backdrop-filter: blur(2px);
360
- backdrop-filter: blur(2px);
361
- }
362
- .dialog-header {
363
- display: flex;
364
- justify-content: space-between;
365
- align-items: center;
366
- margin-bottom: 1.25rem;
367
- }
368
- .dialog-header h2 {
369
- margin: 0;
370
- font-size: 1.1rem;
371
- }
372
- .wallet-list {
373
- display: flex;
374
- flex-direction: column;
375
- gap: 0.5rem;
376
- margin-top: 1rem;
377
- }
378
- .wallet-list button {
379
- display: flex;
380
- align-items: center;
381
- gap: 0.6rem;
382
- width: 100%;
383
- justify-content: flex-start;
384
- }
@@ -32,7 +32,7 @@ const SOURCE_OF: Record<string, string> = {
32
32
  'Move.toml': 'Move.toml',
33
33
  'source.move': 'sources/sui_token_template.move',
34
34
  'scripts/publish.sh': 'templates/publish.sh',
35
- '.gitignore': 'templates/.gitignore',
35
+ '.gitignore': 'templates/gitignore',
36
36
  'README.md': 'templates/README.md',
37
37
  'deployments.md': 'templates/deployments.md',
38
38
  'CLAUDE.md': 'templates/CLAUDE.md',
package/.env.production DELETED
@@ -1,7 +0,0 @@
1
- # Committed defaults for the standalone SPA production build (auto-loaded by `vite build`).
2
- # These are public Sui addresses — not secrets — so they are safe to commit.
3
- # Override at deploy time via Cloudflare Pages environment variables (takes precedence).
4
- # Local dev: copy .env.example to .env.local and edit there (never commit .env.local).
5
-
6
- VITE_FEE_TREASURY_TESTNET=0xe6b2810abfc5a6f37a375f73e3ba76cfc37584196e453255ad3c9ca2f0ede0ed
7
- VITE_FEE_TREASURY_MAINNET=0xe6b2810abfc5a6f37a375f73e3ba76cfc37584196e453255ad3c9ca2f0ede0ed
@@ -1,15 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
-
4
- const props = defineProps<{ type?: 'info' | 'error' | 'ok' }>()
5
-
6
- const role = computed(() => {
7
- if (props.type === 'error') return 'alert'
8
- if (props.type === 'ok') return 'status'
9
- return undefined
10
- })
11
- </script>
12
-
13
- <template>
14
- <p :class="['notice', type && type !== 'info' ? type : '']" :role="role"><slot /></p>
15
- </template>
@@ -1,81 +0,0 @@
1
- <script setup lang="ts">
2
- import type { ColorMode } from '../composables/useColorMode'
3
-
4
- /**
5
- * Presentational light/dark/system control. Owns no state — bind with `v-model`
6
- * to external state (`useColorMode`). Vendored mirror of `@meddleware/ui`'s
7
- * `ColorModeControl` for parity with the dashboard.
8
- * TODO(unify): consume `@meddleware/ui` once published.
9
- */
10
- withDefaults(
11
- defineProps<{
12
- modelValue: ColorMode
13
- modes?: ColorMode[]
14
- }>(),
15
- { modes: () => ['light', 'dark', 'system'] },
16
- )
17
-
18
- defineEmits<{ 'update:modelValue': [ColorMode] }>()
19
-
20
- const LABEL: Record<ColorMode, string> = { light: 'Light', dark: 'Dark', system: 'System' }
21
- const GLYPH: Record<ColorMode, string> = { light: '☀', dark: '☾', system: '◐' }
22
- </script>
23
-
24
- <template>
25
- <div class="mode-control" role="group" aria-label="Colour mode">
26
- <button
27
- v-for="m in modes"
28
- :key="m"
29
- type="button"
30
- class="mode-control__btn"
31
- :class="{ 'is-active': modelValue === m }"
32
- :aria-pressed="modelValue === m"
33
- :title="LABEL[m]"
34
- @click="$emit('update:modelValue', m)"
35
- >
36
- <span aria-hidden="true">{{ GLYPH[m] }}</span>
37
- <span class="mode-control__label">{{ LABEL[m] }}</span>
38
- </button>
39
- </div>
40
- </template>
41
-
42
- <style scoped>
43
- .mode-control {
44
- display: inline-flex;
45
- gap: 2px;
46
- padding: 2px;
47
- border: 1px solid var(--border);
48
- border-radius: var(--radius);
49
- background: var(--surface);
50
- }
51
- .mode-control__btn {
52
- display: inline-flex;
53
- align-items: center;
54
- gap: 0.35rem;
55
- padding: 0.25rem 0.5rem;
56
- border: 0;
57
- border-radius: calc(var(--radius) - 3px);
58
- background: transparent;
59
- color: var(--text);
60
- font: inherit;
61
- font-size: 0.8rem;
62
- cursor: pointer;
63
- opacity: 0.7;
64
- }
65
- .mode-control__btn:hover {
66
- opacity: 1;
67
- }
68
- .mode-control__btn.is-active {
69
- opacity: 1;
70
- background: var(--lift);
71
- }
72
- .mode-control__btn:focus-visible {
73
- outline: 2px solid var(--accent);
74
- outline-offset: 1px;
75
- }
76
- @media (max-width: 640px) {
77
- .mode-control__label {
78
- display: none;
79
- }
80
- }
81
- </style>
@@ -1,79 +0,0 @@
1
- <script setup lang="ts">
2
- defineProps<{ fieldId: string }>()
3
-
4
- function onKeyDown(e: KeyboardEvent) {
5
- if (e.key === 'Escape') (e.currentTarget as HTMLElement).blur()
6
- }
7
- </script>
8
-
9
- <template>
10
- <span class="field-hint">
11
- <button
12
- type="button"
13
- class="field-hint-btn"
14
- :aria-describedby="`${fieldId}-hint`"
15
- aria-label="Help"
16
- @keydown="onKeyDown"
17
- >?</button>
18
- <span :id="`${fieldId}-hint`" role="tooltip" class="field-hint-tip">
19
- <slot />
20
- </span>
21
- </span>
22
- </template>
23
-
24
- <style scoped>
25
- .field-hint {
26
- position: relative;
27
- display: inline-block;
28
- margin-left: 0.35rem;
29
- vertical-align: middle;
30
- }
31
-
32
- .field-hint-btn {
33
- display: inline-flex;
34
- align-items: center;
35
- justify-content: center;
36
- width: 1.1em;
37
- height: 1.1em;
38
- padding: 0;
39
- font-size: 0.75rem;
40
- font-weight: 700;
41
- line-height: 1;
42
- border-radius: 50%;
43
- border: 1px solid var(--muted);
44
- color: var(--muted);
45
- background: transparent;
46
- cursor: help;
47
- }
48
-
49
- .field-hint-tip {
50
- position: absolute;
51
- top: calc(100% + 6px);
52
- left: 0;
53
- z-index: 10;
54
- min-width: 200px;
55
- max-width: min(300px, 90vw);
56
- padding: 0.6rem 0.75rem;
57
- background: var(--surface);
58
- border: 1px solid var(--border);
59
- border-radius: 8px;
60
- font-size: 0.85rem;
61
- font-weight: 400;
62
- line-height: 1.5;
63
- color: var(--text);
64
- box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
65
- visibility: hidden;
66
- opacity: 0;
67
- transition:
68
- opacity 0.15s ease,
69
- visibility 0.15s ease;
70
- pointer-events: none;
71
- }
72
-
73
- .field-hint:hover .field-hint-tip,
74
- .field-hint:focus-within .field-hint-tip {
75
- visibility: visible;
76
- opacity: 1;
77
- pointer-events: auto;
78
- }
79
- </style>
@@ -1,28 +0,0 @@
1
- <script setup lang="ts">
2
- import { computed } from 'vue'
3
-
4
- const props = defineProps<{
5
- id: string
6
- label: string
7
- error?: string | null
8
- hint?: string
9
- }>()
10
-
11
- const errorId = computed(() => `${props.id}-err`)
12
- const slotAttrs = computed(() => ({
13
- id: props.id,
14
- 'aria-invalid': props.error ? (true as const) : undefined,
15
- 'aria-describedby': props.error ? errorId.value : undefined,
16
- }))
17
- </script>
18
-
19
- <template>
20
- <div class="form-field">
21
- <label :for="id">
22
- {{ label }}<slot name="label-suffix" />
23
- </label>
24
- <slot :attrs="slotAttrs" />
25
- <p v-if="hint && !error" class="hint">{{ hint }}</p>
26
- <p v-if="error" :id="errorId" class="field-error">{{ error }}</p>
27
- </div>
28
- </template>