zimage-lora-builder / 01_index.html
Kotajiro's picture
Rename index.html to 01_index.html
ddb298b verified
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Z-Image LoRA Command Builder</title>
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@300;400;600&family=Syne:wght@400;600;800&display=swap" rel="stylesheet">
<style>
:root {
--bg: #0a0c10; --surface: #111318; --surface2: #181c24; --border: #252a35;
--accent: #00e5a0; --text: #e2e8f0; --text-dim: #64748b; --text-bright: #f8fafc;
--radius: 8px; --mono: 'JetBrains Mono', monospace; --sans: 'Syne', sans-serif;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body { background: var(--bg); color: var(--text); font-family: var(--sans); min-height: 100vh; line-height: 1.6; }
body::before {
content: ''; position: fixed; inset: 0;
background-image: linear-gradient(rgba(0,229,160,0.025) 1px, transparent 1px), linear-gradient(90deg, rgba(0,229,160,0.025) 1px, transparent 1px);
background-size: 40px 40px; pointer-events: none; z-index: 0;
}
.container { max-width: 1000px; margin: 0 auto; padding: 32px 24px; position: relative; z-index: 1; }
header { margin-bottom: 24px; border-left: 3px solid var(--accent); padding-left: 20px; }
header h1 { font-size: 1.8rem; font-weight: 800; color: var(--text-bright); letter-spacing: -0.5px; }
header h1 span { color: var(--accent); }
header p { color: var(--text-dim); font-size: 0.8rem; font-family: var(--mono); margin-top: 4px; }
.section { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px 22px; margin-bottom: 12px; }
.section-title { font-size: 0.6rem; font-family: var(--mono); color: var(--accent); text-transform: uppercase; letter-spacing: 2px; margin-bottom: 14px; display: flex; align-items: center; gap: 8px; }
.section-title::after { content: ''; flex: 1; height: 1px; background: var(--border); }
.grid { display: grid; gap: 12px; }
.g2 { grid-template-columns: 1fr 1fr; }
.g3 { grid-template-columns: 1fr 1fr 1fr; }
.g4 { grid-template-columns: 1fr 1fr 1fr 1fr; }
.span2 { grid-column: span 2; }
.full { grid-column: 1 / -1; }
@media (max-width: 680px) { .g2,.g3,.g4 { grid-template-columns: 1fr; } .span2,.full { grid-column: span 1; } }
.field { display: flex; flex-direction: column; gap: 5px; }
label { font-size: 0.7rem; font-family: var(--mono); color: var(--text-dim); display: flex; justify-content: space-between; align-items: baseline; }
label .hint { font-size: 0.6rem; color: rgba(100,116,139,0.5); }
input[type="text"], input[type="number"], select {
background: var(--surface2); border: 1px solid var(--border); border-radius: 6px;
color: var(--text-bright); padding: 8px 11px; font-family: var(--mono); font-size: 0.78rem;
outline: none; transition: border-color 0.15s, box-shadow 0.15s; width: 100%;
}
input:focus, select:focus { border-color: rgba(0,229,160,0.4); box-shadow: 0 0 0 2px rgba(0,229,160,0.07); }
select option { background: #181c24; }
.check-group { display: flex; flex-wrap: wrap; gap: 12px; }
.check-item { display: flex; align-items: center; gap: 6px; cursor: pointer; font-family: var(--mono); font-size: 0.74rem; color: var(--text); white-space: nowrap; }
.check-item input[type="checkbox"] { width: 14px; height: 14px; accent-color: var(--accent); cursor: pointer; }
.sep { height: 1px; background: var(--border); margin: 12px 0; }
.os-toggle { display: flex; gap: 2px; background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; padding: 3px; width: fit-content; }
.os-btn { padding: 5px 18px; border: none; border-radius: 4px; background: none; color: var(--text-dim); font-family: var(--mono); font-size: 0.74rem; cursor: pointer; transition: all 0.15s; }
.os-btn.active { background: var(--surface); color: var(--accent); border: 1px solid rgba(0,229,160,0.2); }
.gen-btn { background: var(--accent); color: #000; border: none; padding: 13px; border-radius: var(--radius); font-family: var(--mono); font-size: 0.85rem; font-weight: 600; cursor: pointer; width: 100%; margin: 16px 0 14px; transition: all 0.15s; letter-spacing: 0.5px; }
.gen-btn:hover { background: #00ffb3; transform: translateY(-1px); box-shadow: 0 4px 20px rgba(0,229,160,0.25); }
.gen-btn:active { transform: translateY(0); }
.output-wrap { background: #060810; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
.output-header { display: flex; justify-content: space-between; align-items: center; padding: 9px 16px; background: var(--surface); border-bottom: 1px solid var(--border); }
.output-label { font-size: 0.6rem; font-family: var(--mono); color: var(--text-dim); text-transform: uppercase; letter-spacing: 1.5px; }
.copy-btn { background: rgba(0,229,160,0.1); border: 1px solid rgba(0,229,160,0.2); color: var(--accent); padding: 4px 14px; border-radius: 4px; font-family: var(--mono); font-size: 0.7rem; cursor: pointer; transition: all 0.15s; }
.copy-btn:hover { background: rgba(0,229,160,0.2); }
.copy-btn.copied { color: #000; background: var(--accent); border-color: var(--accent); }
pre { padding: 20px; font-family: var(--mono); font-size: 0.75rem; line-height: 1.9; color: var(--text); white-space: pre-wrap; word-break: break-all; }
.k { color: #7dd3fc; } .c { color: var(--accent); font-weight: 600; } .ct { color: #f97316; }
/* ===== PRESET BAR ===== */
.preset-bar { display: flex; gap: 10px; margin-bottom: 16px; }
.preset-btn {
flex: 1; padding: 11px 16px; border-radius: var(--radius); font-family: var(--mono);
font-size: 0.78rem; font-weight: 600; cursor: pointer; border: none; transition: all 0.15s;
display: flex; align-items: center; justify-content: center; gap: 8px;
}
.pbtn-export { background: rgba(0,229,160,0.12); color: var(--accent); border: 1px solid rgba(0,229,160,0.25); }
.pbtn-export:hover { background: rgba(0,229,160,0.22); }
.pbtn-export.done { background: var(--accent); color: #000; border-color: var(--accent); }
/* ===== DROP ZONE ===== */
.drop-zone {
border: 2px dashed var(--border); border-radius: var(--radius);
background: var(--surface); padding: 0;
display: flex; align-items: center; gap: 16px;
cursor: pointer; transition: all 0.2s; position: relative; overflow: hidden;
min-height: 56px; padding: 0 20px;
}
.drop-zone:hover, .drop-zone.drag-over { border-color: var(--accent); background: rgba(0,229,160,0.05); }
.drop-zone.drag-over { box-shadow: 0 0 0 3px rgba(0,229,160,0.12); }
.drop-zone .dz-icon { font-size: 1.4rem; flex-shrink: 0; }
.drop-zone .dz-text { font-family: var(--mono); font-size: 0.75rem; color: var(--text-dim); }
.drop-zone .dz-text span { color: var(--accent); }
.drop-zone input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; width: 100%; height: 100%; }
.drop-zone.loaded { border-color: var(--accent); border-style: solid; }
.drop-zone.loaded .dz-text { color: var(--accent); }
/* ===== TOAST ===== */
.toast {
position: fixed; bottom: 28px; right: 28px; z-index: 999;
background: var(--surface); border: 1px solid var(--accent); border-radius: var(--radius);
padding: 12px 20px; font-family: var(--mono); font-size: 0.78rem; color: var(--accent);
box-shadow: 0 4px 24px rgba(0,0,0,0.5);
transform: translateY(16px); opacity: 0; transition: all 0.22s; pointer-events: none;
}
.toast.show { transform: translateY(0); opacity: 1; }
.toast.err { border-color: #f97316; color: #f97316; }
</style>
</head>
<body>
<div class="container">
<header>
<h1>Z-Image <span>LoRA</span> Command Builder</h1>
<p>musubi-tuner (kohya-ss) — zimage_train_network.py</p>
</header>
<!-- PRESET EXPORT / IMPORT -->
<div class="preset-bar">
<button class="preset-btn pbtn-export" id="exportBtn" onclick="exportSettings()">
⬇ 設定をJSONで保存
</button>
</div>
<div class="drop-zone" id="dropZone">
<span class="dz-icon">📂</span>
<span class="dz-text">設定ファイル(JSON)を<span>ドラッグ&ドロップ</span>、またはクリックして読み込む</span>
<input type="file" accept=".json" onchange="loadFile(this.files[0])">
</div>
<div style="height:16px"></div>
<!-- OS -->
<div class="section">
<div class="section-title">OS / 改行スタイル</div>
<div class="os-toggle">
<button class="os-btn active" id="btn-win" onclick="setOS('win')">Windows ( ^ )</button>
<button class="os-btn" id="btn-lnx" onclick="setOS('lnx')">Linux / Mac ( \ )</button>
</div>
</div>
<!-- PATHS -->
<div class="section">
<div class="section-title">モデル・パス</div>
<div class="grid g2">
<div class="field span2">
<label>--dit <span class="hint">分割safetensorsは1枚目を指定</span></label>
<input type="text" id="dit" value="path/to/dit_model">
</div>
<div class="field">
<label>--vae</label>
<input type="text" id="vae" value="path/to/vae_model">
</div>
<div class="field">
<label>--text_encoder</label>
<input type="text" id="te" value="path/to/text_encoder">
</div>
<div class="field span2">
<label>--dataset_config <span class="hint">TOML</span></label>
<input type="text" id="ds" value="path/to/dataset_config.toml">
</div>
<div class="field">
<label>--output_dir</label>
<input type="text" id="outdir" value="path/to/output_dir">
</div>
<div class="field">
<label>--output_name</label>
<input type="text" id="outname" value="my_lora">
</div>
</div>
</div>
<!-- LORA -->
<div class="section">
<div class="section-title">LoRAパラメータ</div>
<div class="grid g4">
<div class="field">
<label>--network_module</label>
<select id="netmod">
<option value="networks.lora_zimage" selected>lora_zimage(推奨)</option>
<option value="networks.lora">lora(汎用)</option>
</select>
</div>
<div class="field">
<label>--network_dim <span class="hint">Rank</span></label>
<select id="ndim" onchange="syncAlpha()">
<option value="4">4</option><option value="8">8</option><option value="16">16</option>
<option value="32" selected>32</option><option value="64">64</option><option value="128">128</option>
</select>
</div>
<div class="field">
<label>--network_alpha</label>
<select id="nalpha">
<option value="4">4</option><option value="8">8</option><option value="16">16</option>
<option value="32" selected>32</option><option value="64">64</option><option value="128">128</option>
</select>
</div>
<div class="field">
<label style="opacity:0">-</label>
<label class="check-item" style="padding-top:8px;"><input type="checkbox" id="use_loha"> LoHa/LoKr</label>
</div>
</div>
</div>
<!-- STEPS -->
<div class="section">
<div class="section-title">ステップ・シード</div>
<div class="grid g4">
<div class="field">
<label>--max_train_steps</label>
<input type="number" id="steps" value="3000" min="100" step="100">
</div>
<div class="field">
<label>--save_every_n_steps</label>
<input type="number" id="save_steps" value="200" min="50" step="50">
</div>
<div class="field">
<label>--seed</label>
<input type="number" id="seed" value="42">
</div>
<div class="field">
<label>--max_data_loader_n_workers</label>
<input type="number" id="workers" value="2" min="0" max="8">
</div>
</div>
</div>
<!-- OPTIMIZER -->
<div class="section">
<div class="section-title">オプティマイザ / 学習率</div>
<div class="grid g4">
<div class="field">
<label>--optimizer_type</label>
<select id="opttype" onchange="onOptimizerChange()">
<option value="adamw8bit" selected>adamw8bit</option>
<option value="adamw">adamw</option>
<option value="adafactor">adafactor</option>
<option value="prodigy">prodigy</option>
<option value="came">came</option>
<option value="Lion">Lion</option>
<option value="Lion8bit">Lion8bit</option>
</select>
</div>
<div class="field">
<label>--learning_rate <span class="hint" id="lr-hint"></span></label>
<select id="lr" onchange="toggleLRCustom()">
<option value="1e-4" selected>1e-4</option>
<option value="5e-5">5e-5(安定)</option>
<option value="1e-5">1e-5(Lion推奨)</option>
<option value="2e-4">2e-4(高速)</option>
<option value="1e-3">1e-3</option>
<option value="custom">カスタム入力</option>
</select>
</div>
<div class="field" id="lr_custom_wrap" style="display:none">
<label>カスタム値</label>
<input type="text" id="lr_custom" placeholder="例: 3e-5">
</div>
</div>
</div>
<!-- LR SCHEDULER -->
<div class="section">
<div class="section-title">LRスケジューラ</div>
<div class="grid g4">
<div class="field">
<label>--lr_scheduler</label>
<select id="lr_sched" onchange="onSchedChange()">
<option value="constant" selected>constant</option>
<option value="cosine">cosine</option>
<option value="cosine_with_restarts">cosine_with_restarts</option>
<option value="constant_with_warmup">constant_with_warmup</option>
<option value="linear">linear</option>
<option value="polynomial">polynomial</option>
</select>
</div>
<div class="field" id="warmup_wrap" style="display:none">
<label>--lr_warmup_steps</label>
<input type="number" id="lr_warmup" value="100" min="0" step="10">
</div>
<div class="field" id="restart_wrap" style="display:none">
<label>--lr_scheduler_num_cycles</label>
<input type="number" id="lr_cycles" value="1" min="1">
</div>
<div class="field" id="poly_wrap" style="display:none">
<label>--lr_scheduler_power</label>
<input type="number" id="lr_power" value="1.0" min="0.1" step="0.1">
</div>
</div>
</div>
<!-- TIMESTEP -->
<div class="section">
<div class="section-title">Timestep / Flow</div>
<div class="grid g3">
<div class="field">
<label>--timestep_sampling</label>
<select id="ts">
<option value="shift" selected>shift(Z-Image推奨)</option>
<option value="sigmoid">sigmoid</option>
<option value="uniform">uniform</option>
<option value="logit_normal">logit_normal</option>
</select>
</div>
<div class="field">
<label>--discrete_flow_shift <span class="hint">Z-Image: 2.0</span></label>
<select id="dfs" onchange="toggleDFSCustom()">
<option value="2.0" selected>2.0</option>
<option value="1.0">1.0</option>
<option value="3.0">3.0</option>
<option value="custom">カスタム入力</option>
</select>
</div>
<div class="field" id="dfs_custom_wrap" style="display:none">
<label>カスタム値</label>
<input type="text" id="dfs_custom" placeholder="例: 1.5">
</div>
<div class="field">
<label>--weighting_scheme</label>
<select id="ws">
<option value="none" selected>none</option>
<option value="sigma_sqrt">sigma_sqrt</option>
<option value="logit_normal">logit_normal</option>
<option value="mode">mode</option>
</select>
</div>
</div>
</div>
<!-- PRECISION -->
<div class="section">
<div class="section-title">精度 / アテンション</div>
<div class="grid g2">
<div class="field">
<label>--mixed_precision</label>
<select id="mp">
<option value="bf16" selected>bf16(推奨)</option>
<option value="fp16">fp16</option>
<option value="no">no (fp32)</option>
</select>
</div>
<div class="field">
<label style="margin-bottom:8px;">アテンション</label>
<div class="check-group">
<label class="check-item"><input type="checkbox" id="sdpa" checked> --sdpa</label>
<label class="check-item"><input type="checkbox" id="xf"> --xformers</label>
</div>
</div>
</div>
</div>
<!-- VRAM -->
<div class="section">
<div class="section-title">VRAM最適化</div>
<div class="check-group" style="margin-bottom:14px;">
<label class="check-item"><input type="checkbox" id="gc" checked> --gradient_checkpointing</label>
<label class="check-item"><input type="checkbox" id="pdlw" checked> --persistent_data_loader_workers</label>
<label class="check-item"><input type="checkbox" id="fp8"> --fp8_base</label>
<label class="check-item"><input type="checkbox" id="fp8te"> --fp8_llm</label>
<label class="check-item"><input type="checkbox" id="bswap_patch"> --block_swap_optimizer_patch_params <span style="color:#f97316;font-size:0.65rem;margin-left:4px;">Lion+blocks_to_swap時に必要</span></label>
</div>
<div class="grid g4">
<div class="field">
<label>--blocks_to_swap <span class="hint">0=無効</span></label>
<input type="number" id="bswap" value="0" min="0" max="36">
</div>
</div>
</div>
<!-- ACCELERATE -->
<div class="section">
<div class="section-title">accelerate設定</div>
<div class="grid g4">
<div class="field">
<label>--num_cpu_threads_per_process</label>
<input type="number" id="cpu_t" value="1" min="1">
</div>
<div class="field">
<label>--num_processes <span class="hint">マルチGPU時</span></label>
<input type="number" id="nproc" value="1" min="1">
</div>
</div>
</div>
<button class="gen-btn" onclick="gen()">▶ コマンドを生成</button>
<div class="output-wrap">
<div class="output-header">
<span class="output-label">Generated Command</span>
<button class="copy-btn" id="copybtn" onclick="copyCmd()">Copy</button>
</div>
<pre id="output"></pre>
</div>
</div>
<!-- TOAST -->
<div class="toast" id="toast"></div>
<script>
let OS = 'win';
// ===== SETTINGS KEYS =====
// すべてのフォームIDを列挙(テキスト/数値/select)
const TEXT_IDS = ['dit','vae','te','ds','outdir','outname','lr_custom','dfs_custom'];
const SELECT_IDS = ['netmod','ndim','nalpha','opttype','lr','lr_sched','ts','dfs','ws','mp'];
const NUMBER_IDS = ['steps','save_steps','seed','workers','lr_warmup','lr_cycles','lr_power','cpu_t','nproc','bswap'];
const CHECK_IDS = ['sdpa','xf','gc','pdlw','fp8','fp8te','bswap_patch','use_loha'];
// ===== EXPORT =====
function exportSettings() {
const data = { _version: 1, os: OS };
TEXT_IDS.forEach(id => { const el = document.getElementById(id); if(el) data[id] = el.value; });
SELECT_IDS.forEach(id => { const el = document.getElementById(id); if(el) data[id] = el.value; });
NUMBER_IDS.forEach(id => { const el = document.getElementById(id); if(el) data[id] = el.value; });
CHECK_IDS.forEach(id => { const el = document.getElementById(id); if(el) data[id] = el.checked; });
const json = JSON.stringify(data, null, 2);
const blob = new Blob([json], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
const name = (document.getElementById('outname').value || 'lora-settings').replace(/[\\/:*?"<>|]/g,'_');
a.href = url;
a.download = name + '.json';
a.click();
URL.revokeObjectURL(url);
const btn = document.getElementById('exportBtn');
btn.classList.add('done');
btn.textContent = '✓ 保存しました';
setTimeout(() => { btn.classList.remove('done'); btn.textContent = '⬇ 設定をJSONで保存'; }, 2200);
}
// ===== IMPORT =====
function applySettings(data) {
if (!data || data._version !== 1) { showToast('⚠ 形式が不正なファイルです', true); return; }
if (data.os) setOS(data.os);
TEXT_IDS.forEach(id => { if (data[id] !== undefined) { const el = document.getElementById(id); if(el) el.value = data[id]; } });
SELECT_IDS.forEach(id => {
if (data[id] !== undefined) {
const el = document.getElementById(id);
if (!el) return;
const exists = Array.from(el.options).some(o => o.value === String(data[id]));
el.value = exists ? data[id] : el.options[0].value;
}
});
NUMBER_IDS.forEach(id => {
if (data[id] !== undefined) { const el = document.getElementById(id); if(el) el.value = data[id]; }
});
CHECK_IDS.forEach(id => { if (data[id] !== undefined) { const el = document.getElementById(id); if(el) el.checked = !!data[id]; } });
// UI状態を再同期
onSchedChange();
onOptimizerChange();
toggleLRCustom();
toggleDFSCustom();
gen();
showToast('✓ 設定を読み込みました');
}
function loadFile(file) {
if (!file) return;
// 拡張子またはMIMEタイプで判定(.jsonまたはapplication/json)
const isJson = file.name.toLowerCase().endsWith('.json') || file.type === 'application/json';
if (!isJson) { showToast('⚠ JSONファイルを選択してください', true); return; }
const reader = new FileReader();
reader.onload = e => {
try {
const raw = e.target.result;
const data = JSON.parse(raw);
applySettings(data);
const dz = document.getElementById('dropZone');
dz.classList.add('loaded');
dz.querySelector('.dz-text').innerHTML = `✓ <span>${file.name}</span> を読み込みました`;
} catch(err) {
console.error('JSON parse error:', err);
showToast('⚠ ファイルの読み込みに失敗しました: ' + err.message, true);
}
};
reader.onerror = () => showToast('⚠ ファイルの読み取りに失敗しました', true);
reader.readAsText(file, 'UTF-8');
}
// ===== DRAG & DROP =====
const dropZone = document.getElementById('dropZone');
['dragenter','dragover'].forEach(ev => {
dropZone.addEventListener(ev, e => { e.preventDefault(); dropZone.classList.add('drag-over'); });
});
['dragleave','dragend'].forEach(ev => {
dropZone.addEventListener(ev, () => dropZone.classList.remove('drag-over'));
});
dropZone.addEventListener('drop', e => {
e.preventDefault();
dropZone.classList.remove('drag-over');
const file = e.dataTransfer.files[0];
if (file) loadFile(file);
});
// ===== TOAST =====
function showToast(msg, isErr = false) {
const t = document.getElementById('toast');
t.textContent = msg;
t.className = 'toast' + (isErr ? ' err' : '');
void t.offsetWidth;
t.classList.add('show');
setTimeout(() => t.classList.remove('show'), 2800);
}
// ===== OS =====
function setOS(os) {
OS = os;
document.getElementById('btn-win').classList.toggle('active', os==='win');
document.getElementById('btn-lnx').classList.toggle('active', os==='lnx');
gen();
}
function syncAlpha() {
document.getElementById('nalpha').value = document.getElementById('ndim').value;
gen();
}
function onSchedChange() {
const v = document.getElementById('lr_sched').value;
const needsWarmup = ['constant_with_warmup','cosine_with_restarts','cosine','linear','polynomial'].includes(v);
const needsCycles = v === 'cosine_with_restarts';
const needsPoly = v === 'polynomial';
document.getElementById('warmup_wrap').style.display = needsWarmup ? 'block' : 'none';
document.getElementById('restart_wrap').style.display = needsCycles ? 'block' : 'none';
document.getElementById('poly_wrap').style.display = needsPoly ? 'block' : 'none';
gen();
}
function onOptimizerChange() {
const opt = document.getElementById('opttype').value;
const lrHint = document.getElementById('lr-hint');
if (opt === 'Lion' || opt === 'Lion8bit') {
lrHint.textContent = 'Lion推奨: 1e-5〜1e-4';
lrHint.style.color = '#f97316';
} else {
lrHint.textContent = '';
}
gen();
}
function toggleLRCustom() {
document.getElementById('lr_custom_wrap').style.display =
document.getElementById('lr').value === 'custom' ? 'block' : 'none';
gen();
}
function toggleDFSCustom() {
document.getElementById('dfs_custom_wrap').style.display =
document.getElementById('dfs').value === 'custom' ? 'block' : 'none';
gen();
}
function getLR() {
const v = document.getElementById('lr').value;
return v === 'custom' ? (document.getElementById('lr_custom').value || '1e-4') : v;
}
function getDFS() {
const v = document.getElementById('dfs').value;
return v === 'custom' ? (document.getElementById('dfs_custom').value || '2.0') : v;
}
function esc(s) { return s.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;'); }
// ===== COMMAND GENERATOR =====
function gen() {
const cont = OS === 'win' ? '^' : '\\';
const C = ` ${cont}`;
const v = id => document.getElementById(id)?.value ?? '';
const c = id => document.getElementById(id)?.checked ?? false;
const dit=v('dit'), vae=v('vae'), te=v('te'), ds=v('ds');
const outdir=v('outdir'), outname=v('outname');
const netmod=v('netmod'), ndim=v('ndim'), nalpha=v('nalpha');
const steps=v('steps'), saveS=v('save_steps'), seed=v('seed'), workers=v('workers');
const opttype=v('opttype'), mp=v('mp'), ts=v('ts'), ws=v('ws');
const cpuT=v('cpu_t'), nproc=v('nproc'), bswap=parseInt(v('bswap'))||0;
const lr_sched=v('lr_sched'), lr_warmup=v('lr_warmup'), lr_cycles=v('lr_cycles'), lr_power=v('lr_power');
const dfs=getDFS();
let raw = '';
raw += `accelerate launch --num_cpu_threads_per_process ${cpuT} --mixed_precision ${mp}`;
if (parseInt(nproc) > 1) raw += ` --num_processes ${nproc}`;
raw += `${C}\n`;
raw += ` src/musubi_tuner/zimage_train_network.py${C}\n`;
raw += ` --dit ${dit}${C}\n`;
raw += ` --vae ${vae}${C}\n`;
raw += ` --text_encoder ${te}${C}\n`;
raw += ` --dataset_config ${ds}${C}\n`;
let attn = [];
if (c('sdpa')) attn.push('--sdpa');
if (c('xf')) attn.push('--xformers');
attn.push(`--mixed_precision ${mp}`);
raw += ` ${attn.join(' ')}${C}\n`;
raw += ` --timestep_sampling ${ts} --weighting_scheme ${ws} --discrete_flow_shift ${dfs}${C}\n`;
raw += ` --optimizer_type ${opttype} --learning_rate ${getLR()}`;
if (c('gc')) raw += ' --gradient_checkpointing';
raw += `${C}\n`;
if (lr_sched !== 'constant') {
let sl = ` --lr_scheduler ${lr_sched}`;
const nw = ['constant_with_warmup','cosine_with_restarts','cosine','linear','polynomial'].includes(lr_sched);
if (nw && parseInt(lr_warmup) > 0) sl += ` --lr_warmup_steps ${lr_warmup}`;
if (lr_sched === 'cosine_with_restarts') sl += ` --lr_scheduler_num_cycles ${lr_cycles}`;
if (lr_sched === 'polynomial') sl += ` --lr_scheduler_power ${lr_power}`;
raw += `${sl}${C}\n`;
}
raw += ` --max_data_loader_n_workers ${workers}`;
if (c('pdlw')) raw += ' --persistent_data_loader_workers';
raw += `${C}\n`;
raw += ` --network_module ${netmod}${C}\n`;
raw += ` --network_dim ${ndim} --network_alpha ${nalpha}${C}\n`;
raw += ` --max_train_steps ${steps} --save_every_n_steps ${saveS} --seed ${seed}`;
if (c('fp8')) raw += `${C}\n --fp8_base`;
if (c('fp8te')) raw += `${C}\n --fp8_llm`;
if (bswap > 0) raw += `${C}\n --blocks_to_swap ${bswap}`;
if (c('bswap_patch'))raw += `${C}\n --block_swap_optimizer_patch_params`;
raw += `${C}\n --output_dir ${outdir} --output_name ${outname}`;
document.getElementById('output').innerHTML = esc(raw)
.replace(/(accelerate launch)/g, '<span class="c">$1</span>')
.replace(/(--[\w_-]+)/g, '<span class="k">$1</span>')
.replace(/(\^|\\)(?=\n)/g, '<span class="ct">$1</span>');
}
function copyCmd() {
const text = document.getElementById('output').innerText;
navigator.clipboard.writeText(text).then(() => {
const btn = document.getElementById('copybtn');
btn.textContent = '✓ Copied';
btn.classList.add('copied');
setTimeout(() => { btn.textContent = 'Copy'; btn.classList.remove('copied'); }, 2000);
});
}
document.querySelectorAll('input:not([type=file]), select').forEach(el => {
el.addEventListener('change', gen);
el.addEventListener('input', gen);
});
onSchedChange();
gen();
</script>
</body>
</html>