feat: 添加 LoRA 类型和反馈控制器,优化数据集名称和目标模型保存功能
This commit is contained in:
@@ -12,3 +12,14 @@ export const ModelChoices: Option<TargetModels>[] = [
|
||||
{ label: 'Flux', value: 'flux' },
|
||||
{ label: 'Flux 2', value: 'flux2' },
|
||||
]
|
||||
|
||||
export type LoRATypes = 'char' | 'clothing' | 'style' | 'concept' | 'scene' | 'other';
|
||||
|
||||
export const LoRATypeChoices: Option<LoRATypes>[] = [
|
||||
{ label: 'Character', value: 'char' },
|
||||
{ label: 'Clothing', value: 'clothing' },
|
||||
{ label: 'Style', value: 'style' },
|
||||
{ label: 'Concept', value: 'concept' },
|
||||
{ label: 'Scene', value: 'scene' },
|
||||
{ label: 'Other', value: 'other' },
|
||||
]
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
export type SaveFeedbackState = 'idle' | 'updated' | 'not-updated';
|
||||
|
||||
export function createSaveFeedbackController(
|
||||
setState: (state: SaveFeedbackState) => void,
|
||||
resetDelayMs = 2000,
|
||||
) {
|
||||
let feedbackTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
const resetLater = () => {
|
||||
if (feedbackTimer) {
|
||||
clearTimeout(feedbackTimer);
|
||||
}
|
||||
|
||||
feedbackTimer = setTimeout(() => {
|
||||
setState('idle');
|
||||
}, resetDelayMs);
|
||||
};
|
||||
|
||||
return {
|
||||
markUpdated() {
|
||||
setState('updated');
|
||||
resetLater();
|
||||
},
|
||||
markNotUpdated() {
|
||||
setState('not-updated');
|
||||
resetLater();
|
||||
},
|
||||
dispose() {
|
||||
if (feedbackTimer) {
|
||||
clearTimeout(feedbackTimer);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createDebouncedTrigger(callback: () => void, delayMs: number) {
|
||||
let timer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
return {
|
||||
trigger() {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
|
||||
timer = setTimeout(() => {
|
||||
callback();
|
||||
}, delayMs);
|
||||
},
|
||||
cancel() {
|
||||
if (timer) {
|
||||
clearTimeout(timer);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,16 +1,65 @@
|
||||
<script lang="ts">
|
||||
import { activeDatasetMeta } from '$lib/stores/dataset';
|
||||
import { onMount } from 'svelte';
|
||||
import { activeDatasetMeta, updateActiveDatasetMeta } from '$lib/stores/dataset';
|
||||
import {
|
||||
createDebouncedTrigger,
|
||||
createSaveFeedbackController,
|
||||
type SaveFeedbackState,
|
||||
} from '$lib/utils/form-save';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
let name = $state('');
|
||||
let saveFeedback = $state<SaveFeedbackState>('idle');
|
||||
|
||||
const feedback = createSaveFeedbackController((state) => {
|
||||
saveFeedback = state;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
name = get(activeDatasetMeta)?.name ?? '';
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
saveLater.cancel();
|
||||
feedback.dispose();
|
||||
});
|
||||
|
||||
async function saveDatasetName(value: string) {
|
||||
const nextName = value.trim();
|
||||
|
||||
if (!nextName) {
|
||||
feedback.markNotUpdated();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateActiveDatasetMeta({ name: nextName });
|
||||
feedback.markUpdated();
|
||||
} catch (error) {
|
||||
console.error('Failed to save dataset name:', error);
|
||||
feedback.markNotUpdated();
|
||||
}
|
||||
}
|
||||
|
||||
const saveLater = createDebouncedTrigger(() => {
|
||||
void saveDatasetName(name);
|
||||
}, 5000);
|
||||
|
||||
function saveNameDelayed() {
|
||||
saveLater.trigger();
|
||||
}
|
||||
</script>
|
||||
|
||||
<label class="input w-fit in-focus-within:outline-0">
|
||||
<label
|
||||
class={[
|
||||
'input w-fit in-focus-within:outline-0 transition-colors duration-300 ease-out',
|
||||
saveFeedback === 'updated' && 'border-green-500',
|
||||
saveFeedback === 'not-updated' && 'border-red-500',
|
||||
]}>
|
||||
<span class="label min-w-[10em]">Dataset Name</span>
|
||||
<input type="text" bind:value={name} class="min-w-[20em] focus:outline-0" />
|
||||
<input
|
||||
type="text"
|
||||
bind:value={name}
|
||||
oninput={saveNameDelayed}
|
||||
class="min-w-[20em] focus:outline-0" />
|
||||
</label>
|
||||
|
||||
@@ -1,62 +1,46 @@
|
||||
<script lang="ts">
|
||||
import { activeDatasetMeta, updateActiveDatasetMeta } from '$lib/stores/dataset';
|
||||
import { ModelChoices, type TargetModels } from '$lib/types/models';
|
||||
import { createSaveFeedbackController, type SaveFeedbackState } from '$lib/utils/form-save';
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { get } from 'svelte/store';
|
||||
|
||||
let selected: TargetModels | null = $state(null);
|
||||
let updated = $state(false);
|
||||
let notUpdated = $state(false);
|
||||
let feedbackTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
let saveFeedback = $state<SaveFeedbackState>('idle');
|
||||
|
||||
const feedback = createSaveFeedbackController((state) => {
|
||||
saveFeedback = state;
|
||||
});
|
||||
|
||||
onMount(() => {
|
||||
selected = get(activeDatasetMeta)?.targetModel ?? null;
|
||||
});
|
||||
|
||||
onDestroy(() => {
|
||||
if (feedbackTimer) {
|
||||
clearTimeout(feedbackTimer);
|
||||
}
|
||||
feedback.dispose();
|
||||
});
|
||||
|
||||
function resetFeedbackIn2s() {
|
||||
if (feedbackTimer) {
|
||||
clearTimeout(feedbackTimer);
|
||||
}
|
||||
|
||||
feedbackTimer = setTimeout(() => {
|
||||
updated = false;
|
||||
notUpdated = false;
|
||||
}, 2000);
|
||||
}
|
||||
|
||||
async function saveTargetModel() {
|
||||
if (!selected) {
|
||||
updated = false;
|
||||
notUpdated = true;
|
||||
resetFeedbackIn2s();
|
||||
feedback.markNotUpdated();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await updateActiveDatasetMeta({ targetModel: selected });
|
||||
updated = true;
|
||||
notUpdated = false;
|
||||
feedback.markUpdated();
|
||||
} catch (error) {
|
||||
console.error('Failed to save target model:', error);
|
||||
updated = false;
|
||||
notUpdated = true;
|
||||
feedback.markNotUpdated();
|
||||
}
|
||||
|
||||
resetFeedbackIn2s();
|
||||
}
|
||||
</script>
|
||||
|
||||
<label
|
||||
class={[
|
||||
'input w-fit in-focus-within:outline-0 transition-colors duration-300 ease-out',
|
||||
updated && 'border-green-500',
|
||||
notUpdated && 'border-red-500',
|
||||
saveFeedback === 'updated' && 'border-green-500',
|
||||
saveFeedback === 'not-updated' && 'border-red-500',
|
||||
]}>
|
||||
<span class="label min-w-[10em]">Target Model</span>
|
||||
<select bind:value={selected} onchange={saveTargetModel} class="min-w-[20em] focus:outline-0">
|
||||
|
||||
Reference in New Issue
Block a user