@leavepulse/ui 0.92.0 → 0.93.0
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/assets/{LpTable-B3hNJQVl.css → LpTable-gpdZSVMX.css} +39 -30
- package/dist/chunks/{LpInfraNode-BYiMFDnA.js → LpInfraNode-tD5x5YOy.js} +19 -8
- package/dist/chunks/{LpTopologyCanvas-CQ0l6A5h.js → LpTopologyCanvas-B1YFfMaq.js} +1 -1
- package/dist/components/LpInfraNode.vue.d.ts +9 -0
- package/dist/components/LpInfraNode.vue.js +1 -1
- package/dist/components/LpShaderBackdrop.vue.d.ts +2 -2
- package/dist/components/LpTable.vue.js +2 -2
- package/dist/components/LpTopologyCanvas.vue.js +1 -1
- package/dist/index.js +2 -2
- package/package.json +1 -1
- package/src/components/LpInfraNode.vue +33 -18
- package/src/components/LpTable.vue +10 -1
|
@@ -6,19 +6,28 @@
|
|
|
6
6
|
* outside and only the dots between facts are seen. A cell whose slot drew
|
|
7
7
|
* nothing (a v-if that failed, an empty value) is left out, dot and all.
|
|
8
8
|
*/
|
|
9
|
-
.lp-line[data-v-
|
|
9
|
+
.lp-line[data-v-5ac91b9e] {
|
|
10
10
|
--lp-line-sep: 1rem;
|
|
11
11
|
}
|
|
12
|
-
.lp-line-row[data-v-
|
|
12
|
+
.lp-line-row[data-v-5ac91b9e] {
|
|
13
13
|
margin-left: calc(-1 * var(--lp-line-sep));
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
|
|
16
|
+
/* The dot lives in the field's own left padding, so whatever the cell draws,
|
|
17
|
+
a word or a block of two lines, starts after it and never in the clipped
|
|
18
|
+
strip. */
|
|
19
|
+
.lp-line-field[data-v-5ac91b9e] {
|
|
20
|
+
position: relative;
|
|
21
|
+
padding-left: var(--lp-line-sep);
|
|
22
|
+
}
|
|
23
|
+
.lp-line-field[data-v-5ac91b9e]::before {
|
|
16
24
|
content: "·" / "";
|
|
17
|
-
|
|
25
|
+
position: absolute;
|
|
26
|
+
left: 0;
|
|
18
27
|
width: var(--lp-line-sep);
|
|
19
28
|
text-align: center;
|
|
20
29
|
}
|
|
21
|
-
.lp-line-field[data-v-
|
|
30
|
+
.lp-line-field[data-v-5ac91b9e]:has(> .lp-line-value:empty) {
|
|
22
31
|
display: none;
|
|
23
32
|
}
|
|
24
33
|
|
|
@@ -34,17 +43,17 @@
|
|
|
34
43
|
* fade before closing the gap and the whole list lurches at the end instead of
|
|
35
44
|
* gliding.
|
|
36
45
|
*/
|
|
37
|
-
.lp-row-enter-active[data-v-
|
|
38
|
-
.lp-row-leave-active[data-v-
|
|
46
|
+
.lp-row-enter-active[data-v-5ac91b9e],
|
|
47
|
+
.lp-row-leave-active[data-v-5ac91b9e] {
|
|
39
48
|
transition:
|
|
40
49
|
opacity var(--duration-fast) var(--ease-emphasized),
|
|
41
50
|
transform var(--duration-fast) var(--ease-emphasized);
|
|
42
51
|
}
|
|
43
|
-
.lp-row-move[data-v-
|
|
52
|
+
.lp-row-move[data-v-5ac91b9e] {
|
|
44
53
|
transition: transform var(--duration-medium) var(--ease-emphasized);
|
|
45
54
|
}
|
|
46
|
-
.lp-row-enter-from[data-v-
|
|
47
|
-
.lp-row-leave-to[data-v-
|
|
55
|
+
.lp-row-enter-from[data-v-5ac91b9e],
|
|
56
|
+
.lp-row-leave-to[data-v-5ac91b9e] {
|
|
48
57
|
opacity: 0;
|
|
49
58
|
transform: translateX(-0.5rem);
|
|
50
59
|
}
|
|
@@ -58,7 +67,7 @@
|
|
|
58
67
|
* drops out of the table layout entirely. Nothing sits below this row to be
|
|
59
68
|
* dragged around, so simply not animating its exit costs nothing.
|
|
60
69
|
*/
|
|
61
|
-
.lp-row-empty.lp-row-leave-active[data-v-
|
|
70
|
+
.lp-row-empty.lp-row-leave-active[data-v-5ac91b9e] {
|
|
62
71
|
display: none;
|
|
63
72
|
}
|
|
64
73
|
|
|
@@ -77,7 +86,7 @@
|
|
|
77
86
|
* once anything is selected. An earlier version collapsed the column to nothing
|
|
78
87
|
* and floated the hover box over the first cell, where it sat on the text.
|
|
79
88
|
*/
|
|
80
|
-
.lp-tick[data-v-
|
|
89
|
+
.lp-tick[data-v-5ac91b9e] {
|
|
81
90
|
display: flex;
|
|
82
91
|
align-items: center;
|
|
83
92
|
max-width: 0;
|
|
@@ -89,17 +98,17 @@
|
|
|
89
98
|
padding var(--duration-medium) var(--ease-emphasized),
|
|
90
99
|
opacity var(--duration-fast) ease;
|
|
91
100
|
}
|
|
92
|
-
.lp-tick-open[data-v-
|
|
101
|
+
.lp-tick-open[data-v-5ac91b9e] {
|
|
93
102
|
max-width: 3.25rem;
|
|
94
103
|
padding-inline: 1rem;
|
|
95
104
|
opacity: 1;
|
|
96
105
|
}
|
|
97
|
-
.lp-tick-hidden[data-v-
|
|
98
|
-
.lp-tick-cue[data-v-
|
|
106
|
+
.lp-tick-hidden[data-v-5ac91b9e],
|
|
107
|
+
.lp-tick-cue[data-v-5ac91b9e] {
|
|
99
108
|
opacity: 0;
|
|
100
109
|
}
|
|
101
|
-
tr:hover .lp-tick-cue[data-v-
|
|
102
|
-
.lp-tick-cue[data-v-
|
|
110
|
+
tr:hover .lp-tick-cue[data-v-5ac91b9e],
|
|
111
|
+
.lp-tick-cue[data-v-5ac91b9e]:focus-within {
|
|
103
112
|
opacity: 1;
|
|
104
113
|
}
|
|
105
114
|
|
|
@@ -107,15 +116,15 @@ tr:hover .lp-tick-cue[data-v-62285817],
|
|
|
107
116
|
turned down the rows change without animating rather than not changing. */
|
|
108
117
|
/* Only where there is a hover to reveal it; a finger long-presses instead. */
|
|
109
118
|
@media (hover: none) {
|
|
110
|
-
tr:hover .lp-tick-cue[data-v-
|
|
119
|
+
tr:hover .lp-tick-cue[data-v-5ac91b9e] {
|
|
111
120
|
opacity: 0;
|
|
112
121
|
}
|
|
113
122
|
|
|
114
123
|
/* Nor is there anything for the column's width to hold until then: on a
|
|
115
124
|
touch screen it stays closed until a long press starts selecting, and
|
|
116
125
|
eases open like the rest of the column. */
|
|
117
|
-
.lp-tick-hidden[data-v-
|
|
118
|
-
.lp-tick-cue[data-v-
|
|
126
|
+
.lp-tick-hidden[data-v-5ac91b9e],
|
|
127
|
+
.lp-tick-cue[data-v-5ac91b9e] {
|
|
119
128
|
max-width: 0;
|
|
120
129
|
padding-inline: 0;
|
|
121
130
|
}
|
|
@@ -125,7 +134,7 @@ tr:hover .lp-tick-cue[data-v-62285817] {
|
|
|
125
134
|
* A list line's tick opens from nothing, as the table's column does, so lines
|
|
126
135
|
* do not carry an empty gap until selection starts.
|
|
127
136
|
*/
|
|
128
|
-
.lp-tick-line[data-v-
|
|
137
|
+
.lp-tick-line[data-v-5ac91b9e] {
|
|
129
138
|
max-width: 2rem;
|
|
130
139
|
opacity: 1;
|
|
131
140
|
}
|
|
@@ -135,28 +144,28 @@ tr:hover .lp-tick-cue[data-v-62285817] {
|
|
|
135
144
|
* open; a cue shows it only while
|
|
136
145
|
* the card is pointed at or holds focus, the same rule as a row's.
|
|
137
146
|
*/
|
|
138
|
-
.lp-tick-card[data-v-
|
|
147
|
+
.lp-tick-card[data-v-5ac91b9e] {
|
|
139
148
|
max-width: none;
|
|
140
149
|
padding-inline: 0;
|
|
141
150
|
opacity: 1;
|
|
142
151
|
}
|
|
143
|
-
.lp-tick[data-cue][data-v-
|
|
152
|
+
.lp-tick[data-cue][data-v-5ac91b9e] {
|
|
144
153
|
opacity: 0;
|
|
145
154
|
}
|
|
146
|
-
.lp-item:hover .lp-tick[data-cue][data-v-
|
|
147
|
-
.lp-tick[data-cue][data-v-
|
|
155
|
+
.lp-item:hover .lp-tick[data-cue][data-v-5ac91b9e],
|
|
156
|
+
.lp-tick[data-cue][data-v-5ac91b9e]:focus-within {
|
|
148
157
|
opacity: 1;
|
|
149
158
|
}
|
|
150
159
|
@media (hover: none) {
|
|
151
|
-
.lp-item:hover .lp-tick[data-cue][data-v-
|
|
160
|
+
.lp-item:hover .lp-tick[data-cue][data-v-5ac91b9e] {
|
|
152
161
|
opacity: 0;
|
|
153
162
|
}
|
|
154
163
|
}
|
|
155
164
|
@media (prefers-reduced-motion: reduce) {
|
|
156
|
-
.lp-row-enter-active[data-v-
|
|
157
|
-
.lp-row-leave-active[data-v-
|
|
158
|
-
.lp-row-move[data-v-
|
|
159
|
-
.lp-tick[data-v-
|
|
165
|
+
.lp-row-enter-active[data-v-5ac91b9e],
|
|
166
|
+
.lp-row-leave-active[data-v-5ac91b9e],
|
|
167
|
+
.lp-row-move[data-v-5ac91b9e],
|
|
168
|
+
.lp-tick[data-v-5ac91b9e] {
|
|
160
169
|
transition: none;
|
|
161
170
|
}
|
|
162
171
|
}
|
|
@@ -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)]" :
|
|
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
|
-
|
|
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]",
|
|
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
|
-
|
|
118
|
+
state.value ? (openBlock(), createBlock(_sfc_main$1, {
|
|
107
119
|
key: 0,
|
|
108
|
-
state:
|
|
109
|
-
pulse: __props.data.online,
|
|
120
|
+
state: state.value,
|
|
110
121
|
"hide-label": ""
|
|
111
|
-
}, null, 8, ["state"
|
|
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-
|
|
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). */
|
|
@@ -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" | "
|
|
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" | "
|
|
31
|
+
onFallback?: ((reason: "compile" | "unavailable" | "lost" | "slow") => any) | undefined;
|
|
32
32
|
}>, {
|
|
33
33
|
speed: number;
|
|
34
34
|
paused: boolean;
|
|
@@ -34,7 +34,7 @@ import { toCsv, downloadFile, fileStem } from "./exportData.js";
|
|
|
34
34
|
import { toHiddenSets, sameHidden, summarizeShown, parseTableState, mergeRestored, serializeTableState, fromHiddenSets } from "./tableFilter.js";
|
|
35
35
|
import { resolveRowMenu } from "./tableRowMenu.js";
|
|
36
36
|
import { _ as _export_sfc } from "../chunks/_plugin-vue_export-helper-1tPrXgE0.js";
|
|
37
|
-
import '../assets/LpTable-
|
|
37
|
+
import '../assets/LpTable-gpdZSVMX.css';const _hoisted_1 = {
|
|
38
38
|
key: 0,
|
|
39
39
|
class: "flex min-h-8 flex-wrap items-center gap-x-3 gap-y-2 pb-3 text-xs"
|
|
40
40
|
};
|
|
@@ -1898,7 +1898,7 @@ const _sfc_main = /* @__PURE__ */ defineComponent({
|
|
|
1898
1898
|
};
|
|
1899
1899
|
}
|
|
1900
1900
|
});
|
|
1901
|
-
const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-
|
|
1901
|
+
const LpTable = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-5ac91b9e"]]);
|
|
1902
1902
|
export {
|
|
1903
1903
|
LpTable as default
|
|
1904
1904
|
};
|
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-
|
|
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-
|
|
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
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
|
171
|
-
|
|
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="
|
|
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
|
|
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)]">
|
|
@@ -2444,9 +2444,18 @@ usePaletteCommands(() => {
|
|
|
2444
2444
|
margin-left: calc(-1 * var(--lp-line-sep));
|
|
2445
2445
|
}
|
|
2446
2446
|
|
|
2447
|
+
/* The dot lives in the field's own left padding, so whatever the cell draws,
|
|
2448
|
+
a word or a block of two lines, starts after it and never in the clipped
|
|
2449
|
+
strip. */
|
|
2450
|
+
.lp-line-field {
|
|
2451
|
+
position: relative;
|
|
2452
|
+
padding-left: var(--lp-line-sep);
|
|
2453
|
+
}
|
|
2454
|
+
|
|
2447
2455
|
.lp-line-field::before {
|
|
2448
2456
|
content: "·" / "";
|
|
2449
|
-
|
|
2457
|
+
position: absolute;
|
|
2458
|
+
left: 0;
|
|
2450
2459
|
width: var(--lp-line-sep);
|
|
2451
2460
|
text-align: center;
|
|
2452
2461
|
}
|