diff --git a/generation_orchestrator.py b/generation_orchestrator.py index 64c545c..5c2b419 100644 --- a/generation_orchestrator.py +++ b/generation_orchestrator.py @@ -422,6 +422,7 @@ def run_generation_loop( pending_batch = [] current_job = 0 total_generated = 0 + gen_index_offset = len(existing_data.get("items", [])) # Sequential index for deterministic sort ordering skipped_count = 0 job_durations = [] eta_start_time = time.time() @@ -1020,7 +1021,8 @@ def run_generation_loop( meta = create_image_metadata( conf, w, h, duration, current_seed, batch_idx, - actual_positive_prompt, actual_negative_prompt + actual_positive_prompt, actual_negative_prompt, + gen_index=gen_index_offset + total_generated ) if pos_hash or neg_hash: meta["conditioning_pos_hash"] = pos_hash diff --git a/image_generation.py b/image_generation.py index 9350995..6c0c445 100644 --- a/image_generation.py +++ b/image_generation.py @@ -141,10 +141,10 @@ def save_image_to_disk(image, output_dir, filename): return filepath -def create_image_metadata(config, width, height, duration, seed, batch_idx, actual_positive_prompt, actual_negative_prompt): +def create_image_metadata(config, width, height, duration, seed, batch_idx, actual_positive_prompt, actual_negative_prompt, gen_index=None): """ Create metadata dictionary for an image. - + Args: config: Configuration dictionary width: Image width @@ -154,7 +154,8 @@ def create_image_metadata(config, width, height, duration, seed, batch_idx, actu batch_idx: Batch index actual_positive_prompt: Final positive prompt (with triggers) actual_negative_prompt: Final negative prompt - + gen_index: Sequential generation index for deterministic sort ordering (optional, backwards-compatible) + Returns: dict: Metadata dictionary """ @@ -178,7 +179,7 @@ def create_image_metadata(config, width, height, duration, seed, batch_idx, actu if meta.get("attention_mode") == "default": meta.pop("attention_mode", None) - meta.update({ + update_dict = { "width": width, "height": height, "duration": duration, @@ -186,7 +187,10 @@ def create_image_metadata(config, width, height, duration, seed, batch_idx, actu "batch_idx": batch_idx, "positive": actual_positive_prompt, "negative": actual_negative_prompt - }) + } + if gen_index is not None: + update_dict["gen_index"] = gen_index + meta.update(update_dict) return meta diff --git a/resources/logic_pipeline.js b/resources/logic_pipeline.js index bd0b217..3b23a38 100644 --- a/resources/logic_pipeline.js +++ b/resources/logic_pipeline.js @@ -12,7 +12,7 @@ const ASYNC_PIPELINE_THRESHOLD = 500; // current sort/filter, used for "Go To Card #" and the #N tag on cards. function refreshIndices() { if (!activeData) return; - const sorted = activeData.slice().sort((a, b) => a.id - b.id); + const sorted = activeData.slice().sort((a, b) => (a.gen_index ?? a.id) - (b.gen_index ?? b.id)); idToIndexMap = new Map(sorted.map((item, index) => [item.id, index + 1])); } @@ -167,8 +167,8 @@ function setSecondarySort(id) { // Unified Comparison Helper function compareItems(a, b, sortKey) { switch (sortKey) { - case 'newest': return b.id - a.id; - case 'oldest': return a.id - b.id; + case 'newest': return (b.gen_index ?? b.id) - (a.gen_index ?? a.id); + case 'oldest': return (a.gen_index ?? a.id) - (b.gen_index ?? b.id); case 'fastest': return a.duration - b.duration; case 'favorited': return (b.favorited ? 1 : 0) - (a.favorited ? 1 : 0); case 'cfg': return a.cfg - b.cfg; @@ -199,9 +199,9 @@ function runMultiSort(list) { res = compareItems(a, b, currentSecondarySort); } - // 3. Fallback to ID (Stable sort) + // 3. Fallback to gen_index/ID (Stable sort, backwards-compatible) if (res === 0) { - return a.id - b.id; + return (a.gen_index ?? a.id) - (b.gen_index ?? b.id); } return res; }); @@ -313,10 +313,11 @@ function executePipeline() { }); // Apply sorting - // Fast path: for oldest/newest with no secondary sort, items from activeData.filter() - // are already in ID order — just reverse for newest + // Fast path: for oldest/newest with no secondary sort, activeData is stored newest-first + // (items are inserted/unshifted at position 0), so processedData after filter() is also newest-first. + // For 'oldest' we reverse; for 'newest' it's already correct. if ((currentSort === 'oldest' || currentSort === 'newest') && currentSecondarySort === 'none') { - if (currentSort === 'newest') { + if (currentSort === 'oldest') { processedData.reverse(); } } else { @@ -402,7 +403,7 @@ function binaryInsert(arr, item) { cmp = compareItems(arr[mid], item, currentSecondarySort); } if (cmp === 0) { - cmp = arr[mid].id - item.id; // Stable sort fallback + cmp = (arr[mid].gen_index ?? arr[mid].id) - (item.gen_index ?? item.id); // Stable sort fallback } if (cmp <= 0) { low = mid + 1; diff --git a/resources/logic_virtual.js b/resources/logic_virtual.js index 0a168d0..7effdbe 100644 --- a/resources/logic_virtual.js +++ b/resources/logic_virtual.js @@ -242,7 +242,7 @@ function renderVisibleItems(forcePositionUpdate = false) { itemsToShow.forEach((data, offsetIndex) => { const globalIndex = visibleRange.start + offsetIndex; - const displayNumber = globalIndex + 1; // 1-based position in current sorted view + const genOrderNumber = idToIndexMap.get(data.id) || (globalIndex + 1); // Generation order number (backwards-compatible) const row = Math.floor(globalIndex / columnsCount); const col = globalIndex % columnsCount; @@ -259,9 +259,9 @@ function renderVisibleItems(forcePositionUpdate = false) { card.style.width = `${itemWidth - 10}px`; card.style.zIndex = globalIndex; - // Update card number to reflect sorted position + // Update card number to reflect generation order const indexTag = card.querySelector('.index-tag'); - if (indexTag) indexTag.textContent = `#${displayNumber}`; + if (indexTag) indexTag.textContent = `#${genOrderNumber}`; nodeMap.set(data.id, card); fragment.appendChild(card); @@ -286,7 +286,7 @@ function renderVisibleItems(forcePositionUpdate = false) { // Update card number when position changes (sort/filter changed) const indexTag = card.querySelector('.index-tag'); - if (indexTag) indexTag.textContent = `#${displayNumber}`; + if (indexTag) indexTag.textContent = `#${genOrderNumber}`; } } });