Made the UI mobile friendly, finally...

This commit is contained in:
Jaret Burkett
2026-05-19 09:27:36 -06:00
parent 6ecaf679dc
commit df851b3497
22 changed files with 325 additions and 118 deletions

View File

@@ -10,7 +10,7 @@ export default function Dashboard() {
<>
<TopBar>
<div>
<h1 className="text-lg">Dashboard</h1>
<h1 className="text-base sm:text-lg">Dashboard</h1>
</div>
<div className="flex-1"></div>
</TopBar>

View File

@@ -100,25 +100,29 @@ export default function DatasetPage({ params }: { params: { datasetName: string
<>
{/* Fixed top bar */}
<TopBar>
<div>
<Button className="text-gray-500 dark:text-gray-300 px-3 mt-1" onClick={() => history.back()}>
<div className="flex-shrink-0">
<Button className="text-gray-500 dark:text-gray-300 px-2 sm:px-3 mt-1" onClick={() => history.back()}>
<FaChevronLeft />
</Button>
</div>
<div>
<h1 className="text-lg">Dataset: {datasetName}</h1>
<div className="min-w-0 flex-shrink">
<h1 className="text-base sm:text-lg truncate">
<span className="hidden sm:inline">Dataset: </span>
{datasetName}
</h1>
</div>
<div className="flex-1"></div>
<div>
<div className="flex-shrink-0 flex items-center gap-1 sm:gap-2">
<AutoCaptionButton
datasetPath={`${pathJoin(settings.DATASETS_FOLDER, datasetName)}`}
setIsAutoCaptioning={setIsAutoCaptioning}
/>
<Button
className="text-white bg-slate-600 px-3 py-1 rounded-md"
className="text-white bg-slate-600 px-2 sm:px-3 py-1 rounded-md text-sm sm:text-base whitespace-nowrap"
onClick={() => openImagesModal(datasetName, () => refreshImageList(datasetName))}
>
Add Images
<span className="sm:hidden">+ Add</span>
<span className="hidden sm:inline">Add Images</span>
</Button>
</div>
</TopBar>

View File

@@ -114,15 +114,16 @@ export default function Datasets() {
<>
<TopBar>
<div>
<h1 className="text-lg">Datasets</h1>
<h1 className="text-base sm:text-lg">Datasets</h1>
</div>
<div className="flex-1"></div>
<div>
<Button
className="text-white bg-slate-600 px-3 py-1 rounded-md hover:bg-slate-500 transition-colors"
className="text-white bg-slate-600 px-2 sm:px-3 py-1 rounded-md hover:bg-slate-500 transition-colors text-sm sm:text-base whitespace-nowrap"
onClick={() => openNewDatasetModal()}
>
New Dataset
<span className="sm:hidden">+ New</span>
<span className="hidden sm:inline">New Dataset</span>
</Button>
</div>
</TopBar>

View File

@@ -43,6 +43,21 @@ body {
font-family: Arial, Helvetica, sans-serif;
}
/* Prevent iOS Safari from auto-zooming when a form field is focused.
iOS only zooms when the field's font-size is < 16px. */
@media (max-width: 639px) {
input,
textarea,
select,
.aitk-react-select-container .aitk-react-select__control,
.aitk-react-select-container .aitk-react-select__input,
.aitk-react-select-container .aitk-react-select__single-value,
.aitk-react-select-container .aitk-react-select__placeholder,
.aitk-react-select-container .aitk-react-select__option {
font-size: 16px !important;
}
}
@layer components {
/* control */
.aitk-react-select-container .aitk-react-select__control {

View File

@@ -79,13 +79,13 @@ export default function JobPage({ params }: { params: { jobID: string } }) {
<>
{/* Fixed top bar */}
<TopBar>
<div>
<Button className="text-gray-500 dark:text-gray-300 px-3 mt-1" onClick={() => redirect('/jobs')}>
<div className="flex-shrink-0">
<Button className="text-gray-500 dark:text-gray-300 pl-0 pr-1 sm:px-3 mt-1" onClick={() => redirect('/jobs')}>
<FaChevronLeft />
</Button>
</div>
<div>
<h1 className="text-lg">{title}</h1>
<div className="min-w-0 flex-shrink">
<h1 className="text-base sm:text-lg truncate">{title}</h1>
</div>
<div className="flex-1"></div>
{job && (
@@ -112,7 +112,7 @@ export default function JobPage({ params }: { params: { jobID: string } }) {
</>
)}
</MainContent>
<div className="bg-gray-800 absolute top-12 left-0 w-full h-8 flex items-center px-2 text-sm">
<div className="bg-gray-800 absolute top-12 left-0 w-full h-8 flex items-center px-0 sm:px-2 text-sm sm:overflow-x-auto whitespace-nowrap">
{pages.map(page => {
if (page.jobTypes && !page.jobTypes.includes(jobType)) {
return null;
@@ -121,16 +121,16 @@ export default function JobPage({ params }: { params: { jobID: string } }) {
<Button
key={page.value}
onClick={() => setPageKey(page.value)}
className={`px-4 py-1 h-8 flex items-center gap-1.5 ${page.value === pageKey ? 'bg-gray-300 dark:bg-gray-700 text-white' : ''}`}
className={`flex-1 sm:flex-initial justify-center px-2 sm:px-4 py-1 h-8 flex items-center gap-1.5 sm:flex-shrink-0 ${page.value === pageKey ? 'bg-gray-300 dark:bg-gray-700 text-white' : ''}`}
>
<page.icon className="text-sm" />
{page.name}
<span className="hidden sm:inline">{page.name}</span>
</Button>
);
})}
{page?.menuItem && (
<>
<div className="flex-grow"></div>
<div className="hidden sm:block flex-grow"></div>
<page.menuItem job={job} />
</>
)}

View File

@@ -190,36 +190,38 @@ export default function TrainingForm() {
return (
<>
<TopBar>
<div>
<Button className="text-gray-500 dark:text-gray-300 px-3 mt-1" onClick={() => history.back()}>
<div className="flex-shrink-0">
<Button className="text-gray-500 dark:text-gray-300 px-2 sm:px-3 mt-1" onClick={() => history.back()}>
<FaChevronLeft />
</Button>
</div>
<div>
<h1 className="text-lg">{runId ? 'Edit Training Job' : 'New Training Job'}</h1>
<div className="flex-shrink-0">
<h1 className="text-base sm:text-lg truncate max-w-[120px] sm:max-w-none">
{runId ? 'Edit Training Job' : 'New Training Job'}
</h1>
</div>
<div className="flex-1"></div>
{showAdvancedView && (
<>
<div>
<div className="hidden sm:block">
<SelectInput
value={`${gpuIDs}`}
onChange={value => setGpuIDs(value)}
options={gpuList.map((gpu: any) => ({ value: `${gpu.index}`, label: `GPU #${gpu.index}` }))}
/>
</div>
<div className="mx-4 bg-gray-200 dark:bg-gray-800 w-1 h-6"></div>
<div>
<div className="hidden sm:block mx-4 bg-gray-200 dark:bg-gray-800 w-1 h-6"></div>
<div className="hidden md:block">
<Button className="text-gray-200 bg-gray-800 px-3 py-1 rounded-md" onClick={handleImportConfig}>
Import Config
</Button>
</div>
<div className="mx-4 bg-gray-200 dark:bg-gray-800 w-1 h-6"></div>
<div className="hidden md:block mx-4 bg-gray-200 dark:bg-gray-800 w-1 h-6"></div>
</>
)}
{!showAdvancedView && (
<>
<div>
<div className="hidden sm:block">
<SelectInput
value={`${jobConfig?.config.process[0].type}`}
onChange={value => {
@@ -246,25 +248,33 @@ export default function TrainingForm() {
options={jobTypeOptions}
/>
</div>
<div className="mx-4 bg-gray-200 dark:bg-gray-800 w-1 h-6"></div>
<div className="hidden sm:block mx-4 bg-gray-200 dark:bg-gray-800 w-1 h-6"></div>
</>
)}
<div className="pr-2">
<div className="pr-1 sm:pr-2 flex-shrink-0">
<Button
className="text-gray-200 bg-gray-800 px-3 py-1 rounded-md"
className="text-gray-200 bg-gray-800 px-2 sm:px-3 py-1 rounded-md text-xs sm:text-base"
onClick={() => setShowAdvancedView(!showAdvancedView)}
>
{showAdvancedView ? 'Show Simple' : 'Show Advanced'}
<span className="sm:hidden">{showAdvancedView ? 'Simple' : 'Advanced'}</span>
<span className="hidden sm:inline">{showAdvancedView ? 'Show Simple' : 'Show Advanced'}</span>
</Button>
</div>
<div>
<div className="flex-shrink-0">
<Button
className="text-white bg-green-600 hover:bg-green-700 px-3 py-1 rounded-md"
className="text-white bg-green-600 hover:bg-green-700 px-2 sm:px-3 py-1 rounded-md text-xs sm:text-base"
onClick={() => saveJob()}
disabled={status === 'saving'}
>
{status === 'saving' ? 'Saving...' : runId ? 'Update Job' : 'Create Job'}
{status === 'saving' ? (
'Saving...'
) : (
<>
<span className="sm:hidden">{runId ? 'Update' : 'Create'}</span>
<span className="hidden sm:inline">{runId ? 'Update Job' : 'Create Job'}</span>
</>
)}
</Button>
</div>
</TopBar>

View File

@@ -9,12 +9,16 @@ export default function Dashboard() {
<>
<TopBar>
<div>
<h1 className="text-lg">Queue</h1>
<h1 className="text-base sm:text-lg">Queue</h1>
</div>
<div className="flex-1"></div>
<div>
<Link href="/jobs/new" className="text-white bg-slate-600 px-3 py-1 rounded-md">
New Training Job
<Link
href="/jobs/new"
className="text-white bg-slate-600 px-2 sm:px-3 py-1 rounded-md text-sm sm:text-base whitespace-nowrap"
>
<span className="sm:hidden">+ New Job</span>
<span className="hidden sm:inline">New Training Job</span>
</Link>
</div>
</TopBar>

View File

@@ -20,6 +20,12 @@ export const metadata: Metadata = {
description: 'A toolkit for building AI things.',
};
export const viewport = {
width: 'device-width',
initialScale: 1,
maximumScale: 5,
};
export default function RootLayout({ children }: { children: React.ReactNode }) {
// Check if the AI_TOOLKIT_AUTH environment variable is set
const authRequired = process.env.AI_TOOLKIT_AUTH ? true : false;

View File

@@ -36,7 +36,7 @@ export default function Settings() {
<>
<TopBar>
<div>
<h1 className="text-lg">Settings</h1>
<h1 className="text-base sm:text-lg">Settings</h1>
</div>
<div className="flex-1"></div>
</TopBar>