Files
ketle-manandClaude Sonnet 5 98d848ed15 半自動マンガに「画像を一括生成(Nanobanana)」を追加、Nanobananaの「2K」生成トグル追加+実効性のないI2I強度設定を削除 (v1.29.0)
- スクリプトタブの半自動マンガ作成に、既存「画像を一括生成(I2I)」(Workflow Studio連携)と対になる、Nanobanana(Gemini画像生成API)を使うバッチI2I生成機能を新規ボタン・専用モーダルとして追加
- コマのbboxアスペクト比に近いNanobanana解像度プリセットを自動選択し、生成結果はコマのbboxへスケール調整して挿入
- nanobanana.js内の「APIキー確認」「生成API呼び出し」「保存+Eagle自動保存」ロジックを共有関数として切り出し、既存Nanobananaタブと半自動マンガ側で共有
- Gemini画像生成APIの公式ドキュメント確認により、I2I変化度合いを数値指定するパラメータ(denoising strength等)が存在しないことが判明。既存Nanobananaタブ・半自動マンガモーダル両方から、効果のなかったI2I強度スライダーを削除
- 同時にGenerationConfig.imageConfigのimageSizeフィールド("1K"/"2K"/"4K"、対応モデルのみ)を発見し、両UIに「2K」チェックボックスを追加。選択したアスペクト比を維持したまま約2倍相当の解像度で生成できるようにした(py/ccc.pyのhandle_nanobanana_generateにimage_sizeパラメータを追加)
- Kaptureで実機E2E確認済み: 既存Nanobananaタブの回帰なし、新モーダルの動作・2K生成(1024→2048を実測確認)・strength削除・保存後リロードでの画像永続化を確認

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-05 13:23:14 +09:00

105 lines
5.3 KiB
JavaScript

// ============================================================
// 半自動マンガ作成: スクリプト⇄コマ対応付けロジック(DOM非依存)
// スクリプトタブのコマ割りデータ(セリフ・画像プロンプト)と、レイアウトタブの
// テンプレート適用済みページの実パネルを「パネル番号順」で対応付ける。
// コマ割りの自動生成(グリッド計算等)は行わない。ユーザーが事前にレイアウトタブで
// 選択・適用した既存テンプレートのパネル番号(06c-template-wizard.jsがテンプレートSVGの
// polygon id="panel_N" から採番したもの)をそのまま処理順として使う。
// type="module" として読み込まれる。DOM/state に依存しないため単体テスト可能。
// ============================================================
/**
* スクリプトの1ページ分のコマ割りデータを、実パネル配列(state.activePage.panels相当)と
* パネル番号順に対応付ける。
* @param {{scene?: string, panels: Array<{dialogues: Array<{character:string,text:string}>, imagePrompt?: string}>}} scriptPage
* @param {Array<{id: string, number?: number, points?: string}>} panels
* @returns {{
* mapped: Array<{panelId: string, panelNumber: number, scriptPanelIndex: number, dialogues: Array, imagePrompt: string}>,
* warning: {templateCount: number, scriptCount: number} | null
* }}
*/
function mapScriptPageToPanels(scriptPage, panels) {
const scriptPanels = (scriptPage && Array.isArray(scriptPage.panels)) ? scriptPage.panels : [];
const sortedPanels = Array.isArray(panels)
? [...panels].sort((a, b) => (a.number ?? 0) - (b.number ?? 0))
: [];
const count = Math.min(sortedPanels.length, scriptPanels.length);
const mapped = [];
for (let i = 0; i < count; i++) {
const panel = sortedPanels[i];
const scriptPanel = scriptPanels[i];
mapped.push({
panelId: panel.id,
panelNumber: panel.number ?? (i + 1),
scriptPanelIndex: i,
dialogues: Array.isArray(scriptPanel.dialogues) ? scriptPanel.dialogues : [],
imagePrompt: typeof scriptPanel.imagePrompt === 'string' ? scriptPanel.imagePrompt : '',
});
}
const warning = (sortedPanels.length !== scriptPanels.length)
? { templateCount: sortedPanels.length, scriptCount: scriptPanels.length }
: null;
return { mapped, warning };
}
// SDXL標準解像度(横長/やや横長/正方形/やや縦長/縦長の5段階)。
// 参考実装ComfyUI_sloppy-comicのsdxl_ratio_to_resと同じ考え方で、コマのbboxアスペクト比に
// 最も近い段階を選ぶ(画像生成をコマのサイズに応じて行いたいというユーザー要望への対応)。
const SDXL_RESOLUTIONS = [
{ key: 'horizontal_wide', width: 1344, height: 768 },
{ key: 'horizontal', width: 1216, height: 832 },
{ key: 'square', width: 1024, height: 1024 },
{ key: 'vertical', width: 832, height: 1216 },
{ key: 'vertical_tall', width: 768, height: 1344 },
];
/**
* コマのbboxアスペクト比(width/height)に最も近いSDXL標準解像度を選ぶ。
* 対数スケールで比較する(例: 2倍横長 と 0.5倍(=2倍縦長) を対称に扱うため)。
* @param {number} aspectRatio コマのwidth/height
* @returns {{width: number, height: number}}
*/
function pickSdxlResolution(aspectRatio) {
const targetLogRatio = Math.log(aspectRatio > 0 ? aspectRatio : 1);
let best = SDXL_RESOLUTIONS[2];
let bestDiff = Infinity;
for (const r of SDXL_RESOLUTIONS) {
const diff = Math.abs(targetLogRatio - Math.log(r.width / r.height));
if (diff < bestDiff) { bestDiff = diff; best = r; }
}
return { width: best.width, height: best.height };
}
// Nanobananaの解像度プリセット(templates/index.htmlの#nanobanana-resolutionセレクトと同じ5種類)。
// 値はSDXL_RESOLUTIONSと現状同一だが、Nanobanana側のプリセットが将来変更された場合に
// 個別追従できるよう、意図的に独立した定数として持つ。
const NANOBANANA_RESOLUTIONS = [
{ key: 'horizontal_wide', width: 1344, height: 768 },
{ key: 'horizontal', width: 1216, height: 832 },
{ key: 'square', width: 1024, height: 1024 },
{ key: 'vertical', width: 832, height: 1216 },
{ key: 'vertical_tall', width: 768, height: 1344 },
];
/**
* コマのbboxアスペクト比(width/height)に最も近いNanobanana解像度プリセットを選ぶ。
* pickSdxlResolution()と同じ対数スケール比較ロジック。
* @param {number} aspectRatio コマのwidth/height
* @returns {{width: number, height: number}}
*/
function pickNanobananaResolution(aspectRatio) {
const targetLogRatio = Math.log(aspectRatio > 0 ? aspectRatio : 1);
let best = NANOBANANA_RESOLUTIONS[2];
let bestDiff = Infinity;
for (const r of NANOBANANA_RESOLUTIONS) {
const diff = Math.abs(targetLogRatio - Math.log(r.width / r.height));
if (diff < bestDiff) { bestDiff = diff; best = r; }
}
return { width: best.width, height: best.height };
}
export { mapScriptPageToPanels, pickSdxlResolution, pickNanobananaResolution };