Polish topology tree UI and harden LLDP peer matching.

Make left-tree selection, hex browser, and sidebar chrome clearer while matching peers by hostname/ports instead of mgmt IP and reducing fabric-node insert deadlocks.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-08-02 15:42:15 +08:00
parent 3908b78dbb
commit cb8e0d23c5
6 changed files with 1520 additions and 364 deletions

View file

@ -3514,23 +3514,155 @@ pre {
flex: 1;
}
.topo-sidebar__head {
display: flex;
.app-main .topo-sidebar__icon-btn,
.topo-sidebar__icon-btn {
display: inline-flex;
align-items: center;
justify-content: space-between;
gap: 8px;
justify-content: center;
width: 24px;
height: 24px;
min-width: 24px;
padding: 0 !important;
margin: 0;
border: 0 !important;
border-radius: 4px !important;
background: transparent !important;
box-shadow: none !important;
color: #64748b;
cursor: pointer;
flex: 0 0 auto;
}
.topo-sidebar__head > strong {
font-size: 13px;
font-weight: 650;
color: var(--nm-brand);
.app-main .topo-sidebar__icon-btn:hover:not(:disabled),
.topo-sidebar__icon-btn:hover:not(:disabled) {
color: var(--nm-brand, #0f2744);
background: rgba(15, 39, 68, 0.06) !important;
}
.topo-sidebar__head-actions {
.app-main .topo-sidebar__icon-btn:disabled,
.topo-sidebar__icon-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.topo-tree-search {
position: relative;
margin: 0 0 4px;
}
.topo-tree-search__bar {
display: flex;
align-items: center;
gap: 2px;
min-width: 0;
}
.topo-tree-search__bar > .input {
flex: 1 1 auto;
min-width: 0;
}
.topo-tree-search__panel {
position: absolute;
z-index: 40;
top: calc(100% + 4px);
left: 0;
right: 0;
max-height: min(360px, 50vh);
overflow: auto;
padding: 4px;
border: 1px solid #b8c4d2;
border-radius: 8px;
background: #fff;
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
}
.topo-tree-search__item {
display: flex;
flex-direction: column;
gap: 2px;
padding: 2px;
border-radius: 6px;
}
.topo-tree-search__item + .topo-tree-search__item {
margin-top: 2px;
border-top: 1px solid #eef2f6;
padding-top: 4px;
}
.topo-tree-search__ne {
display: grid;
grid-template-columns: 1fr auto;
grid-template-rows: auto auto;
gap: 0 8px;
width: 100%;
padding: 6px 8px;
border: 0;
border-radius: 6px;
background: transparent;
text-align: left;
cursor: pointer;
color: inherit;
}
.topo-tree-search__ne:hover {
background: #f1f5f9;
}
.topo-tree-search__name {
grid-column: 1;
font-size: 12px;
font-weight: 600;
color: var(--nm-ink, #0f172a);
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.topo-tree-search__ip {
grid-column: 1;
font-size: 11px;
}
.topo-tree-search__meta {
grid-column: 2;
grid-row: 1 / span 2;
align-self: center;
font-size: 11px;
white-space: nowrap;
}
.topo-tree-search__views {
display: flex;
flex-wrap: wrap;
gap: 4px;
padding: 0 6px 4px;
}
.topo-tree-search__view {
max-width: 100%;
padding: 2px 8px;
border: 1px solid #d0dae6;
border-radius: 999px;
background: #f8fafc;
font-size: 11px;
color: var(--nm-brand, #1d4ed8);
cursor: pointer;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.topo-tree-search__view:hover {
background: #e8eef8;
border-color: #9eb6d8;
}
.topo-tree-search__hint {
margin: 0;
padding: 8px;
font-size: 12px;
}
.topo-palette-source {
@ -3859,9 +3991,156 @@ pre {
gap: 14px;
align-content: start;
padding: 16px 18px 20px;
background-image:
radial-gradient(circle at 1px 1px, rgba(148, 163, 184, 0.22) 1px, transparent 0);
background-size: 18px 18px;
background: #eef2f7;
}
.topo-browser__grid--regions {
grid-template-columns: repeat(auto-fill, minmax(148px, 160px));
gap: 20px 22px;
padding: 24px 22px 32px;
background: #e8eef6;
justify-content: start;
}
/* Beat .app-main button { background:#fff; color:#1f2937; border } */
.app-main .topo-region-hex,
.topo-region-hex {
width: 140px;
height: 156px;
box-sizing: border-box;
border: 0 !important;
border-radius: 0;
box-shadow: none;
padding: 34px 18px 30px;
margin: 0;
clip-path: polygon(50% 0%, 100% 25%, 100% 75%, 50% 100%, 0% 75%, 0% 25%);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 4px;
text-align: center;
cursor: pointer;
color: #fff !important;
transition: transform 0.15s ease, filter 0.15s ease;
}
.app-main .topo-region-hex:hover,
.topo-region-hex:hover,
.app-main .topo-region-hex.is-hot,
.topo-region-hex.is-hot {
transform: translateY(-2px) scale(1.03);
filter: brightness(1.06);
}
.app-main .topo-region-hex.is-selected,
.topo-region-hex.is-selected,
.app-main .topo-region-hex.is-hot,
.topo-region-hex.is-hot {
box-shadow:
0 0 0 3px rgba(255, 255, 255, 0.92),
0 0 0 6px rgba(26, 86, 168, 0.55);
z-index: 1;
}
.app-main .topo-region-hex.is-selected,
.topo-region-hex.is-selected {
filter: brightness(1.08);
transform: translateY(-2px) scale(1.04);
}
.app-main .topo-region-hex--tone-0,
.topo-region-hex--tone-0 {
background: linear-gradient(160deg, #5b9cf0 0%, #1a56a8 52%, #0f766e 100%) !important;
}
.app-main .topo-region-hex--tone-1,
.topo-region-hex--tone-1 {
background: linear-gradient(160deg, #67e8f9 0%, #0284c7 48%, #1d4ed8 100%) !important;
}
.app-main .topo-region-hex--tone-2,
.topo-region-hex--tone-2 {
background: linear-gradient(160deg, #5eead4 0%, #0d9488 50%, #115e59 100%) !important;
}
.app-main .topo-region-hex--tone-3,
.topo-region-hex--tone-3 {
background: linear-gradient(160deg, #fcd34d 0%, #f59e0b 46%, #b45309 100%) !important;
}
.app-main .topo-region-hex--tone-4,
.topo-region-hex--tone-4 {
background: linear-gradient(160deg, #cbd5e1 0%, #64748b 48%, #1e3a5f 100%) !important;
}
.app-main .topo-region-hex--tone-physical,
.topo-region-hex--tone-physical {
background: linear-gradient(160deg, #fcd34d 0%, #f59e0b 42%, #b45309 100%) !important;
}
.app-main .topo-region-hex--add,
.topo-region-hex--add {
background: linear-gradient(160deg, #f8fafc 0%, #dbe7f5 100%) !important;
color: #1a56a8 !important;
}
.app-main .topo-region-hex--add:hover,
.topo-region-hex--add:hover {
background: linear-gradient(160deg, #ffffff 0%, #cfe0f4 100%) !important;
color: #0f2744 !important;
}
.app-main .topo-region-hex--add:disabled,
.topo-region-hex--add:disabled {
opacity: 0.55;
cursor: not-allowed;
}
.topo-region-hex__icon {
display: grid;
place-items: center;
width: 32px;
height: 32px;
border-radius: 8px;
background: rgba(255, 255, 255, 0.22);
color: #fff;
flex: 0 0 auto;
}
.topo-region-hex--add .topo-region-hex__icon {
background: rgba(26, 86, 168, 0.12);
color: #1a56a8;
}
.topo-region-hex__name {
max-width: 100%;
font-size: 14px;
font-weight: 700;
line-height: 1.25;
color: inherit;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.topo-region-hex__title {
display: flex;
align-items: baseline;
justify-content: center;
flex-wrap: wrap;
gap: 4px 6px;
max-width: 100%;
min-width: 0;
}
.topo-region-hex__title .topo-region-hex__name {
max-width: 70%;
}
.topo-region-hex__meta {
display: inline;
flex: 0 0 auto;
font-size: 11px;
font-weight: 600;
line-height: 1.25;
color: rgba(255, 255, 255, 0.95);
white-space: nowrap;
}
.topo-region-hex--add .topo-region-hex__meta {
color: #3d6f9c;
}
.topo-region-hex__plus {
display: grid;
place-items: center;
width: 32px;
height: 32px;
border-radius: 50%;
font-size: 22px;
line-height: 1;
background: rgba(26, 86, 168, 0.12);
color: #1a56a8;
flex: 0 0 auto;
}
.topo-map-card {
display: flex;
@ -3988,22 +4267,51 @@ pre {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: 4px;
gap: 2px 6px;
min-width: 0;
font-size: 13px;
font-weight: 550;
letter-spacing: -0.01em;
line-height: 1.3;
color: var(--nm-brand, #0f2744);
}
.app-main .topo-breadcrumb__link,
.topo-breadcrumb__link {
border: 0;
background: transparent;
color: var(--nm-accent, #2563eb);
border: 0 !important;
background: transparent !important;
box-shadow: none !important;
color: #5b6b7c !important;
cursor: pointer;
padding: 0;
padding: 0 !important;
margin: 0;
border-radius: 0 !important;
font: inherit;
font-weight: 550;
line-height: inherit;
width: auto;
}
.app-main .topo-breadcrumb__link:hover,
.topo-breadcrumb__link:hover {
color: var(--nm-accent, #1a56a8) !important;
background: transparent !important;
text-decoration: underline;
text-underline-offset: 2px;
}
.topo-breadcrumb__sep {
opacity: 0.45;
color: #94a3b8;
opacity: 1;
font-weight: 400;
user-select: none;
}
.topo-breadcrumb__current {
font-weight: 600;
display: inline;
padding: 0;
border-radius: 0;
font: inherit;
font-weight: 700;
color: var(--nm-accent, #1a56a8);
background: transparent;
box-shadow: none;
}
.topo-toolbar__level {
margin-left: 8px;
@ -4277,7 +4585,7 @@ pre {
.topo-region-list {
display: flex;
flex-direction: column;
gap: 6px;
gap: 2px;
padding: 2px 0;
}
@ -4285,49 +4593,216 @@ pre {
margin: 0;
width: 100%;
min-width: 0;
padding: 2px 0 4px;
}
.topo-region-list__row {
align-items: stretch;
align-items: center;
}
.topo-region-list__block.is-active > .topo-region-list__row {
border-color: var(--nm-accent);
background: var(--nm-nav-active);
box-shadow: inset 2px 0 0 var(--nm-accent);
/* Flat list rows — no boxed cards */
.topo-region-list .topo-map-list__row {
border: 0;
border-radius: 8px;
background: transparent;
box-shadow: none;
overflow: visible;
gap: 2px;
padding-right: 2px;
transition: background 0.12s ease;
}
.topo-region-list__block.is-active > .topo-region-list__row .topo-map-list__actions {
border-left-color: #9bb6d9;
.topo-region-list .topo-map-list__row:hover,
.topo-region-list__block.is-hot > .topo-region-list__row,
.topo-region-list__maps > li.is-hot > .topo-map-list__row {
background: rgba(255, 255, 255, 0.78);
}
/* Selected = deep wash + left accent */
.topo-region-list__block.is-active > .topo-region-list__row,
.topo-region-list__maps > li.is-active > .topo-map-list__row {
border: 0;
background: #8eb6e3;
box-shadow: inset 3px 0 0 #0f3f7a;
}
.topo-region-list__block.is-branch-active > .topo-region-list__row {
border: 0;
background: rgba(142, 182, 227, 0.35);
box-shadow: none;
}
.topo-region-list__block.is-active > .topo-region-list__row .topo-map-list__actions,
.topo-region-list__maps > li.is-active > .topo-map-list__row .topo-map-list__actions {
border-left: 0;
}
.topo-region-list__glyph {
display: inline-flex;
align-items: center;
margin-right: 5px;
vertical-align: -2px;
justify-content: center;
width: 22px;
height: 22px;
margin-right: 2px;
border-radius: 6px;
background: rgba(15, 118, 110, 0.1);
color: #0f766e;
flex: 0 0 auto;
}
.topo-region-list__maps .topo-region-list__glyph {
background: rgba(26, 86, 168, 0.1);
color: #1a56a8;
}
.topo-region-list__maps {
margin: 4px 0 0 18px;
padding: 0;
position: relative;
margin: 2px 0 0;
padding: 2px 0 2px 0;
overflow: visible;
flex: none;
}
.topo-region-list__maps::before {
content: "";
position: absolute;
left: 18px;
top: 0;
bottom: 6px;
width: 1px;
background: rgba(148, 163, 184, 0.55);
}
.topo-region-list__maps > li {
margin: 0 0 4px;
margin: 0;
width: 100%;
position: relative;
}
.topo-region-list__maps > li + li {
margin-top: 1px;
}
.topo-region-list__maps .topo-map-list__row {
width: 100%;
}
.app-main .topo-region-list .topo-map-list__item,
.topo-region-list .topo-map-list__item {
padding: 8px 6px 8px 8px;
border-radius: 8px;
}
.app-main .topo-region-list__maps .topo-map-list__item,
.topo-region-list__maps .topo-map-list__item {
padding: 7px 6px 7px 36px;
}
.topo-region-list .topo-map-list__item:hover,
.topo-region-list__block.is-active > .topo-region-list__row .topo-map-list__item,
.topo-region-list__block.is-branch-active > .topo-region-list__row .topo-map-list__item,
.topo-region-list__maps > li.is-active .topo-map-list__item,
.topo-region-list__maps > li.is-active .topo-map-list__item:hover {
background: transparent !important;
}
.topo-region-list .topo-map-list__actions {
opacity: 0;
pointer-events: none;
border-left: 0;
gap: 4px;
padding-right: 4px;
align-items: center;
transition: opacity 0.12s ease;
}
/* Edit/delete only while the row itself is hovered */
.topo-region-list .topo-map-list__row:hover .topo-map-list__actions {
opacity: 1;
pointer-events: auto;
}
.app-main .topo-region-list .topo-map-list__icon,
.topo-region-list .topo-map-list__icon {
width: 24px;
height: 24px;
min-height: 24px;
border-radius: 5px;
color: #475569;
background: transparent !important;
border: 0 !important;
}
.app-main .topo-region-list .topo-map-list__icon:hover:not(:disabled),
.topo-region-list .topo-map-list__icon:hover:not(:disabled) {
background: rgba(15, 39, 68, 0.12) !important;
color: var(--nm-brand);
}
.topo-region-list .topo-map-list__name {
display: flex;
align-items: center;
gap: 8px;
min-width: 0;
width: 100%;
}
.topo-region-list .topo-map-list__title {
min-width: 0;
flex: 1 1 auto;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
font-size: 13px;
font-weight: 650;
color: var(--nm-brand, #0f2744);
}
.topo-region-list__maps .topo-map-list__title {
font-size: 12px;
font-weight: 600;
}
.topo-region-list .topo-map-list__count {
flex: 0 0 auto;
margin-left: 4px;
padding: 0;
border-radius: 0;
font-size: 11px;
font-weight: 600;
letter-spacing: 0.02em;
color: #64748b;
background: transparent;
box-shadow: none;
font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace;
}
.topo-region-list__maps .topo-map-list__count {
color: #64748b;
background: transparent;
}
.topo-region-list__block.is-active .topo-map-list__count,
.topo-region-list__maps > li.is-active .topo-map-list__count {
background: transparent;
color: #0a274f;
}
.topo-region-list__block.is-hot:not(.is-active) .topo-map-list__count,
.topo-region-list__maps > li.is-hot:not(.is-active) .topo-map-list__count {
background: transparent;
color: #1a56a8;
}
.topo-region-list__block.is-active .topo-map-list__title,
.topo-region-list__maps > li.is-active .topo-map-list__title {
color: #0a274f;
font-weight: 700;
}
.topo-region-list__maps > li.is-active .topo-region-list__glyph {
background: #0f3f7a;
color: #fff;
}
.topo-map-list li,
@ -4361,6 +4836,29 @@ pre {
box-shadow: inset 2px 0 0 var(--nm-accent);
}
.topo-region-list__block.is-active > .topo-map-list__row,
.topo-region-list__maps > li.is-active > .topo-map-list__row {
border: 0 !important;
background: #8eb6e3 !important;
box-shadow: inset 3px 0 0 #0f3f7a !important;
}
.topo-region-list__block.is-active > .topo-map-list__row:hover,
.topo-region-list__maps > li.is-active > .topo-map-list__row:hover {
background: #7aa8db !important;
}
.topo-region-list__block.is-branch-active > .topo-map-list__row {
border: 0 !important;
background: rgba(142, 182, 227, 0.35) !important;
box-shadow: none !important;
}
.topo-region-list__block.is-hot:not(.is-active):not(.is-branch-active) > .topo-map-list__row,
.topo-region-list__maps > li.is-hot:not(.is-active) > .topo-map-list__row {
background: #c5daf3 !important;
}
.topo-palette > li.is-on-canvas .topo-palette__row {
background: #eef2f7;
border-color: #d7dee8;