feat(pro): wire all 7 Pro components into app
- ProjectBox: 5 Pro tabs (Analytics, Budget, Export, Symbols, Agent Mem) with PERSISTED-LAZY mount, proStatus() feature gate, peach accent color - SettingsPanel: Pro tab (Accounts + Marketplace) conditionally shown - ProSettings.svelte: wrapper with sub-tabs for AccountSwitcher + PluginMarketplace - Feature detection via dynamic import of pro-bridge + proStatus() call - All tabs hidden when agor-pro plugin not loaded (community edition)
This commit is contained in:
parent
0953395423
commit
d1463d4d1e
4 changed files with 102 additions and 4 deletions
|
|
@ -16,6 +16,11 @@
|
|||
import TestingTab from './TestingTab.svelte';
|
||||
import MetricsPanel from './MetricsPanel.svelte';
|
||||
import AuditLogTab from './AuditLogTab.svelte';
|
||||
import AnalyticsDashboard from '../../commercial/AnalyticsDashboard.svelte';
|
||||
import SessionExporter from '../../commercial/SessionExporter.svelte';
|
||||
import BudgetManager from '../../commercial/BudgetManager.svelte';
|
||||
import ProjectMemory from '../../commercial/ProjectMemory.svelte';
|
||||
import CodeIntelligence from '../../commercial/CodeIntelligence.svelte';
|
||||
import {
|
||||
getTerminalTabs, getActiveGroup,
|
||||
getFocusFlashProjectId, onProjectTabSwitch, onTerminalToggle,
|
||||
|
|
@ -43,8 +48,14 @@
|
|||
let mainSessionId = $state<string | null>(null);
|
||||
let terminalExpanded = $state(false);
|
||||
|
||||
type ProjectTab = 'model' | 'docs' | 'context' | 'files' | 'ssh' | 'memories' | 'metrics' | 'tasks' | 'architecture' | 'selenium' | 'tests' | 'audit';
|
||||
type ProjectTab = 'model' | 'docs' | 'context' | 'files' | 'ssh' | 'memories' | 'metrics' | 'tasks' | 'architecture' | 'selenium' | 'tests' | 'audit' | 'analytics' | 'export' | 'budget' | 'promemory' | 'symbols';
|
||||
let activeTab = $state<ProjectTab>('model');
|
||||
let proAvailable = $state(false);
|
||||
|
||||
// Detect Pro edition availability
|
||||
$effect(() => {
|
||||
import('../../commercial/pro-bridge').then(m => m.proStatus()).then(() => { proAvailable = true; }).catch(() => {});
|
||||
});
|
||||
|
||||
let activeGroup = $derived(getActiveGroup());
|
||||
let agentRole = $derived(project.agentRole);
|
||||
|
|
@ -299,6 +310,13 @@
|
|||
{#if isAgent && agentRole === 'manager'}
|
||||
<button class="ptab ptab-role" class:active={activeTab === 'audit'} onclick={() => switchTab('audit')}>Audit</button>
|
||||
{/if}
|
||||
{#if proAvailable}
|
||||
<button class="ptab ptab-pro" class:active={activeTab === 'analytics'} onclick={() => switchTab('analytics')}>Analytics</button>
|
||||
<button class="ptab ptab-pro" class:active={activeTab === 'budget'} onclick={() => switchTab('budget')}>Budget</button>
|
||||
<button class="ptab ptab-pro" class:active={activeTab === 'export'} onclick={() => switchTab('export')}>Export</button>
|
||||
<button class="ptab ptab-pro" class:active={activeTab === 'symbols'} onclick={() => switchTab('symbols')}>Symbols</button>
|
||||
<button class="ptab ptab-pro" class:active={activeTab === 'promemory'} onclick={() => switchTab('promemory')}>Agent Mem</button>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="project-content-area">
|
||||
|
|
@ -362,6 +380,31 @@
|
|||
<AuditLogTab groupId={activeGroup.id} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if proAvailable && everActivated['analytics']}
|
||||
<div class="content-pane" style:display={activeTab === 'analytics' ? 'flex' : 'none'}>
|
||||
<AnalyticsDashboard projectId={project.id} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if proAvailable && everActivated['budget']}
|
||||
<div class="content-pane" style:display={activeTab === 'budget' ? 'flex' : 'none'}>
|
||||
<BudgetManager projectId={project.id} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if proAvailable && everActivated['export']}
|
||||
<div class="content-pane" style:display={activeTab === 'export' ? 'flex' : 'none'}>
|
||||
<SessionExporter projectId={project.id} sessionId={mainSessionId ?? undefined} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if proAvailable && everActivated['symbols']}
|
||||
<div class="content-pane" style:display={activeTab === 'symbols' ? 'flex' : 'none'}>
|
||||
<CodeIntelligence projectPath={project.cwd} />
|
||||
</div>
|
||||
{/if}
|
||||
{#if proAvailable && everActivated['promemory']}
|
||||
<div class="content-pane" style:display={activeTab === 'promemory' ? 'flex' : 'none'}>
|
||||
<ProjectMemory projectId={project.id} />
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="terminal-section" style:display={activeTab === 'model' ? 'flex' : 'none'}>
|
||||
|
|
@ -470,6 +513,14 @@
|
|||
color: var(--ctp-text);
|
||||
}
|
||||
|
||||
.ptab-pro {
|
||||
color: var(--ctp-peach);
|
||||
}
|
||||
|
||||
.ptab-pro:hover {
|
||||
color: var(--ctp-text);
|
||||
}
|
||||
|
||||
.project-content-area {
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
|
|
|||
|
|
@ -9,13 +9,14 @@
|
|||
import ProjectSettings from './categories/ProjectSettings.svelte';
|
||||
import OrchestrationSettings from './categories/OrchestrationSettings.svelte';
|
||||
import AdvancedSettings from './categories/AdvancedSettings.svelte';
|
||||
import ProSettings from './categories/ProSettings.svelte';
|
||||
|
||||
interface Props {
|
||||
onClose?: () => void;
|
||||
}
|
||||
let { onClose }: Props = $props();
|
||||
|
||||
const CATEGORIES: { id: SettingsCategory; label: string; icon: string }[] = [
|
||||
const BASE_CATEGORIES: { id: SettingsCategory; label: string; icon: string }[] = [
|
||||
{ id: 'appearance', label: 'Appearance', icon: '🎨' },
|
||||
{ id: 'agents', label: 'Agents', icon: '🤖' },
|
||||
{ id: 'security', label: 'Security', icon: '🛡' },
|
||||
|
|
@ -24,6 +25,11 @@
|
|||
{ id: 'advanced', label: 'Advanced', icon: '⚡' },
|
||||
];
|
||||
|
||||
let proAvailable = $state(false);
|
||||
let CATEGORIES = $derived(proAvailable
|
||||
? [...BASE_CATEGORIES, { id: 'pro' as SettingsCategory, label: 'Pro', icon: '💎' }]
|
||||
: BASE_CATEGORIES);
|
||||
|
||||
let activeCategory = $state<SettingsCategory>('appearance');
|
||||
let searchQuery = $state('');
|
||||
let searchResults = $derived(searchQuery.length >= 2
|
||||
|
|
@ -78,7 +84,10 @@
|
|||
}
|
||||
|
||||
let searchInput: HTMLInputElement | undefined = $state();
|
||||
onMount(() => { searchInput?.focus(); });
|
||||
onMount(() => {
|
||||
searchInput?.focus();
|
||||
import('../commercial/pro-bridge').then(m => m.proStatus()).then(() => { proAvailable = true; }).catch(() => {});
|
||||
});
|
||||
</script>
|
||||
|
||||
<div class="settings-panel" onkeydown={handleSidebarKeydown}>
|
||||
|
|
@ -142,6 +151,8 @@
|
|||
<OrchestrationSettings />
|
||||
{:else if activeCategory === 'advanced'}
|
||||
<AdvancedSettings />
|
||||
{:else if activeCategory === 'pro'}
|
||||
<ProSettings />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
35
src/lib/settings/categories/ProSettings.svelte
Normal file
35
src/lib/settings/categories/ProSettings.svelte
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<script lang="ts">
|
||||
import AccountSwitcher from '../../commercial/AccountSwitcher.svelte';
|
||||
import PluginMarketplace from '../../commercial/PluginMarketplace.svelte';
|
||||
|
||||
let activeSection = $state<'accounts' | 'marketplace'>('accounts');
|
||||
</script>
|
||||
|
||||
<div class="pro-settings">
|
||||
<div class="pro-tabs">
|
||||
<button class="pro-tab" class:active={activeSection === 'accounts'} onclick={() => activeSection = 'accounts'}>
|
||||
Accounts
|
||||
</button>
|
||||
<button class="pro-tab" class:active={activeSection === 'marketplace'} onclick={() => activeSection = 'marketplace'}>
|
||||
Marketplace
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="pro-content">
|
||||
{#if activeSection === 'accounts'}
|
||||
<AccountSwitcher />
|
||||
{:else}
|
||||
<PluginMarketplace />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.pro-settings { display: flex; flex-direction: column; gap: 0.5rem; }
|
||||
.pro-tabs { display: flex; gap: 0.25rem; padding-bottom: 0.5rem; border-bottom: 1px solid var(--ctp-surface0); }
|
||||
.pro-tab { padding: 0.3rem 0.75rem; background: none; border: none; border-radius: 0.25rem;
|
||||
color: var(--ctp-subtext0); cursor: pointer; font-size: 0.8rem; }
|
||||
.pro-tab:hover { background: var(--ctp-surface0); color: var(--ctp-text); }
|
||||
.pro-tab.active { background: color-mix(in srgb, var(--ctp-peach) 15%, transparent); color: var(--ctp-peach); }
|
||||
.pro-content { flex: 1; overflow-y: auto; }
|
||||
</style>
|
||||
|
|
@ -9,7 +9,8 @@ export type SettingsCategory =
|
|||
| 'security'
|
||||
| 'projects'
|
||||
| 'orchestration'
|
||||
| 'advanced';
|
||||
| 'advanced'
|
||||
| 'pro';
|
||||
|
||||
export interface SettingEntry {
|
||||
key: string;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue