fix: ui change

small changes to the ui
This commit is contained in:
Dustin Loring 2025-01-15 11:21:08 -05:00 committed by GitHub
commit 77ebb76b54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 63 additions and 62 deletions

View File

@ -1,59 +1,18 @@
import JSZip from 'jszip';
import { chatStore } from '~/lib/stores/chat';
import { workbenchStore } from '~/lib/stores/workbench';
import { classNames } from '~/utils/classNames';
import { useStore } from '@nanostores/react';
import type { FileMap } from '~/lib/stores/files';
import { saveAs } from 'file-saver';
interface HeaderActionButtonsProps {}
export function HeaderActionButtons({}: HeaderActionButtonsProps) {
const showWorkbench = useStore(workbenchStore.showWorkbench);
const { showChat } = useStore(chatStore);
const files = useStore(workbenchStore.files) as FileMap;
const canHideChat = showWorkbench || !showChat;
const downloadZip = async () => {
const zip = new JSZip();
for (const [filePath, dirent] of Object.entries(files)) {
if (dirent?.type === 'file' && !dirent.isBinary) {
// remove '/home/project/' from the beginning of the path
const relativePath = filePath.replace(/^\/home\/project\//, '');
// split the path into segments
const pathSegments = relativePath.split('/');
// if there's more than one segment, we need to create folders
if (pathSegments.length > 1) {
let currentFolder = zip;
for (let i = 0; i < pathSegments.length - 1; i++) {
currentFolder = currentFolder.folder(pathSegments[i])!;
}
currentFolder.file(pathSegments[pathSegments.length - 1], dirent.content);
} else {
// if there's only one segment, it's a file in the root
zip.file(relativePath, dirent.content);
}
}
}
const content = await zip.generateAsync({ type: 'blob' });
saveAs(content, 'project.zip');
};
return (
<div className="flex gap-2">
<button
onClick={downloadZip}
className="rounded-md items-center justify-center outline-accent-600 px-3 py-1.25 text-xs bg-[#232323] text-bolt-elements-button-secondary-text enabled:hover:bg-bolt-elements-button-secondary-backgroundHover flex gap-1.7"
>
<div className="i-ph:download-bold" />
<span>Download</span>
</button>
<div className="flex border border-bolt-elements-borderColor rounded-md overflow-hidden">
<Button
active={showChat}

View File

@ -40,7 +40,7 @@ export const IconButton = memo(
return (
<button
className={classNames(
'flex items-center p-1.5 text-bolt-elements-item-contentDefault hover:text-bolt-elements-item-contentActive rounded-md',
'flex items-center justify-center p-1.5 text-bolt-elements-item-contentActive bg-bolt-elements-item-backgroundActive rounded-md hover:bg-bolt-elements-item-backgroundHover transition-colors',
{
[classNames('opacity-30', disabledClassName)]: disabled,
},
@ -56,22 +56,23 @@ export const IconButton = memo(
onClick?.(event);
}}
>
{children ? children : <div className={classNames(icon, getIconSize(size), iconClassName)}></div>}
{children ? children : <div className={classNames(icon, getIconSize(size), 'scale-125', iconClassName)}></div>}
</button>
);
},
);
function getIconSize(size: IconSize) {
if (size === 'sm') {
return 'text-sm';
} else if (size === 'md') {
return 'text-md';
} else if (size === 'lg') {
return 'text-lg';
} else if (size === 'xl') {
return 'text-xl';
} else {
return 'text-2xl';
switch (size) {
case 'sm':
return 'text-sm';
case 'md':
return 'text-base';
case 'lg':
return 'text-lg';
case 'xl':
return 'text-xl';
case 'xxl':
return 'text-2xl';
}
}

View File

@ -3,6 +3,8 @@ import { motion, type HTMLMotionProps, type Variants } from 'framer-motion';
import { computed } from 'nanostores';
import { memo, useCallback, useEffect } from 'react';
import { toast } from 'react-toastify';
import JSZip from 'jszip';
import { saveAs } from 'file-saver';
import {
type OnChangeCallback as OnEditorChange,
type OnScrollCallback as OnEditorScroll,
@ -63,6 +65,36 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
const files = useStore(workbenchStore.files);
const selectedView = useStore(workbenchStore.currentView);
const downloadZip = async () => {
const zip = new JSZip();
for (const [filePath, dirent] of Object.entries(files)) {
if (dirent?.type === 'file' && !dirent.isBinary) {
// remove '/home/project/' from the beginning of the path
const relativePath = filePath.replace(/^\/home\/project\//, '');
// split the path into segments
const pathSegments = relativePath.split('/');
// if there's more than one segment, we need to create folders
if (pathSegments.length > 1) {
let currentFolder = zip;
for (let i = 0; i < pathSegments.length - 1; i++) {
currentFolder = currentFolder.folder(pathSegments[i])!;
}
currentFolder.file(pathSegments[pathSegments.length - 1], dirent.content);
} else {
// if there's only one segment, it's a file in the root
zip.file(relativePath, dirent.content);
}
}
}
const content = await zip.generateAsync({ type: 'blob' });
saveAs(content, 'project.zip');
};
const setSelectedView = (view: WorkbenchViewType) => {
workbenchStore.currentView.set(view);
};
@ -122,15 +154,24 @@ export const Workbench = memo(({ chatStarted, isStreaming }: WorkspaceProps) =>
<Slider selected={selectedView} options={sliderOptions} setSelected={setSelectedView} />
<div className="ml-auto" />
{selectedView === 'code' && (
<PanelHeaderButton
className="mr-1 text-sm"
onClick={() => {
workbenchStore.toggleTerminal(!workbenchStore.showTerminal.get());
}}
>
<div className="i-ph:terminal" />
Toggle Terminal
</PanelHeaderButton>
<>
<PanelHeaderButton
className="mr-1 text-sm"
onClick={downloadZip}
>
<div className="i-ph:download-bold" />
Download
</PanelHeaderButton>
<PanelHeaderButton
className="mr-1 text-sm"
onClick={() => {
workbenchStore.toggleTerminal(!workbenchStore.showTerminal.get());
}}
>
<div className="i-ph:terminal" />
Toggle Terminal
</PanelHeaderButton>
</>
)}
<IconButton
icon="i-ph:x-circle"