@meddleware/ui 0.1.9 → 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,6 +53,24 @@ All three accept a `variant` prop:
53
53
  | `UiSelect` | Styled select input. |
54
54
  | `UiNotice` | Notice / alert banner. |
55
55
  | `SidebarItem` | Navigation button for use inside `AppSidebar`. Accepts `label`, `icon`, `active`, and `disabled` props. |
56
+ | `CopyableAddress` | Shows a value (address/blob id/tx digest) with a dedicated **copy icon** (the value text is not the copy trigger). Put a link in the default slot to make it both linkable and copyable. Props: `address`, `truncate`, `chars`, `label`. |
57
+ | `ExplorerLink` | External block-explorer link with a truncated label (or slot). Chain-agnostic — pass `href` (build it with `suiExplorerUrl`, or an app helper such as a Walruscan URL). Nest inside `CopyableAddress` for link + copy. |
58
+
59
+ Copy + link compose — nest `ExplorerLink` in `CopyableAddress` so the value links out while the
60
+ icon copies:
61
+
62
+ ```vue
63
+ <CopyableAddress :address="addr">
64
+ <ExplorerLink :href="suiExplorerUrl('account', addr, 'testnet')" :value="addr" />
65
+ </CopyableAddress>
66
+ ```
67
+
68
+ ### Explorer helper
69
+
70
+ | Export | Type | Description |
71
+ | --- | --- | --- |
72
+ | `suiExplorerUrl(kind, id, network?)` | Function | Builds a SuiVision URL. `kind`: `'account' \| 'object' \| 'txblock'`; `network` defaults to `'testnet'`. Single source of truth for Sui explorer links. |
73
+ | `SuiNetwork`, `SuiExplorerKind` | Types | `'mainnet' \| 'testnet' \| 'devnet'` and the link kinds. |
56
74
 
57
75
  ### Colour mode
58
76
 
package/dist/base.css CHANGED
@@ -1,2 +1,2 @@
1
- *,:before,:after{box-sizing:border-box}html,body{margin:0;padding:0}body{background:var(--bg);min-height:100vh;color:var(--text);font-family:var(--mw-font-sans);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;font-size:16px;line-height:1.55}:where(ul,ol):where([role=list],[role=listitem]){margin:0;padding:0;list-style:none}:where(img,svg,video,canvas){max-width:100%;display:block}:where(fieldset){border:0;margin:0;padding:0}a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}:where(button,input,select,textarea){font-family:inherit}:where(a,button,input,select,textarea):focus-visible{outline:2px solid var(--accent);outline-offset:2px}.mw-mono{font-family:var(--mw-font-mono)}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.001ms!important;animation-duration:.001ms!important}}.mw-header[data-v-ad7cd1c5]{height:var(--mw-header-height,56px);background:var(--_bg);color:var(--_text);border-bottom:1px solid var(--_border);font-family:var(--mw-font-sans);align-items:center;gap:1rem;padding:0 1rem;display:flex}.mw-header--sticky[data-v-ad7cd1c5]{z-index:50;position:sticky;top:0}.mw-header__brand[data-v-ad7cd1c5]{letter-spacing:.01em;white-space:nowrap;align-items:center;gap:.5rem;font-weight:650;display:flex}.mw-header__center[data-v-ad7cd1c5]{flex:auto;min-width:0}.mw-header__actions[data-v-ad7cd1c5]{align-items:center;gap:.5rem;margin-left:auto;display:flex}.mw-sidebar[data-v-23c3c7f3]{height:100%;width:var(--mw-sidebar-width,240px);background:var(--_bg);color:var(--_text);border-right:1px solid var(--_border);font-family:var(--mw-font-sans);flex-direction:column;display:flex}.mw-sidebar__head[data-v-23c3c7f3]{border-bottom:1px solid var(--_border);padding:1rem}.mw-sidebar__nav[data-v-23c3c7f3]{flex-direction:column;flex:auto;gap:.25rem;padding:.75rem;display:flex;overflow-y:auto}.mw-sidebar__body[data-v-23c3c7f3]{flex-shrink:0}.mw-sidebar__foot[data-v-23c3c7f3]{border-top:1px solid var(--_border);color:var(--_muted);padding:.75rem 1rem;font-size:.85rem}.mw-footer[data-v-c646b8e1]{background:var(--_bg);color:var(--_muted);border-top:1px solid var(--_border);font-family:var(--mw-font-sans);flex-wrap:wrap;align-items:center;gap:1rem;padding:.75rem 1rem;font-size:.85rem;display:flex}.mw-footer--transparent[data-v-c646b8e1]{border-top:1px solid var(--border)}.mw-footer__center[data-v-c646b8e1]{flex:auto;min-width:0}.mw-footer__end[data-v-c646b8e1]{margin-left:auto}.mw-mode[data-v-e4a6bf1d]{border:1px solid var(--_border,var(--border));border-radius:var(--mw-radius,10px);background:color-mix(in srgb, var(--_text,var(--text)) 6%, transparent);gap:2px;padding:2px;display:inline-flex}.mw-mode__btn[data-v-e4a6bf1d]{border-radius:calc(var(--mw-radius,10px) - 3px);color:var(--_text,var(--text));font:inherit;cursor:pointer;opacity:.7;background:0 0;border:0;align-items:center;gap:.35rem;padding:.25rem .5rem;font-size:.8rem;display:inline-flex}.mw-mode__btn[data-v-e4a6bf1d]:hover{opacity:1}.mw-mode__btn.is-active[data-v-e4a6bf1d]{opacity:1;background:color-mix(in srgb, var(--_text,var(--text)) 14%, transparent)}.mw-mode__btn[data-v-e4a6bf1d]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}@media (width<=640px){.mw-mode__label[data-v-e4a6bf1d]{display:none}}.mw-btn[data-v-299de2c3]{border-radius:var(--mw-radius,10px);font:inherit;cursor:pointer;border:1px solid #0000;justify-content:center;align-items:center;gap:.4rem;padding:.5rem .9rem;font-weight:550;transition:filter .12s,background .12s;display:inline-flex}.mw-btn[data-v-299de2c3]:disabled{opacity:.55;cursor:not-allowed}.mw-btn[data-v-299de2c3]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.mw-btn--primary[data-v-299de2c3]{background:var(--accent);color:var(--accent-contrast)}.mw-btn--primary[data-v-299de2c3]:not(:disabled):hover{filter:brightness(1.08)}.mw-btn--secondary[data-v-299de2c3]{background:var(--secondary);color:var(--secondary-contrast)}.mw-btn--secondary[data-v-299de2c3]:not(:disabled):hover{filter:brightness(1.08)}.mw-btn--ghost[data-v-299de2c3]{color:var(--text);border-color:var(--border);background:0 0}.mw-btn--ghost[data-v-299de2c3]:not(:disabled):hover{background:var(--lift)}.mw-btn--danger[data-v-299de2c3]{background:var(--danger);color:var(--mw-neutral-000)}.mw-btn--danger[data-v-299de2c3]:not(:disabled):hover{filter:brightness(1.08)}.mw-card[data-v-fd2fd7ab]{background:var(--surface);border:1px solid var(--border);border-radius:var(--mw-radius,10px);color:var(--text);overflow:hidden}.mw-card__header[data-v-fd2fd7ab]{border-bottom:1px solid var(--border);padding:.85rem 1rem}.mw-card__title[data-v-fd2fd7ab]{margin:0;font-size:1rem;font-weight:600}.mw-card__body[data-v-fd2fd7ab]{padding:1rem}.mw-card__footer[data-v-fd2fd7ab]{border-top:1px solid var(--border);background:var(--lift);padding:.75rem 1rem}.mw-select[data-v-b9468387]{position:relative}.mw-select select[data-v-b9468387]{appearance:none;background:var(--surface);width:100%;color:var(--text);border:1px solid var(--border);border-radius:var(--mw-radius,10px);font:inherit;padding:.5rem 2.5rem .5rem .7rem}.mw-select select[data-v-b9468387]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.mw-select__chevron[data-v-b9468387]{pointer-events:none;stroke:var(--muted);stroke-width:1.5px;fill:none;position:absolute;top:50%;right:.75rem;transform:translateY(-50%)}.mw-notice[data-v-6f53d0be]{border:1px solid var(--border);border-radius:var(--mw-radius,10px);background:var(--lift);color:var(--text);border-left-width:3px;margin:0;padding:.6rem .8rem;font-size:.9rem}.mw-notice--error[data-v-6f53d0be]{border-left-color:var(--danger);color:var(--danger);background:color-mix(in srgb, var(--danger) 8%, transparent)}.mw-notice--ok[data-v-6f53d0be]{border-left-color:var(--ok);color:var(--ok);background:color-mix(in srgb, var(--ok) 8%, transparent)}.sidebar-item[data-v-e15afdf7]{border-radius:var(--mw-radius,10px);width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;opacity:.85;background:0 0;border:0;align-items:center;gap:.6rem;padding:.55rem .7rem;display:flex}.sidebar-item[data-v-e15afdf7]:hover:not(:disabled){background:color-mix(in srgb, currentColor 10%, transparent);opacity:1}.sidebar-item.is-active[data-v-e15afdf7]{background:color-mix(in srgb, var(--accent) 26%, transparent);opacity:1}.sidebar-item[data-v-e15afdf7]:disabled{cursor:default}.sidebar-item[data-v-e15afdf7]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.sidebar-item__icon[data-v-e15afdf7]{text-align:center;width:1.1rem}.ca-root[data-v-e2d23de9]{font-family:monospace;font-size:inherit;color:var(--muted,#888);cursor:pointer;text-underline-offset:2px;background:0 0;border:none;padding:0;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;transition:color .15s;display:inline-block}.ca-root[data-v-e2d23de9]:hover{color:var(--text,#f0f0f0)}.ca-root--copied[data-v-e2d23de9]{color:var(--accent,#6366f1);text-decoration:none}.status-widget[data-v-20197983]{color:inherit;opacity:.85;align-items:center;gap:.4rem;font-size:.8rem;text-decoration:none;display:inline-flex}.status-widget[data-v-20197983]:hover{opacity:1}.status-widget__dot[data-v-20197983]{border-radius:50%;flex-shrink:0;width:.55rem;height:.55rem}.status-widget--ok .status-widget__dot[data-v-20197983]{background:var(--mw-ok-500)}.status-widget--degraded .status-widget__dot[data-v-20197983]{background:var(--mw-gold-500)}.status-widget--error .status-widget__dot[data-v-20197983]{background:var(--mw-danger-500)}
1
+ *,:before,:after{box-sizing:border-box}html,body{margin:0;padding:0}body{background:var(--bg);min-height:100vh;color:var(--text);font-family:var(--mw-font-sans);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;font-size:16px;line-height:1.55}:where(ul,ol):where([role=list],[role=listitem]){margin:0;padding:0;list-style:none}:where(img,svg,video,canvas){max-width:100%;display:block}:where(fieldset){border:0;margin:0;padding:0}a{color:var(--accent);text-decoration:none}a:hover{text-decoration:underline}:where(button,input,select,textarea){font-family:inherit}:where(a,button,input,select,textarea):focus-visible{outline:2px solid var(--accent);outline-offset:2px}.mw-mono{font-family:var(--mw-font-mono)}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.001ms!important;animation-duration:.001ms!important}}.mw-header[data-v-ad7cd1c5]{height:var(--mw-header-height,56px);background:var(--_bg);color:var(--_text);border-bottom:1px solid var(--_border);font-family:var(--mw-font-sans);align-items:center;gap:1rem;padding:0 1rem;display:flex}.mw-header--sticky[data-v-ad7cd1c5]{z-index:50;position:sticky;top:0}.mw-header__brand[data-v-ad7cd1c5]{letter-spacing:.01em;white-space:nowrap;align-items:center;gap:.5rem;font-weight:650;display:flex}.mw-header__center[data-v-ad7cd1c5]{flex:auto;min-width:0}.mw-header__actions[data-v-ad7cd1c5]{align-items:center;gap:.5rem;margin-left:auto;display:flex}.mw-sidebar[data-v-23c3c7f3]{height:100%;width:var(--mw-sidebar-width,240px);background:var(--_bg);color:var(--_text);border-right:1px solid var(--_border);font-family:var(--mw-font-sans);flex-direction:column;display:flex}.mw-sidebar__head[data-v-23c3c7f3]{border-bottom:1px solid var(--_border);padding:1rem}.mw-sidebar__nav[data-v-23c3c7f3]{flex-direction:column;flex:auto;gap:.25rem;padding:.75rem;display:flex;overflow-y:auto}.mw-sidebar__body[data-v-23c3c7f3]{flex-shrink:0}.mw-sidebar__foot[data-v-23c3c7f3]{border-top:1px solid var(--_border);color:var(--_muted);padding:.75rem 1rem;font-size:.85rem}.mw-footer[data-v-c646b8e1]{background:var(--_bg);color:var(--_muted);border-top:1px solid var(--_border);font-family:var(--mw-font-sans);flex-wrap:wrap;align-items:center;gap:1rem;padding:.75rem 1rem;font-size:.85rem;display:flex}.mw-footer--transparent[data-v-c646b8e1]{border-top:1px solid var(--border)}.mw-footer__center[data-v-c646b8e1]{flex:auto;min-width:0}.mw-footer__end[data-v-c646b8e1]{margin-left:auto}.mw-mode[data-v-186f8312]{border:1px solid var(--_border,var(--border));border-radius:var(--mw-radius,10px);background:color-mix(in srgb, var(--_text,var(--text)) 6%, transparent);gap:2px;padding:2px;display:inline-flex}.mw-mode__btn[data-v-186f8312]{border-radius:calc(var(--mw-radius,10px) - 3px);color:var(--_text,var(--text));font:inherit;cursor:pointer;opacity:.7;background:0 0;border:0;align-items:center;gap:.35rem;padding:.25rem .5rem;font-size:.8rem;display:inline-flex}.mw-mode__btn[data-v-186f8312]:hover{opacity:1}.mw-mode__btn.is-active[data-v-186f8312]{opacity:1;background:color-mix(in srgb, var(--_text,var(--text)) 14%, transparent)}.mw-mode__btn[data-v-186f8312]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}@media (width<=640px){.mw-mode__label[data-v-186f8312]{display:none}}.mw-btn[data-v-299de2c3]{border-radius:var(--mw-radius,10px);font:inherit;cursor:pointer;border:1px solid #0000;justify-content:center;align-items:center;gap:.4rem;padding:.5rem .9rem;font-weight:550;transition:filter .12s,background .12s;display:inline-flex}.mw-btn[data-v-299de2c3]:disabled{opacity:.55;cursor:not-allowed}.mw-btn[data-v-299de2c3]:focus-visible{outline:2px solid var(--accent);outline-offset:2px}.mw-btn--primary[data-v-299de2c3]{background:var(--accent);color:var(--accent-contrast)}.mw-btn--primary[data-v-299de2c3]:not(:disabled):hover{filter:brightness(1.08)}.mw-btn--secondary[data-v-299de2c3]{background:var(--secondary);color:var(--secondary-contrast)}.mw-btn--secondary[data-v-299de2c3]:not(:disabled):hover{filter:brightness(1.08)}.mw-btn--ghost[data-v-299de2c3]{color:var(--text);border-color:var(--border);background:0 0}.mw-btn--ghost[data-v-299de2c3]:not(:disabled):hover{background:var(--lift)}.mw-btn--danger[data-v-299de2c3]{background:var(--danger);color:var(--mw-neutral-000)}.mw-btn--danger[data-v-299de2c3]:not(:disabled):hover{filter:brightness(1.08)}.mw-card[data-v-fd2fd7ab]{background:var(--surface);border:1px solid var(--border);border-radius:var(--mw-radius,10px);color:var(--text);overflow:hidden}.mw-card__header[data-v-fd2fd7ab]{border-bottom:1px solid var(--border);padding:.85rem 1rem}.mw-card__title[data-v-fd2fd7ab]{margin:0;font-size:1rem;font-weight:600}.mw-card__body[data-v-fd2fd7ab]{padding:1rem}.mw-card__footer[data-v-fd2fd7ab]{border-top:1px solid var(--border);background:var(--lift);padding:.75rem 1rem}.mw-select[data-v-b9468387]{position:relative}.mw-select select[data-v-b9468387]{appearance:none;background:var(--surface);width:100%;color:var(--text);border:1px solid var(--border);border-radius:var(--mw-radius,10px);font:inherit;padding:.5rem 2.5rem .5rem .7rem}.mw-select select[data-v-b9468387]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.mw-select__chevron[data-v-b9468387]{pointer-events:none;stroke:var(--muted);stroke-width:1.5px;fill:none;position:absolute;top:50%;right:.75rem;transform:translateY(-50%)}.mw-notice[data-v-6f53d0be]{border:1px solid var(--border);border-radius:var(--mw-radius,10px);background:var(--lift);color:var(--text);border-left-width:3px;margin:0;padding:.6rem .8rem;font-size:.9rem}.mw-notice--error[data-v-6f53d0be]{border-left-color:var(--danger);color:var(--danger);background:color-mix(in srgb, var(--danger) 8%, transparent)}.mw-notice--ok[data-v-6f53d0be]{border-left-color:var(--ok);color:var(--ok);background:color-mix(in srgb, var(--ok) 8%, transparent)}.sidebar-item[data-v-e15afdf7]{border-radius:var(--mw-radius,10px);width:100%;color:inherit;font:inherit;text-align:left;cursor:pointer;opacity:.85;background:0 0;border:0;align-items:center;gap:.6rem;padding:.55rem .7rem;display:flex}.sidebar-item[data-v-e15afdf7]:hover:not(:disabled){background:color-mix(in srgb, currentColor 10%, transparent);opacity:1}.sidebar-item.is-active[data-v-e15afdf7]{background:color-mix(in srgb, var(--accent) 26%, transparent);opacity:1}.sidebar-item[data-v-e15afdf7]:disabled{cursor:default}.sidebar-item[data-v-e15afdf7]:focus-visible{outline:2px solid var(--accent);outline-offset:1px}.sidebar-item__icon[data-v-e15afdf7]{text-align:center;width:1.1rem}.mw-tab-nav[data-v-88758ede]{border-bottom:2px solid var(--border);gap:.25rem;display:flex}.mw-tab-nav__tab[data-v-88758ede]{appearance:none;cursor:pointer;color:var(--muted);background:0 0;border:none;border-bottom:2px solid #0000;margin-bottom:-2px;padding:.5rem 1rem;font-size:.95rem;transition:color .1s,border-color .1s}.mw-tab-nav__tab[data-v-88758ede]:hover{color:var(--text)}.mw-tab-nav__tab--active[data-v-88758ede]{border-bottom-color:var(--accent);color:var(--text);font-weight:600}.ca-root[data-v-08c3420b]{align-items:center;gap:.35em;display:inline-flex}.ca-value[data-v-08c3420b]{font-family:monospace;font-size:inherit}.ca-copy[data-v-08c3420b]{color:var(--muted,#888);cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;padding:0;line-height:1;transition:color .15s;display:inline-flex}.ca-copy[data-v-08c3420b]:hover{color:var(--text,#f0f0f0)}.ca-copy--copied[data-v-08c3420b]{color:var(--accent,#6366f1)}.ca-icon[data-v-08c3420b]{display:block}.el-root[data-v-7a44607e]{font-family:monospace;font-size:inherit;color:var(--accent,#6366f1);text-underline-offset:2px;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;transition:color .15s}.el-root[data-v-7a44607e]:hover{color:var(--text,#f0f0f0);text-decoration-style:solid}.status-widget[data-v-20197983]{color:inherit;opacity:.85;align-items:center;gap:.4rem;font-size:.8rem;text-decoration:none;display:inline-flex}.status-widget[data-v-20197983]:hover{opacity:1}.status-widget__dot[data-v-20197983]{border-radius:50%;flex-shrink:0;width:.55rem;height:.55rem}.status-widget--ok .status-widget__dot[data-v-20197983]{background:var(--mw-ok-500)}.status-widget--degraded .status-widget__dot[data-v-20197983]{background:var(--mw-gold-500)}.status-widget--error .status-widget__dot[data-v-20197983]{background:var(--mw-danger-500)}
2
2
  /*$vite$:1*/
@@ -0,0 +1,16 @@
1
+ export interface AppTab {
2
+ id: string;
3
+ label: string;
4
+ }
5
+ type __VLS_Props = {
6
+ tabs: AppTab[];
7
+ modelValue: string;
8
+ ariaLabel?: string;
9
+ };
10
+ declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
11
+ "update:modelValue": (id: string) => any;
12
+ }, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{
13
+ "onUpdate:modelValue"?: ((id: string) => any) | undefined;
14
+ }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
15
+ declare const _default: typeof __VLS_export;
16
+ export default _default;
@@ -1,13 +1,27 @@
1
1
  type __VLS_Props = {
2
+ /** The full value to copy (and to truncate for the default label). */
2
3
  address: string;
3
- /** Show prefix…suffix truncation (default true). Full address shown in title attribute always. */
4
+ /** Prefix…suffix truncation for the default label (default true). Ignored when a slot is given. */
4
5
  truncate?: boolean;
5
- /** [prefixChars, suffixChars] — only used when truncate is true. */
6
+ /** [prefixChars, suffixChars] — only used for the default label when truncate is true. */
6
7
  chars?: [number, number];
8
+ /** Accessible label / tooltip for the copy button (default "Copy"). */
9
+ label?: string;
7
10
  };
8
- declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
11
+ declare var __VLS_1: {};
12
+ type __VLS_Slots = {} & {
13
+ default?: (props: typeof __VLS_1) => any;
14
+ };
15
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
16
+ label: string;
9
17
  truncate: boolean;
10
18
  chars: [number, number];
11
19
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
20
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
12
21
  declare const _default: typeof __VLS_export;
13
22
  export default _default;
23
+ type __VLS_WithSlots<T, S> = T & {
24
+ new (): {
25
+ $slots: S;
26
+ };
27
+ };
@@ -0,0 +1,26 @@
1
+ type __VLS_Props = {
2
+ /** Destination URL (opens in a new tab). */
3
+ href: string;
4
+ /** Value used for the truncated label + title; omit when providing slot content. */
5
+ value?: string;
6
+ /** Prefix…suffix truncation of `value` (default true). */
7
+ truncate?: boolean;
8
+ /** [prefixChars, suffixChars] — only used when truncate is true. */
9
+ chars?: [number, number];
10
+ };
11
+ declare var __VLS_1: {};
12
+ type __VLS_Slots = {} & {
13
+ default?: (props: typeof __VLS_1) => any;
14
+ };
15
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
16
+ truncate: boolean;
17
+ chars: [number, number];
18
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
19
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
20
+ declare const _default: typeof __VLS_export;
21
+ export default _default;
22
+ type __VLS_WithSlots<T, S> = T & {
23
+ new (): {
24
+ $slots: S;
25
+ };
26
+ };
@@ -7,8 +7,8 @@ type __VLS_Props = {
7
7
  pollInterval?: number;
8
8
  };
9
9
  declare const __VLS_export: import("vue").DefineComponent<__VLS_Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
10
- apiUrl: string;
11
10
  href: string;
11
+ apiUrl: string;
12
12
  pollInterval: number;
13
13
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
14
  declare const _default: typeof __VLS_export;
@@ -0,0 +1,13 @@
1
+ /** Sui networks SuiVision serves. */
2
+ export type SuiNetwork = 'mainnet' | 'testnet' | 'devnet';
3
+ /** The kind of Sui entity to link, mapped to SuiVision's URL path segment. */
4
+ export type SuiExplorerKind = 'account' | 'object' | 'txblock';
5
+ /**
6
+ * Build a SuiVision explorer URL for a Sui entity.
7
+ *
8
+ * @param kind - `account` (address), `object`, or `txblock` (transaction digest).
9
+ * @param id - The address / object id / transaction digest.
10
+ * @param network - Target network (default `testnet`).
11
+ * @returns The full SuiVision URL, e.g. `https://testnet.suivision.xyz/account/0x…`.
12
+ */
13
+ export declare function suiExplorerUrl(kind: SuiExplorerKind, id: string, network?: SuiNetwork): string;
package/dist/index.d.ts CHANGED
@@ -8,8 +8,13 @@ export { default as UiCard } from './components/UiCard.vue';
8
8
  export { default as UiSelect } from './components/UiSelect.vue';
9
9
  export { default as UiNotice } from './components/UiNotice.vue';
10
10
  export { default as SidebarItem } from './components/SidebarItem.vue';
11
+ export { default as AppTabNav } from './components/AppTabNav.vue';
12
+ export type { AppTab } from './components/AppTabNav.vue';
11
13
  export { default as CopyableAddress } from './components/CopyableAddress.vue';
14
+ export { default as ExplorerLink } from './components/ExplorerLink.vue';
12
15
  export { default as StatusWidget } from './components/StatusWidget.vue';
16
+ export { suiExplorerUrl } from './explorer.js';
17
+ export type { SuiNetwork, SuiExplorerKind } from './explorer.js';
13
18
  export { useColorMode } from './composables/useColorMode.js';
14
19
  export type { ColorMode } from './composables/useColorMode.js';
15
20
  export type { PanelVariant, PanelColors } from './internal/panel.js';
package/dist/ui.js CHANGED
@@ -1,6 +1,6 @@
1
- import { Fragment as e, computed as t, createCommentVNode as n, createElementBlock as r, createElementVNode as i, defineComponent as a, mergeProps as o, normalizeClass as s, normalizeStyle as c, onMounted as l, onUnmounted as u, openBlock as d, ref as f, renderList as p, renderSlot as m, toDisplayString as h, useModel as ee, vModelSelect as te, watch as ne, withDirectives as g } from "vue";
1
+ import { Fragment as e, computed as t, createCommentVNode as n, createElementBlock as r, createElementVNode as i, createTextVNode as a, defineComponent as o, mergeProps as s, normalizeClass as c, normalizeStyle as l, onMounted as ee, onUnmounted as u, openBlock as d, ref as f, renderList as p, renderSlot as m, toDisplayString as h, useModel as te, vModelSelect as ne, watch as re, withDirectives as ie } from "vue";
2
2
  //#region src/internal/panel.ts
3
- function _(e, t) {
3
+ function g(e, t) {
4
4
  let n;
5
5
  return n = e === "transparent" ? {
6
6
  "--_bg": "transparent",
@@ -24,7 +24,7 @@ function _(e, t) {
24
24
  }
25
25
  //#endregion
26
26
  //#region src/components/AppHeader.vue?vue&type=script&setup=true&lang.ts
27
- var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae = { class: "mw-header__actions" }, oe = /*@__PURE__*/ a({
27
+ var ae = { class: "mw-header__brand" }, oe = { class: "mw-header__center" }, se = { class: "mw-header__actions" }, ce = /*@__PURE__*/ o({
28
28
  __name: "AppHeader",
29
29
  props: {
30
30
  variant: { default: "dark" },
@@ -35,33 +35,33 @@ var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae
35
35
  }
36
36
  },
37
37
  setup(e) {
38
- let n = e, a = t(() => _(n.variant, n.colors));
38
+ let n = e, a = t(() => g(n.variant, n.colors));
39
39
  return (t, n) => (d(), r("header", {
40
- class: s(["mw-header", [`mw-header--${e.variant}`, { "mw-header--sticky": e.sticky }]]),
41
- style: c(a.value)
40
+ class: c(["mw-header", [`mw-header--${e.variant}`, { "mw-header--sticky": e.sticky }]]),
41
+ style: l(a.value)
42
42
  }, [
43
- i("div", re, [m(t.$slots, "brand", {}, void 0, !0)]),
44
- i("div", ie, [m(t.$slots, "default", {}, void 0, !0)]),
45
- i("div", ae, [m(t.$slots, "actions", {}, void 0, !0)])
43
+ i("div", ae, [m(t.$slots, "brand", {}, void 0, !0)]),
44
+ i("div", oe, [m(t.$slots, "default", {}, void 0, !0)]),
45
+ i("div", se, [m(t.$slots, "actions", {}, void 0, !0)])
46
46
  ], 6));
47
47
  }
48
- }), v = (e, t) => {
48
+ }), _ = (e, t) => {
49
49
  let n = e.__vccOpts || e;
50
50
  for (let [e, r] of t) n[e] = r;
51
51
  return n;
52
- }, y = /*#__PURE__*/ v(oe, [["__scopeId", "data-v-ad7cd1c5"]]), b = {
52
+ }, v = /*#__PURE__*/ _(ce, [["__scopeId", "data-v-ad7cd1c5"]]), y = {
53
53
  key: 0,
54
54
  class: "mw-sidebar__head"
55
- }, x = {
55
+ }, b = {
56
56
  class: "mw-sidebar__nav",
57
57
  "aria-label": "Primary"
58
- }, S = {
58
+ }, x = {
59
59
  key: 1,
60
60
  class: "mw-sidebar__body"
61
- }, C = {
61
+ }, S = {
62
62
  key: 2,
63
63
  class: "mw-sidebar__foot"
64
- }, w = /*#__PURE__*/ v(/* @__PURE__ */ a({
64
+ }, C = /*#__PURE__*/ _(/* @__PURE__ */ o({
65
65
  __name: "AppSidebar",
66
66
  props: {
67
67
  variant: { default: "dark" },
@@ -70,45 +70,45 @@ var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae
70
70
  },
71
71
  setup(e) {
72
72
  let a = e, o = t(() => {
73
- let e = _(a.variant, a.colors);
73
+ let e = g(a.variant, a.colors);
74
74
  return a.width && (e["--mw-sidebar-width"] = a.width), e;
75
75
  });
76
76
  return (t, a) => (d(), r("aside", {
77
- class: s(["mw-sidebar", `mw-sidebar--${e.variant}`]),
78
- style: c(o.value)
77
+ class: c(["mw-sidebar", `mw-sidebar--${e.variant}`]),
78
+ style: l(o.value)
79
79
  }, [
80
- t.$slots.head ? (d(), r("div", b, [m(t.$slots, "head", {}, void 0, !0)])) : n("", !0),
81
- i("nav", x, [m(t.$slots, "default", {}, void 0, !0)]),
82
- t.$slots.body ? (d(), r("div", S, [m(t.$slots, "body", {}, void 0, !0)])) : n("", !0),
83
- t.$slots.foot ? (d(), r("div", C, [m(t.$slots, "foot", {}, void 0, !0)])) : n("", !0)
80
+ t.$slots.head ? (d(), r("div", y, [m(t.$slots, "head", {}, void 0, !0)])) : n("", !0),
81
+ i("nav", b, [m(t.$slots, "default", {}, void 0, !0)]),
82
+ t.$slots.body ? (d(), r("div", x, [m(t.$slots, "body", {}, void 0, !0)])) : n("", !0),
83
+ t.$slots.foot ? (d(), r("div", S, [m(t.$slots, "foot", {}, void 0, !0)])) : n("", !0)
84
84
  ], 6));
85
85
  }
86
- }), [["__scopeId", "data-v-23c3c7f3"]]), T = { class: "mw-footer__start" }, E = { class: "mw-footer__center" }, D = { class: "mw-footer__end" }, O = /*#__PURE__*/ v(/* @__PURE__ */ a({
86
+ }), [["__scopeId", "data-v-23c3c7f3"]]), w = { class: "mw-footer__start" }, T = { class: "mw-footer__center" }, E = { class: "mw-footer__end" }, D = /*#__PURE__*/ _(/* @__PURE__ */ o({
87
87
  __name: "AppFooter",
88
88
  props: {
89
89
  variant: { default: "transparent" },
90
90
  colors: {}
91
91
  },
92
92
  setup(e) {
93
- let n = e, a = t(() => _(n.variant, n.colors));
93
+ let n = e, a = t(() => g(n.variant, n.colors));
94
94
  return (t, n) => (d(), r("footer", {
95
- class: s(["mw-footer", `mw-footer--${e.variant}`]),
96
- style: c(a.value)
95
+ class: c(["mw-footer", `mw-footer--${e.variant}`]),
96
+ style: l(a.value)
97
97
  }, [
98
- i("div", T, [m(t.$slots, "start", {}, void 0, !0)]),
99
- i("div", E, [m(t.$slots, "default", {}, void 0, !0)]),
100
- i("div", D, [m(t.$slots, "end", {}, void 0, !0)])
98
+ i("div", w, [m(t.$slots, "start", {}, void 0, !0)]),
99
+ i("div", T, [m(t.$slots, "default", {}, void 0, !0)]),
100
+ i("div", E, [m(t.$slots, "end", {}, void 0, !0)])
101
101
  ], 6));
102
102
  }
103
- }), [["__scopeId", "data-v-c646b8e1"]]), k = {
103
+ }), [["__scopeId", "data-v-c646b8e1"]]), O = {
104
104
  class: "mw-mode",
105
105
  role: "group",
106
106
  "aria-label": "Colour mode"
107
- }, A = [
107
+ }, k = [
108
108
  "aria-pressed",
109
109
  "title",
110
110
  "onClick"
111
- ], j = { "aria-hidden": "true" }, M = { class: "mw-mode__label" }, N = /*#__PURE__*/ v(/* @__PURE__ */ a({
111
+ ], A = { "aria-hidden": "true" }, j = { class: "mw-mode__label" }, M = /*#__PURE__*/ _(/* @__PURE__ */ o({
112
112
  __name: "ColorModeControl",
113
113
  props: {
114
114
  modelValue: {},
@@ -127,18 +127,18 @@ var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae
127
127
  }, a = {
128
128
  light: "☀",
129
129
  dark: "☾",
130
- system: "◐"
130
+ system: "🖵"
131
131
  };
132
- return (o, c) => (d(), r("div", k, [(d(!0), r(e, null, p(t.modes, (e) => (d(), r("button", {
132
+ return (o, s) => (d(), r("div", O, [(d(!0), r(e, null, p(t.modes, (e) => (d(), r("button", {
133
133
  key: e,
134
134
  type: "button",
135
- class: s(["mw-mode__btn", { "is-active": t.modelValue === e }]),
135
+ class: c(["mw-mode__btn", { "is-active": t.modelValue === e }]),
136
136
  "aria-pressed": t.modelValue === e,
137
137
  title: n[e],
138
138
  onClick: (t) => o.$emit("update:modelValue", e)
139
- }, [i("span", j, h(a[e]), 1), i("span", M, h(n[e]), 1)], 10, A))), 128))]));
139
+ }, [i("span", A, h(a[e]), 1), i("span", j, h(n[e]), 1)], 10, k))), 128))]));
140
140
  }
141
- }), [["__scopeId", "data-v-e4a6bf1d"]]), P = ["type", "disabled"], F = /*#__PURE__*/ v(/* @__PURE__ */ a({
141
+ }), [["__scopeId", "data-v-186f8312"]]), N = ["type", "disabled"], P = /*#__PURE__*/ _(/* @__PURE__ */ o({
142
142
  __name: "UiButton",
143
143
  props: {
144
144
  variant: { default: "primary" },
@@ -152,26 +152,26 @@ var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae
152
152
  return (t, n) => (d(), r("button", {
153
153
  type: e.type,
154
154
  disabled: e.disabled,
155
- class: s(["mw-btn", `mw-btn--${e.variant}`])
156
- }, [m(t.$slots, "default", {}, void 0, !0)], 10, P));
155
+ class: c(["mw-btn", `mw-btn--${e.variant}`])
156
+ }, [m(t.$slots, "default", {}, void 0, !0)], 10, N));
157
157
  }
158
- }), [["__scopeId", "data-v-299de2c3"]]), I = { class: "mw-card" }, L = {
158
+ }), [["__scopeId", "data-v-299de2c3"]]), F = { class: "mw-card" }, I = {
159
159
  key: 0,
160
160
  class: "mw-card__header"
161
- }, R = { class: "mw-card__title" }, z = { class: "mw-card__body" }, B = {
161
+ }, L = { class: "mw-card__title" }, R = { class: "mw-card__body" }, z = {
162
162
  key: 1,
163
163
  class: "mw-card__footer"
164
- }, se = /*#__PURE__*/ v(/* @__PURE__ */ a({
164
+ }, B = /*#__PURE__*/ _(/* @__PURE__ */ o({
165
165
  __name: "UiCard",
166
166
  props: { title: {} },
167
167
  setup(e) {
168
- return (t, a) => (d(), r("section", I, [
169
- e.title || t.$slots.header ? (d(), r("header", L, [m(t.$slots, "header", {}, () => [i("h3", R, h(e.title), 1)], !0)])) : n("", !0),
170
- i("div", z, [m(t.$slots, "default", {}, void 0, !0)]),
171
- t.$slots.footer ? (d(), r("footer", B, [m(t.$slots, "footer", {}, void 0, !0)])) : n("", !0)
168
+ return (t, a) => (d(), r("section", F, [
169
+ e.title || t.$slots.header ? (d(), r("header", I, [m(t.$slots, "header", {}, () => [i("h3", L, h(e.title), 1)], !0)])) : n("", !0),
170
+ i("div", R, [m(t.$slots, "default", {}, void 0, !0)]),
171
+ t.$slots.footer ? (d(), r("footer", z, [m(t.$slots, "footer", {}, void 0, !0)])) : n("", !0)
172
172
  ]));
173
173
  }
174
- }), [["__scopeId", "data-v-fd2fd7ab"]]), V = { class: "mw-select" }, H = /*#__PURE__*/ v(/* @__PURE__ */ a({
174
+ }), [["__scopeId", "data-v-fd2fd7ab"]]), V = { class: "mw-select" }, H = /*#__PURE__*/ _(/* @__PURE__ */ o({
175
175
  inheritAttrs: !1,
176
176
  __name: "UiSelect",
177
177
  props: {
@@ -180,8 +180,8 @@ var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae
180
180
  },
181
181
  emits: ["update:modelValue"],
182
182
  setup(e) {
183
- let t = ee(e, "modelValue");
184
- return (e, n) => (d(), r("div", V, [g(i("select", o(e.$attrs, { "onUpdate:modelValue": n[0] ||= (e) => t.value = e }), [m(e.$slots, "default", {}, void 0, !0)], 16), [[te, t.value]]), n[1] ||= i("svg", {
183
+ let t = te(e, "modelValue");
184
+ return (e, n) => (d(), r("div", V, [ie(i("select", s(e.$attrs, { "onUpdate:modelValue": n[0] ||= (e) => t.value = e }), [m(e.$slots, "default", {}, void 0, !0)], 16), [[ne, t.value]]), n[1] ||= i("svg", {
185
185
  class: "mw-select__chevron",
186
186
  xmlns: "http://www.w3.org/2000/svg",
187
187
  width: "12",
@@ -190,7 +190,7 @@ var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae
190
190
  "aria-hidden": "true"
191
191
  }, [i("path", { d: "M1 1l5 5 5-5" })], -1)]));
192
192
  }
193
- }), [["__scopeId", "data-v-b9468387"]]), U = ["role"], W = /*#__PURE__*/ v(/* @__PURE__ */ a({
193
+ }), [["__scopeId", "data-v-b9468387"]]), U = ["role"], W = /*#__PURE__*/ _(/* @__PURE__ */ o({
194
194
  __name: "UiNotice",
195
195
  props: { type: {} },
196
196
  setup(e) {
@@ -199,15 +199,15 @@ var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae
199
199
  if (n.type === "ok") return "status";
200
200
  });
201
201
  return (t, n) => (d(), r("p", {
202
- class: s(["mw-notice", e.type && e.type !== "info" ? `mw-notice--${e.type}` : ""]),
202
+ class: c(["mw-notice", e.type && e.type !== "info" ? `mw-notice--${e.type}` : ""]),
203
203
  role: i.value
204
204
  }, [m(t.$slots, "default", {}, void 0, !0)], 10, U));
205
205
  }
206
- }), [["__scopeId", "data-v-6f53d0be"]]), G = ["disabled", "aria-current"], K = {
206
+ }), [["__scopeId", "data-v-6f53d0be"]]), le = ["disabled", "aria-current"], ue = {
207
207
  key: 0,
208
208
  class: "sidebar-item__icon",
209
209
  "aria-hidden": "true"
210
- }, ce = { class: "sidebar-item__label" }, le = /*#__PURE__*/ v(/* @__PURE__ */ a({
210
+ }, G = { class: "sidebar-item__label" }, de = /*#__PURE__*/ _(/* @__PURE__ */ o({
211
211
  __name: "SidebarItem",
212
212
  props: {
213
213
  label: {},
@@ -224,12 +224,57 @@ var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae
224
224
  setup(e) {
225
225
  return (t, a) => (d(), r("button", {
226
226
  type: "button",
227
- class: s(["sidebar-item", { "is-active": e.active }]),
227
+ class: c(["sidebar-item", { "is-active": e.active }]),
228
228
  disabled: e.disabled,
229
229
  "aria-current": e.active ? "page" : void 0
230
- }, [e.icon ? (d(), r("span", K, h(e.icon), 1)) : n("", !0), i("span", ce, h(e.label), 1)], 10, G));
230
+ }, [e.icon ? (d(), r("span", ue, h(e.icon), 1)) : n("", !0), i("span", G, h(e.label), 1)], 10, le));
231
231
  }
232
- }), [["__scopeId", "data-v-e15afdf7"]]), ue = ["title"], de = /*#__PURE__*/ v(/* @__PURE__ */ a({
232
+ }), [["__scopeId", "data-v-e15afdf7"]]), fe = ["aria-label"], pe = ["aria-current", "onClick"], me = /*#__PURE__*/ _(/* @__PURE__ */ o({
233
+ __name: "AppTabNav",
234
+ props: {
235
+ tabs: {},
236
+ modelValue: {},
237
+ ariaLabel: {}
238
+ },
239
+ emits: ["update:modelValue"],
240
+ setup(t, { emit: n }) {
241
+ let i = n;
242
+ return (n, a) => (d(), r("nav", {
243
+ class: "mw-tab-nav",
244
+ "aria-label": t.ariaLabel
245
+ }, [(d(!0), r(e, null, p(t.tabs, (e) => (d(), r("button", {
246
+ key: e.id,
247
+ type: "button",
248
+ class: c(["mw-tab-nav__tab", { "mw-tab-nav__tab--active": t.modelValue === e.id }]),
249
+ "aria-current": t.modelValue === e.id ? "page" : void 0,
250
+ onClick: (t) => i("update:modelValue", e.id)
251
+ }, h(e.label), 11, pe))), 128))], 8, fe));
252
+ }
253
+ }), [["__scopeId", "data-v-88758ede"]]), he = { class: "ca-root" }, ge = ["title"], _e = ["title", "aria-label"], ve = {
254
+ key: 0,
255
+ class: "ca-icon",
256
+ viewBox: "0 0 24 24",
257
+ width: "1em",
258
+ height: "1em",
259
+ fill: "none",
260
+ stroke: "currentColor",
261
+ "stroke-width": "2",
262
+ "stroke-linecap": "round",
263
+ "stroke-linejoin": "round",
264
+ "aria-hidden": "true"
265
+ }, ye = {
266
+ key: 1,
267
+ class: "ca-icon",
268
+ viewBox: "0 0 24 24",
269
+ width: "1em",
270
+ height: "1em",
271
+ fill: "none",
272
+ stroke: "currentColor",
273
+ "stroke-width": "2",
274
+ "stroke-linecap": "round",
275
+ "stroke-linejoin": "round",
276
+ "aria-hidden": "true"
277
+ }, be = /*#__PURE__*/ _(/* @__PURE__ */ o({
233
278
  __name: "CopyableAddress",
234
279
  props: {
235
280
  address: {},
@@ -237,44 +282,81 @@ var re = { class: "mw-header__brand" }, ie = { class: "mw-header__center" }, ae
237
282
  type: Boolean,
238
283
  default: !0
239
284
  },
240
- chars: { default: () => [6, 4] }
285
+ chars: { default: () => [6, 4] },
286
+ label: { default: "Copy" }
241
287
  },
242
288
  setup(e) {
243
- let n = e, i = f(!1), a = t(() => {
244
- if (i.value) return "Copied!";
289
+ let n = e, o = f(!1), s = t(() => {
245
290
  if (!n.truncate) return n.address;
246
291
  let [e, t] = n.chars;
247
292
  return `${n.address.slice(0, e)}…${n.address.slice(-t)}`;
248
293
  });
249
- async function o() {
250
- await navigator.clipboard.writeText(n.address), i.value = !0, setTimeout(() => {
251
- i.value = !1;
294
+ async function l() {
295
+ await navigator.clipboard.writeText(n.address), o.value = !0, setTimeout(() => {
296
+ o.value = !1;
252
297
  }, 1500);
253
298
  }
254
- return (t, n) => (d(), r("button", {
299
+ return (t, n) => (d(), r("span", he, [i("span", {
300
+ class: "ca-value",
301
+ title: e.address
302
+ }, [m(t.$slots, "default", {}, () => [a(h(s.value), 1)], !0)], 8, ge), i("button", {
255
303
  type: "button",
256
- class: s(["ca-root", { "ca-root--copied": i.value }]),
257
- title: e.address,
258
- onClick: o
259
- }, h(a.value), 11, ue));
304
+ class: c(["ca-copy", { "ca-copy--copied": o.value }]),
305
+ title: o.value ? "Copied!" : e.label,
306
+ "aria-label": o.value ? "Copied" : e.label,
307
+ onClick: l
308
+ }, [o.value ? (d(), r("svg", ve, [...n[0] ||= [i("polyline", { points: "20 6 9 17 4 12" }, null, -1)]])) : (d(), r("svg", ye, [...n[1] ||= [i("rect", {
309
+ x: "9",
310
+ y: "9",
311
+ width: "13",
312
+ height: "13",
313
+ rx: "2",
314
+ ry: "2"
315
+ }, null, -1), i("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" }, null, -1)]]))], 10, _e)]));
316
+ }
317
+ }), [["__scopeId", "data-v-08c3420b"]]), xe = ["href", "title"], Se = /*#__PURE__*/ _(/* @__PURE__ */ o({
318
+ __name: "ExplorerLink",
319
+ props: {
320
+ href: {},
321
+ value: {},
322
+ truncate: {
323
+ type: Boolean,
324
+ default: !0
325
+ },
326
+ chars: { default: () => [6, 4] }
327
+ },
328
+ setup(e) {
329
+ let n = e, i = t(() => {
330
+ if (!n.value) return "";
331
+ if (!n.truncate) return n.value;
332
+ let [e, t] = n.chars;
333
+ return `${n.value.slice(0, e)}…${n.value.slice(-t)}`;
334
+ });
335
+ return (t, n) => (d(), r("a", {
336
+ class: "el-root",
337
+ href: e.href,
338
+ target: "_blank",
339
+ rel: "noopener noreferrer",
340
+ title: e.value
341
+ }, [m(t.$slots, "default", {}, () => [a(h(i.value), 1)], !0)], 8, xe));
260
342
  }
261
- }), [["__scopeId", "data-v-e2d23de9"]]), fe = /* @__PURE__ */ new Set([
343
+ }), [["__scopeId", "data-v-7a44607e"]]), Ce = /* @__PURE__ */ new Set([
262
344
  "operational",
263
345
  "degraded",
264
346
  "down",
265
347
  "unknown"
266
348
  ]);
267
- function q(e) {
268
- return typeof e == "string" && fe.has(e);
349
+ function K(e) {
350
+ return typeof e == "string" && Ce.has(e);
269
351
  }
270
- function J(e) {
352
+ function q(e) {
271
353
  if (typeof e != "object" || !e) return null;
272
354
  let t = e;
273
- return !q(t.overall) || typeof t.generated_at != "string" || !Array.isArray(t.groups) ? null : e;
355
+ return !K(t.overall) || typeof t.generated_at != "string" || !Array.isArray(t.groups) ? null : e;
274
356
  }
275
357
  //#endregion
276
358
  //#region src/components/StatusWidget.vue?vue&type=script&setup=true&lang.ts
277
- var pe = ["href", "title"], me = { class: "status-widget__label" }, he = /*#__PURE__*/ v(/* @__PURE__ */ a({
359
+ var we = ["href", "title"], Te = { class: "status-widget__label" }, Ee = /*#__PURE__*/ _(/* @__PURE__ */ o({
278
360
  __name: "StatusWidget",
279
361
  props: {
280
362
  apiUrl: { default: "https://status.meddleware.co.uk/api/status" },
@@ -292,44 +374,54 @@ var pe = ["href", "title"], me = { class: "status-widget__label" }, he = /*#__PU
292
374
  degraded: "Degraded",
293
375
  down: "Major outage",
294
376
  unknown: "Status unknown"
295
- }, o = f("ok"), c = f("All systems operational"), p = null;
296
- async function m() {
377
+ }, o = f("ok"), s = f("All systems operational"), l = null;
378
+ async function p() {
297
379
  try {
298
380
  let e = await fetch(t.apiUrl);
299
381
  if (!e.ok) throw Error(`HTTP ${e.status}`);
300
- let r = J(await e.json());
382
+ let r = q(await e.json());
301
383
  if (!r) throw Error("unexpected response shape");
302
- o.value = n[r.overall], c.value = a[r.overall];
384
+ o.value = n[r.overall], s.value = a[r.overall];
303
385
  } catch {
304
- o.value = "error", c.value = "Status unavailable";
386
+ o.value = "error", s.value = "Status unavailable";
305
387
  }
306
388
  }
307
- return l(() => {
308
- m(), p = setInterval(m, t.pollInterval);
389
+ return ee(() => {
390
+ p(), l = setInterval(p, t.pollInterval);
309
391
  }), u(() => {
310
- p !== null && clearInterval(p);
392
+ l !== null && clearInterval(l);
311
393
  }), (t, n) => (d(), r("a", {
312
394
  href: e.href,
313
395
  target: "_blank",
314
396
  rel: "noopener",
315
- class: s(["status-widget", `status-widget--${o.value}`]),
316
- title: `Platform status: ${c.value}`
397
+ class: c(["status-widget", `status-widget--${o.value}`]),
398
+ title: `Platform status: ${s.value}`
317
399
  }, [n[0] ||= i("span", {
318
400
  class: "status-widget__dot",
319
401
  "aria-hidden": "true"
320
- }, null, -1), i("span", me, h(c.value), 1)], 10, pe));
402
+ }, null, -1), i("span", Te, h(s.value), 1)], 10, we));
321
403
  }
322
- }), [["__scopeId", "data-v-20197983"]]), Y = "mw-color-mode", X = f("system"), Z = !1;
323
- function ge() {
404
+ }), [["__scopeId", "data-v-20197983"]]), J = {
405
+ mainnet: "https://suivision.xyz",
406
+ testnet: "https://testnet.suivision.xyz",
407
+ devnet: "https://devnet.suivision.xyz"
408
+ };
409
+ function De(e, t, n = "testnet") {
410
+ return `${J[n] ?? J.testnet}/${e}/${t}`;
411
+ }
412
+ //#endregion
413
+ //#region src/composables/useColorMode.ts
414
+ var Y = "mw-color-mode", X = f("system"), Z = !1;
415
+ function Oe() {
324
416
  return typeof window < "u" && typeof window.matchMedia == "function" && window.matchMedia("(prefers-color-scheme: dark)").matches;
325
417
  }
326
418
  function Q(e) {
327
- return e === "dark" || e === "system" && ge();
419
+ return e === "dark" || e === "system" && Oe();
328
420
  }
329
421
  function $(e) {
330
422
  typeof document > "u" || document.documentElement.setAttribute("data-theme", Q(e) ? "dark" : "light");
331
423
  }
332
- function _e(e = "system") {
424
+ function ke(e = "system") {
333
425
  if (!Z) {
334
426
  Z = !0;
335
427
  let t = null;
@@ -338,7 +430,7 @@ function _e(e = "system") {
338
430
  } catch {}
339
431
  X.value = t ?? e, $(X.value), typeof window < "u" && typeof window.matchMedia == "function" && window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
340
432
  X.value === "system" && $("system");
341
- }), ne(X, (e) => {
433
+ }), re(X, (e) => {
342
434
  $(e);
343
435
  try {
344
436
  typeof localStorage < "u" && localStorage.setItem(Y, e);
@@ -359,4 +451,4 @@ function _e(e = "system") {
359
451
  };
360
452
  }
361
453
  //#endregion
362
- export { O as AppFooter, y as AppHeader, w as AppSidebar, N as ColorModeControl, de as CopyableAddress, le as SidebarItem, he as StatusWidget, F as UiButton, se as UiCard, W as UiNotice, H as UiSelect, q as isStatusLevel, J as parseSnapshot, _e as useColorMode };
454
+ export { D as AppFooter, v as AppHeader, C as AppSidebar, me as AppTabNav, M as ColorModeControl, be as CopyableAddress, Se as ExplorerLink, de as SidebarItem, Ee as StatusWidget, P as UiButton, B as UiCard, W as UiNotice, H as UiSelect, K as isStatusLevel, q as parseSnapshot, De as suiExplorerUrl, ke as useColorMode };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meddleware/ui",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "description": "Shared Vue 3 component library for @meddleware UIs \u2014 layout shell (header/sidebar/footer), colour-mode control, and primitives. Built on @meddleware/design-tokens.",
5
5
  "license": "0BSD",
6
6
  "type": "module",
@@ -23,7 +23,8 @@
23
23
  ],
24
24
  "scripts": {
25
25
  "build": "rm -rf dist && vue-tsc -p tsconfig.build.json && vite build",
26
- "type-check": "vue-tsc --noEmit"
26
+ "type-check": "vue-tsc --noEmit",
27
+ "test": "vitest run"
27
28
  },
28
29
  "peerDependencies": {
29
30
  "vue": "^3.5.0"
@@ -35,6 +36,7 @@
35
36
  "@vitejs/plugin-vue": "^6.0.8",
36
37
  "typescript": "^6.0.0",
37
38
  "vite": "^8.2.2",
39
+ "vitest": "~4.1.11",
38
40
  "vue": "^3.5.42",
39
41
  "vue-tsc": "~3.3.11"
40
42
  },