mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-25 09:47:37 +00:00
Merge pull request #14 from vgcman16/codex/fix-errors-or-bugs
Fix lint errors and type issues
This commit is contained in:
commit
407f60d74c
@ -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]);
|
||||||
|
@ -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>
|
||||||
|
@ -47,6 +47,7 @@ export function usePromptEnhancer() {
|
|||||||
const decoder = new TextDecoder();
|
const decoder = new TextDecoder();
|
||||||
|
|
||||||
_input = '';
|
_input = '';
|
||||||
|
|
||||||
let _error;
|
let _error;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -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) : [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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) {
|
||||||
|
@ -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');
|
||||||
|
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user