b4f809d8b5
- Smart auto-scroll: only follow SSE stream when user is near bottom (200px threshold), scroll once on send/switch session - Brighten dark mode text colors (primary #e0→#f0, secondary #a0→#c0, muted #66→#88) - Fix tool-call panel height to match thinking video (120px→213px) - Fix tool-call item background invisible in dark mode - Fix gateway start button using hardcoded dark color Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
136 lines
3.1 KiB
Vue
136 lines
3.1 KiB
Vue
<script setup lang="ts">
|
|
import { onMounted } from 'vue'
|
|
import { NSpin, NButton, NTag, useMessage } from 'naive-ui'
|
|
import { useI18n } from 'vue-i18n'
|
|
import { useGatewayStore } from '@/stores/hermes/gateways'
|
|
|
|
const { t } = useI18n()
|
|
const message = useMessage()
|
|
const gatewayStore = useGatewayStore()
|
|
|
|
onMounted(() => {
|
|
gatewayStore.fetchStatus()
|
|
})
|
|
|
|
async function handleToggle(name: string, running: boolean) {
|
|
try {
|
|
if (running) {
|
|
await gatewayStore.stop(name)
|
|
message.success(`${t('gateways.stopped')}: ${name}`)
|
|
} else {
|
|
await gatewayStore.start(name)
|
|
message.success(`${t('gateways.started')}: ${name}`)
|
|
}
|
|
} catch (err: any) {
|
|
message.error(err.message)
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<template>
|
|
<div class="gateways-view">
|
|
<header class="page-header">
|
|
<h2 class="header-title">{{ t('gateways.title') }}</h2>
|
|
</header>
|
|
|
|
<div class="gateways-content">
|
|
<NSpin :show="gatewayStore.loading" size="large">
|
|
<div v-if="gatewayStore.gateways.length === 0" class="empty-state">
|
|
{{ t('common.noData') }}
|
|
</div>
|
|
|
|
<div v-else class="gateway-list">
|
|
<div v-for="gw in gatewayStore.gateways" :key="gw.profile" class="gateway-card">
|
|
<div class="gateway-info">
|
|
<div class="gateway-name">{{ gw.profile }}</div>
|
|
<div class="gateway-meta">
|
|
<span class="meta-item">{{ gw.host }}:{{ gw.port }}</span>
|
|
<span v-if="gw.pid" class="meta-item">PID: {{ gw.pid }}</span>
|
|
</div>
|
|
</div>
|
|
<div class="gateway-actions">
|
|
<NTag :type="gw.running ? 'success' : 'default'" size="small" round>
|
|
{{ gw.running ? t('gateways.running') : t('gateways.stopped') }}
|
|
</NTag>
|
|
<NButton
|
|
size="small"
|
|
:type="gw.running ? 'warning' : 'primary'"
|
|
round
|
|
@click="handleToggle(gw.profile, gw.running)"
|
|
>
|
|
{{ gw.running ? t('common.stop') : t('common.start') }}
|
|
</NButton>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</NSpin>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
@use '@/styles/variables' as *;
|
|
|
|
.gateways-view {
|
|
height: calc(100 * var(--vh));
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.gateways-content {
|
|
flex: 1;
|
|
overflow-y: auto;
|
|
padding: 20px;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
color: $text-muted;
|
|
padding: 40px 0;
|
|
}
|
|
|
|
.gateway-list {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 12px;
|
|
}
|
|
|
|
.gateway-card {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: space-between;
|
|
padding: 16px 20px;
|
|
background-color: $bg-card;
|
|
border: 1px solid $border-color;
|
|
border-radius: $radius-md;
|
|
transition: border-color $transition-fast;
|
|
|
|
&:hover {
|
|
border-color: $text-muted;
|
|
}
|
|
}
|
|
|
|
.gateway-name {
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
color: $text-primary;
|
|
margin-bottom: 4px;
|
|
}
|
|
|
|
.gateway-meta {
|
|
display: flex;
|
|
gap: 12px;
|
|
}
|
|
|
|
.meta-item {
|
|
font-size: 12px;
|
|
color: $text-muted;
|
|
}
|
|
|
|
.gateway-actions {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
</style>
|