- Importing {examples.find((e) => e.id === importingId)?.label ?? "example"}...
+
+
+
+
Train a LoRA
+
+ Teach an image model a style, character, or subject from your own images. The
+ finished adapter shows up in the Create tab's LoRA picker.
- )}
+
- {dataset === UPLOAD_DATASET ? (
-
-
setUploadName(e.target.value)}
- className="h-8 text-xs"
- aria-label="New dataset name"
- />
-
-
-
-
-
- 10-50 images are plenty. Captions are optional: without them, the trigger
- prompt below describes every image.
-
-
- ) : (
- selectedDataset && (
- <>
- {selectedDataset.image_count > 0 && !gridOpen && (
-
setGridOpen(true)}
- />
- )}
- setGridOpen((o) => !o)}
- />
- {gridOpen && (
- void refreshInfo()}
- />
- )}
- {selectedDataset.caption_count === 0 && !gridOpen && (
-
- No captions yet: the trigger prompt below will describe every image,
- or open Review captions to write your own.
-
- )}
- >
- )
- )}
-
- void importExample(ex)}
- />
-
-
- {/* Trigger + adapter name (trigger first: it describes the dataset, the name
- just labels the output) */}
- {fullyCaptioned ? (
-
- All {selectedDataset?.image_count} images have captions - no trigger prompt needed.
- The style applies to any prompt after training.
-
- ) : (
+ {/* Family + base */}
-
+
+
+ {family?.vram_note && (
+
{family.vram_note}
+ )}
+
+
+
+
+
+ {effectiveBase === CUSTOM_BASE && (
+ setCustomBase(e.target.value)}
+ className="h-8 text-xs"
+ />
+ )}
+
+
+ {/* Dataset */}
+
+
+
+ {importingId && (
+
+ Importing {examples.find((e) => e.id === importingId)?.label ?? "example"}...
+
+ )}
+
+ {dataset === UPLOAD_DATASET ? (
+
+
setUploadName(e.target.value)}
+ className="h-8 text-xs"
+ aria-label="New dataset name"
+ />
+
+ {/* The native input is the file picker but never the control the
+ user sees, so the row keeps the app's button styling. */}
+ setPickedFileCount(e.target.files?.length ?? 0)}
+ />
+
+
+ {pickedFileCount > 0
+ ? `${pickedFileCount} file${pickedFileCount === 1 ? "" : "s"} selected`
+ : "No files selected"}
+
+
+
+
+ 10-50 images are plenty. Captions are optional: without them, the trigger
+ prompt below describes every image.
+
+
+ ) : (
+ selectedDataset && (
+ <>
+ {selectedDataset.image_count > 0 && !gridOpen && (
+
setGridOpen(true)}
+ />
+ )}
+ setGridOpen((o) => !o)}
+ />
+ {gridOpen && (
+ void refreshInfo()}
+ />
+ )}
+ {selectedDataset.caption_count === 0 && !gridOpen && (
+
+ No captions yet: the trigger prompt below will describe every image,
+ or open Review captions to write your own.
+
+ )}
+ >
+ )
+ )}
+
+ void importExample(ex)}
+ />
+
+
+ {/* Trigger + adapter name (trigger first: it describes the dataset, the name
+ just labels the output) */}
+ {fullyCaptioned ? (
+
+ All {selectedDataset?.image_count} images have captions - no trigger prompt needed.
+ The style applies to any prompt after training.
+
+ ) : (
+
+
+ setInstancePrompt(e.target.value)}
+ className="h-8 text-xs"
+ />
+
+ )}
+
+
setInstancePrompt(e.target.value)}
+ value={outputDir}
+ placeholder="my-style-lora"
+ spellCheck={false}
+ onChange={(e) => setOutputDir(e.target.value)}
className="h-8 text-xs"
/>
- )}
-
-
- setOutputDir(e.target.value)}
- className="h-8 text-xs"
- />
-
- {/* Start lives here; Stop lives in the run card next to the live stats. */}
-
-
+ {/* Start lives here; Stop lives in the run card next to the live stats. */}
+
+
+
@@ -1218,12 +1271,12 @@ export function DiffusionTrainPanel({
front) plus the previous-runs history; during/after a run the live view takes
over (progress with Stop, then the saved-adapter card ABOVE the charts).
Selecting a previous run re-plots its persisted logs read-only. */}
- {/* p-px so the cards' ring-1 (drawn outside the box) isn't clipped by this
+ {/* p-1.5 so the cards' shadow (drawn outside the box) isn't clipped by this
scroll container. */}
-