xiaohongshu
xiaohongshu
kumo-query-module
Expert assistant for Kumo's Query Module (src/features/query). Use when working with SQL Editor, Monaco editor integration, query execution, result grids, tabs management, AI features, or any query-related components. Provides architecture patterns, component APIs, hooks, utilities, and examples.
Full skill instructions
Comprehensive guide for developing and extending Kumo's Query Module (src/features/query) - the SQL Editor and query execution system.
Use this skill when:
SQLEditor.tsx)src/features/query/
├── SQLEditor.tsx # Main SQL editor component (800+ lines)
├── ResultGrid.tsx # Query results display with inline editing
├── NaturalLanguageToSQL.tsx # AI: Convert natural language to SQL
├── AIResultsPanel.tsx # AI analysis results display
├── ExplainVisualizer.tsx # EXPLAIN query visualization
├── QueryHistoryPanel.tsx # Query history sidebar
├── SavedQueriesPanel.tsx # Saved queries sidebar
├── QuerySnippetsPanel.tsx # SQL snippets sidebar
├── QueryResultsCompare.tsx # Compare query results (split view)
│
├── components/
│ ├── EditorToolbar/ # Toolbar buttons and controls
│ │ ├── index.tsx # Main toolbar component
│ │ ├── RunButton.tsx # Execute query button
│ │ ├── FormatButtons.tsx # Format/minify SQL buttons
│ │ ├── AIMenuButton.tsx # AI features dropdown
│ │ ├── AutoRefreshToggle.tsx # Auto-refresh control
│ │ ├── UtilityButtons.tsx # Misc utility buttons
│ │ └── ToolbarRightButtons.tsx # Right-aligned buttons
│ │
│ ├── EditorTabBar/ # Tab management
│ │ ├── index.tsx # Tab bar component
│ │ ├── EditorTab.tsx # Single tab component
│ │ ├── TabColorPicker.tsx # Tab color selector
│ │ └── NewTabButton.tsx # Add new tab button
│ │
│ ├── RightPanelSidebar/ # History/Saved/Snippets panels
│ │ └── index.tsx # Sidebar toggle component
│ │
│ ├── DateTimeDisplay.tsx # Timezone-aware datetime rendering
│ ├── ResultTable.tsx # Virtual scrolling results table
│ ├── ResultGridHeader.tsx # Result grid toolbar
│ ├── NonSelectResult.tsx # INSERT/UPDATE/DELETE results
│ ├── ContextMenu.tsx # Right-click context menu
│ ├── ContextMenuLogic.tsx # Context menu state logic
│ ├── PivotPanel.tsx # Pivot table & charts
│ ├── PivotFullScreenOverlay.tsx # Fullscreen pivot view
│ ├── ToastNotification.tsx # Toast messages
│ │
│ ├── TableColumns.tsx # Result grid column definitions (hook)
│ ├── CopyFunctions.tsx # Copy to clipboard functions (hook)
│ ├── ExportFunctions.tsx # Export CSV/JSON/Excel (hook)
│ ├── QueryGenerator.tsx # Generate UPDATE/INSERT/DELETE (hook)
│ ├── PivotFunctions.tsx # Pivot table logic (hook)
│ ├── EditModeFunctions.tsx # Inline edit mode logic (hook)
│ ├── DatabaseResolution.tsx # Resolve database/table from SQL (hook)
│ └── QueryUtils.tsx # Utility functions
│
└── utils/
├── sqlUtils.ts # SQL parsing, formatting, validation
└── tabUtils.ts # Tab state management
Main component (SQLEditor.tsx) - Central orchestrator for the query module.
Key Responsibilities:
State Structure:
const [sql, setSql] = useState<string>(''); // Current SQL
const [results, setResults] = useState<QueryResult[] | null>(); // Query results
const [tabs, setTabs] = useState<EditorTab[]>(); // All tabs
const [activeEditorTab, setActiveEditorTab] = useState(0); // Active tab index
const [selectedTimezone, setSelectedTimezone] = useState('default');
const [splitEnabled, setSplitEnabled] = useState(false); // Split editor
const [autoRefreshEnabled, setAutoRefreshEnabled] = useState(false);
// ... 30+ more state variables
Key Features:
User clicks Run → handleExecuteQuery()
↓
getQueryAtCursor() → Extract SQL at cursor position
↓
executeQueryMutation.mutate() → Send to backend API
↓
Backend executes query → Returns QueryResult[]
↓
setResults() → Update state
↓
ResultGrid renders → Display results with inline editing
Key API: useExecuteQuery hook from hooks/useQuery.ts
Setup:
import Editor from '@monaco-editor/react';
<Editor
height="260px"
language="sql"
theme={isDarkMode ? 'vs-dark' : 'vs'}
value={sql}
onChange={(value) => setSql(value || '')}
onMount={handleEditorMount}
options={{
minimap: { enabled: false },
fontSize: 14,
lineNumbers: 'on',
automaticLayout: true,
formatOnPaste: formatOnPaste,
// ... more options
}}
/>
Custom Features:
validateSQL() in sqlUtils.ts)formatSQL() using sql-formatter)minifySQL() - remove comments/whitespace)getQueryAtCursor() - execute specific query)EditorTab Type:
type EditorTab = {
id: string; // Unique tab ID
name: string; // Tab display name
sql: string; // SQL content
results: QueryResult[] | null;
error: string | null;
isRunning: boolean;
isPinned?: boolean; // Prevent tab close
color?: string; // Tab color (#hex)
executionTime?: number; // Last execution time
rowsAffected?: number; // Rows affected
};
Persistence: Tabs saved to localStorage (excluding runtime state)
Key Functions (from tabUtils.ts):
loadSavedTabs() - Load tabs from localStorage on mountsaveTabs() - Save tabs to localStorage (auto on change)createNewTab() - Create new tab with default SQLduplicateTab() - Duplicate existing tabgenerateTabId() - Generate unique tab identifierPurpose: Display query results with advanced features:
Architecture:
ResultGrid (main)
├── ResultGridHeader (toolbar)
├── ResultTable (virtualized table)
├── PivotPanel (pivot/chart preview)
├── PivotFullScreenOverlay (fullscreen pivot)
├── ContextMenu (right-click menu)
└── ToastNotification (feedback messages)
See: components.md for detailed component API
Location: src/features/query/components/EditorToolbar/
Steps:
MyButton.tsx)EditorToolbar/index.tsxExample:
// MyButton.tsx
import { Sparkles } from 'lucide-react';
interface MyButtonProps {
onAction: () => void;
disabled?: boolean;
}
export const MyButton: React.FC<MyButtonProps> = ({ onAction, disabled }) => {
return (
<button
onClick={onAction}
disabled={disabled}
className="px-3 py-1.5 text-sm rounded hover:bg-gray-100 dark:hover:bg-gray-800"
title="My Custom Action"
>
<Sparkles className="w-4 h-4" />
</button>
);
};
// EditorToolbar/index.tsx
import { MyButton } from './MyButton';
export const EditorToolbar = ({ ... }) => {
const handleMyAction = () => {
// Your logic here
};
return (
<div className="toolbar">
{/* ... other buttons ... */}
<MyButton onAction={handleMyAction} />
</div>
);
};
Location: src/features/query/utils/sqlUtils.ts
Example: Add a function to extract table names from SQL
// sqlUtils.ts
/**
* Extract table names from SQL query
*/
export const extractTableNames = (sql: string): string[] => {
const tables: string[] = [];
const upperSql = sql.toUpperCase();
// Match FROM and JOIN clauses
const fromRegex = /FROM\s+`?(\w+)`?/gi;
const joinRegex = /JOIN\s+`?(\w+)`?/gi;
let match;
while ((match = fromRegex.exec(sql)) !== null) {
tables.push(match[1]);
}
while ((match = joinRegex.exec(sql)) !== null) {
tables.push(match[1]);
}
return [...new Set(tables)]; // Remove duplicates
};
Location: src/features/query/SQLEditor.tsx and AI components
Steps:
aiResultType stateAIMenuButton.tsxAIResultsPanel.tsxExample: Add "Suggest Indexes" feature
// SQLEditor.tsx
// 1. Add to type (if needed)
type AIFeature = 'explain' | 'optimize' | 'analyze' | 'schema' | 'suggest-indexes';
// 2. Create handler
const handleSuggestIndexes = async () => {
if (!connectionId) return;
setIsAIProcessing(true);
setAiResultType('suggest-indexes');
try {
const response = await aiApi.suggestIndexes(connectionId, currentDatabase, sql);
setAiResultContent(response.suggestions);
} catch (error) {
setAiResultContent('Failed to generate index suggestions.');
} finally {
setIsAIProcessing(false);
}
};
// 3. Add menu item in AIMenuButton.tsx
<button onClick={handleSuggestIndexes}>
<Database className="w-4 h-4" />
Suggest Indexes
</button>
// 4. Handle display in AIResultsPanel.tsx
{aiResultType === 'suggest-indexes' && (
<div className="suggestions">
<h3>Index Suggestions</h3>
<pre>{aiResultContent}</pre>
</div>
)}
Location: src/features/query/components/DateTimeDisplay.tsx
Use Case: Change datetime format or add new timezone logic
Current Logic:
YYYY-MM-DDYYYY-MM-DD HH:MM:SSExample: Add milliseconds to datetime display
// DateTimeDisplay.tsx
const formatDateInTimezone = (date: Date, timezone?: string, showMs = false): string => {
// ... existing code ...
const milliseconds = String(date.getMilliseconds()).padStart(3, '0');
if (showMs) {
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}.${milliseconds}`;
}
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
};
Location: src/features/query/components/ExportFunctions.tsx
Example: Add XML export
// ExportFunctions.tsx
const exportToXML = useCallback(async () => {
const dataToExport = /* ... get data ... */;
// Build XML
let xml = '<?xml version="1.0" encoding="UTF-8"?>\n<rows>\n';
dataToExport.forEach((row) => {
xml += ' <row>\n';
Object.entries(row).forEach(([key, value]) => {
xml += ` <${key}>${value}</${key}>\n`;
});
xml += ' </row>\n';
});
xml += '</rows>';
// Download
const blob = new Blob([xml], { type: 'application/xml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `query-results.xml`;
a.click();
URL.revokeObjectURL(url);
}, [rows, selectedTimezone]);
return {
exportToCSV,
exportToJSON,
exportToExcel,
exportToXML, // Add to return
};
utils/sqlUtils.ts)// Get query at cursor position
const query = getQueryAtCursor(editorRef.current);
// Get query with line range
const { query, startLine, endLine } = getQueryAtCursorWithRange(editorRef.current);
// Format SQL
const formatted = formatSQL(sql); // Uses sql-formatter
// Minify SQL
const minified = minifySQL(sql); // Remove comments/whitespace
// Validate SQL
validateSQL(editorRef.current, sql); // Sets Monaco markers
utils/tabUtils.ts)// Load tabs from localStorage
const tabs = loadSavedTabs();
// Save tabs to localStorage
saveTabs(tabs);
// Create new tab
const newTab = createNewTab(existingTabs, 'SELECT * FROM users;', 'Users Query');
// Duplicate tab
const duplicatedTab = duplicateTab(originalTab, existingTabs);
// Generate unique ID
const id = generateTabId(); // 'tab_1234567890_abcd'
From hooks/useQuery.ts:
useExecuteQuery() - Execute single queryuseExecuteMultipleQueries() - Execute multiple queriesuseCancelQuery() - Cancel running queryFrom hooks/useConnections.ts:
useConnection(id) - Get connection detailsFrom hooks/useSavedQueries.ts:
useCreateSavedQuery() - Save query to libraryCustom Hooks in Components:
useResultTableColumns() - Generate result grid columnsuseCopyFunctions() - Copy operationsuseExportFunctions() - Export operationsuseQueryGenerator() - Generate UPDATE/INSERT/DELETEusePivotFunctions() - Pivot table logicuseEditModeFunctions() - Inline edit modeuseDatabaseResolution() - Resolve DB/table from SQLuseContextMenuLogic() - Context menu stateDO:
// Use refs for frequently changing callbacks
const onEditCellRef = useRef(onEditCell);
useEffect(() => { onEditCellRef.current = onEditCell; }, [onEditCell]);
// Keep deps minimal in useMemo/useCallback
const columns = useMemo(() => {
// ...
}, [columnInfo, editable]); // Only essential deps
DON'T:
// Don't recreate functions on every render
const handleClick = () => { // ❌ New function every render
doSomething();
};
// Use useCallback instead
const handleClick = useCallback(() => { // ✅
doSomething();
}, []);
DO:
// Store editor reference
const editorRef = useRef<any>(null);
const handleEditorMount = (editor: any) => {
editorRef.current = editor;
// Set up editor features
validateSQL(editor, sql);
};
DON'T:
// Don't access editor directly without ref ❌
DO:
// Auto-save tabs on change
useEffect(() => {
saveTabs(tabs);
}, [tabs]);
// Load on mount
const [tabs, setTabs] = useState(() => loadSavedTabs());
DO:
try {
const result = await executeQuery();
setResults(result);
} catch (error) {
setError(error.message);
// Show toast notification
setToast({ message: error.message, type: 'error' });
}
DO:
// Use virtual scrolling for large result sets
import { useVirtualizer } from '@tanstack/react-virtual';
// Lazy load components
const ExplainVisualizer = lazy(() => import('./ExplainVisualizer'));
// Debounce expensive operations
import { useDebounce } from 'use-debounce';
const [debouncedSearch] = useDebounce(search, 300);
// Good: Extract complex logic into custom hook
export function useMyFeature({ sql, connectionId }: Props) {
const [state, setState] = useState();
const doSomething = useCallback(() => {
// Logic here
}, [sql, connectionId]);
useEffect(() => {
// Side effects
}, [connectionId]);
return { state, doSomething };
}
// Usage
const { state, doSomething } = useMyFeature({ sql, connectionId });
// Render dropdown at document root to avoid z-index issues
{showDropdown && ReactDOM.createPortal(
<div style={{ position: 'absolute', left: pos.x, top: pos.y }}>
<Dropdown />
</div>,
document.body
)}
// Use Monaco editor's keyboard API
editor.addAction({
id: 'execute-query',
label: 'Execute Query',
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.Enter],
run: () => handleExecuteQuery(),
});
Solution: Check language mode is set to 'sql'
<Editor language="sql" /> // Not 'mysql' or 'postgres'
Solution: Check localStorage quota and error handling
try {
localStorage.setItem('sqlEditorTabs', JSON.stringify(tabs));
} catch (e) {
console.error('Failed to save tabs:', e); // Quota exceeded?
}
Solution: Ensure editor is mounted and has content
if (!editorRef.current) return;
const model = editorRef.current.getModel();
if (!model) return;
Solution: Check selectedTimezone state and DateTimeDisplay props
<DateTimeDisplay value={value} timezone={selectedTimezone} columnType={field.type} />
Main Files:
SQLEditor.tsx - Main component (800+ lines)ResultGrid.tsx - Query results displayutils/sqlUtils.ts - SQL utilitiesutils/tabUtils.ts - Tab managementcomponents/DateTimeDisplay.tsx - Datetime renderingKey State:
sql - Current SQL contenttabs - All editor tabsactiveEditorTab - Active tab indexresults - Query resultsselectedTimezone - Timezone for datetime displayKey Hooks:
useExecuteQuery() - Execute queryuseResultTableColumns() - Result grid columnsuseExportFunctions() - Export dataUtilities:
getQueryAtCursor() - Get query at cursorformatSQL() - Format SQLvalidateSQL() - Validate and set markersxiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience