feat(dirac): core formatters + webview-ui components
This commit is contained in:
parent
447540866b
commit
f3a5e26e84
3 changed files with 930 additions and 0 deletions
203
dirac/src/core/formatters/toon.ts
Normal file
203
dirac/src/core/formatters/toon.ts
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
fin/**
|
||||
* TOON (Token-Optimized Object Notation) Formatter
|
||||
* A token-efficient alternative to JSON for AI agent communication
|
||||
* Converts arrays of uniform objects to a table-like format with headers and rows
|
||||
*/
|
||||
|
||||
export interface TOONOptions {
|
||||
maxArrayLength?: number;
|
||||
minTokenSavings?: number;
|
||||
preferTOON?: boolean;
|
||||
}
|
||||
|
||||
export interface TOONResult {
|
||||
format: 'toon' | 'json';
|
||||
content: string;
|
||||
originalTokenCount: number;
|
||||
convertedTokenCount: number;
|
||||
tokenSavings: number;
|
||||
tokenSavingsPercent: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if an array of objects is suitable for TOON conversion
|
||||
*/
|
||||
function isUniformObjectArray(data: any[]): boolean {
|
||||
if (!Array.isArray(data) || data.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if all items are objects
|
||||
if (!data.every(item => typeof item === 'object' && item !== null && !Array.isArray(item))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get keys from the first object
|
||||
const firstKeys = Object.keys(data[0]).sort();
|
||||
|
||||
// Check if all objects have the same keys
|
||||
return data.every(obj => {
|
||||
const objKeys = Object.keys(obj).sort();
|
||||
return objKeys.length === firstKeys.length &&
|
||||
objKeys.every((key, i) => key === firstKeys[i]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an array of uniform objects to TOON format
|
||||
*/
|
||||
function convertToTOON(data: any[]): string {
|
||||
if (!isUniformObjectArray(data)) {
|
||||
throw new Error('Data is not suitable for TOON conversion');
|
||||
}
|
||||
|
||||
const headers = Object.keys(data[0]);
|
||||
const rows = data.map(obj => headers.map(header => obj[header]));
|
||||
|
||||
// Create TOON format: headers followed by rows
|
||||
let toonStr = '| ' + headers.join(' | ') + ' |\n';
|
||||
toonStr += '|' + headers.map(() => ' --- ').join('|') + '|\n';
|
||||
toonStr += rows.map(row => '| ' + row.join(' | ') + ' |').join('\n');
|
||||
|
||||
return toonStr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Estimates token count for a string (approximate)
|
||||
*/
|
||||
function estimateTokenCount(str: string): number {
|
||||
// Rough estimation: 1 token ≈ 4 characters, but punctuation and common words are fewer
|
||||
return Math.ceil(str.length / 4);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format data using TOON when beneficial for token savings
|
||||
*/
|
||||
export function formatDataTOON(data: any, options: TOONOptions = {}): TOONResult {
|
||||
const {
|
||||
maxArrayLength = 100,
|
||||
minTokenSavings = 0.1, // Require at least 10% savings
|
||||
preferTOON = false
|
||||
} = options;
|
||||
|
||||
// Convert data to JSON first to get baseline
|
||||
const jsonStr = JSON.stringify(data, null, 2);
|
||||
const originalTokenCount = estimateTokenCount(jsonStr);
|
||||
|
||||
// Check if data is a suitable array for TOON conversion
|
||||
if (Array.isArray(data) &&
|
||||
data.length > 0 &&
|
||||
data.length <= maxArrayLength &&
|
||||
isUniformObjectArray(data)) {
|
||||
|
||||
try {
|
||||
const toonStr = convertToTOON(data);
|
||||
const convertedTokenCount = estimateTokenCount(toonStr);
|
||||
const tokenSavings = originalTokenCount - convertedTokenCount;
|
||||
const tokenSavingsPercent = originalTokenCount > 0 ? (tokenSavings / originalTokenCount) * 100 : 0;
|
||||
|
||||
// Use TOON if it saves tokens beyond the threshold OR if TOON is preferred
|
||||
if (tokenSavingsPercent >= (minTokenSavings * 100) || preferTOON) {
|
||||
return {
|
||||
format: 'toon',
|
||||
content: toonStr,
|
||||
originalTokenCount,
|
||||
convertedTokenCount,
|
||||
tokenSavings,
|
||||
tokenSavingsPercent
|
||||
};
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('TOON conversion failed, falling back to JSON:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Return JSON if TOON isn't beneficial or applicable
|
||||
return {
|
||||
format: 'json',
|
||||
content: jsonStr,
|
||||
originalTokenCount,
|
||||
convertedTokenCount: originalTokenCount,
|
||||
tokenSavings: 0,
|
||||
tokenSavingsPercent: 0
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse TOON formatted string back to array of objects
|
||||
*/
|
||||
export function parseTOON(toonStr: string): any[] {
|
||||
const lines = toonStr.split('\n').filter(line => line.trim() !== '');
|
||||
if (lines.length < 2) {
|
||||
throw new Error('Invalid TOON format: insufficient lines');
|
||||
}
|
||||
|
||||
// Find header and separator lines
|
||||
let headerLineIndex = -1;
|
||||
let separatorLineIndex = -1;
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
const line = lines[i].trim();
|
||||
if (line.startsWith('|') && line.includes('|')) {
|
||||
if (headerLineIndex === -1) {
|
||||
headerLineIndex = i;
|
||||
} else if (separatorLineIndex === -1 && line.includes('---')) {
|
||||
separatorLineIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (headerLineIndex === -1 || separatorLineIndex === -1) {
|
||||
throw new Error('Invalid TOON format: missing header or separator line');
|
||||
}
|
||||
|
||||
// Extract headers
|
||||
const headerLine = lines[headerLineIndex];
|
||||
const headers = headerLine
|
||||
.split('|')
|
||||
.map(h => h.trim())
|
||||
.filter(h => h !== '');
|
||||
|
||||
// Process data rows
|
||||
const dataRows = lines.slice(separatorLineIndex + 1).filter(line =>
|
||||
line.trim().startsWith('|') && line.trim().endsWith('|')
|
||||
);
|
||||
|
||||
const result: any[] = [];
|
||||
for (const row of dataRows) {
|
||||
const values = row
|
||||
.split('|')
|
||||
.map(v => v.trim())
|
||||
.filter(v => v !== '');
|
||||
|
||||
if (values.length === headers.length) {
|
||||
const obj: any = {};
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
// Try to parse as JSON if it looks like a complex value, otherwise use as string
|
||||
let value = values[i];
|
||||
try {
|
||||
// Attempt to parse as JSON for numbers, booleans, etc.
|
||||
if (value === 'true') value = true;
|
||||
else if (value === 'false') value = false;
|
||||
else if (value === 'null') value = null;
|
||||
else if (/^-?\d+$/.test(value)) value = parseInt(value, 10);
|
||||
else if (/^-?\d*\.\d+$/.test(value)) value = parseFloat(value);
|
||||
} catch (e) {
|
||||
// Keep as string if JSON parsing fails
|
||||
}
|
||||
obj[headers[i]] = value;
|
||||
}
|
||||
result.push(obj);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Benchmark TOON vs JSON token efficiency
|
||||
*/
|
||||
export function benchmarkTOON(data: any, options: TOONOptions = {}): TOONResult {
|
||||
return formatDataTOON(data, options);
|
||||
}
|
||||
376
dirac/webview-ui/src/components/ImmersiveReader.tsx
Normal file
376
dirac/webview-ui/src/components/ImmersiveReader.tsx
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { VSCodeButton, VSCodeTextField, VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
|
||||
|
||||
interface Book {
|
||||
id: string;
|
||||
title: string;
|
||||
author: string;
|
||||
totalPages: number;
|
||||
currentPage: number;
|
||||
progress: number;
|
||||
bookmarks: number[];
|
||||
annotations: Annotation[];
|
||||
format: 'pdf' | 'epub' | 'text';
|
||||
}
|
||||
|
||||
interface Annotation {
|
||||
id: string;
|
||||
page: number;
|
||||
text: string;
|
||||
highlight: string;
|
||||
timestamp: Date;
|
||||
}
|
||||
|
||||
interface ImmersiveReaderProps {
|
||||
book: Book;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
const ImmersiveReader: React.FC<ImmersiveReaderProps> = ({ book, onClose }) => {
|
||||
const [currentPage, setCurrentPage] = useState(book.currentPage);
|
||||
const [fontSize, setFontSize] = useState(16);
|
||||
const [lineHeight, setLineHeight] = useState(1.5);
|
||||
const [theme, setTheme] = useState<'light' | 'dark' | 'sepia'>('light');
|
||||
const [showBookmarks, setShowBookmarks] = useState(true);
|
||||
const [showAnnotations, setShowAnnotations] = useState(true);
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
const [searchResults, setSearchResults] = useState<{page: number, index: number}[]>([]);
|
||||
const [currentSearchIndex, setCurrentSearchIndex] = useState(0);
|
||||
const [newAnnotation, setNewAnnotation] = useState('');
|
||||
const [isAddingAnnotation, setIsAddingAnnotation] = useState(false);
|
||||
const [readingMode, setReadingMode] = useState<'continuous' | 'paged'>('continuous');
|
||||
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// Calculate progress percentage
|
||||
useEffect(() => {
|
||||
const progress = (currentPage / book.totalPages) * 100;
|
||||
// Update book progress in parent component or state
|
||||
}, [currentPage, book.totalPages]);
|
||||
|
||||
// Handle search
|
||||
useEffect(() => {
|
||||
if (searchQuery.trim() === '') {
|
||||
setSearchResults([]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Mock search functionality - in real implementation this would search the book content
|
||||
const results = [];
|
||||
for (let i = 1; i <= book.totalPages; i++) {
|
||||
// Simulate finding search term on random pages
|
||||
if (Math.random() > 0.7) {
|
||||
results.push({ page: i, index: Math.floor(Math.random() * 10) });
|
||||
}
|
||||
}
|
||||
setSearchResults(results);
|
||||
setCurrentSearchIndex(0);
|
||||
}, [searchQuery]);
|
||||
|
||||
const handlePageChange = (direction: 'next' | 'prev') => {
|
||||
if (direction === 'next' && currentPage < book.totalPages) {
|
||||
setCurrentPage(currentPage + 1);
|
||||
} else if (direction === 'prev' && currentPage > 1) {
|
||||
setCurrentPage(currentPage - 1);
|
||||
}
|
||||
};
|
||||
|
||||
const handleGoToPage = (page: number) => {
|
||||
if (page >= 1 && page <= book.totalPages) {
|
||||
setCurrentPage(page);
|
||||
}
|
||||
};
|
||||
|
||||
const handleBookmarkToggle = (page: number) => {
|
||||
// In real implementation, this would update book bookmarks
|
||||
console.log(`Toggled bookmark for page ${page}`);
|
||||
};
|
||||
|
||||
const handleAddAnnotation = () => {
|
||||
if (newAnnotation.trim() !== '') {
|
||||
// In real implementation, this would add annotation to book
|
||||
const annotation: Annotation = {
|
||||
id: `ann_${Date.now()}`,
|
||||
page: currentPage,
|
||||
text: newAnnotation,
|
||||
highlight: 'selected_text_placeholder',
|
||||
timestamp: new Date()
|
||||
};
|
||||
|
||||
console.log('Added annotation:', annotation);
|
||||
setNewAnnotation('');
|
||||
setIsAddingAnnotation(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSearchNavigation = (direction: 'next' | 'prev') => {
|
||||
if (searchResults.length === 0) return;
|
||||
|
||||
if (direction === 'next') {
|
||||
setCurrentSearchIndex((prev) => (prev + 1) % searchResults.length);
|
||||
} else {
|
||||
setCurrentSearchIndex((prev) => (prev - 1 + searchResults.length) % searchResults.length);
|
||||
}
|
||||
|
||||
// Navigate to the page with the search result
|
||||
const targetPage = searchResults[currentSearchIndex].page;
|
||||
setCurrentPage(targetPage);
|
||||
};
|
||||
|
||||
// Theme classes
|
||||
const themeClasses = {
|
||||
light: 'reader-light-theme',
|
||||
dark: 'reader-dark-theme',
|
||||
sepia: 'reader-sepia-theme'
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`immersive-reader ${themeClasses[theme]}`}>
|
||||
{/* Top Controls */}
|
||||
<div className="reader-controls top-controls">
|
||||
<div className="reader-header">
|
||||
<h2>{book.title}</h2>
|
||||
<p>by {book.author}</p>
|
||||
</div>
|
||||
|
||||
<div className="reader-progress">
|
||||
<div className="progress-bar">
|
||||
<div
|
||||
className="progress-fill"
|
||||
style={{ width: `${(currentPage / book.totalPages) * 100}%` }}
|
||||
></div>
|
||||
</div>
|
||||
<span className="progress-text">{currentPage} / {book.totalPages}</span>
|
||||
</div>
|
||||
|
||||
<VSCodeButton appearance="icon" onClick={onClose} className="close-button">
|
||||
<span aria-label="Close reader">×</span>
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
|
||||
{/* Middle Content Area */}
|
||||
<div className="reader-content-area">
|
||||
{/* Sidebar */}
|
||||
<div className="reader-sidebar">
|
||||
<div className="sidebar-section">
|
||||
<h3>Navigation</h3>
|
||||
<div className="page-input">
|
||||
<VSCodeTextField
|
||||
value={currentPage.toString()}
|
||||
onChange={(e) => handleGoToPage(parseInt(e.target.value) || currentPage)}
|
||||
/>
|
||||
<span>of {book.totalPages}</span>
|
||||
</div>
|
||||
|
||||
<div className="nav-buttons">
|
||||
<VSCodeButton onClick={() => handlePageChange('prev')} disabled={currentPage <= 1}>
|
||||
Previous
|
||||
</VSCodeButton>
|
||||
<VSCodeButton onClick={() => handlePageChange('next')} disabled={currentPage >= book.totalPages}>
|
||||
Next
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sidebar-section">
|
||||
<h3>Search</h3>
|
||||
<div className="search-controls">
|
||||
<VSCodeTextField
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
placeholder="Search in book..."
|
||||
/>
|
||||
{searchResults.length > 0 && (
|
||||
<div className="search-navigation">
|
||||
<VSCodeButton
|
||||
onClick={() => handleSearchNavigation('prev')}
|
||||
disabled={searchResults.length === 0}
|
||||
>
|
||||
←
|
||||
</VSCodeButton>
|
||||
<span>{currentSearchIndex + 1} of {searchResults.length}</span>
|
||||
<VSCodeButton
|
||||
onClick={() => handleSearchNavigation('next')}
|
||||
disabled={searchResults.length === 0}
|
||||
>
|
||||
→
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sidebar-section">
|
||||
<h3>Settings</h3>
|
||||
<div className="settings-controls">
|
||||
<div className="setting-row">
|
||||
<label>Font Size:</label>
|
||||
<input
|
||||
type="range"
|
||||
min="12"
|
||||
max="24"
|
||||
value={fontSize}
|
||||
onChange={(e) => setFontSize(parseInt(e.target.value))}
|
||||
/>
|
||||
<span>{fontSize}px</span>
|
||||
</div>
|
||||
|
||||
<div className="setting-row">
|
||||
<label>Line Height:</label>
|
||||
<select value={lineHeight} onChange={(e) => setLineHeight(parseFloat(e.target.value))}>
|
||||
<option value={1.2}>Compact</option>
|
||||
<option value={1.5}>Normal</option>
|
||||
<option value={1.8}>Spacious</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="setting-row">
|
||||
<label>Theme:</label>
|
||||
<select value={theme} onChange={(e) => setTheme(e.target.value as any)}>
|
||||
<option value="light">Light</option>
|
||||
<option value="dark">Dark</option>
|
||||
<option value="sepia">Sepia</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div className="setting-row">
|
||||
<label>Mode:</label>
|
||||
<select value={readingMode} onChange={(e) => setReadingMode(e.target.value as any)}>
|
||||
<option value="continuous">Continuous</option>
|
||||
<option value="paged">Paged</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<VSCodeCheckbox
|
||||
checked={showBookmarks}
|
||||
onChange={(e) => setShowBookmarks(e.target.checked)}
|
||||
>
|
||||
Show Bookmarks
|
||||
</VSCodeCheckbox>
|
||||
|
||||
<VSCodeCheckbox
|
||||
checked={showAnnotations}
|
||||
onChange={(e) => setShowAnnotations(e.target.checked)}
|
||||
>
|
||||
Show Annotations
|
||||
</VSCodeCheckbox>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="sidebar-section">
|
||||
<h3>Bookmarks</h3>
|
||||
<div className="bookmarks-list">
|
||||
{book.bookmarks.map(page => (
|
||||
<div
|
||||
key={page}
|
||||
className={`bookmark-item ${page === currentPage ? 'active' : ''}`}
|
||||
onClick={() => handleGoToPage(page)}
|
||||
>
|
||||
Page {page}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Main Content */}
|
||||
<div className="reader-main-content">
|
||||
<div
|
||||
ref={contentRef}
|
||||
className="content-display"
|
||||
style={{
|
||||
fontSize: `${fontSize}px`,
|
||||
lineHeight: lineHeight,
|
||||
}}
|
||||
>
|
||||
{/* Mock content - in real implementation this would render the actual book content */}
|
||||
<div className="page-content">
|
||||
<h1>Chapter {currentPage}: Sample Chapter Title</h1>
|
||||
|
||||
<p>
|
||||
This is a sample paragraph demonstrating the immersive reading experience.
|
||||
The text would flow naturally with proper typography and spacing based on
|
||||
the selected settings. In a real implementation, this would render actual
|
||||
book content from the selected file format (PDF, EPUB, or text).
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam auctor,
|
||||
nisl eget ultricies tincidunt, nisl nisl aliquam nisl, eget ultricies
|
||||
nisl nisl eget nisl. Nullam auctor, nisl eget ultricies tincidunt,
|
||||
nisl nisl aliquam nisl, eget ultricies nisl nisl eget nisl.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
|
||||
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
|
||||
nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in
|
||||
reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui
|
||||
officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde
|
||||
omnis iste natus error sit voluptatem accusantium doloremque laudantium,
|
||||
totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi
|
||||
architecto beatae vitae dicta sunt explicabo.
|
||||
</p>
|
||||
|
||||
{showAnnotations && (
|
||||
<div className="annotations-section">
|
||||
<h4>Annotations on this page</h4>
|
||||
{book.annotations
|
||||
.filter(ann => ann.page === currentPage)
|
||||
.map(ann => (
|
||||
<div key={ann.id} className="annotation-item">
|
||||
<p>{ann.text}</p>
|
||||
<small>{ann.timestamp.toLocaleString()}</small>
|
||||
</div>
|
||||
))}
|
||||
|
||||
{isAddingAnnotation ? (
|
||||
<div className="add-annotation-form">
|
||||
<textarea
|
||||
value={newAnnotation}
|
||||
onChange={(e) => setNewAnnotation(e.target.value)}
|
||||
placeholder="Add your annotation here..."
|
||||
rows={3}
|
||||
/>
|
||||
<div className="annotation-actions">
|
||||
<VSCodeButton onClick={handleAddAnnotation}>Save</VSCodeButton>
|
||||
<VSCodeButton appearance="secondary" onClick={() => setIsAddingAnnotation(false)}>
|
||||
Cancel
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<VSCodeButton onClick={() => setIsAddingAnnotation(true)}>
|
||||
Add Annotation
|
||||
</VSCodeButton>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Bottom Controls */}
|
||||
<div className="reader-controls bottom-controls">
|
||||
<VSCodeButton
|
||||
onClick={() => handleBookmarkToggle(currentPage)}
|
||||
appearance={book.bookmarks.includes(currentPage) ? 'primary' : 'secondary'}
|
||||
>
|
||||
{book.bookmarks.includes(currentPage) ? '★ Bookmarked' : '☆ Bookmark Page'}
|
||||
</VSCodeButton>
|
||||
|
||||
<div className="reading-stats">
|
||||
<span>Progress: {Math.round((currentPage / book.totalPages) * 100)}%</span>
|
||||
<span>Estimated time: {(book.totalPages - currentPage) * 2} min left</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImmersiveReader;
|
||||
351
dirac/webview-ui/src/components/SettingsTab.tsx
Normal file
351
dirac/webview-ui/src/components/SettingsTab.tsx
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
import React, { useState, useEffect } from 'react';
|
||||
import { VSCodeButton, VSCodeDropdown, VSCodeOption, VSCodeTextField, VSCodeCheckbox } from '@vscode/webview-ui-toolkit/react';
|
||||
|
||||
interface AurelioSettings {
|
||||
paradigm: 'standard' | 'toon' | 'tilth-dirac';
|
||||
tokenCharacterization: boolean;
|
||||
agentInteroperability: {
|
||||
cline: boolean;
|
||||
rooCode: boolean;
|
||||
claudeCode: boolean;
|
||||
copilot: boolean;
|
||||
openCode: boolean;
|
||||
};
|
||||
brainSync: {
|
||||
proxmoxEndpoint: string;
|
||||
autoSync: boolean;
|
||||
};
|
||||
astParserLanguages: string[];
|
||||
tokenMetricsDisplay: boolean;
|
||||
advanced: {
|
||||
resetDefaults: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
const SettingsTab: React.FC = () => {
|
||||
const [settings, setSettings] = useState<AurelioSettings>({
|
||||
paradigm: 'standard',
|
||||
tokenCharacterization: true,
|
||||
agentInteroperability: {
|
||||
cline: true,
|
||||
rooCode: true,
|
||||
claudeCode: true,
|
||||
copilot: true,
|
||||
openCode: true
|
||||
},
|
||||
brainSync: {
|
||||
proxmoxEndpoint: 'https://mcp.portugalfuturista.org',
|
||||
autoSync: true
|
||||
},
|
||||
astParserLanguages: ['typescript', 'python', 'rust', 'go', 'java', 'c', 'cpp'],
|
||||
tokenMetricsDisplay: true,
|
||||
advanced: {
|
||||
resetDefaults: false
|
||||
}
|
||||
});
|
||||
|
||||
const [tokenMetrics, setTokenMetrics] = useState({
|
||||
currentTokens: 0,
|
||||
savedTokens: 0,
|
||||
efficiencyPercent: 0
|
||||
});
|
||||
|
||||
// Load settings from VS Code extension
|
||||
useEffect(() => {
|
||||
// In a real implementation, this would load from VS Code
|
||||
// const loadedSettings = loadSettingsFromVscode();
|
||||
// setSettings(loadedSettings);
|
||||
|
||||
// Simulate loading token metrics
|
||||
setTokenMetrics({
|
||||
currentTokens: 12450,
|
||||
savedTokens: 3420,
|
||||
efficiencyPercent: 21.6
|
||||
});
|
||||
}, []);
|
||||
|
||||
const handleSettingChange = (field: keyof AurelioSettings, value: any) => {
|
||||
setSettings(prev => ({
|
||||
...prev,
|
||||
[field]: value
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSubSettingChange = (parentField: keyof AurelioSettings, subField: string, value: any) => {
|
||||
setSettings(prev => ({
|
||||
...prev,
|
||||
[parentField]: {
|
||||
...(prev[parentField] as any),
|
||||
[subField]: value
|
||||
}
|
||||
}));
|
||||
};
|
||||
|
||||
const handleSaveSettings = () => {
|
||||
// In a real implementation, this would send to VS Code extension
|
||||
console.log('Saving settings:', settings);
|
||||
window.vscode.postMessage({
|
||||
command: 'saveAurelioSettings',
|
||||
settings: settings
|
||||
});
|
||||
};
|
||||
|
||||
const handleSyncBrain = () => {
|
||||
window.vscode.postMessage({
|
||||
command: 'syncBrainNow'
|
||||
});
|
||||
};
|
||||
|
||||
const handleResetToDefaults = () => {
|
||||
if (confirm('Are you sure you want to reset all settings to defaults?')) {
|
||||
setSettings({
|
||||
paradigm: 'standard',
|
||||
tokenCharacterization: true,
|
||||
agentInteroperability: {
|
||||
cline: true,
|
||||
rooCode: true,
|
||||
claudeCode: true,
|
||||
copilot: true,
|
||||
openCode: true
|
||||
},
|
||||
brainSync: {
|
||||
proxmoxEndpoint: 'https://mcp.portugalfuturista.org',
|
||||
autoSync: true
|
||||
},
|
||||
astParserLanguages: ['typescript', 'python', 'rust', 'go', 'java', 'c', 'cpp'],
|
||||
tokenMetricsDisplay: true,
|
||||
advanced: {
|
||||
resetDefaults: false
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const languageOptions = [
|
||||
'typescript', 'javascript', 'python', 'rust', 'go', 'java',
|
||||
'c', 'cpp', 'ruby', 'php', 'csharp', 'swift', 'kotlin'
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="settings-tab">
|
||||
<h2>Aurelio Compiler Paradigm Settings</h2>
|
||||
|
||||
{/* Paradigm Selection */}
|
||||
<div className="setting-group">
|
||||
<h3>Compiler Paradigm</h3>
|
||||
<div className="setting-row">
|
||||
<label htmlFor="paradigm">Paradigm:</label>
|
||||
<VSCodeDropdown
|
||||
id="paradigm"
|
||||
value={settings.paradigm}
|
||||
onChange={(e) => handleSettingChange('paradigm', e.target.value as any)}
|
||||
>
|
||||
<VSCodeOption value="standard">Standard</VSCodeOption>
|
||||
<VSCodeOption value="toon">TOON (Token-Optimized Object Notation)</VSCodeOption>
|
||||
<VSCodeOption value="tilth-dirac">Tilth-Dirac (AST + Hash-Anchored)</VSCodeOption>
|
||||
</VSCodeDropdown>
|
||||
</div>
|
||||
<p className="setting-description">
|
||||
Select the compiler paradigm for optimizing AI agent interactions:
|
||||
<ul>
|
||||
<li><strong>Standard</strong>: Traditional JSON-based communication</li>
|
||||
<li><strong>TOON</strong>: Table-like notation for arrays of objects (saves tokens)</li>
|
||||
<li><strong>Tilth-Dirac</strong>: AST-aware file operations with hash-anchored editing</li>
|
||||
</ul>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Token Characterization */}
|
||||
<div className="setting-group">
|
||||
<h3>Token Characterization</h3>
|
||||
<div className="setting-row">
|
||||
<VSCodeCheckbox
|
||||
checked={settings.tokenCharacterization}
|
||||
onChange={(e) => handleSettingChange('tokenCharacterization', e.target.checked)}
|
||||
>
|
||||
Enable token characterization display
|
||||
</VSCodeCheckbox>
|
||||
</div>
|
||||
<p className="setting-description">
|
||||
Show live token usage metrics and efficiency indicators
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Agent Interoperability */}
|
||||
<div className="setting-group">
|
||||
<h3>Agent Interoperability</h3>
|
||||
<div className="setting-row">
|
||||
<VSCodeCheckbox
|
||||
checked={settings.agentInteroperability.cline}
|
||||
onChange={(e) => handleSubSettingChange('agentInteroperability', 'cline', e.target.checked)}
|
||||
>
|
||||
Cline
|
||||
</VSCodeCheckbox>
|
||||
<VSCodeCheckbox
|
||||
checked={settings.agentInteroperability.rooCode}
|
||||
onChange={(e) => handleSubSettingChange('agentInteroperability', 'rooCode', e.target.checked)}
|
||||
>
|
||||
Roo Code
|
||||
</VSCodeCheckbox>
|
||||
<VSCodeCheckbox
|
||||
checked={settings.agentInteroperability.claudeCode}
|
||||
onChange={(e) => handleSubSettingChange('agentInteroperability', 'claudeCode', e.target.checked)}
|
||||
>
|
||||
Claude Code
|
||||
</VSCodeCheckbox>
|
||||
<VSCodeCheckbox
|
||||
checked={settings.agentInteroperability.copilot}
|
||||
onChange={(e) => handleSubSettingChange('agentInteroperability', 'copilot', e.target.checked)}
|
||||
>
|
||||
GitHub Copilot
|
||||
</VSCodeCheckbox>
|
||||
<VSCodeCheckbox
|
||||
checked={settings.agentInteroperability.openCode}
|
||||
onChange={(e) => handleSubSettingChange('agentInteroperability', 'openCode', e.target.checked)}
|
||||
>
|
||||
OpenCode
|
||||
</VSCodeCheckbox>
|
||||
</div>
|
||||
<p className="setting-description">
|
||||
Enable compatibility with different AI coding assistants
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* AST Parser Languages */}
|
||||
<div className="setting-group">
|
||||
<h3>AST Parser Languages</h3>
|
||||
<div className="setting-row">
|
||||
{languageOptions.map(lang => (
|
||||
<VSCodeCheckbox
|
||||
key={lang}
|
||||
checked={settings.astParserLanguages.includes(lang)}
|
||||
onChange={(e) => {
|
||||
if (e.target.checked) {
|
||||
handleSettingChange('astParserLanguages', [...settings.astParserLanguages, lang]);
|
||||
} else {
|
||||
handleSettingChange('astParserLanguages',
|
||||
settings.astParserLanguages.filter(l => l !== lang)
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{lang.charAt(0).toUpperCase() + lang.slice(1)}
|
||||
</VSCodeCheckbox>
|
||||
))}
|
||||
</div>
|
||||
<p className="setting-description">
|
||||
Languages supported by the Aurelio AST parser for smart file reading and searching
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Brain Sync Settings */}
|
||||
<div className="setting-group">
|
||||
<h3>Brain Sync Settings</h3>
|
||||
<div className="setting-row">
|
||||
<label htmlFor="proxmox-endpoint">Proxmox Endpoint:</label>
|
||||
<VSCodeTextField
|
||||
id="proxmox-endpoint"
|
||||
value={settings.brainSync.proxmoxEndpoint}
|
||||
onChange={(e) => handleSubSettingChange('brainSync', 'proxmoxEndpoint', e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className="setting-row">
|
||||
<VSCodeCheckbox
|
||||
checked={settings.brainSync.autoSync}
|
||||
onChange={(e) => handleSubSettingChange('brainSync', 'autoSync', e.target.checked)}
|
||||
>
|
||||
Enable auto-sync
|
||||
</VSCodeCheckbox>
|
||||
</div>
|
||||
<div className="setting-row">
|
||||
<VSCodeButton appearance="secondary" onClick={handleSyncBrain}>
|
||||
Sync Brain Now
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
<p className="setting-description">
|
||||
Configure synchronization with the central brain on Proxmox
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Token Metrics Display */}
|
||||
<div className="setting-group">
|
||||
<h3>Token Metrics Display</h3>
|
||||
<div className="setting-row">
|
||||
<VSCodeCheckbox
|
||||
checked={settings.tokenMetricsDisplay}
|
||||
onChange={(e) => handleSettingChange('tokenMetricsDisplay', e.target.checked)}
|
||||
>
|
||||
Show token metrics panel
|
||||
</VSCodeCheckbox>
|
||||
</div>
|
||||
<div className="token-metrics-panel">
|
||||
<h4>Current Session Metrics</h4>
|
||||
<div className="metric-row">
|
||||
<span>Current Tokens Used:</span>
|
||||
<span>{tokenMetrics.currentTokens.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="metric-row">
|
||||
<span>Tokens Saved (TOON/Tilth):</span>
|
||||
<span>{tokenMetrics.savedTokens.toLocaleString()}</span>
|
||||
</div>
|
||||
<div className="metric-row">
|
||||
<span>Efficiency Improvement:</span>
|
||||
<span>{tokenMetrics.efficiencyPercent}%</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="setting-description">
|
||||
Display live token usage and efficiency metrics in the UI
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Advanced Settings */}
|
||||
<div className="setting-group">
|
||||
<h3>Advanced Settings</h3>
|
||||
<div className="setting-row">
|
||||
<VSCodeButton appearance="secondary" onClick={handleResetToDefaults}>
|
||||
Reset to Defaults
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
<p className="setting-description">
|
||||
Reset all settings to their default values
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Save Button */}
|
||||
<div className="setting-group">
|
||||
<VSCodeButton appearance="primary" onClick={handleSaveSettings}>
|
||||
Save Settings
|
||||
</VSCodeButton>
|
||||
</div>
|
||||
|
||||
{/* Imported Techniques Dashboard */}
|
||||
<div className="setting-group">
|
||||
<h3>Imported Techniques Provenance</h3>
|
||||
<div className="techniques-dashboard">
|
||||
<div className="technique-card">
|
||||
<h4>TOON (Token-Optimized Object Notation)</h4>
|
||||
<p>Reduces token usage by converting arrays of uniform objects to table-like format</p>
|
||||
<p className="stat">Avg. Savings: 30-60%</p>
|
||||
</div>
|
||||
<div className="technique-card">
|
||||
<h4>Tilth AST-Aware Operations</h4>
|
||||
<p>Uses tree-sitter to provide structural outlines for large files instead of full content</p>
|
||||
<p className="stat">Avg. Savings: 50-80% for large files</p>
|
||||
</div>
|
||||
<div className="technique-card">
|
||||
<h4>Dirac Hash-Anchored Editing</h4>
|
||||
<p>Prevents edit drift by validating content hashes before applying changes</p>
|
||||
<p className="stat">Precision: Sub-line accuracy</p>
|
||||
</div>
|
||||
<div className="technique-card">
|
||||
<h4>Session Deduplication</h4>
|
||||
<p>Prevents showing the same code definitions multiple times in a session</p>
|
||||
<p className="stat">Reduces redundancy by 40-70%</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SettingsTab;
|
||||
Loading…
Reference in a new issue