This is an automated email from the ASF dual-hosted git repository.

wu-sheng pushed a commit to branch refactor/decompose-large-files
in repository https://gitbox.apache.org/repos/asf/skywalking-horizon-ui.git

commit c3a0e82adc97f06649ac275939958a702fad4389
Author: Wu Sheng <[email protected]>
AuthorDate: Fri Jun 26 23:28:50 2026 +0800

    fix(alarms,k8s): timeline selection band + legend, narrow-width detail, 
dashboard heights
    
    The alarm timeline draws the orange selection band on a flag click (not 
only on drag) and gains a firing/recovered legend so the red-bottom / green-top 
stack reads clearly. The alarm list/detail split stacks below ~1080px instead 
of overflowing, and long service names wrap cleanly. K8s dashboard status 
tables (k8s_service Pods Waiting / Pod Restarts; kubernetes Node / Deployment / 
Service / Pod status) drop from rowSpan 3-4 to 2 to remove the blank space.
---
 apps/bff/src/bundled_templates/layers/k8s.json     | 12 ++---
 .../src/bundled_templates/layers/k8s_service.json  |  4 +-
 apps/ui/src/components/charts/AlarmsTimeline.vue   | 54 +++++++++++++++-------
 apps/ui/src/features/alarms/AlarmDetailPanel.vue   |  4 +-
 apps/ui/src/features/alarms/AlarmsView.vue         |  7 +++
 5 files changed, 56 insertions(+), 25 deletions(-)

diff --git a/apps/bff/src/bundled_templates/layers/k8s.json 
b/apps/bff/src/bundled_templates/layers/k8s.json
index e40c368..f82745a 100644
--- a/apps/bff/src/bundled_templates/layers/k8s.json
+++ b/apps/bff/src/bundled_templates/layers/k8s.json
@@ -209,7 +209,7 @@
         ],
         "showTableValues": false,
         "span": 4,
-        "rowSpan": 4
+        "rowSpan": 2
       },
       {
         "id": "deployment_status",
@@ -225,7 +225,7 @@
         ],
         "showTableValues": false,
         "span": 4,
-        "rowSpan": 4
+        "rowSpan": 2
       },
       {
         "id": "deployment_replicas",
@@ -241,7 +241,7 @@
         ],
         "showTableValues": true,
         "span": 4,
-        "rowSpan": 4
+        "rowSpan": 2
       },
       {
         "id": "service_pod_status",
@@ -257,7 +257,7 @@
         ],
         "showTableValues": false,
         "span": 4,
-        "rowSpan": 4
+        "rowSpan": 2
       },
       {
         "id": "pod_not_running",
@@ -273,7 +273,7 @@
         ],
         "showTableValues": false,
         "span": 4,
-        "rowSpan": 4
+        "rowSpan": 2
       },
       {
         "id": "pod_waiting",
@@ -289,7 +289,7 @@
         ],
         "showTableValues": false,
         "span": 4,
-        "rowSpan": 4
+        "rowSpan": 2
       }
     ],
     "instance": [
diff --git a/apps/bff/src/bundled_templates/layers/k8s_service.json 
b/apps/bff/src/bundled_templates/layers/k8s_service.json
index adf68aa..8a541c5 100644
--- a/apps/bff/src/bundled_templates/layers/k8s_service.json
+++ b/apps/bff/src/bundled_templates/layers/k8s_service.json
@@ -83,7 +83,7 @@
         ],
         "showTableValues": false,
         "span": 4,
-        "rowSpan": 3
+        "rowSpan": 2
       },
       {
         "id": "pod_restarts",
@@ -98,7 +98,7 @@
         ],
         "showTableValues": true,
         "span": 4,
-        "rowSpan": 3
+        "rowSpan": 2
       },
       {
         "id": "cpu_resources",
diff --git a/apps/ui/src/components/charts/AlarmsTimeline.vue 
b/apps/ui/src/components/charts/AlarmsTimeline.vue
index c4457aa..f92b148 100644
--- a/apps/ui/src/components/charts/AlarmsTimeline.vue
+++ b/apps/ui/src/components/charts/AlarmsTimeline.vue
@@ -359,27 +359,49 @@ watch(
   { deep: true },
 );
 
-/* Parent-driven brush clear: when the operator hits the "clear
- * selection" button in the list header, the parent sets its
- * `selectedRange` ref to null. Mirror that into the chart by
- * wiping the brush rectangle. We only act on the null transition —
- * a non-null selection on this side is either echoed from a drag we
- * just did (no-op needed) or from a click on a flag (no rectangle
- * needed; the row list narrowing is the visual feedback). */
-watch(
-  () => props.selectedRange,
-  (next) => {
-    if (next === null || next === undefined) clearBrush();
-  },
-);
+/** Draw the orange selection band for a range (or wipe it when null).
+ *  Keeps the chart's rectangle in sync with the parent's `selectedRange`
+ *  whatever the source — a drag (snaps to the whole-minute range), a flag
+ *  click (a single-minute band, so the operator sees which slice is
+ *  active, not just the narrowed list), or the parent's clear button. */
+function drawBrush(range: { startTime: number; endTime: number } | null | 
undefined): void {
+  if (!chart) return;
+  if (range === null || range === undefined) {
+    clearBrush();
+    return;
+  }
+  chart.dispatchAction({
+    type: 'brush',
+    areas: [{ brushType: 'lineX', xAxisIndex: 0, coordRange: [range.startTime, 
range.endTime] }],
+  });
+}
+watch(() => props.selectedRange, (next) => drawBrush(next));
 </script>
 
 <template>
-  <div ref="container" class="alarms-timeline" :style="{ height: `${height ?? 
110}px` }" />
+  <div class="alarms-timeline-wrap">
+    <div ref="container" class="alarms-timeline" :style="{ height: `${height 
?? 110}px` }" />
+    <!-- Legend — the stack is firing (red) on the bottom, recovered (green)
+         on top; without this the colored areas / pins read ambiguously. -->
+    <div class="atl-legend">
+      <span class="atl-leg"><i class="atl-swatch firing" /> firing</span>
+      <span class="atl-leg"><i class="atl-swatch recovered" /> recovered</span>
+    </div>
+  </div>
 </template>
 
 <style scoped>
-.alarms-timeline {
-  width: 100%;
+.alarms-timeline-wrap { width: 100%; }
+.alarms-timeline { width: 100%; }
+.atl-legend {
+  display: flex;
+  gap: 16px;
+  padding: 2px 4px 0;
+  font-size: 10.5px;
+  color: var(--sw-fg-3);
 }
+.atl-leg { display: inline-flex; align-items: center; gap: 5px; }
+.atl-swatch { width: 9px; height: 9px; border-radius: 2px; display: 
inline-block; }
+.atl-swatch.firing { background: #ef4444; }
+.atl-swatch.recovered { background: #22c55e; }
 </style>
diff --git a/apps/ui/src/features/alarms/AlarmDetailPanel.vue 
b/apps/ui/src/features/alarms/AlarmDetailPanel.vue
index 78d4041..73cd56d 100644
--- a/apps/ui/src/features/alarms/AlarmDetailPanel.vue
+++ b/apps/ui/src/features/alarms/AlarmDetailPanel.vue
@@ -330,7 +330,9 @@ const rulePeriod = computed<number | null>(() => 
ruleDetail.value?.period ?? nul
   color: var(--sw-fg-0);
   margin: 0;
   line-height: 1.4;
-  word-break: break-all;
+  /* `anywhere` breaks long dotted/hyphenated service names only when they
+     can't fit, instead of `break-all` chopping every line mid-character. */
+  overflow-wrap: anywhere;
 }
 /* Probe-source classifier (`agent`, `mesh-svr`, `mesh-dp`, `mesh-cp`).
  * Just operational metadata — muted, not accent. Render as a quiet
diff --git a/apps/ui/src/features/alarms/AlarmsView.vue 
b/apps/ui/src/features/alarms/AlarmsView.vue
index 72095cf..3f9c491 100644
--- a/apps/ui/src/features/alarms/AlarmsView.vue
+++ b/apps/ui/src/features/alarms/AlarmsView.vue
@@ -859,6 +859,13 @@ onMounted(() => {
   grid-template-columns: 1fr 360px;
   gap: 16px;
 }
+/* Narrow viewports: the fixed 360px detail rail + squeezed list overflow,
+   so stack the detail below the list at full width instead. */
+@media (max-width: 1080px) {
+  .ax__split {
+    grid-template-columns: 1fr;
+  }
+}
 .ax__list { display: flex; flex-direction: column; gap: 12px; }
 .ax__empty {
   padding: 24px;

Reply via email to