@meddleware/token-deployer-ui 0.0.8 → 0.0.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/src/styles.css CHANGED
@@ -2,12 +2,18 @@
2
2
  Token variables come from @meddleware/design-tokens, imported before this.
3
3
  Component utility classes live in component-styles.css. */
4
4
 
5
- .theme-bar {
6
- display: flex;
7
- justify-content: flex-end;
5
+ /* The colour-mode control is the page header's first grid item, aligned to the inline end. */
6
+ .theme-toggle {
7
+ justify-self: end;
8
8
  margin-bottom: 0.75rem;
9
9
  }
10
10
 
11
+ .page-footer {
12
+ margin-top: 3rem;
13
+ color: var(--muted);
14
+ font-size: 0.9rem;
15
+ }
16
+
11
17
  body {
12
18
  margin: 0;
13
19
  background: var(--bg);
@@ -21,12 +27,14 @@ body {
21
27
  }
22
28
 
23
29
  main {
24
- max-width: 760px;
30
+ max-width: var(--mw-tool-content-max);
25
31
  margin: 0 auto;
26
32
  padding: 1.5rem 1.25rem 4rem;
27
33
  }
28
34
 
29
- header {
35
+ /* A single-column grid so the colour-mode control can sit at the inline end without a wrapper. */
36
+ .page-header {
37
+ display: grid;
30
38
  padding-bottom: 1.5rem;
31
39
  border-bottom: 1px solid var(--border);
32
40
  margin-bottom: 1.5rem;
@@ -0,0 +1,33 @@
1
+ // Stylelint over the library's CSS and the <style> blocks of its SFCs.
2
+ // postcss-html provides the custom syntax needed to lint Vue single-file components.
3
+ export default {
4
+ extends: ['stylelint-config-standard'],
5
+ overrides: [{ files: ['**/*.vue'], customSyntax: 'postcss-html' }],
6
+ rules: {
7
+ // The design system leans heavily on custom properties and deliberate
8
+ // ordering; these standard rules are too noisy for a token-driven codebase.
9
+ 'custom-property-empty-line-before': null,
10
+ 'declaration-empty-line-before': null,
11
+ 'comment-empty-line-before': null,
12
+ 'rule-empty-line-before': null,
13
+ 'at-rule-empty-line-before': null,
14
+ 'no-descending-specificity': null,
15
+ // BEM-style class names (block__element--modifier) are intentional.
16
+ 'selector-class-pattern': null,
17
+ // Some presentational SFCs use compact single-line multi-declaration rules.
18
+ 'declaration-block-single-line-max-declarations': null,
19
+ // Preserve camelCase CSS keyword values (e.g. text-rendering: optimizeLegibility).
20
+ 'value-keyword-case': null,
21
+ // Vue SFC scoped-style pseudo-classes.
22
+ 'selector-pseudo-class-no-unknown': [
23
+ true,
24
+ { ignorePseudoClasses: ['deep', 'slotted', 'global'] },
25
+ ],
26
+ // Private panel vars use a leading-underscore convention (--_bg, --_text).
27
+ 'custom-property-pattern': null,
28
+ // Vendor-prefixed properties are emitted by autoprefixer at build time; the
29
+ // hand-written -webkit-/-moz- font-smoothing pair is intentional.
30
+ 'property-no-vendor-prefix': null,
31
+ 'value-no-vendor-prefix': null,
32
+ },
33
+ }
@@ -1,4 +1,4 @@
1
1
  {
2
- "status": "passed",
2
+ "status": "failed",
3
3
  "failedTests": []
4
4
  }
package/typedoc.json CHANGED
@@ -13,8 +13,8 @@
13
13
  "src/lib/github.ts",
14
14
  "src/lib/walrus.ts",
15
15
  "src/lib/walrus-constants.ts",
16
- "src/composables/useWallet.ts",
17
- "src/composables/useSuiClient.ts"
16
+ "src/composables/useAccessGate.ts",
17
+ "src/composables/useWalrusRelay.ts"
18
18
  ],
19
19
  "out": "docs/api",
20
20
  "readme": "none",
@@ -1,205 +0,0 @@
1
- <script setup lang="ts">
2
- import { ref, onMounted, computed } from 'vue'
3
- import { useWallet } from '@meddleware/wallet-adapter'
4
- import { buildDeployExecutor } from '../lib/deployExecutor.js'
5
- import { useWalrusRelay } from '../composables/useWalrusRelay.js'
6
- import { ICON_EPOCHS } from '../lib/walrus-constants.js'
7
- import { ICON_MAX_BYTES, validateIconFile } from '../config.js'
8
- import type { WalrusNetwork } from '../lib/walrus.js'
9
- import type { Network } from '../lib/types.js'
10
- import { UiNotice } from '@meddleware/ui'
11
- // Vite serves the Walrus wasm as an asset URL (lightweight import — just the URL).
12
- import walrusWasmUrl from '@mysten/walrus-wasm/web/walrus_wasm_bg.wasm?url'
13
-
14
- const props = defineProps<{ network: WalrusNetwork; connected: boolean }>()
15
- const emit = defineEmits<{ (e: 'uploaded', url: string): void }>()
16
-
17
- const { account } = useWallet()
18
- const {
19
- selectedRelayHost,
20
- availableRelays,
21
- estimatedCost,
22
- fileSizeBytes,
23
- checkOperatorRelayHealth,
24
- } = useWalrusRelay(props.network)
25
-
26
- const uploading = ref(false)
27
- const status = ref('')
28
- const error = ref<string | null>(null)
29
- const fileName = ref('')
30
- let bytes: Uint8Array | null = null
31
-
32
- const maxSizeLabel = `${Math.round(ICON_MAX_BYTES / 1024)} KB`
33
- const ICON_YEARS = Math.round(ICON_EPOCHS / 26)
34
-
35
- onMounted(() => {
36
- checkOperatorRelayHealth()
37
- })
38
-
39
- const showPublicRelayNotice = computed(() => availableRelays.value.length > 1)
40
-
41
- const showFreeRelayTip = computed(() => {
42
- const publicHost = availableRelays.value.find((r) => r.isPublic)?.host
43
- return showPublicRelayNotice.value && selectedRelayHost.value !== publicHost
44
- })
45
-
46
- function onFile(e: Event): void {
47
- error.value = null
48
- bytes = null
49
- fileName.value = ''
50
- const f = (e.target as HTMLInputElement).files?.[0]
51
- if (!f) return
52
-
53
- // UX/cost guard only — the authoritative cap is the edge (relay) body limit.
54
- const invalid = validateIconFile(f)
55
- if (invalid) {
56
- error.value = invalid
57
- return
58
- }
59
-
60
- fileName.value = f.name
61
- fileSizeBytes.value = f.size
62
- const reader = new FileReader()
63
- reader.onload = () => {
64
- bytes = new Uint8Array(reader.result as ArrayBuffer)
65
- }
66
- reader.readAsArrayBuffer(f)
67
- }
68
-
69
- function onRelayChange(newHost: string): void {
70
- selectedRelayHost.value = newHost
71
- }
72
-
73
- async function upload(): Promise<void> {
74
- if (!bytes) {
75
- error.value = 'Choose an image first.'
76
- return
77
- }
78
- if (!account.value) {
79
- error.value = 'Connect your wallet first.'
80
- return
81
- }
82
- uploading.value = true
83
- error.value = null
84
- try {
85
- // Lazily load the heavy @mysten/walrus code and Sui client only on demand.
86
- const { createWalrusClient, createBlobUploadFlow, walrusBlobUrl } =
87
- await import('../lib/walrus.js')
88
- const { getReadClient } = await import('../lib/readClient.js')
89
- const executor = await buildDeployExecutor(props.network as Network)
90
- const suiClient = getReadClient(props.network as Network)
91
- const client = createWalrusClient(props.network, {
92
- wasmUrl: walrusWasmUrl,
93
- uploadRelayHost: selectedRelayHost.value,
94
- })
95
- const flow = createBlobUploadFlow(client, bytes)
96
-
97
- status.value = 'Encoding…'
98
- await flow.encode()
99
-
100
- // Disclose the relay fee before asking for a signature. `calculateUploadRelayTip`
101
- // takes the UNENCODED size and throws if the tip exceeds the client max guard.
102
- let feeNote = ''
103
- try {
104
- const tipMist = await client.walrus.calculateUploadRelayTip({ size: bytes.length })
105
- const tipSui = Number(tipMist) / 1e9
106
- feeNote = tipMist > 0n ? ` Relay fee ≈ ${tipSui.toFixed(4)} SUI + storage (WAL).` : ''
107
- } catch {
108
- // Non-fatal for disclosure; the register step surfaces any real tip error.
109
- }
110
-
111
- status.value = `Registering blob (approve in wallet)…${feeNote}`
112
- const regTx = flow.register({
113
- owner: account.value.address,
114
- epochs: ICON_EPOCHS,
115
- deletable: false,
116
- })
117
- // Set sender and build with the Sui client to resolve CoinWithBalance intents
118
- regTx.setSenderIfNotSet(account.value.address)
119
- await regTx.build({ client: suiClient })
120
- const reg = await executor.signAndExecute(regTx)
121
- await executor.waitForTransaction(reg.digest)
122
-
123
- status.value = 'Uploading to the relay…'
124
- await flow.upload({ digest: reg.digest })
125
-
126
- status.value = 'Certifying (approve in wallet)…'
127
- const certTx = flow.certify()
128
- // Set sender and build the certify transaction with the Sui client
129
- certTx.setSenderIfNotSet(account.value.address)
130
- await certTx.build({ client: suiClient })
131
- const cert = await executor.signAndExecute(certTx)
132
- await executor.waitForTransaction(cert.digest)
133
-
134
- const blob = await flow.getBlob()
135
- emit('uploaded', walrusBlobUrl(props.network, blob.blobId))
136
- status.value = 'Uploaded ✓ — URL filled in above.'
137
- } catch (e) {
138
- error.value = e instanceof Error ? e.message : String(e)
139
- status.value = ''
140
- } finally {
141
- uploading.value = false
142
- }
143
- }
144
- </script>
145
-
146
- <template>
147
- <div>
148
- <label for="walrus-file" class="hint" style="display: block; font-weight: 600">
149
- …or upload an image to Walrus
150
- </label>
151
- <div class="row">
152
- <input
153
- id="walrus-file"
154
- type="file"
155
- accept="image/*"
156
- aria-label="Choose an image to upload to Walrus"
157
- aria-describedby="walrus-help"
158
- style="width: auto"
159
- @change="onFile"
160
- />
161
- <button type="button" :disabled="uploading || !connected || !fileName" @click="upload">
162
- <span v-if="uploading" class="spinner" aria-hidden="true"></span>
163
- Upload to Walrus
164
- </button>
165
- </div>
166
-
167
- <!-- Relay selection (shown only if multiple relays available) -->
168
- <fieldset v-if="fileName && availableRelays.length > 1" style="margin: 0.75rem 0">
169
- <legend>Upload relay</legend>
170
- <div style="display: flex; flex-direction: column; gap: 0.5rem">
171
- <label
172
- v-for="option in availableRelays"
173
- :key="option.host"
174
- style="display: flex; align-items: center; gap: 0.5rem"
175
- >
176
- <input
177
- type="radio"
178
- :value="option.host"
179
- :checked="selectedRelayHost === option.host"
180
- @change="(e) => onRelayChange((e.target as HTMLInputElement).value)"
181
- />
182
- <span>{{ option.label }}</span>
183
- </label>
184
- </div>
185
- </fieldset>
186
-
187
- <!-- Cost estimation (shown after file selection) -->
188
- <UiNotice v-if="fileName && estimatedCost" style="margin: 0.75rem 0">
189
- <strong>Estimated cost:</strong> {{ estimatedCost.label }}
190
- </UiNotice>
191
-
192
- <!-- Public relay notice -->
193
- <p v-if="showFreeRelayTip" class="hint" style="margin: 0.75rem 0">
194
- Tip: select the public relay above to avoid the relay fee.
195
- </p>
196
-
197
- <p id="walrus-help" class="hint" aria-live="polite">
198
- Max {{ maxSizeLabel }} · PNG/JPEG/WebP/SVG. Requires <strong>WAL</strong> (storage) and
199
- <strong>SUI</strong> (gas + a small relay fee) in your wallet; two wallet approvals. Blob
200
- stored for ~{{ ICON_YEARS }} years.
201
- <span v-if="status">{{ status }}</span>
202
- </p>
203
- <UiNotice v-if="error" type="error">{{ error }}</UiNotice>
204
- </div>
205
- </template>