diff --git a/src/web-ui/src/shared/notification-system/components/NotificationCenter.scss b/src/web-ui/src/shared/notification-system/components/NotificationCenter.scss index 0ee671ea07..dcd93b2846 100644 --- a/src/web-ui/src/shared/notification-system/components/NotificationCenter.scss +++ b/src/web-ui/src/shared/notification-system/components/NotificationCenter.scss @@ -1,639 +1,639 @@ -@use '../../../component-library/styles/tokens' as *; + @use '../../../component-library/styles/tokens' as *; -.notification-center { - display: flex; - flex-direction: column; - height: 620px; - max-height: calc(100vh - 120px); - font-family: $font-family-sans; - overflow: hidden; + .notification-center { + display: flex; + flex-direction: column; + height: 620px; + max-height: calc(100vh - 120px); + font-family: $font-family-sans; + overflow: hidden; - &__header { - display: flex; - align-items: center; - gap: $size-gap-2; - padding: 0 $size-gap-4; - border-bottom: 1px solid var(--border-base); - background: var(--color-bg-primary); - height: 40px; - min-height: 40px; - flex-shrink: 0; - border-radius: $size-radius-base $size-radius-base 0 0; - } - - &__title { - font-size: var(--font-size-sm); - font-weight: $font-weight-semibold; - color: var(--color-text-primary); - margin: 0; - flex: 1; - } - - &__header-actions { - display: flex; - align-items: center; - gap: 2px; - flex-shrink: 0; - margin-left: auto; - } - - &__header-button { - display: flex; - align-items: center; - justify-content: center; - width: 26px; - height: 26px; - padding: 0; - background: transparent; - border: none; - border-radius: $size-radius-sm; - color: var(--color-text-secondary); - cursor: pointer; - transition: all $motion-base $easing-standard; - - &:hover { - background: var(--element-bg-base); - color: var(--color-text-primary); - } - - &:active { - transform: scale(0.95); - } - } + &__header { + display: flex; + align-items: center; + gap: $size-gap-2; + padding: 0 $size-gap-4; + border-bottom: 1px solid var(--border-base); + background: var(--color-bg-primary); + height: 40px; + min-height: 40px; + flex-shrink: 0; + border-radius: $size-radius-base $size-radius-base 0 0; + } + + &__title { + font-size: var(--font-size-sm); + font-weight: $font-weight-semibold; + color: var(--color-text-primary); + margin: 0; + flex: 1; + } + + &__header-actions { + display: flex; + align-items: center; + gap: 2px; + flex-shrink: 0; + margin-left: auto; + } + + &__header-button { + display: flex; + align-items: center; + justify-content: center; + width: 26px; + height: 26px; + padding: 0; + background: transparent; + border: none; + border-radius: $size-radius-sm; + color: var(--color-text-secondary); + cursor: pointer; + transition: all $motion-base $easing-standard; + + &:hover { + background: var(--element-bg-base); + color: var(--color-text-primary); + } + + &:active { + transform: scale(0.95); + } + } - &__search { - margin: $size-gap-3 $size-gap-4 0; - flex-shrink: 0; - } + &__search { + margin: $size-gap-3 $size-gap-4 0; + flex-shrink: 0; + } - &__filters { - display: flex; - gap: $size-gap-1; - margin: $size-gap-3 $size-gap-4 0; - padding-bottom: $size-gap-2; - border-bottom: 1px solid var(--border-base); - flex-shrink: 0; - } - - &__filter { - flex: 1; - display: flex; - align-items: center; - justify-content: center; - padding: 6px $size-gap-2; - background: transparent; - border: none; - border-bottom: 2px solid transparent; - font-size: var(--font-size-xs); - font-weight: $font-weight-medium; - color: var(--color-text-secondary); - cursor: pointer; - transition: all $motion-base $easing-standard; - white-space: nowrap; - border-radius: $size-radius-sm $size-radius-sm 0 0; - - &:hover { - color: var(--color-text-primary); - background: var(--element-bg-subtle); - } - - &.is-active { - color: var(--color-accent-500); - border-bottom-color: var(--color-accent-500); - background: transparent; - } - } + &__filters { + display: flex; + gap: $size-gap-1; + margin: $size-gap-3 $size-gap-4 0; + padding-bottom: $size-gap-2; + border-bottom: 1px solid var(--border-base); + flex-shrink: 0; + } + + &__filter { + flex: 1; + display: flex; + align-items: center; + justify-content: center; + padding: 6px $size-gap-2; + background: transparent; + border: none; + border-bottom: 2px solid transparent; + font-size: var(--font-size-xs); + font-weight: $font-weight-medium; + color: var(--color-text-secondary); + cursor: pointer; + transition: all $motion-base $easing-standard; + white-space: nowrap; + border-radius: $size-radius-sm $size-radius-sm 0 0; + + &:hover { + color: var(--color-text-primary); + background: var(--element-bg-subtle); + } + + &.is-active { + color: var(--color-accent-500); + border-bottom-color: var(--color-accent-500); + background: transparent; + } + } - &__content { - flex: 1; - overflow-y: auto; - min-height: 0; + &__content { + flex: 1; + overflow-y: auto; + min-height: 0; - } + } - &__empty { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - padding: 40px $size-gap-4; - height: 100%; - text-align: center; - color: var(--color-text-secondary); - } - - &__empty-icon { - font-size: 32px; - margin-bottom: $size-gap-3; - opacity: 0.5; - } - - &__empty-text { - font-size: var(--font-size-sm); - color: var(--color-text-muted); - } + &__empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 40px $size-gap-4; + height: 100%; + text-align: center; + color: var(--color-text-secondary); + } + + &__empty-icon { + font-size: 32px; + margin-bottom: $size-gap-3; + opacity: 0.5; + } + + &__empty-text { + font-size: var(--font-size-sm); + color: var(--color-text-muted); + } - &__group { - margin-bottom: 2px; - - &:first-child { - margin-top: $size-gap-1; - } - } - - &__group-title { - padding: 6px $size-gap-4; - font-size: 11px; - font-weight: $font-weight-semibold; - color: var(--color-text-muted); - text-transform: uppercase; - letter-spacing: 0.5px; - background: $element-bg-subtle; - user-select: none; - } + &__group { + margin-bottom: 2px; + + &:first-child { + margin-top: $size-gap-1; + } + } + + &__group-title { + padding: 6px $size-gap-4; + font-size: 11px; + font-weight: $font-weight-semibold; + color: var(--color-text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; + background: $element-bg-subtle; + user-select: none; + } - &__item { - position: relative; - display: flex; - align-items: flex-start; - gap: $size-gap-3; - padding: $size-gap-3 $size-gap-4; - cursor: pointer; - transition: all $motion-base $easing-standard; - border-bottom: 1px solid transparent; - - &:hover { - background: var(--element-bg-base); - - &:not(.is-unread) { - background: var(--element-bg-subtle); - } - } - - &.is-unread { - background: rgba(96, 165, 250, 0.03); - - &:hover { - background: rgba(96, 165, 250, 0.06); - } - } - - &:active { - transform: scale(0.995); - } + &__item { + position: relative; + display: flex; + align-items: flex-start; + gap: $size-gap-3; + padding: $size-gap-3 $size-gap-4; + cursor: pointer; + transition: all $motion-base $easing-standard; + border-bottom: 1px solid transparent; + + &:hover { + background: var(--element-bg-base); + + &:not(.is-unread) { + background: var(--element-bg-subtle); + } + } + + &.is-unread { + background: rgba(96, 165, 250, 0.03); + + &:hover { + background: rgba(96, 165, 250, 0.06); + } + } + + &:active { + transform: scale(0.995); + } - &.is-expanded { - .notification-center__item-title { - white-space: normal; - overflow: visible; - text-overflow: unset; - } - - .notification-center__item-message { - -webkit-line-clamp: unset; - line-clamp: unset; - overflow: visible; - } - } - } - - &__item-icon { - flex-shrink: 0; - width: 26px; - height: 26px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 50%; - font-size: 12px; - font-weight: $font-weight-semibold; - - &--success { - background: var(--color-success-bg); - color: var(--color-success); - } - - &--error { - background: var(--color-error-bg); - color: var(--color-error); - } - - &--warning { - background: var(--color-warning-bg); - color: var(--color-warning); - } - - &--info { - background: rgba(234, 179, 8, 0.18); - color: #ca8a04; - } + &.is-expanded { + .notification-center__item-title { + white-space: normal; + overflow: visible; + text-overflow: unset; + } + + .notification-center__item-message { + -webkit-line-clamp: unset; + line-clamp: unset; + overflow: visible; + } + } + } + + &__item-icon { + flex-shrink: 0; + width: 26px; + height: 26px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + font-size: 12px; + font-weight: $font-weight-semibold; + + &--success { + background: var(--color-success-bg); + color: var(--color-success); + } + + &--error { + background: var(--color-error-bg); + color: var(--color-error); + } + + &--warning { + background: var(--color-warning-bg); + color: var(--color-warning); + } + + &--info { + background: rgba(234, 179, 8, 0.18); + color: #ca8a04; + } - &--completed { - background: var(--color-success-bg); - color: var(--color-success); - } - - &--failed { - background: var(--color-error-bg); - color: var(--color-error); - } - - &--cancelled { - background: var(--element-bg-base); - color: var(--color-text-muted); - } - - &--active { - background: rgba(234, 179, 8, 0.18); - color: #ca8a04; - } - } - - &__item-content { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 3px; - } - - &__item-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: $size-gap-1; - } - - &__item-title { - font-size: var(--font-size-sm); - font-weight: $font-weight-medium; - color: var(--color-text-primary); - line-height: 1.4; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex: 1; - } - - &__item-percentage { - font-size: 11px; - font-weight: $font-weight-semibold; - color: #ca8a04; - font-variant-numeric: tabular-nums; - font-family: $font-family-mono; - flex-shrink: 0; - } - - &__item-message { - font-size: var(--font-size-xs); - color: var(--color-text-secondary); - line-height: 1.45; - margin-bottom: 2px; - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; - -webkit-line-clamp: 2; - line-clamp: 2; - -webkit-box-orient: vertical; - word-break: break-word; - } - - &__item-time { - font-size: 10px; - color: var(--color-text-muted); - font-family: $font-family-mono; - } - - &__item-technical-details { - margin-top: $size-gap-2; - padding: $size-gap-2; - border: 1px solid var(--border-base); - border-radius: $size-radius-sm; - background: var(--element-bg-subtle); - cursor: text; - } - - &__item-technical-title { - margin-bottom: 4px; - font-size: 10px; - font-weight: $font-weight-semibold; - color: var(--color-text-muted); - text-transform: uppercase; - letter-spacing: 0.5px; - } - - &__item-technical-body { - margin: 0; - max-height: 160px; - overflow: auto; - white-space: pre-wrap; - word-break: break-word; - font-family: $font-family-mono; - font-size: 11px; - line-height: 1.45; - color: var(--color-text-secondary); - } - - - &__item-progress-bar { - width: 100%; - height: 3px; - background: $element-bg-base; - border-radius: 1px; - overflow: hidden; - margin: 4px 0; - } - - &__item-progress-fill { - height: 100%; - background: linear-gradient(90deg, $color-accent-600, $color-purple-500); - border-radius: 2px; - transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); - - &.is-completed { - background: var(--color-success); - } - - &.is-failed { - background: var(--color-error); - } - - &.is-cancelled { - background: $color-text-muted; - } - } - - &__item-badge { - flex-shrink: 0; - width: 10px; - height: 10px; - background: var(--color-error); - border-radius: 50%; - border: none; - margin-left: auto; - margin-right: $size-gap-1; - align-self: center; - } - - &__item-actions { - flex-shrink: 0; - display: flex; - align-items: center; - gap: 4px; - margin-left: $size-gap-2; - } - - &__item-expand { - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - width: 22px; - height: 22px; - padding: 0; - background: transparent; - border: none; - border-radius: $size-radius-sm; - color: var(--color-text-muted); - cursor: pointer; - opacity: 0; - transition: all $motion-base $easing-standard; - - &:hover { - background: var(--element-bg-base); - color: var(--color-text-primary); - opacity: 1 !important; - } - - &:active { - transform: scale(0.9); - } - } - - &__item-delete { - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - width: 22px; - height: 22px; - padding: 0; - background: transparent; - border: none; - border-radius: $size-radius-sm; - color: var(--color-text-muted); - cursor: pointer; - opacity: 0; - transition: all $motion-base $easing-standard; - - &:hover { - background: var(--element-bg-base); - color: var(--color-error); - opacity: 1 !important; - } - - &:active { - transform: scale(0.9); - } - } - - &__item:hover &__item-expand, - &__item:hover &__item-delete { - opacity: 1; - } + &--completed { + background: var(--color-success-bg); + color: var(--color-success); + } + + &--failed { + background: var(--color-error-bg); + color: var(--color-error); + } + + &--cancelled { + background: var(--element-bg-base); + color: var(--color-text-muted); + } + + &--active { + background: rgba(234, 179, 8, 0.18); + color: #ca8a04; + } + } + + &__item-content { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 3px; + } + + &__item-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: $size-gap-1; + } + + &__item-title { + font-size: var(--font-size-sm); + font-weight: $font-weight-medium; + color: var(--color-text-primary); + line-height: 1.4; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; + } + + &__item-percentage { + font-size: 11px; + font-weight: $font-weight-semibold; + color: #ca8a04; + font-variant-numeric: tabular-nums; + font-family: $font-family-mono; + flex-shrink: 0; + } + + &__item-message { + font-size: var(--font-size-xs); + color: var(--color-text-secondary); + line-height: 1.45; + margin-bottom: 2px; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + line-clamp: 2; + -webkit-box-orient: vertical; + word-break: break-word; + } + + &__item-time { + font-size: 10px; + color: var(--color-text-muted); + font-family: $font-family-mono; + } + + &__item-technical-details { + margin-top: $size-gap-2; + padding: $size-gap-2; + border: 1px solid var(--border-base); + border-radius: $size-radius-sm; + background: var(--element-bg-subtle); + cursor: text; + } + + &__item-technical-title { + margin-bottom: 4px; + font-size: 10px; + font-weight: $font-weight-semibold; + color: var(--color-text-muted); + text-transform: uppercase; + letter-spacing: 0.5px; + } + + &__item-technical-body { + margin: 0; + max-height: 160px; + overflow: auto; + white-space: pre-wrap; + word-break: break-word; + font-family: $font-family-mono; + font-size: 11px; + line-height: 1.45; + color: var(--color-text-secondary); + } - &__item.is-expanded &__item-expand { - opacity: 1; - color: var(--color-text-primary); - } + &__item-progress-bar { + width: 100%; + height: 3px; + background: $element-bg-base; + border-radius: 1px; + overflow: hidden; + margin: 4px 0; + } + + &__item-progress-fill { + height: 100%; + background: linear-gradient(90deg, $color-accent-600, $color-purple-500); + border-radius: 2px; + transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); + + &.is-completed { + background: var(--color-success); + } + + &.is-failed { + background: var(--color-error); + } + + &.is-cancelled { + background: $color-text-muted; + } + } + + &__item-badge { + flex-shrink: 0; + width: 10px; + height: 10px; + background: var(--color-error); + border-radius: 50%; + border: none; + position: absolute; + right: 80px; + align-self: center; + } + + &__item-actions { + flex-shrink: 0; + display: flex; + align-items: center; + gap: 4px; + margin-left: auto; + } + + &__item-expand { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + padding: 0; + background: transparent; + border: none; + border-radius: $size-radius-sm; + color: var(--color-text-muted); + cursor: pointer; + opacity: 0; + transition: all $motion-base $easing-standard; + + &:hover { + background: var(--element-bg-base); + color: var(--color-text-primary); + opacity: 1 !important; + } + + &:active { + transform: scale(0.9); + } + } + + &__item-delete { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + width: 22px; + height: 22px; + padding: 0; + background: transparent; + border: none; + border-radius: $size-radius-sm; + color: var(--color-text-muted); + cursor: pointer; + opacity: 0; + transition: all $motion-base $easing-standard; + + &:hover { + background: var(--element-bg-base); + color: var(--color-error); + opacity: 1 !important; + } + + &:active { + transform: scale(0.9); + } + } + + &__item:hover &__item-expand, + &__item:hover &__item-delete { + opacity: 1; + } + + + &__item.is-expanded &__item-expand { + opacity: 1; + color: var(--color-text-primary); + } - &__active-section { - padding: $size-gap-3 0; - border-bottom: 1px solid var(--border-base); - background: rgba(59, 130, 246, 0.02); - } - - &__active-section-title { - padding: 6px $size-gap-4; - font-size: 11px; - font-weight: $font-weight-semibold; - color: var(--color-accent-500); - text-transform: uppercase; - letter-spacing: 0.5px; - user-select: none; - } - - &__active-section-list { - display: flex; - flex-direction: column; - gap: 2px; - } - - - &__active-task-item { - display: flex; - align-items: flex-start; - gap: $size-gap-3; - padding: $size-gap-3 $size-gap-4; - transition: background $motion-base $easing-standard; - - &:hover { - background: rgba(59, 130, 246, 0.05); - } - } - - &__active-task-icon { - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - color: var(--color-accent-500); - margin-top: 1px; - } - - &__spinner { - animation: bitfun-notification-center-spin 1s linear infinite; - } - - &__active-task-content { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 3px; - } - - &__active-task-header { - display: flex; - align-items: center; - justify-content: space-between; - gap: $size-gap-1; - } - - &__active-task-title { - font-size: var(--font-size-sm); - font-weight: $font-weight-medium; - color: var(--color-text-primary); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - flex: 1; - } - - &__active-task-progress-text { - font-size: 11px; - font-weight: $font-weight-semibold; - color: var(--color-accent-500); - font-variant-numeric: tabular-nums; - font-family: $font-family-mono; - flex-shrink: 0; - } - - &__active-task-message { - font-size: var(--font-size-xs); - color: var(--color-text-secondary); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - &__active-task-progress-bar { - width: 100%; - height: 3px; - background: $element-bg-base; - border-radius: 1px; - overflow: hidden; - margin-top: 4px; - } - - &__active-task-progress-fill { - height: 100%; - background: linear-gradient(90deg, $color-accent-600, $color-purple-500); - border-radius: 1px; - transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); - } - - - &__active-loading-item { - display: flex; - align-items: center; - gap: $size-gap-3; - padding: $size-gap-3 $size-gap-4; - transition: background $motion-base $easing-standard; - - &:hover { - background: rgba(59, 130, 246, 0.05); - } - } - - &__active-loading-icon { - flex-shrink: 0; - display: flex; - align-items: center; - justify-content: center; - width: 20px; - height: 20px; - color: var(--color-accent-500); - } - - &__active-loading-content { - flex: 1; - min-width: 0; - display: flex; - flex-direction: column; - gap: 3px; - } - - &__active-loading-title { - font-size: var(--font-size-sm); - font-weight: $font-weight-medium; - color: var(--color-text-primary); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } - - &__active-loading-message { - font-size: var(--font-size-xs); - color: var(--color-text-secondary); - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; - } -} - - -@keyframes bitfun-notification-center-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - - -@media (max-width: 768px) { - .notification-center { - height: 78vh; - max-height: calc(100vh - 80px); - } -} + &__active-section { + padding: $size-gap-3 0; + border-bottom: 1px solid var(--border-base); + background: rgba(59, 130, 246, 0.02); + } + + &__active-section-title { + padding: 6px $size-gap-4; + font-size: 11px; + font-weight: $font-weight-semibold; + color: var(--color-accent-500); + text-transform: uppercase; + letter-spacing: 0.5px; + user-select: none; + } + + &__active-section-list { + display: flex; + flex-direction: column; + gap: 2px; + } + + + &__active-task-item { + display: flex; + align-items: flex-start; + gap: $size-gap-3; + padding: $size-gap-3 $size-gap-4; + transition: background $motion-base $easing-standard; + + &:hover { + background: rgba(59, 130, 246, 0.05); + } + } + + &__active-task-icon { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + color: var(--color-accent-500); + margin-top: 1px; + } + + &__spinner { + animation: bitfun-notification-center-spin 1s linear infinite; + } + + &__active-task-content { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 3px; + } + + &__active-task-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: $size-gap-1; + } + + &__active-task-title { + font-size: var(--font-size-sm); + font-weight: $font-weight-medium; + color: var(--color-text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + flex: 1; + } + + &__active-task-progress-text { + font-size: 11px; + font-weight: $font-weight-semibold; + color: var(--color-accent-500); + font-variant-numeric: tabular-nums; + font-family: $font-family-mono; + flex-shrink: 0; + } + + &__active-task-message { + font-size: var(--font-size-xs); + color: var(--color-text-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &__active-task-progress-bar { + width: 100%; + height: 3px; + background: $element-bg-base; + border-radius: 1px; + overflow: hidden; + margin-top: 4px; + } + + &__active-task-progress-fill { + height: 100%; + background: linear-gradient(90deg, $color-accent-600, $color-purple-500); + border-radius: 1px; + transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1); + } + + + &__active-loading-item { + display: flex; + align-items: center; + gap: $size-gap-3; + padding: $size-gap-3 $size-gap-4; + transition: background $motion-base $easing-standard; + + &:hover { + background: rgba(59, 130, 246, 0.05); + } + } + + &__active-loading-icon { + flex-shrink: 0; + display: flex; + align-items: center; + justify-content: center; + width: 20px; + height: 20px; + color: var(--color-accent-500); + } + + &__active-loading-content { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; + gap: 3px; + } + + &__active-loading-title { + font-size: var(--font-size-sm); + font-weight: $font-weight-medium; + color: var(--color-text-primary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &__active-loading-message { + font-size: var(--font-size-xs); + color: var(--color-text-secondary); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + } + + + @keyframes bitfun-notification-center-spin { + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } + } + + + @media (max-width: 768px) { + .notification-center { + height: 78vh; + max-height: calc(100vh - 80px); + } + } diff --git a/src/web-ui/src/shared/notification-system/components/NotificationCenter.tsx b/src/web-ui/src/shared/notification-system/components/NotificationCenter.tsx index 147614d0f7..61f12c77fd 100644 --- a/src/web-ui/src/shared/notification-system/components/NotificationCenter.tsx +++ b/src/web-ui/src/shared/notification-system/components/NotificationCenter.tsx @@ -1,4 +1,4 @@ - + import React, { useState, useMemo } from 'react'; import { X, CheckCheck, Trash2, XCircle, ChevronDown, ChevronUp, Loader2 } from 'lucide-react'; @@ -21,30 +21,29 @@ export const NotificationCenter: React.FC = () => { return [...allProgressNotifications, ...allLoadingNotifications]; }, [allProgressNotifications, allLoadingNotifications]); - const handleClose = React.useCallback(() => { notificationService.toggleCenter(false); }, []); - + const handleMarkAllRead = () => { notificationService.markAllAsRead(); }; - + const handleClearAll = () => { notificationService.clearHistory(); }; - + const handleDeleteNotification = (e: React.MouseEvent, notificationId: string) => { - e.stopPropagation(); + e.stopPropagation(); notificationService.deleteFromHistory(notificationId); }; - + const handleNotificationClick = (notification: NotificationRecord) => { - + setExpandedIds(prev => { const newSet = new Set(prev); if (newSet.has(notification.id)) { @@ -55,37 +54,37 @@ export const NotificationCenter: React.FC = () => { return newSet; }); - + if (!notification.read) { notificationService.markAsRead(notification.id); } - - + + if (notification.metadata?.onClick) { notification.metadata.onClick(); } }; - + const filteredHistory = useMemo(() => { let filtered = history; - - + + filtered = filtered.filter(n => { if (n.variant === 'progress' || n.variant === 'loading') { - + return n.status === 'completed' || n.status === 'failed' || n.status === 'cancelled'; } - return true; + return true; }); - + if (filter !== 'all') { filtered = filtered.filter(n => n.type === filter); } - + if (searchQuery.trim()) { const query = searchQuery.toLowerCase(); filtered = filtered.filter(n => @@ -97,7 +96,7 @@ export const NotificationCenter: React.FC = () => { return filtered; }, [history, filter, searchQuery]); - + const groupedHistory = useMemo(() => { const now = Date.now(); const today = new Date(now).setHours(0, 0, 0, 0); @@ -111,7 +110,7 @@ export const NotificationCenter: React.FC = () => { filteredHistory.forEach(notification => { const notificationDate = new Date(notification.timestamp).setHours(0, 0, 0, 0); - + if (notificationDate === today) { groups.today.push(notification); } else if (notificationDate === yesterday) { @@ -124,14 +123,14 @@ export const NotificationCenter: React.FC = () => { return groups; }, [filteredHistory]); - + const formatTime = (timestamp: number) => { return formatDate(timestamp, { hour: '2-digit', minute: '2-digit' }); }; - + const getIcon = (type: string, status?: string) => { - + if (status === 'completed') { return '✓'; } @@ -141,8 +140,8 @@ export const NotificationCenter: React.FC = () => { if (status === 'cancelled') { return '⊘'; } - - + + switch (type) { case 'success': return '✓'; @@ -169,35 +168,35 @@ export const NotificationCenter: React.FC = () => { return diagnostics || (rawError ? `raw_error=${rawError}` : null); }; - + const renderActiveTaskItem = (notification: Notification) => { const isProgress = notification.variant === 'progress'; const isLoading = notification.variant === 'loading'; - - + + const getProgressInfo = () => { if (isLoading) { - return null; + return null; } - + if (isProgress) { const mode = notification.progressMode || (notification.textOnly ? 'text-only' : 'percentage'); if (mode === 'text-only') return null; - + if (mode === 'fraction' && notification.current !== undefined && notification.total !== undefined) { return `${notification.current}/${notification.total}`; } - + if (mode === 'percentage' && notification.progress !== undefined) { return `${Math.round(notification.progress)}%`; } } - + return null; }; - + const progressInfo = getProgressInfo(); - + return (
{
{isProgress && notification.progressText ? notification.progressText : (notification.messageNode ?? notification.message)}
- + {isProgress && (() => { const mode = notification.progressMode || (notification.textOnly ? 'text-only' : 'percentage'); if (mode === 'text-only') return null; - + return (
{ ); }; - + const renderNotificationItem = (notification: NotificationRecord) => { const isProgress = notification.variant === 'progress'; const isLoading = notification.variant === 'loading'; - const iconClass = (isProgress || isLoading) && notification.status - ? `notification-center__item-icon--${notification.status}` + const iconClass = (isProgress || isLoading) && notification.status + ? `notification-center__item-icon--${notification.status}` : `notification-center__item-icon--${notification.type}`; - + const now = Date.now(); const today = new Date(now).setHours(0, 0, 0, 0); const yesterday = today - 86400000; const notificationDate = new Date(notification.timestamp).setHours(0, 0, 0, 0); - + const timeDisplay = notificationDate < yesterday ? formatDate(notification.timestamp, { - month: '2-digit', - day: '2-digit', - hour: '2-digit', - minute: '2-digit' - }) + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit' + }) : formatTime(notification.timestamp); const isExpanded = expandedIds.has(notification.id); @@ -278,30 +277,30 @@ export const NotificationCenter: React.FC = () => {
{notification.title}
- + {isProgress && (() => { const mode = notification.progressMode || (notification.textOnly ? 'text-only' : 'percentage'); if (mode === 'text-only') return null; - + if (mode === 'fraction' && notification.current !== undefined && notification.total !== undefined) { return
{notification.current}/{notification.total}
; } - + if (mode === 'percentage' && notification.progress !== undefined) { return
{Math.round(notification.progress)}%
; } - + return null; })()}
{(isProgress && notification.progressText) ? notification.progressText : (notification.messageNode ?? notification.message)}
- + {isProgress && (() => { const mode = notification.progressMode || (notification.textOnly ? 'text-only' : 'percentage'); if (mode === 'text-only') return null; - + return (
{
{!notification.read &&
}
- + {technicalDetails && ( + + )}
- +
- + {activeTaskNotifications.length > 0 && (
@@ -448,7 +458,7 @@ export const NotificationCenter: React.FC = () => {
) : ( <> - + {groupedHistory.today.length > 0 && (
{t('common:time.today')}
@@ -456,7 +466,7 @@ export const NotificationCenter: React.FC = () => {
)} - + {groupedHistory.yesterday.length > 0 && (
{t('common:time.yesterday')}
@@ -464,7 +474,7 @@ export const NotificationCenter: React.FC = () => {
)} - + {groupedHistory.earlier.length > 0 && (
{t('components:notificationCenter.groups.earlier')}
diff --git a/src/web-ui/src/tools/editor/components/CodeEditor.tsx b/src/web-ui/src/tools/editor/components/CodeEditor.tsx index aa1972989b..e6cccdd0d5 100644 --- a/src/web-ui/src/tools/editor/components/CodeEditor.tsx +++ b/src/web-ui/src/tools/editor/components/CodeEditor.tsx @@ -46,7 +46,6 @@ import { EditorStatusBar } from './EditorStatusBar'; const log = createLogger('CodeEditor'); import { GoToLinePopover, - IndentPopover, EncodingPopover, LanguagePopover, } from './StatusBarPopovers'; @@ -1258,23 +1257,6 @@ const CodeEditor: React.FC = ({ if (editor && model) performJump(editor, model, line, column); }, [performJump]); - const handleIndentConfirm = useCallback((tabSize: number, insertSpaces: boolean) => { - const merged = { tab_size: tabSize, insert_spaces: insertSpaces }; - userIndentRef.current = merged; - setEditorConfig((prev) => ({ ...prev, ...merged })); - const editor = editorRef.current; - if (editor) { - editor.updateOptions({ tabSize, insertSpaces }); - } - // Async persistence, don't block UI update, don't trigger applyConfig override - configManager.getConfig('editor').then((config) => { - const fullMerged = { ...(config || {}), ...merged }; - return configManager.setConfig('editor', fullMerged); - }).catch((err) => { - log.warn('Failed to persist indent config', err); - }); - }, []); - const fetchFileMetadata = useCallback(async () => { const { workspaceAPI } = await import('@/infrastructure/api'); return workspaceAPI.getFileMetadata(filePath); @@ -2154,8 +2136,6 @@ const CodeEditor: React.FC = ({ selectedLines={selection.lines} language={detectedLanguage} encoding={encoding} - tabSize={editorConfig.tab_size || 2} - insertSpaces={editorConfig.insert_spaces !== false} isReadOnly={readOnly} lspStatus={ enableLsp && lspExtensionRegistry.isFileSupported(filePath) @@ -2163,7 +2143,6 @@ const CodeEditor: React.FC = ({ : undefined } onPositionClick={(e) => openStatusBarPopover('position', e)} - onIndentClick={(e) => openStatusBarPopover('indent', e)} onEncodingClick={(e) => openStatusBarPopover('encoding', e)} onLanguageClick={(e) => openStatusBarPopover('language', e)} /> @@ -2177,15 +2156,6 @@ const CodeEditor: React.FC = ({ onClose={closeStatusBarPopover} /> )} - {statusBarPopover === 'indent' && statusBarAnchorRect && ( - - )} {statusBarPopover === 'encoding' && statusBarAnchorRect && ( void; /** Encoding click callback */ onEncodingClick?: (e: React.MouseEvent) => void; - /** Indent click callback */ - onIndentClick?: (e: React.MouseEvent) => void; /** Position click callback */ onPositionClick?: (e: React.MouseEvent) => void; } @@ -96,21 +90,21 @@ const getLspStatusInfo = ( ) => { switch (status) { case 'connected': - return { - icon: , + return { + icon: , className: 'editor-status-bar__lsp--connected', title: t('editor.statusBar.lspConnected') }; case 'connecting': - return { - icon: , + return { + icon: , className: 'editor-status-bar__lsp--connecting', title: t('editor.statusBar.lspConnecting') }; case 'disconnected': default: - return { - icon: , + return { + icon: , className: 'editor-status-bar__lsp--disconnected', title: t('editor.statusBar.lspDisconnected') }; @@ -124,13 +118,10 @@ export const EditorStatusBar: React.FC = ({ selectedLines = 0, language, encoding = 'UTF-8', - tabSize = 2, - insertSpaces = true, isReadOnly = false, lspStatus, onLanguageClick, onEncodingClick, - onIndentClick, onPositionClick, }) => { const { t } = useI18n('tools'); @@ -159,7 +150,7 @@ export const EditorStatusBar: React.FC = ({
-
@@ -170,21 +161,8 @@ export const EditorStatusBar: React.FC = ({
-
- - -
- {insertSpaces ? t('editor.statusBar.indentSpaces', { n: tabSize }) : t('editor.statusBar.indentTab', { n: tabSize })} -
-
- -
- -
@@ -195,7 +173,7 @@ export const EditorStatusBar: React.FC = ({
-
@@ -206,7 +184,7 @@ export const EditorStatusBar: React.FC = ({ {lspStatus && ( <>
-