Skip to content

Commit f8a8729

Browse files
committed
upscale support
1 parent b498a31 commit f8a8729

File tree

5 files changed

+87
-6
lines changed

5 files changed

+87
-6
lines changed

index.html

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
<button class="menu-button" data-mode="i2i" id="btnModeI2I" data-i18n="modes.i2i">I2I生成</button>
3030
<button class="menu-button" data-mode="i2iloop" id="btnModeI2ILoop" data-i18n="modes.i2iLoop">I2Iループ生成</button>
3131
<button class="menu-button" data-mode="i2iangle" id="btnModeI2IAngle" data-i18n="modes.i2iAngle">I2Iアングル生成</button>
32+
<button class="menu-button" data-mode="upscaleloop" id="btnModeUpscaleLoop" data-i18n="modes.upscaleLoop">Upscaleループ</button>
3233
</div>
3334
</div>
3435
<div class="mode-config-panel" id="modeConfigPanel">
@@ -156,6 +157,23 @@
156157
<button class="generate-button" id="btnGenerateI2IAngle" style="flex-shrink:0;" data-i18n="config.angleGenerate">アングル生成</button>
157158
<button class="generate-button cancel-button" id="btnCancelI2IAngle" style="margin-top:8px;flex-shrink:0;display:none;" data-i18n="config.cancel">キャンセル</button>
158159
</div>
160+
<div id="upscaleloopModeConfig" class="mode-config-content">
161+
<div class="form-group" style="flex-shrink:0;">
162+
<label data-i18n="config.workflowUpscale">ワークフロー (Upscale)</label>
163+
<div class="workflow-display" id="upscaleloopWorkflowDisplay" data-i18n="config.notSelected">未選択</div>
164+
</div>
165+
<div class="form-group" style="flex-shrink:0;">
166+
<label><span data-i18n="config.inputImage">入力画像</span> <span class="i2i-image-count" id="upscaleloopImageCount"></span></label>
167+
<div class="i2i-image-upload-area" id="upscaleloopImageUploadArea">
168+
<input type="file" id="upscaleloopImageInput" accept="image/*" multiple style="display:none;">
169+
<div class="i2i-image-preview-grid" id="upscaleloopImagePreview">
170+
<div class="i2i-image-placeholder" data-i18n="config.dropImage">クリックまたはドラッグ&ドロップで画像を選択(複数可)</div>
171+
</div>
172+
</div>
173+
</div>
174+
<button class="generate-button" id="btnGenerateUpscaleLoop" style="flex-shrink:0;" data-i18n="config.upscaleGenerate">アップスケール生成</button>
175+
<button class="generate-button cancel-button" id="btnCancelUpscaleLoop" style="margin-top:8px;flex-shrink:0;display:none;" data-i18n="config.cancel">キャンセル</button>
176+
</div>
159177
</div>
160178
<div class="center-container">
161179
<div class="center-tabs">

js/core/app-init.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,21 @@ $('btnModeLoop').addEventListener('click', () => switchMode('loop'));
4141
$('btnModeI2I').addEventListener('click', () => switchMode('i2i'));
4242
$('btnModeI2ILoop').addEventListener('click', () => switchMode('i2iloop'));
4343
$('btnModeI2IAngle').addEventListener('click', () => switchMode('i2iangle'));
44+
$('btnModeUpscaleLoop').addEventListener('click', () => switchMode('upscaleloop'));
4445
$('btnOpenWorkflow').addEventListener('click', openWorkflowEditor);
4546
$('btnRandomSeed').addEventListener('click', randomSeed);
4647
$('btnGenerateNormal').addEventListener('click', generateImageNormal);
4748
$('btnGenerateLoop').addEventListener('click', generateImageLoop);
4849
$('btnGenerateI2I').addEventListener('click', generateImageI2I);
4950
$('btnGenerateI2ILoop').addEventListener('click', generateImageI2ILoop);
5051
$('btnGenerateI2IAngle').addEventListener('click', generateImageI2IAngle);
52+
$('btnGenerateUpscaleLoop').addEventListener('click', generateImageUpscaleLoop);
5153
$('btnCancelNormal').addEventListener('click', cancelGeneration);
5254
$('btnCancelLoop').addEventListener('click', cancelGeneration);
5355
$('btnCancelI2I').addEventListener('click', cancelGeneration);
5456
$('btnCancelI2ILoop').addEventListener('click', cancelGeneration);
5557
$('btnCancelI2IAngle').addEventListener('click', cancelGeneration);
58+
$('btnCancelUpscaleLoop').addEventListener('click', cancelGeneration);
5659
$('width').addEventListener('change', (e) => normalizeImageSize(e.target));
5760
$('height').addEventListener('change', (e) => normalizeImageSize(e.target));
5861
$('btnTabBasic').addEventListener('click', () => switchTab('basic'));
@@ -77,6 +80,7 @@ updateI2IAngleLineCount();
7780
setupI2IImageUpload('i2iImageInput', 'i2iImagePreview', 'i2iImageUploadArea', () => i2iUploadedFileNames, (names) => { i2iUploadedFileNames = names; }, 'i2iImageCount');
7881
setupI2IImageUpload('i2iloopImageInput', 'i2iloopImagePreview', 'i2iloopImageUploadArea', () => i2iloopUploadedFileNames, (names) => { i2iloopUploadedFileNames = names; }, 'i2iloopImageCount');
7982
setupI2IImageUpload('i2iangleImageInput', 'i2iangleImagePreview', 'i2iangleImageUploadArea', () => i2iangleUploadedFileNames, (names) => { i2iangleUploadedFileNames = names; }, 'i2iangleImageCount');
83+
setupI2IImageUpload('upscaleloopImageInput', 'upscaleloopImagePreview', 'upscaleloopImageUploadArea', () => upscaleloopUploadedFileNames, (names) => { upscaleloopUploadedFileNames = names; }, 'upscaleloopImageCount');
8084
$('i2iangleImageInput').addEventListener('change', (e) => {
8185
const file = e.target.files[0];
8286
if (file && cameraWidgetInstance) {

js/generator/generator.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ let workflowEditorInitialized = false;
55
let i2iUploadedFileNames = [];
66
let i2iloopUploadedFileNames = [];
77
let i2iangleUploadedFileNames = [];
8+
let upscaleloopUploadedFileNames = [];
89
function showCancelButton(mode) {
910
const btnId = 'btnCancel' + mode.charAt(0).toUpperCase() + mode.slice(1);
1011
const btn = $(btnId);
@@ -591,3 +592,49 @@ $('generationStatus').textContent = I18nManager.t('status.error');
591592
$('generationStatus').style.color = '#f44336';
592593
} finally { isGenerating = false; isCancelled = false; hideCancelButton('i2IAngle'); setGenerateButtonGenerating('i2IAngle', false); }
593594
}
595+
function generateImageUpscaleLoop() {
596+
if (upscaleloopUploadedFileNames.length === 0) { createToastError(I18nManager.t('toast.inputError'), I18nManager.t('toast.uploadImage')); return; }
597+
generateImageUpscaleLoopExec();
598+
}
599+
async function generateImageUpscaleLoopExec() {
600+
if (isGenerating) return;
601+
if (upscaleloopUploadedFileNames.length === 0) { createToastError(I18nManager.t('toast.inputError'), I18nManager.t('toast.uploadImage')); return; }
602+
isGenerating = true;
603+
isCancelled = false;
604+
showCancelButton('upscaleLoop');
605+
setGenerateButtonGenerating('upscaleLoop', true);
606+
try {
607+
const baseWorkflow = await comfyUIWorkflowRepository.getEnabledWorkflowByType("Upscaler");
608+
if (!baseWorkflow) {
609+
ErrorGuideDialog.show(ErrorGuideDialog.ERROR_TYPES.WORKFLOW_NOT_FOUND, { workflowType: 'Upscaler' });
610+
$('generationStatus').textContent = I18nManager.t('status.workflowNotSet');
611+
$('generationStatus').style.color = '#f44336';
612+
return;
613+
}
614+
const imageCount = upscaleloopUploadedFileNames.length;
615+
$('generationStatus').textContent = I18nManager.t('status.generatingProgress').replace('{current}', 0).replace('{total}', imageCount);
616+
$('generationStatus').style.color = '#ff9800';
617+
resetGenerationTimeStats();
618+
for (let imgIdx = 0; imgIdx < imageCount; imgIdx++) {
619+
if (isCancelled) { $('generationStatus').textContent = I18nManager.t('status.cancelled'); $('generationStatus').style.color = '#ff9800'; break; }
620+
const uploadFileName = upscaleloopUploadedFileNames[imgIdx];
621+
$('generationStatus').textContent = I18nManager.t('status.generatingProgress').replace('{current}', imgIdx + 1).replace('{total}', imageCount);
622+
const requestData = { uploadFileName: uploadFileName };
623+
const workflow = comfyuiReplacePlaceholders(baseWorkflow, requestData, 'Upscaler');
624+
console.log('Generated Upscale Loop Workflow JSON:', JSON.stringify(workflow, null, 2));
625+
const startTime = performance.now();
626+
const result = await executeWorkflow(workflow);
627+
const endTime = performance.now();
628+
updateGenerationTimeStats(Math.round(endTime - startTime));
629+
if (result && result.image) {
630+
const historyConfig = [];
631+
displayGeneratedImage(result.image, imgIdx + 1, 'Upscale: ' + uploadFileName, historyConfig);
632+
}
633+
}
634+
if (!isCancelled) { $('generationStatus').textContent = I18nManager.t('status.completed'); $('generationStatus').style.color = '#4caf50'; }
635+
} catch (error) {
636+
ErrorGuideDialog.showForError(error, { errorDetail: error.message });
637+
$('generationStatus').textContent = I18nManager.t('status.error');
638+
$('generationStatus').style.color = '#f44336';
639+
} finally { isGenerating = false; isCancelled = false; hideCancelButton('upscaleLoop'); setGenerateButtonGenerating('upscaleLoop', false); }
640+
}

js/i18n/sui-i18n.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,23 @@ const I18nManager = (function() {
1919
t2iLoop: "T2Iプロンプトループ",
2020
i2i: "I2I",
2121
i2iLoop: "I2Iプロンプトループ",
22-
i2iAngle: "I2Iアングル"
22+
i2iAngle: "I2Iアングル",
23+
upscaleLoop: "Upscaleループ"
2324
},
2425
config: {
2526
normalGeneration: "通常生成",
2627
workflow: "ワークフロー",
2728
workflowT2I: "ワークフロー (T2I)",
2829
workflowI2I: "ワークフロー (I2I)",
30+
workflowUpscale: "ワークフロー (Upscale)",
2931
notSelected: "未選択",
3032
generateCount: "生成枚数",
3133
generateCountPerImage: "各画像の生成枚数",
3234
generate: "生成",
3335
cancel: "キャンセル",
3436
loopGenerate: "ループ生成",
3537
angleGenerate: "アングル生成",
38+
upscaleGenerate: "アップスケール生成",
3639
promptPosition: "追加プロンプト位置",
3740
positionStart: "Start(先頭に追加)",
3841
positionEnd: "End(末尾に追加)",
@@ -299,20 +302,23 @@ const I18nManager = (function() {
299302
t2iLoop: "T2I Prompt Loop",
300303
i2i: "I2I",
301304
i2iLoop: "I2I Prompt Loop",
302-
i2iAngle: "I2I Angle"
305+
i2iAngle: "I2I Angle",
306+
upscaleLoop: "Upscale Loop"
303307
},
304308
config: {
305309
normalGeneration: "Normal Generation",
306310
workflow: "Workflow",
307311
workflowT2I: "Workflow (T2I)",
308312
workflowI2I: "Workflow (I2I)",
313+
workflowUpscale: "Workflow (Upscale)",
309314
notSelected: "Not Selected",
310315
generateCount: "Generate Count",
311316
generateCountPerImage: "Generate Count Per Image",
312317
generate: "Generate",
313318
cancel: "Cancel",
314319
loopGenerate: "Loop Generate",
315320
angleGenerate: "Angle Generate",
321+
upscaleGenerate: "Upscale Generate",
316322
promptPosition: "Additional Prompt Position",
317323
positionStart: "Start (Add to beginning)",
318324
positionEnd: "End (Add to end)",
@@ -579,20 +585,23 @@ const I18nManager = (function() {
579585
t2iLoop: "T2I提示词循环",
580586
i2i: "I2I",
581587
i2iLoop: "I2I提示词循环",
582-
i2iAngle: "I2I角度"
588+
i2iAngle: "I2I角度",
589+
upscaleLoop: "Upscale循环"
583590
},
584591
config: {
585592
normalGeneration: "普通生成",
586593
workflow: "工作流",
587594
workflowT2I: "工作流 (T2I)",
588595
workflowI2I: "工作流 (I2I)",
596+
workflowUpscale: "工作流 (Upscale)",
589597
notSelected: "未选择",
590598
generateCount: "生成数量",
591599
generateCountPerImage: "每张图片生成数量",
592600
generate: "生成",
593601
cancel: "取消",
594602
loopGenerate: "循环生成",
595603
angleGenerate: "角度生成",
604+
upscaleGenerate: "放大生成",
596605
promptPosition: "附加提示词位置",
597606
positionStart: "Start(添加到开头)",
598607
positionEnd: "End(添加到末尾)",

js/ui/ui-controller.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@ currentMode = mode;
33
document.querySelectorAll('.menu-button[data-mode]').forEach(btn => btn.classList.remove('active'));
44
document.querySelector(`[data-mode="${mode}"]`).classList.add('active');
55
document.querySelectorAll('.mode-config-content').forEach(el => el.classList.remove('active'));
6-
const modeNameKeys = { 'normal': 'modes.t2i', 'loop': 'modes.t2iLoop', 'wildcard': 'modes.t2i', 'i2i': 'modes.i2i', 'i2iloop': 'modes.i2iLoop', 'i2iangle': 'modes.i2iAngle' };
6+
const modeNameKeys = { 'normal': 'modes.t2i', 'loop': 'modes.t2iLoop', 'wildcard': 'modes.t2i', 'i2i': 'modes.i2i', 'i2iloop': 'modes.i2iLoop', 'i2iangle': 'modes.i2iAngle', 'upscaleloop': 'modes.upscaleLoop' };
77
$('modeConfigTitle').textContent = I18nManager.t(modeNameKeys[mode]);
8-
const configMap = { 'normal': 'normalModeConfig', 'loop': 'loopModeConfig', 'wildcard': 'wildcardModeConfig', 'i2i': 'i2iModeConfig', 'i2iloop': 'i2iloopModeConfig', 'i2iangle': 'i2iangleModeConfig' };
8+
const configMap = { 'normal': 'normalModeConfig', 'loop': 'loopModeConfig', 'wildcard': 'wildcardModeConfig', 'i2i': 'i2iModeConfig', 'i2iloop': 'i2iloopModeConfig', 'i2iangle': 'i2iangleModeConfig', 'upscaleloop': 'upscaleloopModeConfig' };
99
if (configMap[mode]) $(configMap[mode]).classList.add('active');
1010
}
1111
async function openWorkflowEditor() {
@@ -47,14 +47,17 @@ try {
4747
const workflows = await comfyUIWorkflowRepository.getAllWorkflows();
4848
const t2iWorkflow = workflows.find(w => w.type === 'T2I' && w.enabled);
4949
const i2iWorkflow = workflows.find(w => w.type === 'I2I' && w.enabled);
50+
const upscaleWorkflow = workflows.find(w => w.type === 'Upscaler' && w.enabled);
5051
const t2iName = t2iWorkflow ? t2iWorkflow.name : I18nManager.t('config.notSelected');
5152
const i2iName = i2iWorkflow ? i2iWorkflow.name : I18nManager.t('config.notSelected');
53+
const upscaleName = upscaleWorkflow ? upscaleWorkflow.name : I18nManager.t('config.notSelected');
5254
$('normalWorkflowDisplay').textContent = t2iName;
5355
$('loopWorkflowDisplay').textContent = t2iName;
5456
$('i2iWorkflowDisplay').textContent = i2iName;
5557
$('i2iloopWorkflowDisplay').textContent = i2iName;
5658
$('i2iangleWorkflowDisplay').textContent = i2iName;
57-
$('activeWorkflow').textContent = currentMode.startsWith('i2i') ? i2iName : t2iName;
59+
$('upscaleloopWorkflowDisplay').textContent = upscaleName;
60+
$('activeWorkflow').textContent = currentMode.startsWith('i2i') ? i2iName : (currentMode === 'upscaleloop' ? upscaleName : t2iName);
5861
} catch (error) { console.error('ワークフロー表示更新エラー:', error); }
5962
}
6063
function hookWorkflowRepository() {

0 commit comments

Comments
 (0)