@meddleware/dashboard-ui 0.0.0-stage → 0.1.89
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/CHANGELOG.md +79 -0
- package/index.html +13 -0
- package/package.json +84 -4
- package/src/App.vue +277 -0
- package/src/components/ChainSelector.vue +44 -0
- package/src/components/NetworkSelector.vue +109 -0
- package/src/env.d.ts +7 -0
- package/src/main.ts +20 -0
- package/src/router/index.ts +41 -0
- package/src/styles.css +17 -0
- package/src/views/BlockchainView.vue +98 -0
- package/tsconfig.json +18 -0
- package/vite.config.ts +14 -0
- package/README.md +0 -3
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@meddleware/dashboard` are documented here.
|
|
4
|
+
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versioning follows
|
|
5
|
+
[Semantic Versioning](https://semver.org/). Before 0.2.0, breaking changes ship as patch releases.
|
|
6
|
+
Releases before 0.1.85 are recorded in the git history only.
|
|
7
|
+
|
|
8
|
+
## [0.1.89] - 2026-10-24
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
|
|
12
|
+
- Renamed from `dashboard` to `dashboard-ui` (workspace naming convention, `docs/architecture/ADR-0003-naming.md`). No code change beyond names, paths, hostnames and dependency names; the history below keeps the old names.
|
|
13
|
+
|
|
14
|
+
## [0.1.88] - 2026-10-22
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- Embedded apps on their latest releases (access-gate-client 0.0.10 stale-indexer fallback, walrus-client 0.0.30 flow fix, nft-gate-client 0.0.18 rules); image base static-server 0.1.11 (Go 1.26.10 standard library, CVE-2026-111943)
|
|
19
|
+
|
|
20
|
+
## [0.1.87] - 2026-10-19
|
|
21
|
+
|
|
22
|
+
### Changed
|
|
23
|
+
|
|
24
|
+
- Image rebuilt without the `VITE_UPLOAD_RELAY_MAX_TIP_MIST` repository variable (deleted; it still held the old 0.05 SUI ceiling and overrode the 0.005 SUI default introduced in the previous release)
|
|
25
|
+
|
|
26
|
+
## [0.1.86] - 2026-10-19
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Embedded apps on their re-priced releases (workspace cost model, D57): token-deployer-ui 0.0.37 (2 SUI deploy fee, 0.005 SUI tip ceiling), walrus-ui 0.1.59, seal-ui 0.0.39; ui ^0.1.32; dependency updates and the Node 26 baseline merged since 0.1.85
|
|
31
|
+
|
|
32
|
+
## [0.1.85] - 2026-10-18
|
|
33
|
+
|
|
34
|
+
### Changed
|
|
35
|
+
|
|
36
|
+
- Embeds the 2026-10-18 builds of every tool: `@meddleware/walrus-ui` ^0.1.58 and
|
|
37
|
+
`@meddleware/token-deployer-ui` ^0.0.36 (both adopt `@meddleware/walrus-relay` 0.1.31: the access-gate
|
|
38
|
+
price is read from the chain and shown, with the payees and network, before the wallet opens),
|
|
39
|
+
`@meddleware/access-gate-ui` ^0.1.37, `@meddleware/seal-ui` ^0.0.38, `@meddleware/treasury-ui` ^0.0.18,
|
|
40
|
+
`@meddleware/dao-ui` ^0.1.34 (retained, not mounted) and `@meddleware/design-tokens` ^0.1.10; their
|
|
41
|
+
clients resolve to `walrus-client` 0.0.28, `access-gate-client` 0.0.9, `seal-client` 0.0.20 and
|
|
42
|
+
`sui-token-client` 0.0.11. `@mysten/sui` ^2.36.0, `vue` ^3.5.44, `vue-router` ^5.4.0 and merged tooling
|
|
43
|
+
updates. One copy of `@mysten/sui` and of each `@meddleware/*` package (`npm ls`).
|
|
44
|
+
- Image base: `static-server` 0.1.8 (`sha256:e32cf42b…4000`).
|
|
45
|
+
- `npm-publish.yml` `verify` calls `node-ci.yml` (audit gate, signatures, type-check, all linters, tests,
|
|
46
|
+
build, licences, browser smoke test), as the image release does (audit F15).
|
|
47
|
+
- The four image builds pass the same build arguments (the registry-mirror builds lacked
|
|
48
|
+
`VITE_WALRUS_RELAY_MAINNET`).
|
|
49
|
+
- `.env.example` lists every build input; `.env.production` names the current package;
|
|
50
|
+
`CLAUDE.md` and `SECURITY.md` are current.
|
|
51
|
+
|
|
52
|
+
### Removed
|
|
53
|
+
|
|
54
|
+
- `VITE_ACCESS_GATE_PRICE_MIST_TESTNET` (Dockerfile argument, release workflow, docs): walrus-ui and
|
|
55
|
+
token-deployer-ui no longer read it. A build that still sets it ignores it. The repository variable of the
|
|
56
|
+
same name can be deleted.
|
|
57
|
+
|
|
58
|
+
### Added
|
|
59
|
+
|
|
60
|
+
- Unit tests (`npm test`, vitest): route table and tool imports, the network selector's mainnet guard (I7),
|
|
61
|
+
design-token use (no `--warning` text colour, no undefined custom property), the build-input inventory,
|
|
62
|
+
and the inline-script guard.
|
|
63
|
+
- Browser smoke test (`npm run test:e2e`, Playwright): `/`, every tool tab of `/blockchain` and the legacy
|
|
64
|
+
redirects mount in Chromium with no uncaught error and no network.
|
|
65
|
+
- CI builds the image (not pushed) and probes the running container with `scripts/smoke.sh`: non-root user,
|
|
66
|
+
read-only root, the in-image health check, `/` and the SPA fallback with the security headers and the CSP
|
|
67
|
+
(no `'unsafe-inline'` for scripts), immutable `/assets/`, `/THIRD_PARTY_LICENSES`, dot-files refused, POST
|
|
68
|
+
405 (audit F14). A Trivy filesystem scan (vulnerabilities, Dockerfile misconfiguration, secrets; fixable
|
|
69
|
+
CRITICAL/HIGH fail) runs beside it.
|
|
70
|
+
- `tests/workflows.test.ts` pins the release gate: both release workflows call `node-ci.yml`, CI runs every
|
|
71
|
+
check above, third-party actions are pinned by SHA, the Dockerfile runs `check:dist` and sets the user.
|
|
72
|
+
- `npm run check:dist` (also run by the Dockerfile) fails the build when a built page has an inline script or event-handler attribute
|
|
73
|
+
that the CSP would refuse (audit F13).
|
|
74
|
+
- `CHANGELOG.md` (listed in `files` but absent before).
|
|
75
|
+
|
|
76
|
+
### Fixed
|
|
77
|
+
|
|
78
|
+
- CI verifies registry signatures with npm 11.20.0: the npm bundled with Node 24.13 (11.6.2) reports
|
|
79
|
+
`EMISSINGSIGNATUREKEY` for the attestations of recently published packages.
|
package/index.html
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
|
+
<title>Meddleware</title>
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div id="app"></div>
|
|
11
|
+
<script type="module" src="/src/main.ts"></script>
|
|
12
|
+
</body>
|
|
13
|
+
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,86 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@meddleware/dashboard-ui",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "0.1.89",
|
|
4
|
+
"homepage": "https://docs.meddleware.co.uk/",
|
|
5
|
+
"description": "Meddleware tools hub — Vue 3 SPA listing hosted services (Walrus, Access Gate) with white-label deploy options.",
|
|
6
|
+
"author": "Meddleware <dev@meddleware.co.uk>",
|
|
7
|
+
"license": "0BSD",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/meddleware-org/dashboard-ui.git"
|
|
11
|
+
},
|
|
12
|
+
"type": "module",
|
|
13
|
+
"files": [
|
|
14
|
+
"src",
|
|
15
|
+
"index.html",
|
|
16
|
+
"vite.config.ts",
|
|
17
|
+
"tsconfig.json",
|
|
18
|
+
"CHANGELOG.md"
|
|
19
|
+
],
|
|
20
|
+
"scripts": {
|
|
21
|
+
"dev": "vite",
|
|
22
|
+
"build": "vue-tsc --noEmit && vite build",
|
|
23
|
+
"preview": "vite preview",
|
|
24
|
+
"type-check": "vue-tsc --noEmit",
|
|
25
|
+
"test": "vitest run",
|
|
26
|
+
"test:watch": "vitest",
|
|
27
|
+
"test:e2e": "playwright test",
|
|
28
|
+
"lint:css": "stylelint \"src/**/*.{css,vue}\"",
|
|
29
|
+
"lint:js": "eslint .",
|
|
30
|
+
"lint": "npm run lint:css && npm run lint:js && npm run lint:html",
|
|
31
|
+
"lint:html": "html-validate \"src/**/*.vue\"",
|
|
32
|
+
"licenses": "node scripts/third-party-licenses.mjs",
|
|
33
|
+
"check:licenses": "node scripts/third-party-licenses.mjs --check",
|
|
34
|
+
"check:dist": "node scripts/check-inline-scripts.mjs"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"dependencies": {
|
|
40
|
+
"@meddleware/access-gate-ui": "^0.1.39",
|
|
41
|
+
"@meddleware/dao-ui": "^0.1.36",
|
|
42
|
+
"@meddleware/design-tokens": "^0.1.10",
|
|
43
|
+
"@meddleware/sealed-storage-ui": "^0.0.41",
|
|
44
|
+
"@meddleware/token-issuance-ui": "^0.0.39",
|
|
45
|
+
"@meddleware/treasury-ui": "^0.0.20",
|
|
46
|
+
"@meddleware/ui": "^0.1.32",
|
|
47
|
+
"@meddleware/wallet-adapter": "^0.0.17",
|
|
48
|
+
"@meddleware/walrus-storage-ui": "^0.1.62",
|
|
49
|
+
"@mysten/sui": "^2.36.0",
|
|
50
|
+
"vue": "^3.5.44",
|
|
51
|
+
"vue-router": "^5.4.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@meddleware/eslint-config": "^0.0.3",
|
|
55
|
+
"@playwright/test": "~1.64.1",
|
|
56
|
+
"@types/node": "~26.6.5",
|
|
57
|
+
"@vitejs/plugin-vue": "^6.0.10",
|
|
58
|
+
"@vue/eslint-config-typescript": "^14.9.0",
|
|
59
|
+
"@vue/test-utils": "^2.5.1",
|
|
60
|
+
"autoprefixer": "^10.6.1",
|
|
61
|
+
"eslint": "^10.13.0",
|
|
62
|
+
"eslint-plugin-vue": "^10.11.1",
|
|
63
|
+
"eslint-plugin-vuejs-accessibility": "^2.6.2",
|
|
64
|
+
"html-validate": "^11.16.4",
|
|
65
|
+
"html-validate-vue": "^9.3.1",
|
|
66
|
+
"jiti": "^2.7.0",
|
|
67
|
+
"jsdom": "^30.1.3",
|
|
68
|
+
"postcss": "^8.5.30",
|
|
69
|
+
"postcss-html": "^2.0.0",
|
|
70
|
+
"stylelint": "^17.16.0",
|
|
71
|
+
"stylelint-config-standard": "^40.0.0",
|
|
72
|
+
"typescript": "~6.0.0",
|
|
73
|
+
"vite": "^8.3.4",
|
|
74
|
+
"vitest": "~5.0.2",
|
|
75
|
+
"vue-eslint-parser": "^10.4.1",
|
|
76
|
+
"vue-tsc": "~3.3.13"
|
|
77
|
+
},
|
|
78
|
+
"engines": {
|
|
79
|
+
"node": "^22.18.0 || >=24.12.0"
|
|
80
|
+
},
|
|
81
|
+
"browserslist": [
|
|
82
|
+
"> 0.5%",
|
|
83
|
+
"last 2 versions",
|
|
84
|
+
"not dead"
|
|
85
|
+
]
|
|
86
|
+
}
|
package/src/App.vue
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import { computed, ref, watch } from 'vue'
|
|
3
|
+
import { useRoute, useRouter, RouterView, RouterLink } from 'vue-router'
|
|
4
|
+
import {
|
|
5
|
+
AppHeader, AppSidebar, AppFooter,
|
|
6
|
+
ColorModeControl, SidebarItem, StatusWidget, CopyableAddress, ExplorerLink, CopyrightLine,
|
|
7
|
+
suiExplorerUrl, useColorMode,
|
|
8
|
+
} from '@meddleware/ui'
|
|
9
|
+
import { useWallet, useNetwork } from '@meddleware/wallet-adapter'
|
|
10
|
+
import NetworkSelector from './components/NetworkSelector.vue'
|
|
11
|
+
import ChainSelector from './components/ChainSelector.vue'
|
|
12
|
+
|
|
13
|
+
const { mode } = useColorMode('dark')
|
|
14
|
+
const route = useRoute()
|
|
15
|
+
const router = useRouter()
|
|
16
|
+
const { network } = useNetwork()
|
|
17
|
+
|
|
18
|
+
// The sidebar has two modes. Top-level shows the organisation sections (Treasury, Blockchain).
|
|
19
|
+
// Entering the Blockchain section swaps the sidebar to a chain selector with an up-one-level
|
|
20
|
+
// control that returns to the top level without leaving the page.
|
|
21
|
+
type SidebarView = 'top' | 'blockchain'
|
|
22
|
+
// Derived from the route below (immediate watcher) so a direct load / reload of /blockchain
|
|
23
|
+
// shows the chain selector, not the top-level menu.
|
|
24
|
+
const sidebarView = ref<SidebarView>('top')
|
|
25
|
+
const selectedChain = ref('sui')
|
|
26
|
+
|
|
27
|
+
const isTreasury = computed(() => route.path === '/')
|
|
28
|
+
const isBlockchain = computed(() => route.path === '/blockchain')
|
|
29
|
+
|
|
30
|
+
function goTreasury(navigate?: () => void): void {
|
|
31
|
+
sidebarView.value = 'top'
|
|
32
|
+
if (navigate) navigate()
|
|
33
|
+
else router.push('/')
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function enterBlockchain(): void {
|
|
37
|
+
// No-op navigation if already on /blockchain (vue-router won't reload the same route);
|
|
38
|
+
// either way the sidebar switches to the chain selector.
|
|
39
|
+
sidebarView.value = 'blockchain'
|
|
40
|
+
if (route.path !== '/blockchain') router.push('/blockchain')
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function upOneLevel(): void {
|
|
44
|
+
// Return the sidebar to the top level without navigating away from the current page.
|
|
45
|
+
sidebarView.value = 'top'
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Derive the sidebar mode from the route so it's correct on direct load / reload / redirect /
|
|
49
|
+
// back-forward — /blockchain always opens the chain selector. `upOneLevel()` sets 'top' without a
|
|
50
|
+
// route change, so this watcher (which fires only on path changes) leaves that transient override
|
|
51
|
+
// intact until the next navigation.
|
|
52
|
+
watch(
|
|
53
|
+
() => route.path,
|
|
54
|
+
(p) => {
|
|
55
|
+
sidebarView.value = p === '/blockchain' ? 'blockchain' : 'top'
|
|
56
|
+
},
|
|
57
|
+
{ immediate: true },
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
// The dashboard hosts the single shared wallet connection for every inline tool view. It
|
|
61
|
+
// requests the superset of features the tools need so the connect control only offers wallets
|
|
62
|
+
// capable of every operation; individual tools still feature-guard at call time.
|
|
63
|
+
const { account, disconnect } = useWallet({
|
|
64
|
+
requiredFeatures: ['sui:signTransaction', 'sui:signPersonalMessage'],
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
const DOCS_URL = import.meta.env.VITE_DOCS_URL || 'https://docs.meddleware.co.uk/'
|
|
68
|
+
const DEV_URL = import.meta.env.VITE_DEV_URL || 'https://dev.meddleware.co.uk/'
|
|
69
|
+
|
|
70
|
+
// SuiVision has no localnet explorer, so only build a link for public networks; otherwise the
|
|
71
|
+
// address is shown copy-only.
|
|
72
|
+
const accountExplorerHref = computed(() =>
|
|
73
|
+
account.value && network.value !== 'localnet'
|
|
74
|
+
? suiExplorerUrl('account', account.value.address, network.value as 'testnet' | 'mainnet' | 'devnet')
|
|
75
|
+
: null,
|
|
76
|
+
)
|
|
77
|
+
</script>
|
|
78
|
+
|
|
79
|
+
<template>
|
|
80
|
+
<div class="app-shell">
|
|
81
|
+
<AppHeader class="app-shell__header" variant="dark">
|
|
82
|
+
<template #brand>
|
|
83
|
+
<RouterLink to="/" class="brand-link" @click="goTreasury()">
|
|
84
|
+
<span class="brand-mark" aria-hidden="true">◆</span>
|
|
85
|
+
<span>Meddleware</span>
|
|
86
|
+
</RouterLink>
|
|
87
|
+
</template>
|
|
88
|
+
<template #actions>
|
|
89
|
+
<ColorModeControl v-model="mode" />
|
|
90
|
+
</template>
|
|
91
|
+
</AppHeader>
|
|
92
|
+
|
|
93
|
+
<AppSidebar class="app-shell__sidebar" variant="dark">
|
|
94
|
+
<!-- Top level: organisation sections -->
|
|
95
|
+
<template v-if="sidebarView === 'top'">
|
|
96
|
+
<router-link to="/" custom v-slot="{ navigate }">
|
|
97
|
+
<SidebarItem label="Treasury" icon="🏦" :active="isTreasury" @click="goTreasury(navigate)" />
|
|
98
|
+
</router-link>
|
|
99
|
+
<SidebarItem label="Blockchain" icon="⛓" :active="isBlockchain" @click="enterBlockchain" />
|
|
100
|
+
</template>
|
|
101
|
+
|
|
102
|
+
<!-- Blockchain section: chain selector + up-one-level -->
|
|
103
|
+
<template v-else>
|
|
104
|
+
<button type="button" class="sidebar-up" @click="upOneLevel">
|
|
105
|
+
<span aria-hidden="true">←</span> Back
|
|
106
|
+
</button>
|
|
107
|
+
<ChainSelector v-model="selectedChain" />
|
|
108
|
+
</template>
|
|
109
|
+
|
|
110
|
+
<template #body>
|
|
111
|
+
<NetworkSelector v-if="account" class="sidebar-body" />
|
|
112
|
+
</template>
|
|
113
|
+
|
|
114
|
+
<!-- Sidebar foot holds only the wallet control (contextual). Copyright, docs links, and
|
|
115
|
+
status now live in the shell footer to free vertical space as the sidebar grows. -->
|
|
116
|
+
<template #foot>
|
|
117
|
+
<p v-if="account" class="sidebar-foot wallet-connected">
|
|
118
|
+
<CopyableAddress :address="account.address">
|
|
119
|
+
<ExplorerLink v-if="accountExplorerHref" :href="accountExplorerHref" :value="account.address" />
|
|
120
|
+
</CopyableAddress>
|
|
121
|
+
<button type="button" class="wallet-disconnect" @click="disconnect">Disconnect</button>
|
|
122
|
+
</p>
|
|
123
|
+
<p v-else class="sidebar-foot wallet-disconnected">No wallet connected</p>
|
|
124
|
+
</template>
|
|
125
|
+
</AppSidebar>
|
|
126
|
+
|
|
127
|
+
<main class="app-shell__main">
|
|
128
|
+
<RouterView />
|
|
129
|
+
</main>
|
|
130
|
+
|
|
131
|
+
<AppFooter class="app-shell__footer" variant="dark" :docs-url="DOCS_URL" :dev-url="DEV_URL">
|
|
132
|
+
<template #start>
|
|
133
|
+
<CopyrightLine symbolVariant="kopimi" organisation-name="Meddleware" rightsStatement="jam" />
|
|
134
|
+
</template>
|
|
135
|
+
<StatusWidget class="footer-status" />
|
|
136
|
+
</AppFooter>
|
|
137
|
+
</div>
|
|
138
|
+
</template>
|
|
139
|
+
|
|
140
|
+
<style scoped>
|
|
141
|
+
.app-shell {
|
|
142
|
+
display: grid;
|
|
143
|
+
grid-template-columns: var(--mw-sidebar-width, 240px) 1fr;
|
|
144
|
+
grid-template-rows: var(--mw-header-height, 56px) 1fr auto;
|
|
145
|
+
height: 100dvh;
|
|
146
|
+
overflow: hidden;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
.app-shell__header {
|
|
150
|
+
grid-column: 1 / -1;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
.app-shell__footer {
|
|
154
|
+
grid-column: 1 / -1;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.footer-status {
|
|
158
|
+
font-size: var(--font-size-sm);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.app-shell__sidebar {
|
|
162
|
+
min-height: 0;
|
|
163
|
+
overflow: hidden;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.app-shell__main {
|
|
167
|
+
display: flex;
|
|
168
|
+
flex-direction: column;
|
|
169
|
+
overflow-y: auto;
|
|
170
|
+
min-height: 0;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
@media (width <=720px) {
|
|
174
|
+
.app-shell {
|
|
175
|
+
grid-template-columns: 1fr;
|
|
176
|
+
grid-template-rows: var(--mw-header-height, 56px) 1fr auto;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
.app-shell__sidebar {
|
|
180
|
+
display: none;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
.brand-link {
|
|
185
|
+
display: flex;
|
|
186
|
+
align-items: center;
|
|
187
|
+
gap: 0.5rem;
|
|
188
|
+
color: inherit;
|
|
189
|
+
text-decoration: none;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.brand-mark {
|
|
193
|
+
color: var(--accent);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/* ── Up-one-level control ──────────────────────────────── */
|
|
197
|
+
.sidebar-up {
|
|
198
|
+
display: inline-flex;
|
|
199
|
+
align-items: center;
|
|
200
|
+
gap: 0.35rem;
|
|
201
|
+
background: none;
|
|
202
|
+
border: none;
|
|
203
|
+
/* Left padding matches SidebarItem so "← Back" aligns with the items below. */
|
|
204
|
+
padding: 0.2rem var(--space-sm);
|
|
205
|
+
margin-bottom: 0.35rem;
|
|
206
|
+
color: var(--muted);
|
|
207
|
+
font-size: 0.8rem;
|
|
208
|
+
cursor: pointer;
|
|
209
|
+
border-radius: 0;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
.sidebar-up:hover {
|
|
213
|
+
color: var(--text);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
.sidebar-up:focus-visible {
|
|
217
|
+
outline: 2px solid var(--focus-ring);
|
|
218
|
+
outline-offset: 2px;
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
/* ── Sidebar body/foot ──────────────────────────────────
|
|
222
|
+
AppSidebar renders these slots unwrapped, so the shell styles its own content. */
|
|
223
|
+
.sidebar-body {
|
|
224
|
+
flex-shrink: 0;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
.sidebar-foot {
|
|
228
|
+
flex-shrink: 0;
|
|
229
|
+
margin: 0;
|
|
230
|
+
/* Extra bottom space keeps the wallet control clear of the shell footer's border. */
|
|
231
|
+
padding: var(--space-xs) var(--space-sm) calc(var(--space-xs) + 0.5rem);
|
|
232
|
+
border-top: 1px solid var(--_border);
|
|
233
|
+
color: var(--_muted);
|
|
234
|
+
font-size: var(--font-size-sm);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
.wallet-connected {
|
|
238
|
+
display: flex;
|
|
239
|
+
align-items: center;
|
|
240
|
+
justify-content: space-between;
|
|
241
|
+
gap: 0.5rem;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
.wallet-disconnect {
|
|
245
|
+
background: none;
|
|
246
|
+
border: none;
|
|
247
|
+
padding: 0;
|
|
248
|
+
color: var(--muted);
|
|
249
|
+
font-size: 0.8rem;
|
|
250
|
+
cursor: pointer;
|
|
251
|
+
white-space: nowrap;
|
|
252
|
+
text-decoration: underline;
|
|
253
|
+
text-underline-offset: 2px;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
.wallet-disconnect:hover {
|
|
257
|
+
color: var(--text);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
.wallet-disconnected {
|
|
261
|
+
display: flex;
|
|
262
|
+
align-items: center;
|
|
263
|
+
gap: 0.45rem;
|
|
264
|
+
font-size: 0.8rem;
|
|
265
|
+
color: var(--muted);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
/* Decorative status dot (the text states the status). */
|
|
269
|
+
.wallet-disconnected::before {
|
|
270
|
+
content: '';
|
|
271
|
+
width: 6px;
|
|
272
|
+
height: 6px;
|
|
273
|
+
border-radius: 50%;
|
|
274
|
+
background: var(--muted, #666);
|
|
275
|
+
flex-shrink: 0;
|
|
276
|
+
}
|
|
277
|
+
</style>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Modular blockchain selector shown in the sidebar while the Blockchain section is open.
|
|
3
|
+
// Data-driven so new chains are a one-line addition; defaults to Sui (the only live chain).
|
|
4
|
+
import { SidebarItem } from '@meddleware/ui'
|
|
5
|
+
|
|
6
|
+
interface Chain {
|
|
7
|
+
id: string
|
|
8
|
+
label: string
|
|
9
|
+
icon: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const CHAINS: Chain[] = [
|
|
13
|
+
{ id: 'sui', label: 'Sui', icon: '🌊' },
|
|
14
|
+
// Future chains slot in here — the Blockchain view swaps tool sets by the selected id.
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
const model = defineModel<string>({ default: 'sui' })
|
|
18
|
+
</script>
|
|
19
|
+
|
|
20
|
+
<template>
|
|
21
|
+
<div class="chain-selector">
|
|
22
|
+
<div class="chain-selector__title">Select chain</div>
|
|
23
|
+
<SidebarItem
|
|
24
|
+
v-for="c in CHAINS"
|
|
25
|
+
:key="c.id"
|
|
26
|
+
:label="c.label"
|
|
27
|
+
:icon="c.icon"
|
|
28
|
+
:active="model === c.id"
|
|
29
|
+
@click="model = c.id"
|
|
30
|
+
/>
|
|
31
|
+
</div>
|
|
32
|
+
</template>
|
|
33
|
+
|
|
34
|
+
<style scoped>
|
|
35
|
+
.chain-selector__title {
|
|
36
|
+
font-size: 0.7rem;
|
|
37
|
+
letter-spacing: var(--tracking-wide);
|
|
38
|
+
text-transform: uppercase;
|
|
39
|
+
color: var(--muted);
|
|
40
|
+
opacity: 0.75;
|
|
41
|
+
/* Left margin matches SidebarItem's horizontal padding so the label aligns with the items. */
|
|
42
|
+
margin: 0 0 var(--space-2xs) var(--space-sm);
|
|
43
|
+
}
|
|
44
|
+
</style>
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Network selector for the dashboard sidebar. Reads and writes the shared useNetwork()
|
|
3
|
+
// singleton so every embedded tool view reacts to the chosen network immediately.
|
|
4
|
+
import { ref, watch, onMounted } from 'vue'
|
|
5
|
+
import { useNetwork } from '@meddleware/wallet-adapter'
|
|
6
|
+
import type { MwNetwork } from '@meddleware/wallet-adapter'
|
|
7
|
+
|
|
8
|
+
const { network, localnetRpc, setNetwork, setLocalnetRpc } = useNetwork()
|
|
9
|
+
|
|
10
|
+
const localnetInput = ref(localnetRpc.value)
|
|
11
|
+
|
|
12
|
+
// Sync input when singleton changes externally (e.g. another tab via storage event).
|
|
13
|
+
watch(localnetRpc, (v) => { localnetInput.value = v })
|
|
14
|
+
|
|
15
|
+
function onNetworkChange(e: Event): void {
|
|
16
|
+
setNetwork((e.target as HTMLSelectElement).value as MwNetwork)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function commitLocalnetUrl(): void {
|
|
20
|
+
const url = localnetInput.value.trim()
|
|
21
|
+
if (url) setLocalnetRpc(url)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
onMounted(() => {
|
|
25
|
+
if (network.value === 'mainnet') setNetwork('testnet')
|
|
26
|
+
})
|
|
27
|
+
</script>
|
|
28
|
+
|
|
29
|
+
<template>
|
|
30
|
+
<div class="ns-root">
|
|
31
|
+
<label class="ns-label" for="ns-select">Network</label>
|
|
32
|
+
<select id="ns-select" class="ns-select" :value="network" @change="onNetworkChange">
|
|
33
|
+
<option value="testnet">Testnet</option>
|
|
34
|
+
<option value="mainnet" disabled title="Coming soon">Mainnet</option>
|
|
35
|
+
<option value="localnet">Localnet</option>
|
|
36
|
+
</select>
|
|
37
|
+
|
|
38
|
+
<template v-if="network === 'localnet'">
|
|
39
|
+
<label class="ns-label ns-label--url" for="ns-localnet-url">RPC URL</label>
|
|
40
|
+
<input
|
|
41
|
+
id="ns-localnet-url"
|
|
42
|
+
v-model="localnetInput"
|
|
43
|
+
type="url"
|
|
44
|
+
class="ns-input"
|
|
45
|
+
placeholder="http://127.0.0.1:9000"
|
|
46
|
+
@blur="commitLocalnetUrl"
|
|
47
|
+
@keydown.enter="commitLocalnetUrl"
|
|
48
|
+
/>
|
|
49
|
+
</template>
|
|
50
|
+
</div>
|
|
51
|
+
</template>
|
|
52
|
+
|
|
53
|
+
<style scoped>
|
|
54
|
+
.ns-root {
|
|
55
|
+
display: flex;
|
|
56
|
+
flex-direction: column;
|
|
57
|
+
gap: 0.35rem;
|
|
58
|
+
padding: 0.75rem 1rem;
|
|
59
|
+
border-top: 1px solid var(--border, #333);
|
|
60
|
+
border-bottom: 1px solid var(--border, #333);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.ns-label {
|
|
64
|
+
font-size: 0.7rem;
|
|
65
|
+
text-transform: uppercase;
|
|
66
|
+
letter-spacing: 0.06em;
|
|
67
|
+
color: var(--muted, #888);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.ns-label--url {
|
|
71
|
+
margin-top: 0.25rem;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.ns-select {
|
|
75
|
+
appearance: none;
|
|
76
|
+
background: var(--surface, transparent)
|
|
77
|
+
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='%23888'/%3E%3C/svg%3E")
|
|
78
|
+
no-repeat right 0.6rem center;
|
|
79
|
+
border: 1px solid var(--border, #444);
|
|
80
|
+
border-radius: 6px;
|
|
81
|
+
color: var(--text, #f0f0f0);
|
|
82
|
+
font-size: 0.85rem;
|
|
83
|
+
padding: 0.35rem 1.75rem 0.35rem 0.6rem;
|
|
84
|
+
cursor: pointer;
|
|
85
|
+
width: 100%;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
.ns-select:focus {
|
|
89
|
+
outline: 2px solid var(--accent, #6366f1);
|
|
90
|
+
outline-offset: 1px;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.ns-input {
|
|
94
|
+
background: var(--surface, transparent);
|
|
95
|
+
border: 1px solid var(--border, #444);
|
|
96
|
+
border-radius: 6px;
|
|
97
|
+
color: var(--text, #f0f0f0);
|
|
98
|
+
font-size: 0.8rem;
|
|
99
|
+
font-family: monospace;
|
|
100
|
+
padding: 0.35rem 0.6rem;
|
|
101
|
+
width: 100%;
|
|
102
|
+
box-sizing: border-box;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.ns-input:focus {
|
|
106
|
+
outline: 2px solid var(--accent, #6366f1);
|
|
107
|
+
outline-offset: 1px;
|
|
108
|
+
}
|
|
109
|
+
</style>
|
package/src/env.d.ts
ADDED
package/src/main.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { createApp } from 'vue'
|
|
2
|
+
|
|
3
|
+
// Shared design system: tokens first (defines CSS vars), then base element defaults.
|
|
4
|
+
import '@meddleware/design-tokens/tokens.css'
|
|
5
|
+
import '@meddleware/design-tokens/seasons.css'
|
|
6
|
+
import '@meddleware/ui/base.css'
|
|
7
|
+
import './styles.css'
|
|
8
|
+
|
|
9
|
+
import { useColorMode, useSeason } from '@meddleware/ui'
|
|
10
|
+
import App from './App.vue'
|
|
11
|
+
import router from './router'
|
|
12
|
+
|
|
13
|
+
// Default to dark on first load (persisted to localStorage thereafter).
|
|
14
|
+
// Applied before mount to avoid a light-mode flash.
|
|
15
|
+
useColorMode('dark')
|
|
16
|
+
useSeason()
|
|
17
|
+
|
|
18
|
+
const app = createApp(App)
|
|
19
|
+
app.use(router)
|
|
20
|
+
app.mount('#app')
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { createRouter, createWebHistory } from 'vue-router'
|
|
2
|
+
|
|
3
|
+
// Two org-level sections:
|
|
4
|
+
// / → Treasury console (organisation accounting), the default landing route.
|
|
5
|
+
// /blockchain → Blockchain tools hub: a qt-style tabbed interface over the Sui tools.
|
|
6
|
+
// Each view/tool renders inline via the component exported from its own package (no iframes),
|
|
7
|
+
// sharing the dashboard's wallet through the @meddleware/wallet-adapter singleton. Routes are
|
|
8
|
+
// lazy so a tool's deps (incl. the Walrus wasm) load only on navigation.
|
|
9
|
+
const router = createRouter({
|
|
10
|
+
history: createWebHistory(import.meta.env.BASE_URL),
|
|
11
|
+
routes: [
|
|
12
|
+
{
|
|
13
|
+
path: '/',
|
|
14
|
+
name: 'treasury',
|
|
15
|
+
component: () => import('@meddleware/treasury-ui').then((m) => m.TreasuryView),
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
path: '/blockchain',
|
|
19
|
+
name: 'blockchain',
|
|
20
|
+
component: () => import('../views/BlockchainView.vue'),
|
|
21
|
+
},
|
|
22
|
+
// DAO is temporarily retired from the dashboard (the @meddleware/dao-ui dependency is
|
|
23
|
+
// retained). Re-enable this route once DAO participation ships so users aren't shown a
|
|
24
|
+
// governance surface they can't yet act on.
|
|
25
|
+
// {
|
|
26
|
+
// path: '/dao',
|
|
27
|
+
// name: 'dao',
|
|
28
|
+
// component: () => import('@meddleware/dao-ui').then((m) => m.DaoView),
|
|
29
|
+
// },
|
|
30
|
+
|
|
31
|
+
// Legacy per-tool paths now live as tabs inside /blockchain — redirect old bookmarks.
|
|
32
|
+
{ path: '/walrus', redirect: '/blockchain' },
|
|
33
|
+
{ path: '/walrus-storage', redirect: '/blockchain' },
|
|
34
|
+
{ path: '/sealed-storage', redirect: '/blockchain' },
|
|
35
|
+
{ path: '/access-gate', redirect: '/blockchain' },
|
|
36
|
+
{ path: '/token-deployer', redirect: '/blockchain' },
|
|
37
|
+
{ path: '/token-issuance', redirect: '/blockchain' },
|
|
38
|
+
],
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
export default router
|
package/src/styles.css
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Dashboard app-layout styles. Design tokens + element defaults come from
|
|
3
|
+
* @meddleware/design-tokens + @meddleware/ui/base.css (imported in main.ts).
|
|
4
|
+
* App shell grid layout lives in App.vue scoped styles.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
#app {
|
|
8
|
+
min-height: 100dvh;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
.prose {
|
|
12
|
+
max-width: 68ch;
|
|
13
|
+
color: var(--muted);
|
|
14
|
+
}
|
|
15
|
+
.prose h2 {
|
|
16
|
+
color: var(--text);
|
|
17
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
// Blockchain tools hub — a qt-style tabbed console over one chain's tools. Today the only
|
|
3
|
+
// chain is Sui; the component takes a `chain` prop so a future multi-chain sidebar selector can
|
|
4
|
+
// switch tool sets without a rewrite. Each tool view is lazy-loaded (its deps, incl. the Walrus
|
|
5
|
+
// wasm, load only when its tab is first opened) and kept alive so switching tabs preserves state.
|
|
6
|
+
import { computed, defineAsyncComponent, ref, watch } from 'vue'
|
|
7
|
+
import { AppTabNav, UiTabPanel, type AppTab } from '@meddleware/ui'
|
|
8
|
+
|
|
9
|
+
const props = withDefaults(defineProps<{ chain?: string }>(), { chain: 'sui' })
|
|
10
|
+
|
|
11
|
+
interface Tool extends AppTab {
|
|
12
|
+
comp: ReturnType<typeof defineAsyncComponent>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Per-chain tool sets. Add a new key when onboarding a chain.
|
|
16
|
+
const TOOLSETS: Record<string, Tool[]> = {
|
|
17
|
+
sui: [
|
|
18
|
+
{ id: 'walrus-storage', label: 'Walrus Storage', comp: defineAsyncComponent(() => import('@meddleware/walrus-storage-ui').then((m) => m.WalrusStorageView)) },
|
|
19
|
+
{ id: 'sealed-storage', label: 'Sealed Storage', comp: defineAsyncComponent(() => import('@meddleware/sealed-storage-ui').then((m) => m.SealedStorageView)) },
|
|
20
|
+
{ id: 'access-gate', label: 'Access Gate', comp: defineAsyncComponent(() => import('@meddleware/access-gate-ui').then((m) => m.AccessGateView)) },
|
|
21
|
+
{ id: 'token-issuance', label: 'Token Issuance', comp: defineAsyncComponent(() => import('@meddleware/token-issuance-ui').then((m) => m.TokenIssuanceView)) },
|
|
22
|
+
],
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const tools = computed<Tool[]>(() => TOOLSETS[props.chain] ?? [])
|
|
26
|
+
const tabs = computed<AppTab[]>(() => tools.value.map(({ id, label }) => ({ id, label })))
|
|
27
|
+
const active = ref(tools.value[0]?.id ?? '')
|
|
28
|
+
|
|
29
|
+
// Reset to the first tool when the chain changes.
|
|
30
|
+
watch(
|
|
31
|
+
() => props.chain,
|
|
32
|
+
() => {
|
|
33
|
+
active.value = tools.value[0]?.id ?? ''
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
const activeComp = computed(() => tools.value.find((t) => t.id === active.value)?.comp)
|
|
38
|
+
</script>
|
|
39
|
+
|
|
40
|
+
<template>
|
|
41
|
+
<!-- Fills the shell's main area exactly, so main never scrolls on this page: the tool tabs stay
|
|
42
|
+
in view and only the tool panel scrolls (its scrollbar at the right edge of the area). -->
|
|
43
|
+
<div class="blockchain-view">
|
|
44
|
+
<AppTabNav
|
|
45
|
+
v-model="active"
|
|
46
|
+
:tabs="tabs"
|
|
47
|
+
id-prefix="blockchain"
|
|
48
|
+
variant="raised"
|
|
49
|
+
size="lg"
|
|
50
|
+
class="blockchain-view__tabs"
|
|
51
|
+
aria-label="Sui tools"
|
|
52
|
+
/>
|
|
53
|
+
|
|
54
|
+
<UiTabPanel id-prefix="blockchain" :tab="active" class="blockchain-view__content">
|
|
55
|
+
<KeepAlive>
|
|
56
|
+
<component :is="activeComp" :key="active" />
|
|
57
|
+
</KeepAlive>
|
|
58
|
+
</UiTabPanel>
|
|
59
|
+
</div>
|
|
60
|
+
</template>
|
|
61
|
+
|
|
62
|
+
<style scoped>
|
|
63
|
+
.blockchain-view {
|
|
64
|
+
display: flex;
|
|
65
|
+
flex-direction: column;
|
|
66
|
+
height: 100%;
|
|
67
|
+
min-height: 0;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
.blockchain-view__tabs {
|
|
71
|
+
flex: none;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/* The only scroll container on this page. It spans the full width of the main area (so its
|
|
75
|
+
scrollbar sits at the area's edge, not beside the content column); the content column is centred
|
|
76
|
+
and capped at --mw-tool-content-max by inline padding that grows with the spare width. The
|
|
77
|
+
panel is focusable (UiTabPanel), so keyboard users can scroll it. */
|
|
78
|
+
.blockchain-view__content {
|
|
79
|
+
flex: 1;
|
|
80
|
+
display: flex;
|
|
81
|
+
flex-direction: column;
|
|
82
|
+
min-height: 0;
|
|
83
|
+
overflow-y: auto;
|
|
84
|
+
scrollbar-gutter: stable;
|
|
85
|
+
/* Consecutive Fibonacci steps of the spacing scale (13 : 8 : 5 units), so top : bottom and
|
|
86
|
+
bottom : sides are each ≈ φ — generous air under the tabs, tighter at the foot and sides. */
|
|
87
|
+
padding-block: var(--space-lg) var(--space-md);
|
|
88
|
+
padding-inline: max(var(--space-sm), calc((100% - var(--mw-tool-content-max)) / 2 + var(--space-sm)));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/* This page only: the tool tabs sit flush — no inset around the strip and no gaps between tabs.
|
|
92
|
+
Overrides AppTabNav's shared raised/lg spacing for this instance; other tab strips keep theirs.
|
|
93
|
+
The modifier classes match the component's own selector so this wins regardless of CSS order. */
|
|
94
|
+
.blockchain-view__tabs.mw-tab-nav--raised.mw-tab-nav--lg {
|
|
95
|
+
gap: 0;
|
|
96
|
+
padding: 0;
|
|
97
|
+
}
|
|
98
|
+
</style>
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"lib": ["ESNext", "DOM", "DOM.Iterable"],
|
|
7
|
+
"strict": true,
|
|
8
|
+
"esModuleInterop": true,
|
|
9
|
+
"skipLibCheck": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"verbatimModuleSyntax": true,
|
|
13
|
+
"jsx": "preserve",
|
|
14
|
+
"types": ["node"],
|
|
15
|
+
"noEmit": true
|
|
16
|
+
},
|
|
17
|
+
"include": ["src/**/*.ts", "src/**/*.vue", "src/**/*.d.ts", "tests/**/*.ts", "e2e/**/*.ts", "vite.config.ts", "vitest.config.ts", "playwright.config.ts"]
|
|
18
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
|
|
4
|
+
// @meddleware/* resolve from node_modules (published packages — no monorepo aliases).
|
|
5
|
+
export default defineConfig({
|
|
6
|
+
plugins: [vue()],
|
|
7
|
+
optimizeDeps: {
|
|
8
|
+
// The Walrus wasm client is dynamically imported inside the inline WalrusStorageView upload flow;
|
|
9
|
+
// keep it out of the eager dep-optimization graph so it stays a lazy chunk.
|
|
10
|
+
// @meddleware/wallet-adapter ships TS + .vue source and holds the shared wallet singleton; if it
|
|
11
|
+
// were pre-bundled, its .vue files (served raw) would load a second copy of the singleton in dev.
|
|
12
|
+
exclude: ['@mysten/walrus', '@mysten/walrus-wasm', '@meddleware/wallet-adapter'],
|
|
13
|
+
},
|
|
14
|
+
})
|
package/README.md
DELETED