Agent skill
primary-sidebar
Complete guide for adding, updating, and removing tabs in the Primary Sidebar of OrcaQ. Covers the full flow — ActivityBarItemType enum → useActivityBarStore → PrimarySideBar component → Management panel component. Load this skill for any task involving the left sidebar, activity bar tabs, or management panels (Explorer, Schemas, ERD, Roles, Export, Agent).
Install this agent skill to your Project
npx add-skill https://github.com/cin12211/orca-q/tree/main/.github/skills/primary-sidebar
SKILL.md
Primary Sidebar Flow — OrcaQ
Architecture Overview
The Primary Sidebar is driven by a single active tab value in a Pinia store. The flow is:
ActivityBarItemType (enum) ← Tab identity
useActivityBarStore.activityActive ← Which tab is currently active (persisted)
PrimarySideBar.vue ← Watches activityActive, renders the matching component
Management***.vue ← The actual panel content (KeepAlive'd)
The Activity Bar (the narrow icon strip on the far left) calls setActivityActive(type). The Primary Sidebar reacts to the change and swaps the rendered panel — all panels are wrapped in <KeepAlive> so their state is preserved when the user switches tabs.
File Locations
| Purpose | File |
|---|---|
| Tab type enum + store | core/stores/useActivityBarStore.ts |
| Sidebar shell (switcher) | components/modules/app-shell/primary-side-bar/components/PrimarySideBar.vue |
| Sidebar public API | components/modules/app-shell/primary-side-bar/index.ts |
| All management panels | components/modules/management/ |
| Management public API | components/modules/management/index.ts |
| Shared header component | components/modules/management/shared/components/ManagementSidebarHeader.vue |
Management panel locations
| Tab | Panel component file |
|---|---|
| Explorer | components/modules/management/explorer/ManagementExplorer.vue |
| Schemas | components/modules/management/schemas/ManagementSchemas.vue |
| ERD Diagram | components/modules/management/erd-diagram/ManagementErdDiagram.vue |
| Users & Roles | components/modules/management/role-permission/ManagementUsersAndPermission.vue |
| Export | components/modules/management/export/ManagementExport.vue |
| Agent | components/modules/management/agent/ManagementAgent.vue |
How the Switcher Works (PrimarySideBar.vue)
<script setup lang="ts">
const activityStore = useActivityBarStore();
const current = shallowRef();
watch(
() => activityStore.activityActive,
() => {
if (activityStore.activityActive === ActivityBarItemType.Explorer)
current.value = ManagementExplorer;
if (activityStore.activityActive === ActivityBarItemType.Schemas)
current.value = ManagementSchemas;
// ... one branch per tab
},
{ immediate: true }
);
</script>
<template>
<div class="w-full h-full flex flex-col" v-if="appConfigStore.layoutSize[0]">
<KeepAlive>
<component :is="current" />
</KeepAlive>
</div>
</template>
Key points:
- Uses
shallowRef(notref) for the component — avoids deep reactivity on component objects. immediate: trueso the correct panel is rendered on first mount.<KeepAlive>preserves scroll position and internal state when switching tabs.- The panel is only mounted when the sidebar is open (
layoutSize[0] > 0).
How to Add a New Sidebar Tab
Step 1 — Add enum value
In core/stores/useActivityBarStore.ts:
export enum ActivityBarItemType {
Explorer = 'Explorer',
Schemas = 'Schemas',
ErdDiagram = 'ERDiagram',
UsersRoles = 'UsersRoles',
DatabaseExport = 'DatabaseExport',
Agent = 'Agent',
MyNewTab = 'MyNewTab', // ← new
}
Step 2 — Create the management panel module
Create the folder components/modules/management/my-new-tab/ with this structure:
my-new-tab/
├── index.ts ← exports ManagementMyNewTab
├── ManagementMyNewTab.vue ← entry component
├── components/ ← sub-components (optional)
├── hooks/ ← business logic composables (optional)
└── services/ ← API calls (optional)
ManagementMyNewTab.vue minimum template:
<script setup lang="ts">
import { ManagementSidebarHeader } from '../shared';
</script>
<template>
<div class="flex flex-col h-full w-full overflow-y-auto">
<ManagementSidebarHeader title="My New Tab" />
<!-- panel content here -->
</div>
</template>
index.ts:
export { default as ManagementMyNewTab } from './ManagementMyNewTab.vue';
Step 3 — Export from the management module
In components/modules/management/index.ts:
export * from './my-new-tab'; // ← add this line
Step 4 — Register in PrimarySideBar
In components/modules/app-shell/primary-side-bar/components/PrimarySideBar.vue:
// 1. Import the component
import { ManagementMyNewTab } from '#components';
// 2. Add a branch in the watch
watch(
() => activityStore.activityActive,
() => {
// ... existing branches ...
if (activityStore.activityActive === ActivityBarItemType.MyNewTab)
current.value = ManagementMyNewTab;
},
{ immediate: true }
);
Step 5 — Add Activity Bar button
The Activity Bar icon strip that calls setActivityActive is separate from the management module. Find the component that renders the icon list and add a button:
activityStore.setActivityActive(ActivityBarItemType.MyNewTab);
How to Update an Existing Panel
- Find the panel in
components/modules/management/<tab-name>/Management<Tab>.vue. - Business logic (API calls, state) belongs in a hook under
<tab-name>/hooks/. - Static structure (sub-components) belongs in
<tab-name>/components/. - Persisted UI state (expanded nodes, scroll position) goes into
useActivityBarStore— seeschemasExpandedState,schemaCurrentScrollTopas examples.
ManagementSidebarHeader — Shared Header Component
All panels use the shared header. Props:
| Prop | Type | Default | Purpose |
|---|---|---|---|
title |
string |
required | Panel title text |
showConnection |
boolean |
false |
Show ConnectionSelector dropdown |
showSchema |
boolean |
false |
Show SchemaSelector dropdown |
workspaceId |
string |
— | Required when showConnection is true |
showSearch |
boolean |
false |
Show search input |
searchPlaceholder |
string |
'Search...' |
Input placeholder |
It also accepts a v-model:search for two-way search binding and an #actions slot for icon buttons in the title bar.
Standard usage:
<ManagementSidebarHeader
title="My Panel"
:show-connection="true"
:workspaceId="workspaceId"
:show-search="true"
v-model:search="searchInput"
>
<template #actions>
<Button size="iconSm" variant="ghost" @click="onRefresh">
<Icon name="hugeicons:refresh" class="size-4!" />
</Button>
</template>
</ManagementSidebarHeader>
Controlling the Sidebar Programmatically
import {
useActivityBarStore,
ActivityBarItemType,
} from '~/core/stores/useActivityBarStore';
const activityStore = useActivityBarStore();
// Switch to a tab
activityStore.setActivityActive(ActivityBarItemType.Schemas);
// Read current active tab
const isSchemas = activityStore.activityActive === ActivityBarItemType.Schemas;
The sidebar visibility is controlled by appConfigStore.layoutSize[0]. Use onToggleActivityBarPanel() from useAppConfigStore to open/close it — do NOT manipulate layoutSize directly.
Hook Pattern for Panel Logic
Every non-trivial panel delegates logic to a hook. The hook receives UI callbacks via parameter (avoid importing component refs directly):
// hooks/useMyPanelTree.ts
export function useMyPanelTree(callbacks: {
focusNode: (id: string) => void;
collapseAll: () => void;
expandAll: () => void;
isExpandedAll: ComputedRef<boolean>;
}) {
// state, API calls, event handlers
return {
treeData,
searchInput,
onClickNode,
// ...
};
}
Usage in the container:
<script setup lang="ts">
const treePanelRef = useTemplateRef<TreeInstance | null>('treePanelRef');
const { treeData, searchInput, onClickNode } = useMyPanelTree({
focusNode: id => treePanelRef.value?.focusItem(id),
collapseAll: () => treePanelRef.value?.collapseAll(),
expandAll: () => treePanelRef.value?.expandAll(),
isExpandedAll: computed(() => treePanelRef.value?.isExpandedAll ?? false),
});
</script>
Key Constraints
- Never add business logic directly in
PrimarySideBar.vue— it is a pure switcher. All logic lives in the management panel or its hooks. - Always use
shallowReffor thecurrentcomponent —ref()causes deep reactivity overhead on component objects. - Always wrap the
<component :is="current">in<KeepAlive>— this preserves scroll position and tree expand state when switching tabs. - Persisted tree state (expanded keys, scroll positions) goes into
useActivityBarStore, not into local component state. - Imports in
PrimarySideBar.vueuse#components(Nuxt auto-import barrel) — not direct relative paths. - Each management sub-module must have its own
index.tsexporting its root component, and be re-exported fromcomponents/modules/management/index.ts.
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
documentation-expert
Expert in documentation structure, cohesion, flow, audience targeting, and information architecture. Use PROACTIVELY for documentation quality issues, content organization, duplication, navigation problems, or readability concerns. Detects documentation anti-patterns and optimizes for user experience.
ai-sdk-expert
Expert in Vercel AI SDK v5 handling streaming, model integration, tool calling, hooks, state management, edge runtime, prompt engineering, and production patterns. Use PROACTIVELY for any AI SDK implementation, streaming issues, provider integration, or AI application architecture. Detects project setup and adapts approach.
testing-expert
Testing expert with comprehensive knowledge of test structure, mocking strategies, async testing, coverage analysis, and cross-framework debugging. Use PROACTIVELY for test reliability, flaky test debugging, framework migration, and testing architecture decisions. Covers Jest, Vitest, Playwright, and Testing Library.
code-review
Provides comprehensive code review covering 6 focused aspects - architecture & design, code quality, security & dependencies, performance & scalability, testing coverage, and documentation & API design. Use this skill for deep analysis with actionable feedback after significant code changes.
postgres-expert
PostgreSQL query optimization, JSONB operations, advanced indexing strategies, partitioning, connection management, and database administration. Use this skill for PostgreSQL-specific optimizations, performance tuning, replication setup, and PgBouncer configuration.
settings-flow
Complete guide for adding, updating, and removing settings in OrcaQ. Covers the full data flow — type → constant → store → component — for all settings panels (Appearance, Editor, Quick Query, Agent). Load this skill for any task involving user preferences, persistent configs, or the settings modal.
Didn't find tool you were looking for?