@meddleware/token-deployer-ui 0.0.7 → 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/.github/workflows/node-ci.yml +9 -0
- package/.github/workflows/npm-publish.yml +16 -2
- package/.htmlvalidate.mjs +30 -0
- package/CLAUDE.md +1 -1
- package/eslint.config.ts +63 -0
- package/package.json +31 -10
- package/postcss.config.js +8 -0
- package/src/App.vue +8 -6
- package/src/component-styles.css +5 -38
- package/src/components/ConfigForm.vue +116 -27
- package/src/components/DeployProgress.vue +2 -4
- package/src/components/GithubPush.vue +22 -7
- package/src/components/IconPicker.vue +133 -45
- package/src/components/LicensePicker.vue +65 -66
- package/src/components/MyTokens.vue +125 -0
- package/src/components/ResultPanel.vue +2 -4
- package/src/components/ReviewPanel.vue +1 -1
- package/src/components/TermsDialog.vue +28 -27
- package/src/components/TokenDeployerView.vue +109 -40
- package/src/components/WalletBar.vue +67 -78
- package/src/components/WalrusBlobBrowser.vue +33 -25
- package/src/composables/useAccessGate.ts +6 -1
- package/src/composables/useWalrusRelay.ts +1 -1
- package/src/lib/buildPublishTx.ts +1 -1
- package/src/lib/generatePackage.ts +6 -1
- package/src/lib/listMyTokens.ts +45 -0
- package/src/lib/validation.ts +8 -4
- package/src/lib/walrus-constants.ts +1 -1
- package/src/main.ts +8 -3
- package/src/styles.css +13 -5
- package/stylelint.config.js +33 -0
- package/test-results/.last-run.json +1 -1
- package/typedoc.json +2 -2
- package/src/components/WalrusIconUpload.vue +0 -205
|
@@ -27,6 +27,15 @@ jobs:
|
|
|
27
27
|
- name: Type-check
|
|
28
28
|
run: npm run type-check
|
|
29
29
|
|
|
30
|
+
- name: Lint CSS (stylelint)
|
|
31
|
+
run: npm run lint:css --if-present
|
|
32
|
+
|
|
33
|
+
- name: Lint JS (eslint + vuejs-a11y)
|
|
34
|
+
run: npm run lint:js --if-present
|
|
35
|
+
|
|
36
|
+
- name: Lint HTML (html-validate)
|
|
37
|
+
run: npm run lint:html --if-present
|
|
38
|
+
|
|
30
39
|
- name: Unit tests
|
|
31
40
|
run: npm test
|
|
32
41
|
|
|
@@ -62,8 +62,22 @@ jobs:
|
|
|
62
62
|
exit 0
|
|
63
63
|
fi
|
|
64
64
|
VERSION=$(node -p "require('./package.json').version")
|
|
65
|
-
|
|
65
|
+
NAME=$(node -p "require('./package.json').name")
|
|
66
|
+
ENC=$(printf '%s' "$NAME" | sed 's#/#%2f#')
|
|
67
|
+
# Check the registry directly — `npm view` can transiently miss a freshly
|
|
68
|
+
# first-published version and then attempt a re-publish. A 200 means it's live.
|
|
69
|
+
if [ "$(curl -s -o /dev/null -w '%{http_code}' "https://registry.npmjs.org/${ENC}/${VERSION}")" = "200" ]; then
|
|
66
70
|
echo "::notice::v${VERSION} already published to npmjs — skipping."
|
|
67
71
|
else
|
|
68
|
-
|
|
72
|
+
# Publish, but treat a "version already exists" error as success so a
|
|
73
|
+
# manually-published version (or a registry race) leaves the job green.
|
|
74
|
+
set +e; OUT=$(npm publish 2>&1); RC=$?; set -e
|
|
75
|
+
printf '%s\n' "$OUT"
|
|
76
|
+
if [ "$RC" -ne 0 ]; then
|
|
77
|
+
if printf '%s' "$OUT" | grep -qiE 'cannot publish over|previously published|EPUBLISHCONFLICT'; then
|
|
78
|
+
echo "::notice::v${VERSION} already published — treating as success."
|
|
79
|
+
else
|
|
80
|
+
exit "$RC"
|
|
81
|
+
fi
|
|
82
|
+
fi
|
|
69
83
|
fi
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// html-validate over the Vue SFC templates (via html-validate-vue): checks element nesting and
|
|
2
|
+
// content models (e.g. no <p> inside <ul>, no button inside a label) on top of the eslint a11y gate.
|
|
3
|
+
export default {
|
|
4
|
+
plugins: ['html-validate-vue'],
|
|
5
|
+
extends: ['html-validate:recommended', 'html-validate-vue:recommended'],
|
|
6
|
+
elements: [
|
|
7
|
+
'html5',
|
|
8
|
+
// @meddleware/ui components that render a single native element, described to the validator so
|
|
9
|
+
// content models / form rules see through them (keys are lowercased tag names).
|
|
10
|
+
{
|
|
11
|
+
uibutton: { inherit: 'button' },
|
|
12
|
+
uitoolbarbutton: { inherit: 'button' },
|
|
13
|
+
uiselect: { inherit: 'select' },
|
|
14
|
+
uinotice: { inherit: 'p' },
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
transform: {
|
|
18
|
+
'^.*\\.vue$': 'html-validate-vue',
|
|
19
|
+
},
|
|
20
|
+
rules: {
|
|
21
|
+
// Vue components are written in PascalCase (<UiStatusDot>, <KeepAlive>).
|
|
22
|
+
'element-case': ['error', { style: ['lowercase', 'pascalcase'] }],
|
|
23
|
+
'element-name': ['error', { pattern: '^(?:[a-z][a-z0-9\\-._]*-[a-z0-9\\-._]*|[A-Z][A-Za-z0-9]*)$' }],
|
|
24
|
+
// role="list" on styled lists is a deliberate Safari/VoiceOver workaround: WebKit drops list
|
|
25
|
+
// semantics when list-style is removed, so the explicit role must stay (matches the eslint
|
|
26
|
+
// vuejs-accessibility/no-redundant-roles override).
|
|
27
|
+
'no-redundant-role': 'off',
|
|
28
|
+
'prefer-native-element': ['error', { exclude: ['list'] }],
|
|
29
|
+
},
|
|
30
|
+
}
|
package/CLAUDE.md
CHANGED
|
@@ -49,7 +49,7 @@ wallet-adapter `buildExecutor`.
|
|
|
49
49
|
- **Orchestration** — [src/lib/deploy.ts](src/lib/deploy.ts) sequences patch →
|
|
50
50
|
publish → wait → (finalize) via an injectable `Executor` (so it is unit-testable;
|
|
51
51
|
the real one is built from the wallet in
|
|
52
|
-
[src/
|
|
52
|
+
[src/lib/deployExecutor.ts](src/lib/deployExecutor.ts)).
|
|
53
53
|
- **Package generation** — [src/lib/generatePackage.ts](src/lib/generatePackage.ts)
|
|
54
54
|
substitutes the same inputs into the canonical template text
|
|
55
55
|
([src/template-src/files.json](src/template-src/files.json)) and zips it.
|
package/eslint.config.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { globalIgnores } from 'eslint/config'
|
|
2
|
+
import { defineConfigWithVueTs, vueTsConfigs } from '@vue/eslint-config-typescript'
|
|
3
|
+
import pluginVue from 'eslint-plugin-vue'
|
|
4
|
+
import pluginA11y from 'eslint-plugin-vuejs-accessibility'
|
|
5
|
+
|
|
6
|
+
// Flat ESLint config: layers vue-a11y (WAI-ARIA linting for templates) on top of
|
|
7
|
+
// the standard Vue + TypeScript recommended rules, so accessibility regressions
|
|
8
|
+
// are caught in CI.
|
|
9
|
+
export default defineConfigWithVueTs(
|
|
10
|
+
{
|
|
11
|
+
name: 'token-deployer-ui/files-to-lint',
|
|
12
|
+
files: ['**/*.{vue,ts,mts,tsx}'],
|
|
13
|
+
},
|
|
14
|
+
|
|
15
|
+
globalIgnores(['**/dist/**', '**/coverage/**', '**/*.d.ts', 'docs/api/**']),
|
|
16
|
+
|
|
17
|
+
...pluginVue.configs['flat/essential'],
|
|
18
|
+
...pluginA11y.configs['flat/recommended'],
|
|
19
|
+
vueTsConfigs.recommended,
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
name: 'token-deployer-ui/overrides',
|
|
23
|
+
rules: {
|
|
24
|
+
// Accept both valid label-association patterns: a label wrapping its
|
|
25
|
+
// control (nesting) or a label[for] pointing at a control[id]. UiSelect renders a native
|
|
26
|
+
// <select>, so a label nesting it is labelling a control.
|
|
27
|
+
'vuejs-accessibility/label-has-for': [
|
|
28
|
+
'error',
|
|
29
|
+
{ required: { some: ['nesting', 'id'] }, controlComponents: ['UiSelect'] },
|
|
30
|
+
],
|
|
31
|
+
// UiFormField renders the <label for> for the control in its default slot.
|
|
32
|
+
'vuejs-accessibility/form-control-has-label': ['error', { labelComponents: ['UiFormField'] }],
|
|
33
|
+
// role="list" on styled lists is a deliberate Safari/VoiceOver workaround: WebKit drops
|
|
34
|
+
// list semantics when list-style is removed, so the explicit role stays.
|
|
35
|
+
'vuejs-accessibility/no-redundant-roles': 'off',
|
|
36
|
+
// Underscore-prefixed args/vars are an intentional "unused" marker.
|
|
37
|
+
'@typescript-eslint/no-unused-vars': [
|
|
38
|
+
'error',
|
|
39
|
+
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
|
|
44
|
+
{
|
|
45
|
+
// Tests use deliberately loose typing for mocks/stubs.
|
|
46
|
+
name: 'token-deployer-ui/test-overrides',
|
|
47
|
+
files: ['**/*.{test,spec}.{ts,tsx}', 'tests/**', 'e2e/**'],
|
|
48
|
+
rules: {
|
|
49
|
+
'@typescript-eslint/no-explicit-any': 'off',
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
{
|
|
54
|
+
// ConfigForm intentionally edits a parent-owned reactive FormModel in place
|
|
55
|
+
// (the parent passes `:form` and reads the same object back). Converting to
|
|
56
|
+
// defineModel is deferred; the in-place mutation is the current contract.
|
|
57
|
+
name: 'token-deployer-ui/config-form',
|
|
58
|
+
files: ['src/components/ConfigForm.vue'],
|
|
59
|
+
rules: {
|
|
60
|
+
'vue/no-mutating-props': 'off',
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meddleware/token-deployer-ui",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
".": {
|
|
@@ -25,22 +25,25 @@
|
|
|
25
25
|
"e2e:localnet": "VITE_RPC_TESTNET=${VITE_RPC_TESTNET:-http://127.0.0.1:9000} VITE_FEE_TREASURY_TESTNET=${VITE_FEE_TREASURY_TESTNET:-0xfee0000000000000000000000000000000000000000000000000000000000000} vite build && start-server-and-test preview http://localhost:4173 'E2E_NETWORK=localnet node scripts/e2e-deploy.mjs'",
|
|
26
26
|
"e2e:testnet": "vite build && start-server-and-test preview http://localhost:4173 'E2E_NETWORK=testnet node scripts/e2e-deploy.mjs'",
|
|
27
27
|
"e2e:mainnet": "vite build && start-server-and-test preview http://localhost:4173 'E2E_NETWORK=mainnet node scripts/e2e-deploy.mjs'",
|
|
28
|
-
"lint": "
|
|
28
|
+
"lint": "npm run lint:css && npm run lint:js && npm run lint:html",
|
|
29
29
|
"format": "oxfmt src/",
|
|
30
30
|
"docs": "typedoc",
|
|
31
31
|
"regen:template": "bash scripts/regen-template.sh",
|
|
32
32
|
"sync:template": "bash scripts/sync-template-src.sh",
|
|
33
33
|
"gen:og-image": "node scripts/gen-og-image.mjs",
|
|
34
|
-
"verify:template": "node scripts/verify-bytecode.mjs && vitest run tests/templateArtifact.test.ts tests/templateParity.test.ts"
|
|
34
|
+
"verify:template": "node scripts/verify-bytecode.mjs && vitest run tests/templateArtifact.test.ts tests/templateParity.test.ts",
|
|
35
|
+
"lint:css": "stylelint \"src/**/*.{css,vue}\"",
|
|
36
|
+
"lint:js": "eslint .",
|
|
37
|
+
"lint:html": "html-validate \"src/**/*.vue\""
|
|
35
38
|
},
|
|
36
39
|
"dependencies": {
|
|
37
|
-
"@meddleware/design-tokens": "^0.1.
|
|
38
|
-
"@meddleware/ui": "^0.1.
|
|
39
|
-
"@meddleware/wallet-adapter": "^0.0.
|
|
40
|
-
"@meddleware/walrus-relay": "^0.1.
|
|
40
|
+
"@meddleware/design-tokens": "^0.1.7",
|
|
41
|
+
"@meddleware/ui": "^0.1.28",
|
|
42
|
+
"@meddleware/wallet-adapter": "^0.0.9",
|
|
43
|
+
"@meddleware/walrus-relay": "^0.1.18",
|
|
41
44
|
"@mysten/bcs": "~2.1.0",
|
|
42
45
|
"@mysten/move-bytecode-template": "~0.4.0",
|
|
43
|
-
"@mysten/sui": "^2.
|
|
46
|
+
"@mysten/sui": "^2.33.0",
|
|
44
47
|
"@mysten/walrus": "~1.2.0",
|
|
45
48
|
"fflate": "^0.8.3",
|
|
46
49
|
"vue": "^3.5.0"
|
|
@@ -51,23 +54,41 @@
|
|
|
51
54
|
"@vitejs/plugin-vue": "^6.0.8",
|
|
52
55
|
"@vitejs/plugin-vue-jsx": "^5.1.6",
|
|
53
56
|
"@vitest/eslint-plugin": "^1.6.23",
|
|
57
|
+
"@vue/eslint-config-typescript": "^14.9.0",
|
|
54
58
|
"@vue/test-utils": "^2.4.0",
|
|
59
|
+
"autoprefixer": "^10.4.20",
|
|
60
|
+
"eslint": "^10.11.0",
|
|
61
|
+
"eslint-plugin-vue": "^10.11.1",
|
|
62
|
+
"eslint-plugin-vuejs-accessibility": "^2.6.0",
|
|
63
|
+
"html-validate": "^11.16.1",
|
|
64
|
+
"html-validate-vue": "^9.3.1",
|
|
65
|
+
"jiti": "^2.7.0",
|
|
55
66
|
"jsdom": "^29.1.1",
|
|
56
67
|
"oxfmt": "^0.59.0",
|
|
57
68
|
"oxlint": "~1.76.0",
|
|
58
69
|
"playwright": "^1.62.0",
|
|
70
|
+
"postcss": "^8.4.49",
|
|
71
|
+
"postcss-html": "^2.0.0",
|
|
59
72
|
"start-server-and-test": "^3.0.11",
|
|
73
|
+
"stylelint": "^17.15.0",
|
|
74
|
+
"stylelint-config-standard": "^40.0.0",
|
|
60
75
|
"typedoc": "^0.28.20",
|
|
61
76
|
"typescript": "~6.0.0",
|
|
62
77
|
"vite": "^8.1.5",
|
|
63
78
|
"vite-plugin-vue-devtools": "^8.1.5",
|
|
64
79
|
"vitest": "^4.1.10",
|
|
80
|
+
"vue-eslint-parser": "^10.4.1",
|
|
65
81
|
"vue-tsc": "~3.3.0"
|
|
66
82
|
},
|
|
67
83
|
"optionalDependencies": {
|
|
68
|
-
"@meddleware/sui-token-template": "^1.0.
|
|
84
|
+
"@meddleware/sui-token-template": "^1.0.4"
|
|
69
85
|
},
|
|
70
86
|
"engines": {
|
|
71
87
|
"node": "^22.18.0 || >=24.12.0"
|
|
72
|
-
}
|
|
88
|
+
},
|
|
89
|
+
"browserslist": [
|
|
90
|
+
"> 0.5%",
|
|
91
|
+
"last 2 versions",
|
|
92
|
+
"not dead"
|
|
93
|
+
]
|
|
73
94
|
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Autoprefixer runs over the library's shipped CSS (base.css) during `vite build`,
|
|
2
|
+
// using the `browserslist` field in package.json as the target. Consumers therefore
|
|
3
|
+
// receive vendor-prefixed component CSS without needing their own prefixing step.
|
|
4
|
+
import autoprefixer from 'autoprefixer'
|
|
5
|
+
|
|
6
|
+
export default {
|
|
7
|
+
plugins: [autoprefixer()],
|
|
8
|
+
}
|
package/src/App.vue
CHANGED
|
@@ -9,12 +9,14 @@ const { mode: colorMode, set: setColorMode } = useColorMode('system')
|
|
|
9
9
|
<template>
|
|
10
10
|
<a class="skip-link" href="#main-content">Skip to main content</a>
|
|
11
11
|
<main id="main-content">
|
|
12
|
-
<
|
|
13
|
-
<ColorModeControl
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
<header class="page-header">
|
|
13
|
+
<ColorModeControl
|
|
14
|
+
class="theme-toggle"
|
|
15
|
+
:model-value="colorMode"
|
|
16
|
+
@update:model-value="setColorMode"
|
|
17
|
+
/>
|
|
16
18
|
<h1>Create your own Sui coin</h1>
|
|
17
|
-
<p class="lede">
|
|
19
|
+
<p v-once class="lede">
|
|
18
20
|
Deploy a coin on Sui directly from your browser. Your wallet signs and pays gas — nothing is
|
|
19
21
|
compiled or signed on a server — and you keep the full, verifiable source package.
|
|
20
22
|
</p>
|
|
@@ -24,7 +26,7 @@ const { mode: colorMode, set: setColorMode } = useColorMode('system')
|
|
|
24
26
|
|
|
25
27
|
<TokenDeployerView />
|
|
26
28
|
|
|
27
|
-
<footer v-once
|
|
29
|
+
<footer v-once class="page-footer">
|
|
28
30
|
<p>
|
|
29
31
|
Client-side token deployment. This tool never takes custody of your token or keys. Symbol,
|
|
30
32
|
name, and decimals are permanent once published.
|
package/src/component-styles.css
CHANGED
|
@@ -77,12 +77,16 @@ button:disabled {
|
|
|
77
77
|
flex-wrap: wrap;
|
|
78
78
|
align-items: center;
|
|
79
79
|
}
|
|
80
|
+
/* A row of form/navigation actions below a block of content. */
|
|
81
|
+
.actions {
|
|
82
|
+
margin-top: 1rem;
|
|
83
|
+
}
|
|
80
84
|
.grid2 {
|
|
81
85
|
display: grid;
|
|
82
86
|
grid-template-columns: 1fr;
|
|
83
87
|
gap: 0 1rem;
|
|
84
88
|
}
|
|
85
|
-
@media (
|
|
89
|
+
@media (width >= 520px) {
|
|
86
90
|
.grid2 {
|
|
87
91
|
grid-template-columns: 1fr 1fr;
|
|
88
92
|
}
|
|
@@ -144,18 +148,6 @@ details > fieldset {
|
|
|
144
148
|
background: var(--border);
|
|
145
149
|
}
|
|
146
150
|
|
|
147
|
-
.visually-hidden {
|
|
148
|
-
position: absolute;
|
|
149
|
-
width: 1px;
|
|
150
|
-
height: 1px;
|
|
151
|
-
padding: 0;
|
|
152
|
-
margin: -1px;
|
|
153
|
-
overflow: hidden;
|
|
154
|
-
clip: rect(0 0 0 0);
|
|
155
|
-
white-space: nowrap;
|
|
156
|
-
border: 0;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
151
|
.skip-link {
|
|
160
152
|
position: absolute;
|
|
161
153
|
left: 0.5rem;
|
|
@@ -217,31 +209,6 @@ details > fieldset {
|
|
|
217
209
|
}
|
|
218
210
|
}
|
|
219
211
|
|
|
220
|
-
dialog {
|
|
221
|
-
background: var(--surface);
|
|
222
|
-
border: 1px solid var(--border);
|
|
223
|
-
border-radius: var(--radius);
|
|
224
|
-
padding: 1.5rem;
|
|
225
|
-
width: min(420px, 90vw);
|
|
226
|
-
max-height: 90vh;
|
|
227
|
-
overflow-y: auto;
|
|
228
|
-
color: var(--text);
|
|
229
|
-
}
|
|
230
|
-
dialog::backdrop {
|
|
231
|
-
background: rgba(0, 0, 0, 0.45);
|
|
232
|
-
-webkit-backdrop-filter: blur(2px);
|
|
233
|
-
backdrop-filter: blur(2px);
|
|
234
|
-
}
|
|
235
|
-
.dialog-header {
|
|
236
|
-
display: flex;
|
|
237
|
-
justify-content: space-between;
|
|
238
|
-
align-items: center;
|
|
239
|
-
margin-bottom: 1.25rem;
|
|
240
|
-
}
|
|
241
|
-
.dialog-header h2 {
|
|
242
|
-
margin: 0;
|
|
243
|
-
font-size: 1.1rem;
|
|
244
|
-
}
|
|
245
212
|
.wallet-list {
|
|
246
213
|
display: flex;
|
|
247
214
|
flex-direction: column;
|
|
@@ -43,17 +43,46 @@ function onBlur(key: string): void {
|
|
|
43
43
|
|
|
44
44
|
<template>
|
|
45
45
|
<form novalidate @submit.prevent="emit('next')">
|
|
46
|
-
|
|
47
46
|
<!-- Step 1: Identity -->
|
|
48
47
|
<template v-if="formStep === 'identity'">
|
|
49
48
|
<fieldset>
|
|
50
49
|
<legend>Package identity <span class="permanent">(permanent)</span></legend>
|
|
51
50
|
<div class="grid2">
|
|
52
|
-
<UiFormField id="package" label="Package name" :error="visibleError('packageName')"
|
|
53
|
-
<
|
|
51
|
+
<UiFormField id="package" label="Package name" :error="visibleError('packageName')">
|
|
52
|
+
<template #label-suffix>
|
|
53
|
+
<UiFieldHint field-id="package-name">
|
|
54
|
+
The on-chain Move package name (snake_case, e.g. <b>my_token</b>). It identifies the
|
|
55
|
+
published package and cannot be changed after deployment.
|
|
56
|
+
</UiFieldHint>
|
|
57
|
+
</template>
|
|
58
|
+
<template #default="{ attrs }">
|
|
59
|
+
<input
|
|
60
|
+
type="text"
|
|
61
|
+
v-bind="attrs"
|
|
62
|
+
v-model="form.packageName"
|
|
63
|
+
placeholder="my_token"
|
|
64
|
+
autocomplete="off"
|
|
65
|
+
@blur="onBlur('packageName')"
|
|
66
|
+
/>
|
|
67
|
+
</template>
|
|
54
68
|
</UiFormField>
|
|
55
|
-
<UiFormField id="module" label="Module name" :error="visibleError('moduleName')"
|
|
56
|
-
<
|
|
69
|
+
<UiFormField id="module" label="Module name" :error="visibleError('moduleName')">
|
|
70
|
+
<template #label-suffix>
|
|
71
|
+
<UiFieldHint field-id="module-name">
|
|
72
|
+
The Move module inside the package (lowercase, e.g. <b>mytoken</b>). The coin type
|
|
73
|
+
and its one-time-witness struct are derived from this name, so it is permanent too.
|
|
74
|
+
</UiFieldHint>
|
|
75
|
+
</template>
|
|
76
|
+
<template #default="{ attrs }">
|
|
77
|
+
<input
|
|
78
|
+
type="text"
|
|
79
|
+
v-bind="attrs"
|
|
80
|
+
v-model="form.moduleName"
|
|
81
|
+
placeholder="mytoken"
|
|
82
|
+
autocomplete="off"
|
|
83
|
+
@blur="onBlur('moduleName')"
|
|
84
|
+
/>
|
|
85
|
+
</template>
|
|
57
86
|
</UiFormField>
|
|
58
87
|
</div>
|
|
59
88
|
<p class="hint" aria-live="polite">
|
|
@@ -62,8 +91,8 @@ function onBlur(key: string): void {
|
|
|
62
91
|
</template>
|
|
63
92
|
<template v-else>
|
|
64
93
|
Coin type will be <span class="mono">{{ coinTypePreview }}</span
|
|
65
|
-
>. The witness struct <span class="mono">{{ structPreview }}</span> is derived from
|
|
66
|
-
|
|
94
|
+
>. The witness struct <span class="mono">{{ structPreview }}</span> is derived from the
|
|
95
|
+
module name (a Sui requirement).
|
|
67
96
|
</template>
|
|
68
97
|
</p>
|
|
69
98
|
</fieldset>
|
|
@@ -75,25 +104,71 @@ function onBlur(key: string): void {
|
|
|
75
104
|
<legend>Token details</legend>
|
|
76
105
|
|
|
77
106
|
<div class="grid2">
|
|
78
|
-
<UiFormField id="name" label="Token name" :error="visibleError('name')"
|
|
79
|
-
<
|
|
107
|
+
<UiFormField id="name" label="Token name" :error="visibleError('name')">
|
|
108
|
+
<template #label-suffix>
|
|
109
|
+
<UiFieldHint field-id="token-name">
|
|
110
|
+
The human-readable display name shown in wallets and explorers (e.g.
|
|
111
|
+
<b>My Token</b>). Editable later if you keep the Updatable metadata policy.
|
|
112
|
+
</UiFieldHint>
|
|
113
|
+
</template>
|
|
114
|
+
<template #default="{ attrs }">
|
|
115
|
+
<input
|
|
116
|
+
type="text"
|
|
117
|
+
v-bind="attrs"
|
|
118
|
+
v-model="form.name"
|
|
119
|
+
autocomplete="off"
|
|
120
|
+
@blur="onBlur('name')"
|
|
121
|
+
/>
|
|
122
|
+
</template>
|
|
80
123
|
</UiFormField>
|
|
81
|
-
<UiFormField id="symbol" label="Symbol" :error="visibleError('symbol')"
|
|
82
|
-
<
|
|
124
|
+
<UiFormField id="symbol" label="Symbol" :error="visibleError('symbol')">
|
|
125
|
+
<template #label-suffix>
|
|
126
|
+
<UiFieldHint field-id="token-symbol">
|
|
127
|
+
The short ticker shown in wallets (e.g. <b>MYT</b>), typically 3–5 uppercase
|
|
128
|
+
letters.
|
|
129
|
+
</UiFieldHint>
|
|
130
|
+
</template>
|
|
131
|
+
<template #default="{ attrs }">
|
|
132
|
+
<input
|
|
133
|
+
type="text"
|
|
134
|
+
v-bind="attrs"
|
|
135
|
+
v-model="form.symbol"
|
|
136
|
+
autocomplete="off"
|
|
137
|
+
@blur="onBlur('symbol')"
|
|
138
|
+
/>
|
|
139
|
+
</template>
|
|
83
140
|
</UiFormField>
|
|
84
141
|
</div>
|
|
85
142
|
|
|
86
143
|
<UiFormField id="description" label="Description" :error="visibleError('description')">
|
|
87
|
-
<template #label-suffix
|
|
144
|
+
<template #label-suffix>
|
|
145
|
+
<span class="hint">(optional)</span>
|
|
146
|
+
<UiFieldHint field-id="token-description">
|
|
147
|
+
A short blurb stored in the coin metadata and shown in some wallets/explorers. Leave
|
|
148
|
+
blank if you don't need one.
|
|
149
|
+
</UiFieldHint>
|
|
150
|
+
</template>
|
|
88
151
|
<template #default="{ attrs }">
|
|
89
152
|
<textarea v-bind="attrs" v-model="form.description" @blur="onBlur('description')" />
|
|
90
153
|
</template>
|
|
91
154
|
</UiFormField>
|
|
92
155
|
|
|
93
156
|
<UiFormField id="decimals" label="Decimals" :error="visibleError('decimals')">
|
|
94
|
-
<template #label-suffix
|
|
157
|
+
<template #label-suffix>
|
|
158
|
+
<span class="permanent">(permanent)</span>
|
|
159
|
+
<UiFieldHint field-id="token-decimals">
|
|
160
|
+
How many decimal places the coin supports. <b>9</b> is the Sui convention (1 token =
|
|
161
|
+
1,000,000,000 base units). Fixed at deployment and cannot be changed.
|
|
162
|
+
</UiFieldHint>
|
|
163
|
+
</template>
|
|
95
164
|
<template #default="{ attrs }">
|
|
96
|
-
<input
|
|
165
|
+
<input
|
|
166
|
+
type="text"
|
|
167
|
+
v-bind="attrs"
|
|
168
|
+
v-model="form.decimals"
|
|
169
|
+
inputmode="numeric"
|
|
170
|
+
@blur="onBlur('decimals')"
|
|
171
|
+
/>
|
|
97
172
|
</template>
|
|
98
173
|
</UiFormField>
|
|
99
174
|
|
|
@@ -115,14 +190,21 @@ function onBlur(key: string): void {
|
|
|
115
190
|
<UiFormField id="supply" label="Initial supply" :error="visibleError('initialSupply')">
|
|
116
191
|
<template #label-suffix>
|
|
117
192
|
<UiFieldHint field-id="supply">
|
|
118
|
-
The number of whole tokens to mint and send to the recipient at deployment.
|
|
119
|
-
|
|
120
|
-
Enter <b>0</b> or leave blank for no initial supply.
|
|
121
|
-
|
|
193
|
+
The number of whole tokens to mint and send to the recipient at deployment. Uses your
|
|
194
|
+
chosen decimal precision — with 9 decimals, 1 token = 1,000,000,000 base units
|
|
195
|
+
on-chain. Enter <b>0</b> or leave blank for no initial supply. You can mint more later
|
|
196
|
+
if you choose the Mintable supply policy.
|
|
122
197
|
</UiFieldHint>
|
|
123
198
|
</template>
|
|
124
199
|
<template #default="{ attrs }">
|
|
125
|
-
<input
|
|
200
|
+
<input
|
|
201
|
+
type="text"
|
|
202
|
+
v-bind="attrs"
|
|
203
|
+
v-model="form.initialSupply"
|
|
204
|
+
inputmode="numeric"
|
|
205
|
+
placeholder="0"
|
|
206
|
+
@blur="onBlur('initialSupply')"
|
|
207
|
+
/>
|
|
126
208
|
</template>
|
|
127
209
|
</UiFormField>
|
|
128
210
|
|
|
@@ -161,8 +243,8 @@ function onBlur(key: string): void {
|
|
|
161
243
|
<UiFormField id="package-policy" label="Package code">
|
|
162
244
|
<template #label-suffix>
|
|
163
245
|
<UiFieldHint field-id="package-policy">
|
|
164
|
-
<b>Immutable</b>: the UpgradeCap is burned — the on-chain code is sealed.
|
|
165
|
-
|
|
246
|
+
<b>Immutable</b>: the UpgradeCap is burned — the on-chain code is sealed. Recommended
|
|
247
|
+
for trust and auditability.<br />
|
|
166
248
|
<b>Upgradeable</b>: you keep the UpgradeCap and can push code changes to the package
|
|
167
249
|
after deployment.
|
|
168
250
|
</UiFieldHint>
|
|
@@ -178,14 +260,21 @@ function onBlur(key: string): void {
|
|
|
178
260
|
<UiFormField id="recipient" label="Recipient" :error="visibleError('recipient')">
|
|
179
261
|
<template #label-suffix>
|
|
180
262
|
<UiFieldHint field-id="recipient">
|
|
181
|
-
The Sui address that receives the TreasuryCap, MetadataCap, UpgradeCap (if kept),
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
263
|
+
The Sui address that receives the TreasuryCap, MetadataCap, UpgradeCap (if kept), and
|
|
264
|
+
any initial supply minted at deployment. Leave blank to send everything to the
|
|
265
|
+
connected wallet. Enter a different address to separate the deployer wallet from the
|
|
266
|
+
custody wallet.
|
|
185
267
|
</UiFieldHint>
|
|
186
268
|
</template>
|
|
187
269
|
<template #default="{ attrs }">
|
|
188
|
-
<input
|
|
270
|
+
<input
|
|
271
|
+
type="text"
|
|
272
|
+
v-bind="attrs"
|
|
273
|
+
v-model="form.recipient"
|
|
274
|
+
placeholder="0x…"
|
|
275
|
+
autocomplete="off"
|
|
276
|
+
@blur="onBlur('recipient')"
|
|
277
|
+
/>
|
|
189
278
|
</template>
|
|
190
279
|
</UiFormField>
|
|
191
280
|
|
|
@@ -193,7 +282,7 @@ function onBlur(key: string): void {
|
|
|
193
282
|
</fieldset>
|
|
194
283
|
</template>
|
|
195
284
|
|
|
196
|
-
<div class="row
|
|
285
|
+
<div class="row actions">
|
|
197
286
|
<button v-if="formStep !== 'identity'" type="button" @click="emit('back')">Back</button>
|
|
198
287
|
<button type="submit" class="primary" :disabled="!canProceed">
|
|
199
288
|
{{ formStep === 'settings' ? 'Review & deploy' : 'Next' }}
|
|
@@ -25,9 +25,7 @@ function getState(i: number): 'done' | 'active' | 'todo' {
|
|
|
25
25
|
return 'todo'
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
const steps = computed(() =>
|
|
29
|
-
ORDER.map((s, i) => ({ ...s, state: getState(i) }))
|
|
30
|
-
)
|
|
28
|
+
const steps = computed(() => ORDER.map((s, i) => ({ ...s, state: getState(i) })))
|
|
31
29
|
</script>
|
|
32
30
|
|
|
33
31
|
<template>
|
|
@@ -38,7 +36,7 @@ const steps = computed(() =>
|
|
|
38
36
|
<span v-if="s.state === 'active'" class="spinner" aria-hidden="true"></span>
|
|
39
37
|
<span v-else-if="s.state === 'done'" aria-hidden="true">✓ </span>
|
|
40
38
|
{{ s.label }}
|
|
41
|
-
<span v-if="s.state === 'active'" class="visually-hidden">(in progress)</span>
|
|
39
|
+
<span v-if="s.state === 'active'" class="mw-visually-hidden">(in progress)</span>
|
|
42
40
|
</li>
|
|
43
41
|
</ol>
|
|
44
42
|
<p class="hint">Keep this tab open and approve the wallet prompts.</p>
|
|
@@ -56,7 +56,7 @@ async function push(): Promise<void> {
|
|
|
56
56
|
{{ open ? '▾' : '▸' }} Create a GitHub repo (optional)
|
|
57
57
|
</button>
|
|
58
58
|
|
|
59
|
-
<div v-if="open" class="card
|
|
59
|
+
<div v-if="open" class="card github-push__form">
|
|
60
60
|
<p v-once class="hint">
|
|
61
61
|
Uses your <strong>own</strong> GitHub token — the repo is created in your account and this
|
|
62
62
|
site's servers are never involved. The token is sent only to
|
|
@@ -67,24 +67,23 @@ async function push(): Promise<void> {
|
|
|
67
67
|
</p>
|
|
68
68
|
|
|
69
69
|
<label for="gh-repo">Repository name</label>
|
|
70
|
-
<input id="gh-repo" v-model="repoName" autocomplete="off" />
|
|
70
|
+
<input id="gh-repo" v-model="repoName" type="text" autocomplete="off" />
|
|
71
71
|
|
|
72
72
|
<label for="gh-token">GitHub token</label>
|
|
73
73
|
<input
|
|
74
74
|
id="gh-token"
|
|
75
75
|
v-model="token"
|
|
76
76
|
type="password"
|
|
77
|
-
autocomplete="
|
|
77
|
+
autocomplete="new-password"
|
|
78
78
|
spellcheck="false"
|
|
79
79
|
placeholder="github_pat_… or ghp_…"
|
|
80
80
|
/>
|
|
81
81
|
|
|
82
|
-
<label
|
|
83
|
-
<input
|
|
84
|
-
private
|
|
82
|
+
<label class="github-push__private">
|
|
83
|
+
<input v-model="isPrivate" type="checkbox" /> Make the repository private
|
|
85
84
|
</label>
|
|
86
85
|
|
|
87
|
-
<div class="row
|
|
86
|
+
<div class="row github-push__actions">
|
|
88
87
|
<button type="button" class="primary" :disabled="busy || !token || !repoName" @click="push">
|
|
89
88
|
<span v-if="busy" class="spinner" aria-hidden="true"></span> Create repo & push
|
|
90
89
|
</button>
|
|
@@ -98,3 +97,19 @@ async function push(): Promise<void> {
|
|
|
98
97
|
</div>
|
|
99
98
|
</div>
|
|
100
99
|
</template>
|
|
100
|
+
|
|
101
|
+
<style scoped>
|
|
102
|
+
.github-push__form {
|
|
103
|
+
margin-top: 0.5rem;
|
|
104
|
+
}
|
|
105
|
+
.github-push__private {
|
|
106
|
+
margin-top: 0.75rem;
|
|
107
|
+
font-weight: 400;
|
|
108
|
+
}
|
|
109
|
+
.github-push__private input {
|
|
110
|
+
width: auto;
|
|
111
|
+
}
|
|
112
|
+
.github-push__actions {
|
|
113
|
+
margin-top: 0.75rem;
|
|
114
|
+
}
|
|
115
|
+
</style>
|