merge with upstream
This commit is contained in:
@@ -35,6 +35,8 @@ export function getAPIKey(cloudflareEnv: Env, provider: string, userApiKeys?: Re
|
||||
return env.MISTRAL_API_KEY || cloudflareEnv.MISTRAL_API_KEY;
|
||||
case 'OpenAILike':
|
||||
return env.OPENAI_LIKE_API_KEY || cloudflareEnv.OPENAI_LIKE_API_KEY;
|
||||
case 'Together':
|
||||
return env.TOGETHER_API_KEY || cloudflareEnv.TOGETHER_API_KEY;
|
||||
case 'xAI':
|
||||
return env.XAI_API_KEY || cloudflareEnv.XAI_API_KEY;
|
||||
case 'Cohere':
|
||||
@@ -48,6 +50,8 @@ export function getAPIKey(cloudflareEnv: Env, provider: string, userApiKeys?: Re
|
||||
|
||||
export function getBaseURL(cloudflareEnv: Env, provider: string) {
|
||||
switch (provider) {
|
||||
case 'Together':
|
||||
return env.TOGETHER_API_BASE_URL || cloudflareEnv.TOGETHER_API_BASE_URL;
|
||||
case 'OpenAILike':
|
||||
return env.OPENAI_LIKE_API_BASE_URL || cloudflareEnv.OPENAI_LIKE_API_BASE_URL;
|
||||
case 'LMStudio':
|
||||
|
||||
@@ -151,6 +151,8 @@ export function getModel(provider: string, model: string, env: Env, apiKeys?: Re
|
||||
return getGoogleModel(apiKey, model);
|
||||
case 'OpenAILike':
|
||||
return getOpenAILikeModel(baseURL, apiKey, model);
|
||||
case 'Together':
|
||||
return getOpenAILikeModel(baseURL, apiKey, model);
|
||||
case 'Deepseek':
|
||||
return getDeepseekModel(apiKey, model);
|
||||
case 'Mistral':
|
||||
|
||||
52
app/lib/hooks/useSearchFilter.ts
Normal file
52
app/lib/hooks/useSearchFilter.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { useState, useMemo, useCallback } from 'react';
|
||||
import { debounce } from '~/utils/debounce';
|
||||
import type { ChatHistoryItem } from '~/lib/persistence';
|
||||
|
||||
interface UseSearchFilterOptions {
|
||||
items: ChatHistoryItem[];
|
||||
searchFields?: (keyof ChatHistoryItem)[];
|
||||
debounceMs?: number;
|
||||
}
|
||||
|
||||
export function useSearchFilter({
|
||||
items = [],
|
||||
searchFields = ['description'],
|
||||
debounceMs = 300,
|
||||
}: UseSearchFilterOptions) {
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
|
||||
const debouncedSetSearch = useCallback(debounce(setSearchQuery, debounceMs), []);
|
||||
|
||||
const handleSearchChange = useCallback(
|
||||
(event: React.ChangeEvent<HTMLInputElement>) => {
|
||||
debouncedSetSearch(event.target.value);
|
||||
},
|
||||
[debouncedSetSearch],
|
||||
);
|
||||
|
||||
const filteredItems = useMemo(() => {
|
||||
if (!searchQuery.trim()) {
|
||||
return items;
|
||||
}
|
||||
|
||||
const query = searchQuery.toLowerCase();
|
||||
|
||||
return items.filter((item) =>
|
||||
searchFields.some((field) => {
|
||||
const value = item[field];
|
||||
|
||||
if (typeof value === 'string') {
|
||||
return value.toLowerCase().includes(query);
|
||||
}
|
||||
|
||||
return false;
|
||||
}),
|
||||
);
|
||||
}, [items, searchQuery, searchFields]);
|
||||
|
||||
return {
|
||||
searchQuery,
|
||||
filteredItems,
|
||||
handleSearchChange,
|
||||
};
|
||||
}
|
||||
@@ -6,6 +6,11 @@ const logger = createScopedLogger('ChatHistory');
|
||||
|
||||
// this is used at the top level and never rejects
|
||||
export async function openDatabase(): Promise<IDBDatabase | undefined> {
|
||||
if (typeof indexedDB === 'undefined') {
|
||||
console.error('indexedDB is not available in this environment.');
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const request = indexedDB.open('boltHistory', 1);
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ export function useChatHistory() {
|
||||
setReady(true);
|
||||
|
||||
if (persistenceEnabled) {
|
||||
toast.error(`Chat persistence is unavailable`);
|
||||
toast.error('Chat persistence is unavailable');
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -63,7 +63,7 @@ export function useChatHistory() {
|
||||
description.set(storedMessages.description);
|
||||
chatId.set(storedMessages.id);
|
||||
} else {
|
||||
navigate(`/`, { replace: true });
|
||||
navigate('/', { replace: true });
|
||||
}
|
||||
|
||||
setReady(true);
|
||||
|
||||
@@ -84,18 +84,18 @@ export class ActionRunner {
|
||||
}
|
||||
|
||||
if (action.executed) {
|
||||
return;
|
||||
return; // No return value here
|
||||
}
|
||||
|
||||
if (isStreaming && action.type !== 'file') {
|
||||
return;
|
||||
return; // No return value here
|
||||
}
|
||||
|
||||
this.#updateAction(actionId, { ...action, ...data.action, executed: !isStreaming });
|
||||
|
||||
this.#currentExecutionPromise = this.#currentExecutionPromise
|
||||
.then(() => {
|
||||
this.#executeAction(actionId, isStreaming);
|
||||
return this.#executeAction(actionId, isStreaming);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Action failed:', error);
|
||||
|
||||
@@ -14,6 +14,7 @@ import { saveAs } from 'file-saver';
|
||||
import { Octokit, type RestEndpointMethodTypes } from '@octokit/rest';
|
||||
import * as nodePath from 'node:path';
|
||||
import { extractRelativePath } from '~/utils/diff';
|
||||
import { description } from '~/lib/persistence';
|
||||
|
||||
export interface ArtifactState {
|
||||
id: string;
|
||||
@@ -328,6 +329,13 @@ export class WorkbenchStore {
|
||||
const zip = new JSZip();
|
||||
const files = this.files.get();
|
||||
|
||||
// Get the project name from the description input, or use a default name
|
||||
const projectName = (description.value ?? 'project').toLocaleLowerCase().split(' ').join('_');
|
||||
|
||||
// Generate a simple 6-character hash based on the current timestamp
|
||||
const timestampHash = Date.now().toString(36).slice(-6);
|
||||
const uniqueProjectName = `${projectName}_${timestampHash}`;
|
||||
|
||||
for (const [filePath, dirent] of Object.entries(files)) {
|
||||
if (dirent?.type === 'file' && !dirent.isBinary) {
|
||||
const relativePath = extractRelativePath(filePath);
|
||||
@@ -350,8 +358,9 @@ export class WorkbenchStore {
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the zip file and save it
|
||||
const content = await zip.generateAsync({ type: 'blob' });
|
||||
saveAs(content, 'project.zip');
|
||||
saveAs(content, `${uniqueProjectName}.zip`);
|
||||
}
|
||||
|
||||
async syncFiles(targetHandle: FileSystemDirectoryHandle) {
|
||||
@@ -369,7 +378,9 @@ export class WorkbenchStore {
|
||||
}
|
||||
|
||||
// create or get the file
|
||||
const fileHandle = await currentHandle.getFileHandle(pathSegments[pathSegments.length - 1], { create: true });
|
||||
const fileHandle = await currentHandle.getFileHandle(pathSegments[pathSegments.length - 1], {
|
||||
create: true,
|
||||
});
|
||||
|
||||
// write the file content
|
||||
const writable = await fileHandle.createWritable();
|
||||
|
||||
Reference in New Issue
Block a user