mirror of
https://github.com/stackblitz-labs/bolt.diy
synced 2025-06-26 18:26:38 +00:00
Updates for permanent repository URLs (#106)
This commit is contained in:
parent
cb417d8384
commit
f5cd0fd9f1
@ -59,27 +59,30 @@ export const Menu = () => {
|
||||
.catch((error) => toast.error(error.message));
|
||||
}, []);
|
||||
|
||||
const deleteItem = useCallback((event: React.UIEvent, item: ChatContents) => {
|
||||
event.preventDefault();
|
||||
const deleteItem = useCallback(
|
||||
(event: React.UIEvent, item: ChatContents) => {
|
||||
event.preventDefault();
|
||||
|
||||
// Optimistically remove the item from the list while we update the database.
|
||||
setList(list.filter((chat) => chat.id !== item.id));
|
||||
// Optimistically remove the item from the list while we update the database.
|
||||
setList(list.filter((chat) => chat.id !== item.id));
|
||||
|
||||
database
|
||||
.deleteChat(item.id)
|
||||
.then(() => {
|
||||
loadEntries();
|
||||
database
|
||||
.deleteChat(item.id)
|
||||
.then(() => {
|
||||
loadEntries();
|
||||
|
||||
if (chatStore.currentChat.get()?.id === item.id) {
|
||||
// hard page navigation to clear the stores
|
||||
window.location.pathname = '/';
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error('Failed to delete conversation');
|
||||
logger.error(error);
|
||||
});
|
||||
}, [list]);
|
||||
if (chatStore.currentChat.get()?.id === item.id) {
|
||||
// hard page navigation to clear the stores
|
||||
window.location.pathname = '/';
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
toast.error('Failed to delete conversation');
|
||||
logger.error(error);
|
||||
});
|
||||
},
|
||||
[list],
|
||||
);
|
||||
|
||||
const closeDialog = () => {
|
||||
setDialogContent(null);
|
||||
|
@ -27,7 +27,6 @@ export const Preview = memo(() => {
|
||||
const [selectionPoint, setSelectionPoint] = useState<{ x: number; y: number } | null>(null);
|
||||
|
||||
const previewURL = useStore(workbenchStore.previewURL);
|
||||
const previewError = useStore(workbenchStore.previewError);
|
||||
|
||||
// Toggle between responsive mode and device mode
|
||||
const [isDeviceModeOn, setIsDeviceModeOn] = useState(false);
|
||||
@ -278,9 +277,7 @@ export const Preview = memo(() => {
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="flex w-full h-full justify-center items-center bg-white">
|
||||
{previewError ? 'Failed to load preview' : 'Preview loading...'}
|
||||
</div>
|
||||
<div className="flex w-full h-full justify-center items-center bg-white">Preview loading...</div>
|
||||
)}
|
||||
|
||||
{isDeviceModeOn && (
|
||||
|
@ -69,7 +69,7 @@ async function getAllChats(): Promise<ChatContents[]> {
|
||||
}
|
||||
|
||||
const chats = data.map(databaseRowToChatContents);
|
||||
return chats.filter(chat => !deletedChats.has(chat.id));
|
||||
return chats.filter((chat) => !deletedChats.has(chat.id));
|
||||
}
|
||||
|
||||
async function syncLocalChats(): Promise<void> {
|
||||
|
@ -1,86 +1,20 @@
|
||||
// Support using the Nut API for the development server.
|
||||
// Support managing state for the development server URL the preview is loading.
|
||||
|
||||
import { assert, ProtocolClient } from './ReplayProtocolClient';
|
||||
import { workbenchStore } from '~/lib/stores/workbench';
|
||||
import { recordingMessageHandlerScript } from './Recording';
|
||||
|
||||
class DevelopmentServerManager {
|
||||
// Empty if this chat has been destroyed.
|
||||
client: ProtocolClient | undefined;
|
||||
|
||||
// Resolves when the chat has started.
|
||||
chatIdPromise: Promise<string>;
|
||||
|
||||
constructor() {
|
||||
this.client = new ProtocolClient();
|
||||
|
||||
this.chatIdPromise = (async () => {
|
||||
assert(this.client, 'Chat has been destroyed');
|
||||
|
||||
await this.client.initialize();
|
||||
|
||||
const { chatId } = (await this.client.sendCommand({ method: 'Nut.startChat', params: {} })) as { chatId: string };
|
||||
|
||||
console.log('DevelopmentServerChat', new Date().toISOString(), chatId);
|
||||
|
||||
return chatId;
|
||||
})();
|
||||
function getRepositoryURL(repositoryId: string | undefined) {
|
||||
if (!repositoryId) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.client?.close();
|
||||
this.client = undefined;
|
||||
}
|
||||
|
||||
async setRepositoryContents(repositoryId: string): Promise<string | undefined> {
|
||||
assert(this.client, 'Chat has been destroyed');
|
||||
|
||||
try {
|
||||
const chatId = await this.chatIdPromise;
|
||||
const { url } = (await this.client.sendCommand({
|
||||
method: 'Nut.startDevelopmentServer',
|
||||
params: {
|
||||
chatId,
|
||||
repositoryId,
|
||||
injectedScript: recordingMessageHandlerScript,
|
||||
},
|
||||
})) as { url: string };
|
||||
|
||||
return url;
|
||||
} catch (e) {
|
||||
console.error('DevelopmentServerError', e);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
return `https://${repositoryId}.http.replay.io`;
|
||||
}
|
||||
|
||||
let gActiveDevelopmentServer: DevelopmentServerManager | undefined;
|
||||
|
||||
export async function updateDevelopmentServer(repositoryId: string | undefined) {
|
||||
console.log('UpdateDevelopmentServer', new Date().toISOString(), repositoryId);
|
||||
const repositoryURL = getRepositoryURL(repositoryId);
|
||||
console.log('UpdateDevelopmentServer', new Date().toISOString(), repositoryURL);
|
||||
|
||||
workbenchStore.showWorkbench.set(repositoryId !== undefined);
|
||||
workbenchStore.repositoryId.set(repositoryId);
|
||||
workbenchStore.previewURL.set(undefined);
|
||||
workbenchStore.previewError.set(false);
|
||||
|
||||
if (!repositoryId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!gActiveDevelopmentServer) {
|
||||
gActiveDevelopmentServer = new DevelopmentServerManager();
|
||||
}
|
||||
|
||||
const url = await gActiveDevelopmentServer.setRepositoryContents(repositoryId);
|
||||
|
||||
if (workbenchStore.repositoryId.get() != repositoryId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (url) {
|
||||
workbenchStore.previewURL.set(url);
|
||||
} else {
|
||||
workbenchStore.previewError.set(true);
|
||||
}
|
||||
workbenchStore.showWorkbench.set(repositoryURL !== undefined);
|
||||
workbenchStore.repositoryId.set(repositoryURL);
|
||||
workbenchStore.previewURL.set(repositoryURL);
|
||||
}
|
||||
|
@ -1,4 +1,9 @@
|
||||
// Manage state around recording Preview behavior for generating a Replay recording.
|
||||
//
|
||||
// When updating this file the backend's injected script should be updated to match,
|
||||
// see injectedScript.ts.
|
||||
//
|
||||
// Note that to make this easier we don't use backticks ` in this file.
|
||||
|
||||
import { createInjectableFunction } from './injectable';
|
||||
import { assert, stringToBase64, uint8ArrayToBase64 } from './ReplayProtocolClient';
|
||||
@ -208,7 +213,7 @@ const addRecordingMessageHandler = createInjectableFunction(
|
||||
};
|
||||
}
|
||||
}
|
||||
throw new Error(`Unknown request type: ${request}`);
|
||||
throw new Error('Unknown request type: ' + request);
|
||||
}
|
||||
|
||||
window.addEventListener('message', async (event) => {
|
||||
@ -234,10 +239,10 @@ const addRecordingMessageHandler = createInjectableFunction(
|
||||
height: rect.height,
|
||||
|
||||
/*
|
||||
* at times `event.clientX` and `event.clientY` can be slighly off in relation to the element's position
|
||||
* at times event.clientX and event.clientY can be slighly off in relation to the element's position
|
||||
* it's possible that this position might lie outside the element's bounds
|
||||
* the difference likely comes from a subpixel rounding or hit target calculation in the browser
|
||||
* it's possible that we should account for `event.width` and `event.height` here but clamping the values to the bounds of the element should be good enough
|
||||
* it's possible that we should account for event.width and event.height here but clamping the values to the bounds of the element should be good enough
|
||||
*/
|
||||
x: clamp(event.clientX - rect.x, 0, rect.width),
|
||||
y: clamp(event.clientY - rect.y, 0, rect.height),
|
||||
@ -264,7 +269,7 @@ const addRecordingMessageHandler = createInjectableFunction(
|
||||
while (current) {
|
||||
// If element has an ID, use it as it's the most specific
|
||||
if (current.id) {
|
||||
path.unshift(`#${current.id}`);
|
||||
path.unshift('#' + current.id);
|
||||
break;
|
||||
}
|
||||
|
||||
@ -279,7 +284,7 @@ const addRecordingMessageHandler = createInjectableFunction(
|
||||
const index = siblings.indexOf(current) + 1;
|
||||
|
||||
if (siblings.filter((el) => el.tagName === current!.tagName).length > 1) {
|
||||
selector += `:nth-child(${index})`;
|
||||
selector += ':nth-child(' + index + ')';
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,7 +359,7 @@ const addRecordingMessageHandler = createInjectableFunction(
|
||||
);
|
||||
|
||||
function onInterceptedOperation(_name: string) {
|
||||
//console.log(`InterceptedOperation ${name}`);
|
||||
//console.log("InterceptedOperation " + name);
|
||||
}
|
||||
|
||||
function interceptProperty(obj: object, prop: string, interceptor: (basevalue: any) => any) {
|
||||
@ -365,7 +370,7 @@ const addRecordingMessageHandler = createInjectableFunction(
|
||||
Object.defineProperty(obj, prop, {
|
||||
...descriptor,
|
||||
get() {
|
||||
onInterceptedOperation(`Getter:${prop}`);
|
||||
onInterceptedOperation('Getter:' + prop);
|
||||
|
||||
if (!interceptValue) {
|
||||
const baseValue = (descriptor?.get as any).call(obj);
|
||||
@ -526,7 +531,7 @@ const addRecordingMessageHandler = createInjectableFunction(
|
||||
|
||||
return new Proxy(obj, {
|
||||
get(target, prop) {
|
||||
onInterceptedOperation(`ProxyGetter:${name}.${String(prop)}`);
|
||||
onInterceptedOperation('ProxyGetter:' + name + '.' + String(prop));
|
||||
|
||||
let value = target[prop];
|
||||
|
||||
@ -542,7 +547,7 @@ const addRecordingMessageHandler = createInjectableFunction(
|
||||
},
|
||||
|
||||
set(target, prop, value) {
|
||||
onInterceptedOperation(`ProxySetter:${name}.${String(prop)}`);
|
||||
onInterceptedOperation('ProxySetter:' + name + '.' + String(prop));
|
||||
target[prop] = value;
|
||||
|
||||
return true;
|
||||
@ -552,7 +557,7 @@ const addRecordingMessageHandler = createInjectableFunction(
|
||||
|
||||
function createFunctionProxy(fn: any, name: string, handler?: (v: any, ...args: any[]) => any) {
|
||||
return (...args: any[]) => {
|
||||
onInterceptedOperation(`FunctionCall:${name}`);
|
||||
onInterceptedOperation('FunctionCall:' + name);
|
||||
|
||||
const v = fn(...args);
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { pingTelemetry } from '../hooks/pingTelemetry';
|
||||
import { pingTelemetry } from '~/lib/hooks/pingTelemetry';
|
||||
import { createInjectableFunction } from './injectable';
|
||||
|
||||
const replayWsServer = 'wss://dispatch.replay.io';
|
||||
|
@ -7,9 +7,6 @@ export class WorkbenchStore {
|
||||
// Any available preview URL for the current repository.
|
||||
previewURL = atom<string | undefined>(undefined);
|
||||
|
||||
// Whether there was an error loading the preview.
|
||||
previewError = atom<boolean>(false);
|
||||
|
||||
showWorkbench: WritableAtom<boolean> = import.meta.hot?.data.showWorkbench ?? atom(false);
|
||||
|
||||
constructor() {
|
||||
|
Loading…
Reference in New Issue
Block a user