@leavepulse/ui 0.92.1 → 0.93.1

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.
@@ -2,6 +2,8 @@ import { defineComponent, computed, openBlock, createElementBlock, normalizeStyl
2
2
  import { Handle, Position } from "@vue-flow/core";
3
3
  import { _ as _sfc_main$2 } from "./LpBadge-Bh5roBr_.js";
4
4
  import { _ as _sfc_main$1 } from "./LpStatus-C6GqSHJj.js";
5
+ import { TONE_COLOR } from "../components/meter.js";
6
+ import { STATUS_LOOK } from "../components/status.js";
5
7
  const _hoisted_1 = { class: "px-[11px] pb-2.5 pt-[9px]" };
6
8
  const _hoisted_2 = { class: "flex items-center gap-[7px]" };
7
9
  const _hoisted_3 = { class: "text-[13px] text-[var(--accent)]" };
@@ -74,15 +76,25 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
74
76
  const accentLine = computed(
75
77
  () => `color-mix(in srgb, ${accent.value} 35%, var(--color-line))`
76
78
  );
79
+ const state = computed(
80
+ () => props.data.status ?? (props.data.online === void 0 ? void 0 : props.data.online ? "online" : "down")
81
+ );
82
+ const troubled = computed(() => state.value !== void 0 && state.value !== "online");
83
+ const lost = computed(() => state.value === "down" || state.value === "offline");
84
+ const stateColor = computed(
85
+ () => state.value ? `color-mix(in srgb, ${TONE_COLOR[STATUS_LOOK[state.value].tone]} 50%, transparent)` : void 0
86
+ );
77
87
  return (_ctx, _cache) => {
78
88
  return openBlock(), createElementBlock("div", {
79
- class: normalizeClass(["w-[168px] overflow-hidden rounded-card border bg-surface-raised shadow-panel transition-[border-color,box-shadow]", [
80
- __props.selected ? "border-[var(--accent)]" : __props.data.online === false ? "border-dashed border-danger/50" : "border-[var(--accent-line)]"
89
+ class: normalizeClass(["w-[168px] overflow-hidden rounded-card border bg-surface-raised shadow-panel transition-[border-color,box-shadow,opacity] duration-medium ease-[var(--ease-emphasized)]", [
90
+ __props.selected ? "border-[var(--accent)]" : troubled.value ? "border-[var(--state-line)]" : "border-[var(--accent-line)]",
91
+ !__props.selected && lost.value ? "border-dashed" : ""
81
92
  ]]),
82
93
  style: normalizeStyle({
83
94
  "--accent": accent.value,
84
95
  "--accent-line": accentLine.value,
85
- opacity: __props.data.dimmed ? 0.3 : __props.data.online === false ? 0.75 : 1,
96
+ "--state-line": stateColor.value,
97
+ opacity: __props.data.dimmed ? 0.3 : lost.value ? 0.75 : 1,
86
98
  ...__props.selected ? { boxShadow: "0 0 0 2px color-mix(in srgb, var(--accent) 40%, transparent), var(--shadow-panel)" } : {}
87
99
  })
88
100
  }, [
@@ -97,18 +109,17 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
97
109
  class: "!size-2 !border-2 !border-[var(--accent)] !bg-surface"
98
110
  }, null, 8, ["position"]),
99
111
  createElementVNode("div", {
100
- class: normalizeClass(["h-[3px]", __props.data.online === false ? "bg-danger/50" : "bg-[var(--accent)]"])
112
+ class: normalizeClass(["h-[3px] transition-[background-color] duration-medium ease-[var(--ease-emphasized)]", troubled.value ? "bg-[var(--state-line)]" : "bg-[var(--accent)]"])
101
113
  }, null, 2),
102
114
  createElementVNode("div", _hoisted_1, [
103
115
  createElementVNode("div", _hoisted_2, [
104
116
  createElementVNode("span", _hoisted_3, toDisplayString(icon.value), 1),
105
117
  createElementVNode("span", _hoisted_4, toDisplayString(__props.data.name), 1),
106
- __props.data.online !== void 0 ? (openBlock(), createBlock(_sfc_main$1, {
118
+ state.value ? (openBlock(), createBlock(_sfc_main$1, {
107
119
  key: 0,
108
- state: __props.data.online ? "online" : "down",
109
- pulse: __props.data.online,
120
+ state: state.value,
110
121
  "hide-label": ""
111
- }, null, 8, ["state", "pulse"])) : createCommentVNode("", true)
122
+ }, null, 8, ["state"])) : createCommentVNode("", true)
112
123
  ]),
113
124
  createElementVNode("div", _hoisted_5, [
114
125
  createElementVNode("span", _hoisted_6, toDisplayString(__props.data.roleLabel ?? __props.data.role), 1),
@@ -10,7 +10,7 @@ import { nodeWindow, isHTMLElement } from "../composables/realm.js";
10
10
  import { backgroundOf, resolveCssColor, svgToPng } from "../components/exportData.js";
11
11
  import { _ as _sfc_main$6 } from "./LpButton-BvVoF-PO.js";
12
12
  import { _ as _sfc_main$7 } from "./LpIcon-CxHFPwKt.js";
13
- import { _ as _sfc_main$3 } from "./LpInfraNode-BYiMFDnA.js";
13
+ import { _ as _sfc_main$3 } from "./LpInfraNode-tD5x5YOy.js";
14
14
  import { _ as _sfc_main$5 } from "./LpInput-DGMpxrcg.js";
15
15
  import { _ as _sfc_main$1 } from "./LpLaneNode-Bqcdm8Rf.js";
16
16
  import { _ as _sfc_main$2 } from "./LpServiceNode-Cln2ntdw.js";
@@ -8,6 +8,7 @@
8
8
  * component doesn't reach apps that only import the kit's token css.
9
9
  */
10
10
  import { type NodeProps } from "@vue-flow/core";
11
+ import { type StatusKind } from "./status";
11
12
  export interface InfraNodeData {
12
13
  name: string;
13
14
  /** Role key: selects the colour/glyph, and shown as-is unless `roleLabel`. */
@@ -18,7 +19,15 @@ export interface InfraNodeData {
18
19
  */
19
20
  roleLabel?: string;
20
21
  overlay?: string;
22
+ /** Up or down. Superseded by `status` when that is set. */
21
23
  online?: boolean;
24
+ /**
25
+ * The node's state in the kit's status words, for a fleet that knows more
26
+ * than up and down: a box that serves but takes no instruction, one merely
27
+ * unreachable, one starting. Drawn with LpStatus's light and tone; only
28
+ * `down` and `offline` take the dashed, faded frame of a lost machine.
29
+ */
30
+ status?: StatusKind;
22
31
  kind?: string;
23
32
  project?: string;
24
33
  /** Control-plane firewall rule count on this host (badge when > 0). */
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpInfraNode-BYiMFDnA.js";
1
+ import { _ as _sfc_main } from "../chunks/LpInfraNode-tD5x5YOy.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -14,7 +14,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
14
14
  /** Always show the CSS still, never start WebGL: for a low-power mode or a screenshot. */
15
15
  still?: boolean;
16
16
  }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {} & {
17
- fallback: (reason: "compile" | "unavailable" | "slow" | "lost") => any;
17
+ fallback: (reason: "compile" | "unavailable" | "lost" | "slow") => any;
18
18
  }, string, import("vue").PublicProps, Readonly<{
19
19
  preset?: ShaderPreset;
20
20
  /** How strongly the theme colours show over the surface, 0–1. Lower for a backdrop under body text. */
@@ -28,7 +28,7 @@ declare const __VLS_export: import("vue").DefineComponent<{
28
28
  /** Always show the CSS still, never start WebGL: for a low-power mode or a screenshot. */
29
29
  still?: boolean;
30
30
  }> & Readonly<{
31
- onFallback?: ((reason: "compile" | "unavailable" | "slow" | "lost") => any) | undefined;
31
+ onFallback?: ((reason: "compile" | "unavailable" | "lost" | "slow") => any) | undefined;
32
32
  }>, {
33
33
  speed: number;
34
34
  paused: boolean;
@@ -1,4 +1,4 @@
1
- import { _ as _sfc_main } from "../chunks/LpTopologyCanvas-CQ0l6A5h.js";
1
+ import { _ as _sfc_main } from "../chunks/LpTopologyCanvas-B1YFfMaq.js";
2
2
  export {
3
3
  _sfc_main as default
4
4
  };
@@ -75,9 +75,9 @@ export interface ChartAnnotation {
75
75
  }
76
76
  /**
77
77
  * Which x labels to print with every `stride`-th one shown. The last point is
78
- * always labelled (it is usually "now"), and when it lands within half a
79
- * stride to the previous shown label that one gives way: "20 22 23" read as
80
- * one smudge at the end of an hourly axis.
78
+ * always labelled (it is usually "now"), and when it lands closer than a
79
+ * full stride to the previous shown label that one gives way: "20 22 23" read
80
+ * as one smudge at the end of an hourly axis.
81
81
  */
82
82
  export declare function xLabelIndices(count: number, stride: number): number[];
83
83
  /** The x index a marker points at, or -1 when it names nothing on the axis. */
@@ -7,7 +7,7 @@ function xLabelIndices(count, stride) {
7
7
  for (let i = 0; i < count; i += step) out.push(i);
8
8
  const last = count - 1;
9
9
  if (out[out.length - 1] !== last) {
10
- if (out.length > 1 && last - out[out.length - 1] <= step / 2) out.pop();
10
+ if (out.length > 1 && last - out[out.length - 1] < step) out.pop();
11
11
  out.push(last);
12
12
  }
13
13
  return out;
package/dist/index.js CHANGED
@@ -54,7 +54,7 @@ import { _ as _31 } from "./chunks/LpFormField-jbM7K1Hg.js";
54
54
  import { _ as _32 } from "./chunks/LpIcon-CxHFPwKt.js";
55
55
  import { i, b, a, c } from "./chunks/icons-C8ccRCKw.js";
56
56
  import { default as default10 } from "./components/LpImageEditor.vue.js";
57
- import { _ as _33 } from "./chunks/LpInfraNode-BYiMFDnA.js";
57
+ import { _ as _33 } from "./chunks/LpInfraNode-tD5x5YOy.js";
58
58
  import { _ as _34 } from "./chunks/LpInput-DGMpxrcg.js";
59
59
  import { _ as _35 } from "./chunks/LpLightbox-a24V_0Wk.js";
60
60
  import { fileNameOf, isVideoItem } from "./components/lightbox.js";
@@ -131,7 +131,7 @@ import { _ as _87 } from "./chunks/LpHintLayer-nuMqfzvy.js";
131
131
  import { _ as _88 } from "./chunks/LpHints-ByAd3a76.js";
132
132
  import { HINT_SIGNALS, contextMenuTip, createHintSystem, dropzoneTip, hintKey, hintSummary, hotkeyTip, localHintStorage, noteDropzone, noteHotkeyTarget, paletteTip, provideHintSystem, runningHintSystem, useHintSystem } from "./composables/useHintSystem.js";
133
133
  import { _ as _89 } from "./chunks/LpTooltipProvider-CftjV7Xc.js";
134
- import { _ as _90 } from "./chunks/LpTopologyCanvas-CQ0l6A5h.js";
134
+ import { _ as _90 } from "./chunks/LpTopologyCanvas-B1YFfMaq.js";
135
135
  import { clusterGraph, diffTopology, findPath, layeredLayout, neighborhood, topologyToSvg } from "./components/topologyGraph.js";
136
136
  import { _ as _91 } from "./chunks/LpUptimeBar-Dt6SY_-m.js";
137
137
  import { addLeaf, addTab, countBlocks, countLeaves, deserializeLayout, makeLayout, moveLeaf, removeBlock, removeLeaf, reorderTab, resizeAt, serializeLayout, setActiveTab } from "./layout/tree.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@leavepulse/ui",
3
- "version": "0.92.1",
3
+ "version": "0.93.1",
4
4
  "type": "module",
5
5
  "description": "LeavePulse token-driven Vue component kit.",
6
6
  "repository": {
@@ -12,6 +12,8 @@ import { Handle, Position, type NodeProps } from "@vue-flow/core"
12
12
  import { computed } from "vue"
13
13
  import LpBadge from "./LpBadge.vue"
14
14
  import LpStatus from "./LpStatus.vue"
15
+ import { TONE_COLOR } from "./meter"
16
+ import { STATUS_LOOK, type StatusKind } from "./status"
15
17
 
16
18
  export interface InfraNodeData {
17
19
  name: string
@@ -23,7 +25,15 @@ export interface InfraNodeData {
23
25
  */
24
26
  roleLabel?: string
25
27
  overlay?: string
28
+ /** Up or down. Superseded by `status` when that is set. */
26
29
  online?: boolean
30
+ /**
31
+ * The node's state in the kit's status words, for a fleet that knows more
32
+ * than up and down: a box that serves but takes no instruction, one merely
33
+ * unreachable, one starting. Drawn with LpStatus's light and tone; only
34
+ * `down` and `offline` take the dashed, faded frame of a lost machine.
35
+ */
36
+ status?: StatusKind
27
37
  kind?: string
28
38
  project?: string
29
39
  /** Control-plane firewall rule count on this host (badge when > 0). */
@@ -126,6 +136,18 @@ const icon = computed(() => role.value.icon)
126
136
  const accentLine = computed(
127
137
  () => `color-mix(in srgb, ${accent.value} 35%, var(--color-line))`,
128
138
  )
139
+
140
+ /** `status` when given, else the old up/down flag read as a status. */
141
+ const state = computed<StatusKind | undefined>(
142
+ () => props.data.status ?? (props.data.online === undefined ? undefined : props.data.online ? "online" : "down"),
143
+ )
144
+ /** A state other than healthy takes the frame and the role bar in its own tone. */
145
+ const troubled = computed(() => state.value !== undefined && state.value !== "online")
146
+ /** No contact at all: dashed and faded, the way a lost machine has always looked. */
147
+ const lost = computed(() => state.value === "down" || state.value === "offline")
148
+ const stateColor = computed(() =>
149
+ state.value ? `color-mix(in srgb, ${TONE_COLOR[STATUS_LOOK[state.value].tone]} 50%, transparent)` : undefined,
150
+ )
129
151
  </script>
130
152
 
131
153
  <template>
@@ -139,18 +161,16 @@ const accentLine = computed(
139
161
  signals on a node agree instead of arguing.
140
162
  -->
141
163
  <div
142
- class="w-[168px] overflow-hidden rounded-card border bg-surface-raised shadow-panel transition-[border-color,box-shadow]"
164
+ class="w-[168px] overflow-hidden rounded-card border bg-surface-raised shadow-panel transition-[border-color,box-shadow,opacity] duration-medium ease-[var(--ease-emphasized)]"
143
165
  :class="[
144
- selected
145
- ? 'border-[var(--accent)]'
146
- : data.online === false
147
- ? 'border-dashed border-danger/50'
148
- : 'border-[var(--accent-line)]',
166
+ selected ? 'border-[var(--accent)]' : troubled ? 'border-[var(--state-line)]' : 'border-[var(--accent-line)]',
167
+ !selected && lost ? 'border-dashed' : '',
149
168
  ]"
150
169
  :style="{
151
170
  '--accent': accent,
152
171
  '--accent-line': accentLine,
153
- opacity: data.dimmed ? 0.3 : data.online === false ? 0.75 : 1,
172
+ '--state-line': stateColor,
173
+ opacity: data.dimmed ? 0.3 : lost ? 0.75 : 1,
154
174
  ...(selected
155
175
  ? { boxShadow: '0 0 0 2px color-mix(in srgb, var(--accent) 40%, transparent), var(--shadow-panel)' }
156
176
  : {}),
@@ -167,23 +187,18 @@ const accentLine = computed(
167
187
  class="!size-2 !border-2 !border-[var(--accent)] !bg-surface"
168
188
  />
169
189
 
170
- <!-- The role bar reads as "this node is alive and is a proxy"; on an
171
- offline host it must not keep asserting the first half. -->
190
+ <!-- The role bar reads as "this node is alive and is a proxy"; on a host
191
+ in trouble it must not keep asserting the first half. -->
172
192
  <div
173
- class="h-[3px]"
174
- :class="data.online === false ? 'bg-danger/50' : 'bg-[var(--accent)]'"
193
+ class="h-[3px] transition-[background-color] duration-medium ease-[var(--ease-emphasized)]"
194
+ :class="troubled ? 'bg-[var(--state-line)]' : 'bg-[var(--accent)]'"
175
195
  />
176
196
  <div class="px-[11px] pb-2.5 pt-[9px]">
177
197
  <div class="flex items-center gap-[7px]">
178
198
  <span class="text-[13px] text-[var(--accent)]">{{ icon }}</span>
179
199
  <span class="flex-1 truncate text-[13px] font-semibold text-ink">{{ data.name }}</span>
180
- <!-- The kit's status light: a live host breathes, a lost one is a steady red. -->
181
- <LpStatus
182
- v-if="data.online !== undefined"
183
- :state="data.online ? 'online' : 'down'"
184
- :pulse="data.online"
185
- hide-label
186
- />
200
+ <!-- The kit's status light, with LpStatus's own tone and pulse for each state. -->
201
+ <LpStatus v-if="state" :state="state" hide-label />
187
202
  </div>
188
203
  <div class="mt-1.5 flex items-center justify-between">
189
204
  <span class="text-[10px] font-semibold uppercase tracking-wide text-[var(--accent)]">
@@ -91,9 +91,9 @@ export interface ChartAnnotation {
91
91
 
92
92
  /**
93
93
  * Which x labels to print with every `stride`-th one shown. The last point is
94
- * always labelled (it is usually "now"), and when it lands within half a
95
- * stride to the previous shown label that one gives way: "20 22 23" read as
96
- * one smudge at the end of an hourly axis.
94
+ * always labelled (it is usually "now"), and when it lands closer than a
95
+ * full stride to the previous shown label that one gives way: "20 22 23" read
96
+ * as one smudge at the end of an hourly axis.
97
97
  */
98
98
  export function xLabelIndices(count: number, stride: number): number[] {
99
99
  if (count <= 0) return []
@@ -102,7 +102,10 @@ export function xLabelIndices(count: number, stride: number): number[] {
102
102
  for (let i = 0; i < count; i += step) out.push(i)
103
103
  const last = count - 1
104
104
  if (out[out.length - 1] !== last) {
105
- if (out.length > 1 && last - out[out.length - 1] <= step / 2) out.pop()
105
+ // The stride is the spacing at which labels fit, so a last label closer
106
+ // than a full step to the one before would print over it ("10:03 PM10:09
107
+ // PM"): the earlier one gives way.
108
+ if (out.length > 1 && last - out[out.length - 1] < step) out.pop()
106
109
  out.push(last)
107
110
  }
108
111
  return out