fix: address lint issues and type errors

This commit is contained in:
vgcman16 2025-06-05 19:07:41 -05:00
parent 4350bf66c3
commit f23482db2f
9 changed files with 52 additions and 31 deletions

View File

@ -288,8 +288,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
const handleFileUpload = () => { const handleFileUpload = () => {
const input = document.createElement('input'); const input = document.createElement('input');
input.type = 'file'; input.type = 'file';
input.accept = input.accept = 'image/*,.pdf,.docx,.txt,.md,.js,.ts,.tsx,.html,.css,.json';
'image/*,.pdf,.docx,.txt,.md,.js,.ts,.tsx,.html,.css,.json';
input.onchange = async (e) => { input.onchange = async (e) => {
const file = (e.target as HTMLInputElement).files?.[0]; const file = (e.target as HTMLInputElement).files?.[0];
@ -297,6 +296,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
if (file) { if (file) {
if (file.type.startsWith('image/')) { if (file.type.startsWith('image/')) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (ev) => { reader.onload = (ev) => {
const base64Image = ev.target?.result as string; const base64Image = ev.target?.result as string;
setUploadedFiles?.([...uploadedFiles, file]); setUploadedFiles?.([...uploadedFiles, file]);
@ -326,11 +326,16 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
for (const item of items) { for (const item of items) {
if (item.kind === 'file') { if (item.kind === 'file') {
const file = item.getAsFile(); const file = item.getAsFile();
if (!file) continue;
if (!file) {
continue;
}
e.preventDefault(); e.preventDefault();
if (file.type.startsWith('image/')) { if (file.type.startsWith('image/')) {
const reader = new FileReader(); const reader = new FileReader();
reader.onload = (ev) => { reader.onload = (ev) => {
const base64Image = ev.target?.result as string; const base64Image = ev.target?.result as string;
setUploadedFiles?.([...uploadedFiles, file]); setUploadedFiles?.([...uploadedFiles, file]);

View File

@ -449,6 +449,7 @@ function FileContextMenu({
} }
const success = workbenchStore.targetFile(fullPath); const success = workbenchStore.targetFile(fullPath);
if (success) { if (success) {
toast.success(`File targeted`); toast.success(`File targeted`);
} }
@ -465,6 +466,7 @@ function FileContextMenu({
} }
const success = workbenchStore.unTargetFile(fullPath); const success = workbenchStore.unTargetFile(fullPath);
if (success) { if (success) {
toast.success(`File un-targeted`); toast.success(`File un-targeted`);
} }
@ -761,9 +763,7 @@ function File({
title={'File is locked'} title={'File is locked'}
/> />
)} )}
{isTargeted && ( {isTargeted && <span className="shrink-0 i-ph:crosshair text-green-500 scale-80" title="Targeted file" />}
<span className="shrink-0 i-ph:crosshair text-green-500 scale-80" title="Targeted file" />
)}
{unsavedChanges && <span className="i-ph:circle-fill scale-68 shrink-0 text-orange-500" />} {unsavedChanges && <span className="i-ph:circle-fill scale-68 shrink-0 text-orange-500" />}
</div> </div>
</div> </div>

View File

@ -47,6 +47,7 @@ export function usePromptEnhancer() {
const decoder = new TextDecoder(); const decoder = new TextDecoder();
_input = ''; _input = '';
let _error; let _error;
try { try {

View File

@ -12,6 +12,7 @@ function getChatSet(chatId: string, create = false): Set<string> | undefined {
if (create && !targetedFilesMap.has(chatId)) { if (create && !targetedFilesMap.has(chatId)) {
targetedFilesMap.set(chatId, new Set()); targetedFilesMap.set(chatId, new Set());
} }
return targetedFilesMap.get(chatId); return targetedFilesMap.get(chatId);
} }
@ -19,17 +20,22 @@ function initializeCache(): TargetedFile[] {
if (targetedFilesCache !== null) { if (targetedFilesCache !== null) {
return targetedFilesCache; return targetedFilesCache;
} }
try { try {
if (typeof localStorage !== 'undefined') { if (typeof localStorage !== 'undefined') {
const json = localStorage.getItem(TARGETED_FILES_KEY); const json = localStorage.getItem(TARGETED_FILES_KEY);
if (json) { if (json) {
const items = JSON.parse(json) as TargetedFile[]; const items = JSON.parse(json) as TargetedFile[];
targetedFilesCache = items; targetedFilesCache = items;
rebuildLookup(items); rebuildLookup(items);
return items; return items;
} }
} }
targetedFilesCache = []; targetedFilesCache = [];
return []; return [];
} catch { } catch {
targetedFilesCache = []; targetedFilesCache = [];
@ -39,12 +45,15 @@ function initializeCache(): TargetedFile[] {
function rebuildLookup(items: TargetedFile[]): void { function rebuildLookup(items: TargetedFile[]): void {
targetedFilesMap.clear(); targetedFilesMap.clear();
for (const item of items) { for (const item of items) {
let set = targetedFilesMap.get(item.chatId); let set = targetedFilesMap.get(item.chatId);
if (!set) { if (!set) {
set = new Set(); set = new Set();
targetedFilesMap.set(item.chatId, set); targetedFilesMap.set(item.chatId, set);
} }
set.add(item.path); set.add(item.path);
} }
} }
@ -52,6 +61,7 @@ function rebuildLookup(items: TargetedFile[]): void {
export function saveTargetedFiles(items: TargetedFile[]): void { export function saveTargetedFiles(items: TargetedFile[]): void {
targetedFilesCache = [...items]; targetedFilesCache = [...items];
rebuildLookup(items); rebuildLookup(items);
try { try {
if (typeof localStorage !== 'undefined') { if (typeof localStorage !== 'undefined') {
localStorage.setItem(TARGETED_FILES_KEY, JSON.stringify(items)); localStorage.setItem(TARGETED_FILES_KEY, JSON.stringify(items));
@ -67,6 +77,7 @@ export function addTargetedFile(chatId: string, path: string): void {
const files = getTargetedFiles(); const files = getTargetedFiles();
const set = getChatSet(chatId, true)!; const set = getChatSet(chatId, true)!;
set.add(path); set.add(path);
const filtered = files.filter((f) => !(f.chatId === chatId && f.path === path)); const filtered = files.filter((f) => !(f.chatId === chatId && f.path === path));
filtered.push({ chatId, path }); filtered.push({ chatId, path });
saveTargetedFiles(filtered); saveTargetedFiles(filtered);
@ -75,20 +86,28 @@ export function addTargetedFile(chatId: string, path: string): void {
export function removeTargetedFile(chatId: string, path: string): void { export function removeTargetedFile(chatId: string, path: string): void {
const files = getTargetedFiles(); const files = getTargetedFiles();
const set = getChatSet(chatId); const set = getChatSet(chatId);
if (set) set.delete(path);
if (set) {
set.delete(path);
}
const filtered = files.filter((f) => !(f.chatId === chatId && f.path === path)); const filtered = files.filter((f) => !(f.chatId === chatId && f.path === path));
saveTargetedFiles(filtered); saveTargetedFiles(filtered);
} }
export function isFileTargeted(chatId: string, path: string): boolean { export function isFileTargeted(chatId: string, path: string): boolean {
initializeCache(); initializeCache();
const set = getChatSet(chatId); const set = getChatSet(chatId);
return set ? set.has(path) : false; return set ? set.has(path) : false;
} }
export function getTargetedFilesForChat(chatId: string): string[] { export function getTargetedFilesForChat(chatId: string): string[] {
initializeCache(); initializeCache();
const set = getChatSet(chatId); const set = getChatSet(chatId);
return set ? Array.from(set) : []; return set ? Array.from(set) : [];
} }

View File

@ -20,11 +20,7 @@ import {
migrateLegacyLocks, migrateLegacyLocks,
clearCache, clearCache,
} from '~/lib/persistence/lockedFiles'; } from '~/lib/persistence/lockedFiles';
import { import { getTargetedFilesForChat, addTargetedFile, removeTargetedFile } from '~/lib/persistence/targetedFiles';
getTargetedFilesForChat,
addTargetedFile,
removeTargetedFile,
} from '~/lib/persistence/targetedFiles';
import { getCurrentChatId } from '~/utils/fileLocks'; import { getCurrentChatId } from '~/utils/fileLocks';
const logger = createScopedLogger('FilesStore'); const logger = createScopedLogger('FilesStore');
@ -101,6 +97,7 @@ export class FilesStore {
// Load locked files from localStorage // Load locked files from localStorage
this.#loadLockedFiles(); this.#loadLockedFiles();
// Load targeted files from localStorage // Load targeted files from localStorage
this.#loadTargetedFiles(); this.#loadTargetedFiles();
@ -377,6 +374,7 @@ export class FilesStore {
this.files.setKey(filePath, { ...file, isTargeted: true }); this.files.setKey(filePath, { ...file, isTargeted: true });
addTargetedFile(currentChatId, filePath); addTargetedFile(currentChatId, filePath);
return true; return true;
} }
@ -394,6 +392,7 @@ export class FilesStore {
this.files.setKey(filePath, { ...file, isTargeted: false }); this.files.setKey(filePath, { ...file, isTargeted: false });
removeTargetedFile(currentChatId, filePath); removeTargetedFile(currentChatId, filePath);
return true; return true;
} }

View File

@ -7,10 +7,7 @@ import { Header } from '~/components/header/Header';
import BackgroundRays from '~/components/ui/BackgroundRays'; import BackgroundRays from '~/components/ui/BackgroundRays';
export const meta: MetaFunction = () => { export const meta: MetaFunction = () => {
return [ return [{ title: 'Bolt' }, { name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' }];
{ title: 'Bolt' },
{ name: 'description', content: 'Talk with Bolt, an AI assistant from StackBlitz' },
];
}; };
export async function loader(args: LoaderFunctionArgs) { export async function loader(args: LoaderFunctionArgs) {

View File

@ -8,9 +8,11 @@ import { escapeBoltTags } from './projectCommands';
*/ */
function extractFileKey(url: string): string { function extractFileKey(url: string): string {
const match = url.match(/file\/(\w+)/); const match = url.match(/file\/(\w+)/);
if (!match) { if (!match) {
throw new Error('Invalid Figma URL'); throw new Error('Invalid Figma URL');
} }
return match[1]; return match[1];
} }
@ -26,8 +28,9 @@ function collectFrames(node: any, frames: any[]) {
if (node.type === 'FRAME') { if (node.type === 'FRAME') {
frames.push(node); frames.push(node);
} }
if (Array.isArray(node.children)) { if (Array.isArray(node.children)) {
node.children.forEach((child) => collectFrames(child, frames)); node.children.forEach((child: any) => collectFrames(child, frames));
} }
} }
@ -35,6 +38,7 @@ function figmaFramesToComponents(frames: any[]): GeneratedComponent[] {
return frames.map((frame) => { return frames.map((frame) => {
const name = frame.name.replace(/[^a-zA-Z0-9]/g, '') || 'Component'; const name = frame.name.replace(/[^a-zA-Z0-9]/g, '') || 'Component';
const code = `export function ${name}() {\n return <div>${frame.name}</div>;\n}`; const code = `export function ${name}() {\n return <div>${frame.name}</div>;\n}`;
return { name, code }; return { name, code };
}); });
} }
@ -42,10 +46,7 @@ function figmaFramesToComponents(frames: any[]): GeneratedComponent[] {
/** /**
* Create chat messages from a Figma design * Create chat messages from a Figma design
*/ */
export async function createChatFromFigma( export async function createChatFromFigma(figmaUrl: string, token: string): Promise<Message[]> {
figmaUrl: string,
token: string,
): Promise<Message[]> {
const fileKey = extractFileKey(figmaUrl); const fileKey = extractFileKey(figmaUrl);
const res = await fetch(`https://api.figma.com/v1/files/${fileKey}`, { const res = await fetch(`https://api.figma.com/v1/files/${fileKey}`, {
headers: { 'X-Figma-Token': token }, headers: { 'X-Figma-Token': token },
@ -55,14 +56,14 @@ export async function createChatFromFigma(
throw new Error(`Failed to fetch Figma file: ${res.status}`); throw new Error(`Failed to fetch Figma file: ${res.status}`);
} }
const data = await res.json(); const data = (await res.json()) as { document: unknown };
const frames: any[] = []; const frames: any[] = [];
collectFrames(data.document, frames); collectFrames((data as any).document, frames);
const components = figmaFramesToComponents(frames); const components = figmaFramesToComponents(frames);
const componentActions = components const componentActions = components
.map( .map(
(c) => (c) => `<boltAction type="file" filePath="app/components/${c.name}.tsx">${escapeBoltTags(c.code)}</boltAction>`,
`<boltAction type="file" filePath="app/components/${c.name}.tsx">${escapeBoltTags(c.code)}</boltAction>`,
) )
.join('\n'); .join('\n');

View File

@ -9,21 +9,23 @@ export async function extractTextFromFile(file: File): Promise<string> {
const arrayBuffer = await file.arrayBuffer(); const arrayBuffer = await file.arrayBuffer();
const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise; const pdf = await pdfjsLib.getDocument({ data: arrayBuffer }).promise;
const texts: string[] = []; const texts: string[] = [];
for (let i = 1; i <= pdf.numPages; i++) { for (let i = 1; i <= pdf.numPages; i++) {
const page = await pdf.getPage(i); const page = await pdf.getPage(i);
const content = await page.getTextContent(); const content = await page.getTextContent();
texts.push(content.items.map((item: any) => item.str).join(' ')); texts.push(content.items.map((item: any) => item.str).join(' '));
} }
return texts.join('\n'); return texts.join('\n');
} }
if ( if (
file.type === file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
file.name.toLowerCase().endsWith('.docx') file.name.toLowerCase().endsWith('.docx')
) { ) {
const arrayBuffer = await file.arrayBuffer(); const arrayBuffer = await file.arrayBuffer();
const result = await mammoth.extractRawText({ arrayBuffer }); const result = await mammoth.extractRawText({ arrayBuffer });
return result.value; return result.value;
} }

View File

@ -120,10 +120,7 @@ ${files[filePath].content}
`; `;
}; };
export const uploadedFilesToArtifacts = ( export const uploadedFilesToArtifacts = (files: { [path: string]: string }, id: string): string => {
files: { [path: string]: string },
id: string,
): string => {
return ` return `
<boltArtifact id="${id}" title="Uploaded Files"> <boltArtifact id="${id}" title="Uploaded Files">
${Object.keys(files) ${Object.keys(files)