Newer
Older
navi-1 / webclient / src / components / settings / SettingsView.vue
<template>
  <div class="settings-view">
    <!-- Flat header bar — same frame as the chat header (no floating card) -->
    <div class="settings-header">
      <GnIconButton
        icon="ph ph-list"
        label="Toggle sidebar"
        class="settings-sidebar-toggle"
        @click="emit('toggle-sidebar')"
      />
      <h1 class="settings-title">Settings</h1>
    </div>

    <GnTabs v-model="activeTab" :items="tabs" class="settings-tabs" ariaLabel="Settings sections">
      <template #account>
        <ApiKeysPanel v-if="activeTab === 'account'" />
      </template>
      <template #notifications>
        <NotificationSettingsPanel v-if="activeTab === 'notifications'" />
      </template>
      <template #synapse>
        <SynapseReactionsPanel v-if="activeTab === 'synapse'" />
        <SynapseTargetsPanel v-if="activeTab === 'synapse'" />
      </template>
      <template #mcp>
        <McpKeysPanel v-if="activeTab === 'mcp'" />
      </template>
    </GnTabs>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import { GnIconButton, GnTabs } from 'gnexus-ui-kit/vue'
import ApiKeysPanel from './ApiKeysPanel.vue'
import NotificationSettingsPanel from './NotificationSettingsPanel.vue'
import SynapseReactionsPanel from './SynapseReactionsPanel.vue'
import SynapseTargetsPanel from './SynapseTargetsPanel.vue'
import McpKeysPanel from './McpKeysPanel.vue'

const emit = defineEmits(['toggle-sidebar'])

const tabs = [
  { id: 'account', label: 'Account', icon: 'ph ph-user-circle' },
  { id: 'notifications', label: 'Notifications', icon: 'ph ph-bell' },
  { id: 'synapse', label: 'Synapse', icon: 'ph ph-diagram-project' },
  { id: 'mcp', label: 'MCP', icon: 'ph ph-plugs-connected' },
]

const activeTab = ref('account')
</script>

<style scoped lang="scss">
@use 'kit-deps' as *;

$bp-mobile: 767px;

// Matches .chat-area: fills .app-main, no scroll of its own — the single
// scroll region lives inside the active tab panel below.
.settings-view {
  flex: 1 1 0;
  min-height: 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

// Flat bar identical to .chat-header (global app.scss)
.settings-header {
  display: flex;
  align-items: center;
  gap: $space-sm;
  padding: $space-sm $space-md;
  border-bottom: $border-width-base $border-style-base $border-color-muted;
  background: $surface-panel;
  flex-shrink: 0;
  min-height: 54px;
}

.settings-title {
  margin: 0;
  font-size: 14px;
  font-weight: 600;
  color: $color-text-light;
}

.settings-sidebar-toggle {
  display: none;
  @media (max-width: 1279.98px) {
    display: flex;
  }
}

// Tab strip is fixed; the scroll region is the active panel below.
.settings-tabs {
  width: 100%;
  max-width: none;
  padding: $space-sm $space-md 0;
  margin-bottom: 0;

  .tabs-list {
    max-width: none;
  }

  // The single scroll region: full-bleed, scrollbar against the app-main edge
  :deep(.tabs-panels) {
    flex: 1 1 100%;
    min-height: 0;
    overflow-y: auto;
    padding: $space-md $space-md $space-xl;

    @media (max-width: $bp-mobile) {
      padding: $space-sm $space-sm $space-lg;
    }
  }
}

@media (max-width: $bp-mobile) {
  .settings-tabs {
    padding-left: $space-sm;
    padding-right: $space-sm;
  }
}
</style>