From d0214c0df9cbd5bca9265f1767095911fce1e3df Mon Sep 17 00:00:00 2001 From: Jaret Burkett Date: Fri, 21 Feb 2025 06:18:27 -0700 Subject: [PATCH] Make ui more uniform --- ui/src/app/dashboard/page.tsx | 16 +- ui/src/app/datasets/[datasetName]/page.tsx | 9 +- ui/src/app/datasets/page.tsx | 9 +- ui/src/app/settings/page.tsx | 169 +++-- ui/src/app/train/page.tsx | 808 +++++++++++---------- ui/src/components/layout.tsx | 27 + 6 files changed, 550 insertions(+), 488 deletions(-) create mode 100644 ui/src/components/layout.tsx diff --git a/ui/src/app/dashboard/page.tsx b/ui/src/app/dashboard/page.tsx index 94394919..2d989a18 100644 --- a/ui/src/app/dashboard/page.tsx +++ b/ui/src/app/dashboard/page.tsx @@ -1,12 +1,20 @@ 'use client'; import GpuMonitor from '@/components/GPUMonitor'; +import { TopBar, MainContent } from '@/components/layout'; export default function Dashboard() { return ( -
-

Dashboard

- -
+ <> + +
+

Dashboard

+
+
+
+ + + + ); } diff --git a/ui/src/app/datasets/[datasetName]/page.tsx b/ui/src/app/datasets/[datasetName]/page.tsx index cb178279..37976431 100644 --- a/ui/src/app/datasets/[datasetName]/page.tsx +++ b/ui/src/app/datasets/[datasetName]/page.tsx @@ -5,6 +5,7 @@ import { FaChevronLeft } from 'react-icons/fa'; import DatasetImageCard from '@/components/DatasetImageCard'; import { Button } from '@headlessui/react'; import AddImagesModal, { openImagesModal } from '@/components/AddImagesModal'; +import { TopBar, MainContent } from '@/components/layout'; export default function DatasetPage({ params }: { params: { datasetName: string } }) { const [imgList, setImgList] = useState<{ img_path: string }[]>([]); @@ -43,7 +44,7 @@ export default function DatasetPage({ params }: { params: { datasetName: string return ( <> {/* Fixed top bar */} -
+
-
-
+ + {status === 'loading' &&

Loading...

} {status === 'error' &&

Error fetching images

} {status === 'success' && ( @@ -78,7 +79,7 @@ export default function DatasetPage({ params }: { params: { datasetName: string ))}
)} - + ); diff --git a/ui/src/app/datasets/page.tsx b/ui/src/app/datasets/page.tsx index 77851c18..2e2e43b3 100644 --- a/ui/src/app/datasets/page.tsx +++ b/ui/src/app/datasets/page.tsx @@ -8,6 +8,7 @@ import useDatasetList from '@/hooks/useDatasetList'; import { Button } from '@headlessui/react'; import { FaRegTrashAlt } from 'react-icons/fa'; import { openConfirm } from '@/components/ConfirmModal'; +import { TopBar, MainContent } from '@/components/layout'; export default function Datasets() { const { datasets, status, refreshDatasets } = useDatasetList(); @@ -15,7 +16,7 @@ export default function Datasets() { const [isNewDatasetModalOpen, setIsNewDatasetModalOpen] = useState(false); return ( <> -
+

Datasets

@@ -28,8 +29,8 @@ export default function Datasets() { New Dataset
- -
+ + {status === 'loading' &&

Loading...

} {status === 'error' &&

Error fetching datasets

} {status === 'success' && ( @@ -79,7 +80,7 @@ export default function Datasets() { ))}
)} - + setIsNewDatasetModalOpen(false)} diff --git a/ui/src/app/settings/page.tsx b/ui/src/app/settings/page.tsx index 3b50a282..0c8cdf6b 100644 --- a/ui/src/app/settings/page.tsx +++ b/ui/src/app/settings/page.tsx @@ -2,6 +2,7 @@ import { useEffect, useState } from 'react'; import useSettings from '@/hooks/useSettings'; +import { TopBar, MainContent } from '@/components/layout'; export default function Settings() { const { settings, setSettings } = useSettings(); @@ -37,87 +38,97 @@ export default function Settings() { }; return ( -
-

Settings

- -
-
-
- - -
- -
- - -
- -
- - -
+ <> + +
+

Settings

+
+
+ + +
+
+
+
+ + +
- +
+ + +
- {status === 'success' &&

Settings saved successfully!

} - {status === 'error' &&

Error saving settings. Please try again.

} - -
+
+ + +
+
+
+
+ + + + {status === 'success' &&

Settings saved successfully!

} + {status === 'error' &&

Error saving settings. Please try again.

} + + + ); } diff --git a/ui/src/app/train/page.tsx b/ui/src/app/train/page.tsx index f7936b6a..0938308b 100644 --- a/ui/src/app/train/page.tsx +++ b/ui/src/app/train/page.tsx @@ -1,7 +1,6 @@ 'use client'; -// todo update training folder from settings -import { use, useEffect, useMemo, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useSearchParams, useRouter } from 'next/navigation'; import { options } from './options'; import { defaultJobConfig, defaultDatasetConfig } from './jobConfig'; @@ -15,6 +14,7 @@ import useSettings from '@/hooks/useSettings'; import useGPUInfo from '@/hooks/useGPUInfo'; import useDatasetList from '@/hooks/useDatasetList'; import path from 'path'; +import { TopBar, MainContent } from '@/components/layout'; export default function TrainingForm() { const router = useRouter(); @@ -37,7 +37,7 @@ export default function TrainingForm() { setDatasetOptions(datasetOptions); const defaultDatasetPath = defaultDatasetConfig.folder_path; - for(let i = 0; i < jobConfig.config.process[0].datasets.length; i++) { + for (let i = 0; i < jobConfig.config.process[0].datasets.length; i++) { const dataset = jobConfig.config.process[0].datasets[i]; if (dataset.folder_path === defaultDatasetPath) { setJobConfig(datasetOptions[0].value, `config.process[0].datasets[${i}].folder_path`); @@ -105,218 +105,223 @@ export default function TrainingForm() { }; return ( -
-

{runId ? 'Edit Training Run' : 'New Training Run'}

- -
-
- - setJobConfig(value, 'config.name')} - placeholder="Enter training name" - required - /> - setGpuID(parseInt(value))} - options={gpuList.map(gpu => ({ value: `${gpu}`, label: `GPU #${gpu}` }))} - /> - - - {/* Model Configuration Section */} - - { - // see if model changed - const currentModel = options.model.find( - model => model.name_or_path === jobConfig.config.process[0].model.name_or_path, - ); - if (!currentModel || currentModel.name_or_path === value) { - // model has not changed - return; - } - // revert defaults from previous model - for (const key in currentModel.defaults) { - setJobConfig(currentModel.defaults[key][1], key); - } - // set new model - setJobConfig(value, 'config.process[0].model.name_or_path'); - // update the defaults when a model is selected - const model = options.model.find(model => model.name_or_path === value); - if (model?.defaults) { - for (const key in model.defaults) { - setJobConfig(model.defaults[key][0], key); - } - } - }} - options={options.model.map(model => ({ - value: model.name_or_path, - label: model.name_or_path, - }))} - /> - - setJobConfig(value, 'config.process[0].model.quantize')} + <> + +
+

{runId ? 'Edit Training Run' : 'New Training Run'}

+
+
+
+ + +
+ + setJobConfig(value, 'config.name')} + placeholder="Enter training name" + required /> - setJobConfig(value, 'config.process[0].model.quantize_te')} + setGpuID(parseInt(value))} + options={gpuList.map(gpu => ({ value: `${gpu}`, label: `GPU #${gpu}` }))} /> - - - {jobConfig.config.process[0].network?.linear && ( - - + + {/* Model Configuration Section */} + + { - setJobConfig(value, 'config.process[0].network.linear'); - setJobConfig(value, 'config.process[0].network.linear_alpha'); + // see if model changed + const currentModel = options.model.find( + model => model.name_or_path === jobConfig.config.process[0].model.name_or_path, + ); + if (!currentModel || currentModel.name_or_path === value) { + // model has not changed + return; + } + // revert defaults from previous model + for (const key in currentModel.defaults) { + setJobConfig(currentModel.defaults[key][1], key); + } + // set new model + setJobConfig(value, 'config.process[0].model.name_or_path'); + // update the defaults when a model is selected + const model = options.model.find(model => model.name_or_path === value); + if (model?.defaults) { + for (const key in model.defaults) { + setJobConfig(model.defaults[key][0], key); + } + } }} - placeholder="eg. 16" + options={options.model.map(model => ({ + value: model.name_or_path, + label: model.name_or_path, + }))} + /> + + setJobConfig(value, 'config.process[0].model.quantize')} + /> + setJobConfig(value, 'config.process[0].model.quantize_te')} + /> + + + {jobConfig.config.process[0].network?.linear && ( + + { + setJobConfig(value, 'config.process[0].network.linear'); + setJobConfig(value, 'config.process[0].network.linear_alpha'); + }} + placeholder="eg. 16" + min={1} + max={1024} + required + /> + + )} + + setJobConfig(value, 'config.process[0].save.dtype')} + options={[ + { value: 'bf16', label: 'BF16' }, + { value: 'fp16', label: 'FP16' }, + { value: 'fp32', label: 'FP32' }, + ]} + /> + setJobConfig(value, 'config.process[0].save.save_every')} + placeholder="eg. 250" + min={1} + required + /> + setJobConfig(value, 'config.process[0].save.max_step_saves_to_keep')} + placeholder="eg. 4" min={1} - max={1024} required /> - )} - - setJobConfig(value, 'config.process[0].save.dtype')} - options={[ - { value: 'bf16', label: 'BF16' }, - { value: 'fp16', label: 'FP16' }, - { value: 'fp32', label: 'FP32' }, - ]} - /> - setJobConfig(value, 'config.process[0].save.save_every')} - placeholder="eg. 250" - min={1} - required - /> - setJobConfig(value, 'config.process[0].save.max_step_saves_to_keep')} - placeholder="eg. 4" - min={1} - required - /> - -
-
- -
-
- setJobConfig(value, 'config.process[0].train.batch_size')} - placeholder="eg. 4" - min={1} - required - /> - setJobConfig(value, 'config.process[0].train.gradient_accumulation')} - placeholder="eg. 1" - min={1} - required - /> - setJobConfig(value, 'config.process[0].train.steps')} - placeholder="eg. 2000" - min={1} - required - /> +
+
+ +
+
+ setJobConfig(value, 'config.process[0].train.batch_size')} + placeholder="eg. 4" + min={1} + required + /> + setJobConfig(value, 'config.process[0].train.gradient_accumulation')} + placeholder="eg. 1" + min={1} + required + /> + setJobConfig(value, 'config.process[0].train.steps')} + placeholder="eg. 2000" + min={1} + required + /> +
+
+ setJobConfig(value, 'config.process[0].train.optimizer')} + options={[ + { value: 'adamw8bit', label: 'AdamW8Bit' }, + { value: 'adafactor', label: 'Adafactor' }, + ]} + /> + setJobConfig(value, 'config.process[0].train.lr')} + placeholder="eg. 0.0001" + min={0} + required + /> + setJobConfig(value, 'config.process[0].train.optimizer_params.weight_decay')} + placeholder="eg. 0.0001" + min={0} + required + /> +
-
- setJobConfig(value, 'config.process[0].train.optimizer')} - options={[ - { value: 'adamw8bit', label: 'AdamW8Bit' }, - { value: 'adafactor', label: 'Adafactor' }, - ]} - /> - setJobConfig(value, 'config.process[0].train.lr')} - placeholder="eg. 0.0001" - min={0} - required - /> - setJobConfig(value, 'config.process[0].train.optimizer_params.weight_decay')} - placeholder="eg. 0.0001" - min={0} - required - /> -
-
- -
-
- - <> - {jobConfig.config.process[0].datasets.map((dataset, i) => ( -
- -

Dataset {i + 1}

-
-
- setJobConfig(value, `config.process[0].datasets[${i}].folder_path`)} - options={datasetOptions} - /> - {/* +
+
+ + <> + {jobConfig.config.process[0].datasets.map((dataset, i) => ( +
+ +

Dataset {i + 1}

+
+
+ setJobConfig(value, `config.process[0].datasets[${i}].folder_path`)} + options={datasetOptions} + /> + {/* setJobConfig(value, `config.process[0].datasets[${i}].folder_path`)} placeholder="eg. /path/to/images/folder" required /> */} - {/* */} -
-
- setJobConfig(value, `config.process[0].datasets[${i}].default_caption`)} - placeholder="eg. A photo of a cat" - /> - setJobConfig(value, `config.process[0].datasets[${i}].caption_ext`)} - placeholder="eg. txt" - required - /> - setJobConfig(value, `config.process[0].datasets[${i}].caption_dropout_rate`)} - placeholder="eg. 0.05" - min={0} - required - /> -
-
- - - setJobConfig(value, `config.process[0].datasets[${i}].cache_latents_to_disk`) - } +
+
+ setJobConfig(value, `config.process[0].datasets[${i}].default_caption`)} + placeholder="eg. A photo of a cat" /> - setJobConfig(value, `config.process[0].datasets[${i}].is_reg`)} + value={dataset.caption_ext} + onChange={value => setJobConfig(value, `config.process[0].datasets[${i}].caption_ext`)} + placeholder="eg. txt" + required /> - + + setJobConfig(value, `config.process[0].datasets[${i}].caption_dropout_rate`) + } + placeholder="eg. 0.05" + min={0} + required + /> +
+
+ + + setJobConfig(value, `config.process[0].datasets[${i}].cache_latents_to_disk`) + } + /> + setJobConfig(value, `config.process[0].datasets[${i}].is_reg`)} + /> + +
+
+ + {[256, 512, 768, 1024, 1280].map(res => ( + { + const resolutions = dataset.resolution.includes(res) + ? dataset.resolution.filter(r => r !== res) + : [...dataset.resolution, res]; + setJobConfig(resolutions, `config.process[0].datasets[${i}].resolution`); + }} + /> + ))} + +
+
+
+ ))} + + +
+
+
+ +
+
+ setJobConfig(value, 'config.process[0].sample.sample_every')} + placeholder="eg. 250" + min={1} + required + /> + setJobConfig(value, 'config.process[0].sample.sampler')} + options={[{ value: 'flowmatch', label: 'FlowMatch' }]} + /> +
+
+ setJobConfig(value, 'config.process[0].sample.guidance_scale')} + placeholder="eg. 1.0" + min={0} + required + /> + setJobConfig(value, 'config.process[0].sample.sample_steps')} + placeholder="eg. 1" + className="pt-2" + min={1} + required + /> +
+
+ setJobConfig(value, 'config.process[0].sample.width')} + placeholder="eg. 1024" + min={256} + required + /> + setJobConfig(value, 'config.process[0].sample.height')} + placeholder="eg. 1024" + className="pt-2" + min={256} + required + /> +
+ +
+ setJobConfig(value, 'config.process[0].sample.seed')} + placeholder="eg. 0" + min={0} + required + /> + setJobConfig(value, 'config.process[0].sample.walk_seed')} + /> +
+
+ + {jobConfig.config.process[0].sample.prompts.map((prompt, i) => ( +
+
+ setJobConfig(value, `config.process[0].sample.prompts[${i}]`)} + placeholder="Enter prompt" + required + />
- - {[256, 512, 768, 1024, 1280].map(res => ( - { - const resolutions = dataset.resolution.includes(res) - ? dataset.resolution.filter(r => r !== res) - : [...dataset.resolution, res]; - setJobConfig(resolutions, `config.process[0].datasets[${i}].resolution`); - }} - /> - ))} - +
-
- ))} - - - -
-
- -
-
- setJobConfig(value, 'config.process[0].sample.sample_every')} - placeholder="eg. 250" - min={1} - required - /> - setJobConfig(value, 'config.process[0].sample.sampler')} - options={[{ value: 'flowmatch', label: 'FlowMatch' }]} - /> -
-
- setJobConfig(value, 'config.process[0].sample.guidance_scale')} - placeholder="eg. 1.0" - min={0} - required - /> - setJobConfig(value, 'config.process[0].sample.sample_steps')} - placeholder="eg. 1" - className="pt-2" - min={1} - required - /> -
-
- setJobConfig(value, 'config.process[0].sample.width')} - placeholder="eg. 1024" - min={256} - required - /> - setJobConfig(value, 'config.process[0].sample.height')} - placeholder="eg. 1024" - className="pt-2" - min={256} - required - /> -
+ ))} + + + +
-
- setJobConfig(value, 'config.process[0].sample.seed')} - placeholder="eg. 0" - min={0} - required - /> - setJobConfig(value, 'config.process[0].sample.walk_seed')} - /> -
-
- - {jobConfig.config.process[0].sample.prompts.map((prompt, i) => ( -
-
- setJobConfig(value, `config.process[0].sample.prompts[${i}]`)} - placeholder="Enter prompt" - required - /> -
-
- -
-
- ))} - -
- -
+ - - - {status === 'success' &&

Training saved successfully!

} - {status === 'error' &&

Error saving training. Please try again.

} - -
+ {status === 'success' &&

Training saved successfully!

} + {status === 'error' &&

Error saving training. Please try again.

} + + + ); } diff --git a/ui/src/components/layout.tsx b/ui/src/components/layout.tsx new file mode 100644 index 00000000..a23cc45b --- /dev/null +++ b/ui/src/components/layout.tsx @@ -0,0 +1,27 @@ +import classNames from 'classnames'; + +interface Props { + className?: string; + children?: React.ReactNode; +} + +export const TopBar: React.FC = ({ children, className }) => { + return ( +
+ {children ? children : null} +
+ ); +}; + +export const MainContent: React.FC = ({ children, className }) => { + return ( +
+ {children ? children : null} +
+ ); +};