@meddleware/ui 0.1.5 → 0.1.6

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/dist/base.css CHANGED
@@ -1,2 +1,2 @@
1
- .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-5aa0ef5a]{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-5aa0ef5a]{border-bottom:1px solid var(--_border);padding:1rem}.mw-sidebar__nav[data-v-5aa0ef5a]{flex-direction:column;flex:auto;gap:.25rem;padding:.75rem;display:flex;overflow-y:auto}.mw-sidebar__foot[data-v-5aa0ef5a]{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}
1
+ .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-5aa0ef5a]{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-5aa0ef5a]{border-bottom:1px solid var(--_border);padding:1rem}.mw-sidebar__nav[data-v-5aa0ef5a]{flex-direction:column;flex:auto;gap:.25rem;padding:.75rem;display:flex;overflow-y:auto}.mw-sidebar__foot[data-v-5aa0ef5a]{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}.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,15 @@
1
+ type __VLS_Props = {
2
+ /** URL of the `GET /api/status` endpoint to poll. */
3
+ apiUrl?: string;
4
+ /** URL the widget links to (the human-readable status page). */
5
+ href?: string;
6
+ /** Poll interval in milliseconds. */
7
+ pollInterval?: number;
8
+ };
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
+ href: string;
12
+ pollInterval: number;
13
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
14
+ declare const _default: typeof __VLS_export;
15
+ export default _default;
package/dist/index.d.ts CHANGED
@@ -7,6 +7,9 @@ export { default as UiCard } from './components/UiCard.vue';
7
7
  export { default as UiSelect } from './components/UiSelect.vue';
8
8
  export { default as UiNotice } from './components/UiNotice.vue';
9
9
  export { default as SidebarItem } from './components/SidebarItem.vue';
10
+ export { default as StatusWidget } from './components/StatusWidget.vue';
10
11
  export { useColorMode } from './composables/useColorMode.js';
11
12
  export type { ColorMode } from './composables/useColorMode.js';
12
13
  export type { PanelVariant, PanelColors } from './internal/panel.js';
14
+ export { isStatusLevel, parseSnapshot } from './status.js';
15
+ export type { StatusLevel, StatusComponent, StatusGroup, StatusSnapshot } from './status.js';
@@ -0,0 +1,27 @@
1
+ /** Health state values returned by platform-probe's `/api/status` endpoint. */
2
+ export type StatusLevel = 'operational' | 'degraded' | 'down' | 'unknown';
3
+ /** A single monitored component within a group. */
4
+ export interface StatusComponent {
5
+ name: string;
6
+ status: StatusLevel;
7
+ }
8
+ /** A logical capability group containing one or more components. */
9
+ export interface StatusGroup {
10
+ name: string;
11
+ status: StatusLevel;
12
+ components: StatusComponent[];
13
+ }
14
+ /** The full snapshot returned by `GET /api/status`. */
15
+ export interface StatusSnapshot {
16
+ generated_at: string;
17
+ overall: StatusLevel;
18
+ groups: StatusGroup[];
19
+ }
20
+ /** Type guard for `StatusLevel`. Validates that a raw value is one of the known status strings. */
21
+ export declare function isStatusLevel(s: unknown): s is StatusLevel;
22
+ /**
23
+ * Parse and validate a raw API response as a `StatusSnapshot`.
24
+ * Returns `null` if the required fields are absent or have unexpected types,
25
+ * so callers can treat an invalid response the same as a network error.
26
+ */
27
+ export declare function parseSnapshot(raw: unknown): StatusSnapshot | null;
package/dist/ui.js CHANGED
@@ -1,4 +1,4 @@
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, openBlock as l, ref as ee, renderList as u, renderSlot as d, toDisplayString as f, useModel as p, vModelSelect as m, watch as te, withDirectives as ne } from "vue";
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 ee, openBlock as u, ref as d, renderList as f, renderSlot as p, toDisplayString as m, useModel as te, vModelSelect as ne, watch as re, withDirectives as ie } from "vue";
2
2
  //#region src/internal/panel.ts
3
3
  function h(e, t) {
4
4
  let n;
@@ -24,7 +24,7 @@ function h(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" }, g = { class: "mw-header__center" }, _ = { class: "mw-header__actions" }, v = /*@__PURE__*/ a({
27
+ var ae = { class: "mw-header__brand" }, oe = { class: "mw-header__center" }, se = { class: "mw-header__actions" }, g = /*@__PURE__*/ a({
28
28
  __name: "AppHeader",
29
29
  props: {
30
30
  variant: { default: "dark" },
@@ -36,29 +36,29 @@ var re = { class: "mw-header__brand" }, g = { class: "mw-header__center" }, _ =
36
36
  },
37
37
  setup(e) {
38
38
  let n = e, a = t(() => h(n.variant, n.colors));
39
- return (t, n) => (l(), r("header", {
39
+ return (t, n) => (u(), r("header", {
40
40
  class: s(["mw-header", [`mw-header--${e.variant}`, { "mw-header--sticky": e.sticky }]]),
41
41
  style: c(a.value)
42
42
  }, [
43
- i("div", re, [d(t.$slots, "brand", {}, void 0, !0)]),
44
- i("div", g, [d(t.$slots, "default", {}, void 0, !0)]),
45
- i("div", _, [d(t.$slots, "actions", {}, void 0, !0)])
43
+ i("div", ae, [p(t.$slots, "brand", {}, void 0, !0)]),
44
+ i("div", oe, [p(t.$slots, "default", {}, void 0, !0)]),
45
+ i("div", se, [p(t.$slots, "actions", {}, void 0, !0)])
46
46
  ], 6));
47
47
  }
48
- }), y = (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
- }, b = /*#__PURE__*/ y(v, [["__scopeId", "data-v-ad7cd1c5"]]), x = {
52
+ }, v = /*#__PURE__*/ _(g, [["__scopeId", "data-v-ad7cd1c5"]]), y = {
53
53
  key: 0,
54
54
  class: "mw-sidebar__head"
55
- }, S = {
55
+ }, b = {
56
56
  class: "mw-sidebar__nav",
57
57
  "aria-label": "Primary"
58
- }, C = {
58
+ }, x = {
59
59
  key: 1,
60
60
  class: "mw-sidebar__foot"
61
- }, w = /*#__PURE__*/ y(/* @__PURE__ */ a({
61
+ }, S = /*#__PURE__*/ _(/* @__PURE__ */ a({
62
62
  __name: "AppSidebar",
63
63
  props: {
64
64
  variant: { default: "dark" },
@@ -70,16 +70,16 @@ var re = { class: "mw-header__brand" }, g = { class: "mw-header__center" }, _ =
70
70
  let e = h(a.variant, a.colors);
71
71
  return a.width && (e["--mw-sidebar-width"] = a.width), e;
72
72
  });
73
- return (t, a) => (l(), r("aside", {
73
+ return (t, a) => (u(), r("aside", {
74
74
  class: s(["mw-sidebar", `mw-sidebar--${e.variant}`]),
75
75
  style: c(o.value)
76
76
  }, [
77
- t.$slots.head ? (l(), r("div", x, [d(t.$slots, "head", {}, void 0, !0)])) : n("", !0),
78
- i("nav", S, [d(t.$slots, "default", {}, void 0, !0)]),
79
- t.$slots.foot ? (l(), r("div", C, [d(t.$slots, "foot", {}, void 0, !0)])) : n("", !0)
77
+ t.$slots.head ? (u(), r("div", y, [p(t.$slots, "head", {}, void 0, !0)])) : n("", !0),
78
+ i("nav", b, [p(t.$slots, "default", {}, void 0, !0)]),
79
+ t.$slots.foot ? (u(), r("div", x, [p(t.$slots, "foot", {}, void 0, !0)])) : n("", !0)
80
80
  ], 6));
81
81
  }
82
- }), [["__scopeId", "data-v-5aa0ef5a"]]), T = { class: "mw-footer__start" }, E = { class: "mw-footer__center" }, D = { class: "mw-footer__end" }, O = /*#__PURE__*/ y(/* @__PURE__ */ a({
82
+ }), [["__scopeId", "data-v-5aa0ef5a"]]), C = { class: "mw-footer__start" }, w = { class: "mw-footer__center" }, T = { class: "mw-footer__end" }, E = /*#__PURE__*/ _(/* @__PURE__ */ a({
83
83
  __name: "AppFooter",
84
84
  props: {
85
85
  variant: { default: "transparent" },
@@ -87,24 +87,24 @@ var re = { class: "mw-header__brand" }, g = { class: "mw-header__center" }, _ =
87
87
  },
88
88
  setup(e) {
89
89
  let n = e, a = t(() => h(n.variant, n.colors));
90
- return (t, n) => (l(), r("footer", {
90
+ return (t, n) => (u(), r("footer", {
91
91
  class: s(["mw-footer", `mw-footer--${e.variant}`]),
92
92
  style: c(a.value)
93
93
  }, [
94
- i("div", T, [d(t.$slots, "start", {}, void 0, !0)]),
95
- i("div", E, [d(t.$slots, "default", {}, void 0, !0)]),
96
- i("div", D, [d(t.$slots, "end", {}, void 0, !0)])
94
+ i("div", C, [p(t.$slots, "start", {}, void 0, !0)]),
95
+ i("div", w, [p(t.$slots, "default", {}, void 0, !0)]),
96
+ i("div", T, [p(t.$slots, "end", {}, void 0, !0)])
97
97
  ], 6));
98
98
  }
99
- }), [["__scopeId", "data-v-c646b8e1"]]), k = {
99
+ }), [["__scopeId", "data-v-c646b8e1"]]), D = {
100
100
  class: "mw-mode",
101
101
  role: "group",
102
102
  "aria-label": "Colour mode"
103
- }, A = [
103
+ }, O = [
104
104
  "aria-pressed",
105
105
  "title",
106
106
  "onClick"
107
- ], j = { "aria-hidden": "true" }, M = { class: "mw-mode__label" }, N = /*#__PURE__*/ y(/* @__PURE__ */ a({
107
+ ], k = { "aria-hidden": "true" }, A = { class: "mw-mode__label" }, j = /*#__PURE__*/ _(/* @__PURE__ */ a({
108
108
  __name: "ColorModeControl",
109
109
  props: {
110
110
  modelValue: {},
@@ -125,16 +125,16 @@ var re = { class: "mw-header__brand" }, g = { class: "mw-header__center" }, _ =
125
125
  dark: "☾",
126
126
  system: "◐"
127
127
  };
128
- return (o, c) => (l(), r("div", k, [(l(!0), r(e, null, u(t.modes, (e) => (l(), r("button", {
128
+ return (o, c) => (u(), r("div", D, [(u(!0), r(e, null, f(t.modes, (e) => (u(), r("button", {
129
129
  key: e,
130
130
  type: "button",
131
131
  class: s(["mw-mode__btn", { "is-active": t.modelValue === e }]),
132
132
  "aria-pressed": t.modelValue === e,
133
133
  title: n[e],
134
134
  onClick: (t) => o.$emit("update:modelValue", e)
135
- }, [i("span", j, f(a[e]), 1), i("span", M, f(n[e]), 1)], 10, A))), 128))]));
135
+ }, [i("span", k, m(a[e]), 1), i("span", A, m(n[e]), 1)], 10, O))), 128))]));
136
136
  }
137
- }), [["__scopeId", "data-v-e4a6bf1d"]]), P = ["type", "disabled"], F = /*#__PURE__*/ y(/* @__PURE__ */ a({
137
+ }), [["__scopeId", "data-v-e4a6bf1d"]]), M = ["type", "disabled"], N = /*#__PURE__*/ _(/* @__PURE__ */ a({
138
138
  __name: "UiButton",
139
139
  props: {
140
140
  variant: { default: "primary" },
@@ -145,29 +145,29 @@ var re = { class: "mw-header__brand" }, g = { class: "mw-header__center" }, _ =
145
145
  }
146
146
  },
147
147
  setup(e) {
148
- return (t, n) => (l(), r("button", {
148
+ return (t, n) => (u(), r("button", {
149
149
  type: e.type,
150
150
  disabled: e.disabled,
151
151
  class: s(["mw-btn", `mw-btn--${e.variant}`])
152
- }, [d(t.$slots, "default", {}, void 0, !0)], 10, P));
152
+ }, [p(t.$slots, "default", {}, void 0, !0)], 10, M));
153
153
  }
154
- }), [["__scopeId", "data-v-299de2c3"]]), ie = { class: "mw-card" }, I = {
154
+ }), [["__scopeId", "data-v-299de2c3"]]), P = { class: "mw-card" }, F = {
155
155
  key: 0,
156
156
  class: "mw-card__header"
157
- }, L = { class: "mw-card__title" }, R = { class: "mw-card__body" }, z = {
157
+ }, ce = { class: "mw-card__title" }, I = { class: "mw-card__body" }, L = {
158
158
  key: 1,
159
159
  class: "mw-card__footer"
160
- }, B = /*#__PURE__*/ y(/* @__PURE__ */ a({
160
+ }, R = /*#__PURE__*/ _(/* @__PURE__ */ a({
161
161
  __name: "UiCard",
162
162
  props: { title: {} },
163
163
  setup(e) {
164
- return (t, a) => (l(), r("section", ie, [
165
- e.title || t.$slots.header ? (l(), r("header", I, [d(t.$slots, "header", {}, () => [i("h3", L, f(e.title), 1)], !0)])) : n("", !0),
166
- i("div", R, [d(t.$slots, "default", {}, void 0, !0)]),
167
- t.$slots.footer ? (l(), r("footer", z, [d(t.$slots, "footer", {}, void 0, !0)])) : n("", !0)
164
+ return (t, a) => (u(), r("section", P, [
165
+ e.title || t.$slots.header ? (u(), r("header", F, [p(t.$slots, "header", {}, () => [i("h3", ce, m(e.title), 1)], !0)])) : n("", !0),
166
+ i("div", I, [p(t.$slots, "default", {}, void 0, !0)]),
167
+ t.$slots.footer ? (u(), r("footer", L, [p(t.$slots, "footer", {}, void 0, !0)])) : n("", !0)
168
168
  ]));
169
169
  }
170
- }), [["__scopeId", "data-v-fd2fd7ab"]]), V = { class: "mw-select" }, H = /*#__PURE__*/ y(/* @__PURE__ */ a({
170
+ }), [["__scopeId", "data-v-fd2fd7ab"]]), z = { class: "mw-select" }, B = /*#__PURE__*/ _(/* @__PURE__ */ a({
171
171
  inheritAttrs: !1,
172
172
  __name: "UiSelect",
173
173
  props: {
@@ -176,8 +176,8 @@ var re = { class: "mw-header__brand" }, g = { class: "mw-header__center" }, _ =
176
176
  },
177
177
  emits: ["update:modelValue"],
178
178
  setup(e) {
179
- let t = p(e, "modelValue");
180
- return (e, n) => (l(), r("div", V, [ne(i("select", o(e.$attrs, { "onUpdate:modelValue": n[0] ||= (e) => t.value = e }), [d(e.$slots, "default", {}, void 0, !0)], 16), [[m, t.value]]), n[1] ||= i("svg", {
179
+ let t = te(e, "modelValue");
180
+ return (e, n) => (u(), r("div", z, [ie(i("select", o(e.$attrs, { "onUpdate:modelValue": n[0] ||= (e) => t.value = e }), [p(e.$slots, "default", {}, void 0, !0)], 16), [[ne, t.value]]), n[1] ||= i("svg", {
181
181
  class: "mw-select__chevron",
182
182
  xmlns: "http://www.w3.org/2000/svg",
183
183
  width: "12",
@@ -186,7 +186,7 @@ var re = { class: "mw-header__brand" }, g = { class: "mw-header__center" }, _ =
186
186
  "aria-hidden": "true"
187
187
  }, [i("path", { d: "M1 1l5 5 5-5" })], -1)]));
188
188
  }
189
- }), [["__scopeId", "data-v-b9468387"]]), U = ["role"], W = /*#__PURE__*/ y(/* @__PURE__ */ a({
189
+ }), [["__scopeId", "data-v-b9468387"]]), V = ["role"], H = /*#__PURE__*/ _(/* @__PURE__ */ a({
190
190
  __name: "UiNotice",
191
191
  props: { type: {} },
192
192
  setup(e) {
@@ -194,16 +194,16 @@ var re = { class: "mw-header__brand" }, g = { class: "mw-header__center" }, _ =
194
194
  if (n.type === "error") return "alert";
195
195
  if (n.type === "ok") return "status";
196
196
  });
197
- return (t, n) => (l(), r("p", {
197
+ return (t, n) => (u(), r("p", {
198
198
  class: s(["mw-notice", e.type && e.type !== "info" ? `mw-notice--${e.type}` : ""]),
199
199
  role: i.value
200
- }, [d(t.$slots, "default", {}, void 0, !0)], 10, U));
200
+ }, [p(t.$slots, "default", {}, void 0, !0)], 10, V));
201
201
  }
202
- }), [["__scopeId", "data-v-6f53d0be"]]), G = ["disabled", "aria-current"], K = {
202
+ }), [["__scopeId", "data-v-6f53d0be"]]), U = ["disabled", "aria-current"], W = {
203
203
  key: 0,
204
204
  class: "sidebar-item__icon",
205
205
  "aria-hidden": "true"
206
- }, q = { class: "sidebar-item__label" }, J = /*#__PURE__*/ y(/* @__PURE__ */ a({
206
+ }, G = { class: "sidebar-item__label" }, le = /*#__PURE__*/ _(/* @__PURE__ */ a({
207
207
  __name: "SidebarItem",
208
208
  props: {
209
209
  label: {},
@@ -218,24 +218,85 @@ var re = { class: "mw-header__brand" }, g = { class: "mw-header__center" }, _ =
218
218
  }
219
219
  },
220
220
  setup(e) {
221
- return (t, a) => (l(), r("button", {
221
+ return (t, a) => (u(), r("button", {
222
222
  type: "button",
223
223
  class: s(["sidebar-item", { "is-active": e.active }]),
224
224
  disabled: e.disabled,
225
225
  "aria-current": e.active ? "page" : void 0
226
- }, [e.icon ? (l(), r("span", K, f(e.icon), 1)) : n("", !0), i("span", q, f(e.label), 1)], 10, G));
226
+ }, [e.icon ? (u(), r("span", W, m(e.icon), 1)) : n("", !0), i("span", G, m(e.label), 1)], 10, U));
227
227
  }
228
- }), [["__scopeId", "data-v-e15afdf7"]]), Y = "mw-color-mode", X = ee("system"), Z = !1;
229
- function ae() {
228
+ }), [["__scopeId", "data-v-e15afdf7"]]), ue = /* @__PURE__ */ new Set([
229
+ "operational",
230
+ "degraded",
231
+ "down",
232
+ "unknown"
233
+ ]);
234
+ function K(e) {
235
+ return typeof e == "string" && ue.has(e);
236
+ }
237
+ function q(e) {
238
+ if (typeof e != "object" || !e) return null;
239
+ let t = e;
240
+ return !K(t.overall) || typeof t.generated_at != "string" || !Array.isArray(t.groups) ? null : e;
241
+ }
242
+ //#endregion
243
+ //#region src/components/StatusWidget.vue?vue&type=script&setup=true&lang.ts
244
+ var de = ["href", "title"], J = { class: "status-widget__label" }, fe = /*#__PURE__*/ _(/* @__PURE__ */ a({
245
+ __name: "StatusWidget",
246
+ props: {
247
+ apiUrl: { default: "https://status.meddleware.co.uk/api/status" },
248
+ href: { default: "https://status.meddleware.co.uk" },
249
+ pollInterval: { default: 6e4 }
250
+ },
251
+ setup(e) {
252
+ let t = e, n = {
253
+ operational: "ok",
254
+ degraded: "degraded",
255
+ down: "error",
256
+ unknown: "degraded"
257
+ }, a = {
258
+ operational: "All systems operational",
259
+ degraded: "Degraded",
260
+ down: "Major outage",
261
+ unknown: "Status unknown"
262
+ }, o = d("ok"), c = d("All systems operational"), f = null;
263
+ async function p() {
264
+ try {
265
+ let e = await fetch(t.apiUrl);
266
+ if (!e.ok) throw Error(`HTTP ${e.status}`);
267
+ let r = q(await e.json());
268
+ if (!r) throw Error("unexpected response shape");
269
+ o.value = n[r.overall], c.value = a[r.overall];
270
+ } catch {
271
+ o.value = "error", c.value = "Status unavailable";
272
+ }
273
+ }
274
+ return l(() => {
275
+ p(), f = setInterval(p, t.pollInterval);
276
+ }), ee(() => {
277
+ f !== null && clearInterval(f);
278
+ }), (t, n) => (u(), r("a", {
279
+ href: e.href,
280
+ target: "_blank",
281
+ rel: "noopener",
282
+ class: s(["status-widget", `status-widget--${o.value}`]),
283
+ title: `Platform status: ${c.value}`
284
+ }, [n[0] ||= i("span", {
285
+ class: "status-widget__dot",
286
+ "aria-hidden": "true"
287
+ }, null, -1), i("span", J, m(c.value), 1)], 10, de));
288
+ }
289
+ }), [["__scopeId", "data-v-20197983"]]), Y = "mw-color-mode", X = d("system"), Z = !1;
290
+ function pe() {
230
291
  return typeof window < "u" && typeof window.matchMedia == "function" && window.matchMedia("(prefers-color-scheme: dark)").matches;
231
292
  }
232
293
  function Q(e) {
233
- return e === "dark" || e === "system" && ae();
294
+ return e === "dark" || e === "system" && pe();
234
295
  }
235
296
  function $(e) {
236
297
  typeof document > "u" || document.documentElement.setAttribute("data-theme", Q(e) ? "dark" : "light");
237
298
  }
238
- function oe(e = "system") {
299
+ function me(e = "system") {
239
300
  if (!Z) {
240
301
  Z = !0;
241
302
  let t = null;
@@ -244,7 +305,7 @@ function oe(e = "system") {
244
305
  } catch {}
245
306
  X.value = t ?? e, $(X.value), typeof window < "u" && typeof window.matchMedia == "function" && window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => {
246
307
  X.value === "system" && $("system");
247
- }), te(X, (e) => {
308
+ }), re(X, (e) => {
248
309
  $(e);
249
310
  try {
250
311
  typeof localStorage < "u" && localStorage.setItem(Y, e);
@@ -265,4 +326,4 @@ function oe(e = "system") {
265
326
  };
266
327
  }
267
328
  //#endregion
268
- export { O as AppFooter, b as AppHeader, w as AppSidebar, N as ColorModeControl, J as SidebarItem, F as UiButton, B as UiCard, W as UiNotice, H as UiSelect, oe as useColorMode };
329
+ export { E as AppFooter, v as AppHeader, S as AppSidebar, j as ColorModeControl, le as SidebarItem, fe as StatusWidget, N as UiButton, R as UiCard, H as UiNotice, B as UiSelect, K as isStatusLevel, q as parseSnapshot, me as useColorMode };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@meddleware/ui",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
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",