Aided stab at basic tokenizer and api setup.

This commit is contained in:
2025-12-19 18:46:03 -07:00
parent 1e90cbbb39
commit 7686b1b9f0
9 changed files with 446 additions and 1 deletions

View File

@@ -0,0 +1,53 @@
import React, { useState, useEffect } from 'react';
import { TokenizedText, type TokenizedLine } from './TokenizedText';
interface TokenizedDocument {
language: string;
lines: TokenizedLine[];
metadata: {
totalLines: number;
fileSize: number;
language: string;
};
}
interface FileViewerProps {
filePath: string;
}
export const FileViewer: React.FC<FileViewerProps> = ({ filePath }) => {
const [document, setDocument] = useState<TokenizedDocument | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
const loadFile = async () => {
setLoading(true);
try {
const response = await fetch(`/api/file?path=${encodeURIComponent(filePath)}`);
const doc = await response.json();
setDocument(doc);
} catch (error) {
console.error('Failed to load file:', error);
} finally {
setLoading(false);
}
};
loadFile();
}, [filePath]);
if (loading) return <div>Loading...</div>;
if (!document) return <div>Failed to load file</div>;
return (
<div className="file-viewer">
<header className="file-header">
<h2>{filePath}</h2>
<span className="file-info">
{document.metadata.language} {document.metadata.totalLines} lines
</span>
</header>
<TokenizedText lines={document.lines} />
</div>
);
};

View File

@@ -0,0 +1,38 @@
import React from 'react';
interface Token {
type: string;
content: string;
start: number;
end: number;
}
export interface TokenizedLine {
lineNumber: number;
tokens: Token[];
raw: string;
}
interface TokenizedTextProps {
lines: TokenizedLine[];
showLineNumbers?: boolean;
}
export const TokenizedText: React.FC<TokenizedTextProps> = ({ lines, showLineNumbers = true }) => {
return (
<div className="tokenized-text">
{lines.map((line) => (
<div key={line.lineNumber} className="line">
{showLineNumbers && <span className="line-number">{line.lineNumber}</span>}
<span className="line-content">
{line.tokens.map((token, i) => (
<span key={i} className={`token token-${token.type}`}>
{token.content}
</span>
))}
</span>
</div>
))}
</div>
);
};

43
ui/src/styles/syntax.css Normal file
View File

@@ -0,0 +1,43 @@
.tokenized-text {
font-family: 'Monaco', 'Menlo', monospace;
font-size: 14px;
line-height: 1.5;
background: #1e1e1e;
color: #d4d4d4;
padding: 1rem;
}
.line {
display: flex;
min-height: 21px;
}
.line-number {
color: #858585;
margin-right: 1rem;
user-select: none;
min-width: 3ch;
text-align: right;
}
.line-content {
flex: 1;
}
/* Syntax highlighting tokens */
.token-keyword { color: #569cd6; font-weight: bold; }
.token-string { color: #ce9178; }
.token-comment { color: #6a9955; font-style: italic; }
.token-number { color: #b5cea8; }
.token-function { color: #dcdcaa; }
.token-type { color: #4ec9b0; }
.token-header { color: #569cd6; font-weight: bold; }
.token-bold { font-weight: bold; }
.token-italic { font-style: italic; }
.token-link { color: #9cdcfe; text-decoration: underline; }
.token-code-block {
background: #2d2d2d;
padding: 0.2em 0.4em;
border-radius: 3px;
}