refactor(tests/ui): Remove 'subfolder' support

This commit is contained in:
Vito Sansevero
2025-06-28 08:55:25 -07:00
parent 5d1f01e6cb
commit e17fdddcd7
2 changed files with 124 additions and 52 deletions
+16 -28
View File
@@ -61,14 +61,12 @@ class TestKikoSaveImageLogic:
assert filename.startswith("test_prefix_")
assert filename.endswith("_00000.png")
# Test with subfolder
full_path, filename = get_save_image_path(
"test", 1, ".jpg", temp_dir, "subfolder"
)
# Test with empty subfolder (standard behavior)
full_path, filename = get_save_image_path("test", 1, ".jpg", temp_dir, "")
expected_subfolder = os.path.join(temp_dir, "subfolder")
assert full_path.startswith(expected_subfolder)
assert os.path.exists(expected_subfolder) # Should be created
assert full_path.startswith(temp_dir)
assert filename.startswith("test_")
assert filename.endswith("_00001.jpg")
def test_create_png_metadata(self):
"""Test PNG metadata creation"""
@@ -115,6 +113,7 @@ class TestKikoSaveImageLogic:
assert enhanced["format"] == "PNG"
assert enhanced["compress_level"] == 6
assert enhanced["dimensions"] == "32x32"
assert enhanced["popup"] is True # Default popup value
assert "file_size" in enhanced
# Verify file was saved
@@ -151,7 +150,7 @@ class TestKikoSaveImageLogic:
assert enhanced["filename"].endswith(".jpg")
# Verify file exists and can be loaded
filepath = os.path.join(temp_dir, result["filename"])
filepath = os.path.join(temp_dir, results[0]["filename"])
assert os.path.exists(filepath)
saved_img = Image.open(filepath)
@@ -315,7 +314,7 @@ class TestKikoSaveImageNode:
assert "quality" in optional
assert "png_compress_level" in optional
assert "webp_lossless" in optional
assert "subfolder" in optional
assert "popup" in optional
# Check hidden inputs
hidden = input_types["hidden"]
@@ -343,7 +342,7 @@ class TestKikoSaveImageNode:
mock_enhanced = [
{
"filename": "test_00001_00000.png",
"subfolder": "",
"popup": True,
"type": "output",
"format": "PNG",
"file_size": 1024,
@@ -385,7 +384,7 @@ class TestKikoSaveImageNode:
quality=90,
png_compress_level=4,
webp_lossless=False,
subfolder="",
popup=True,
)
def test_validate_inputs_invalid_webp_lossless(self):
@@ -399,33 +398,22 @@ class TestKikoSaveImageNode:
quality=90,
png_compress_level=4,
webp_lossless="not_boolean",
subfolder="",
popup=True,
)
def test_validate_inputs_invalid_subfolder(self):
"""Test validation with invalid subfolder"""
def test_validate_inputs_invalid_popup(self):
"""Test validation with invalid popup"""
images = torch.rand(1, 32, 32, 3)
# Non-string subfolder
with pytest.raises(ValueError, match="subfolder must be a string"):
# Non-boolean popup
with pytest.raises(ValueError, match="popup must be a boolean"):
self.node.validate_inputs(
images=images,
format="PNG",
quality=90,
png_compress_level=4,
webp_lossless=False,
subfolder=123,
)
# Dangerous subfolder paths
with pytest.raises(ValueError, match="subfolder cannot contain"):
self.node.validate_inputs(
images=images,
format="PNG",
quality=90,
png_compress_level=4,
webp_lossless=False,
subfolder="../dangerous",
popup="not_boolean",
)
@patch("kikotools.tools.kiko_save_image.node.process_image_batch")
+108 -24
View File
@@ -25,6 +25,16 @@ kiko-image-viewer {
resize: both;
min-width: 300px;
min-height: 200px;
transition: background 0.3s ease, box-shadow 0.3s ease, border 0.3s ease;
}
kiko-image-viewer.rolled-up {
background: transparent;
box-shadow: none;
border: none;
overflow: visible;
resize: none;
min-height: auto;
}
kiko-image-viewer.dragging {
@@ -41,6 +51,16 @@ kiko-image-viewer.dragging .kiko-viewer-header {
max-height: calc(80vh - 80px);
overflow-y: auto;
overflow-x: hidden;
transition: max-height 0.3s cubic-bezier(0.4, 0, 0.2, 1),
padding 0.3s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.3s ease;
}
kiko-image-viewer.rolled-up .kiko-viewer-container {
max-height: 0;
padding: 0 12px;
opacity: 0;
overflow: hidden;
}
.kiko-viewer-header {
@@ -55,6 +75,31 @@ kiko-image-viewer.dragging .kiko-viewer-header {
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
}
.kiko-viewer-header::after {
content: '';
position: absolute;
bottom: 2px;
left: 50%;
transform: translateX(-50%);
width: 30px;
height: 3px;
background: rgba(255, 255, 255, 0.3);
border-radius: 2px;
opacity: 0;
transition: opacity 0.3s ease;
}
kiko-image-viewer.rolled-up .kiko-viewer-header::after {
opacity: 1;
}
kiko-image-viewer.rolled-up .kiko-viewer-header {
border-bottom: none;
border-radius: 12px;
box-shadow: 0 4px 16px rgba(0,0,0,0.3);
}
.kiko-viewer-title {
@@ -342,20 +387,17 @@ function formatFileSize(bytes) {
}
// Open image in new tab
function openImageInTab(imagePath, subfolder = '') {
console.log(`KikoSaveImage: Attempting to open image: ${imagePath}, subfolder: ${subfolder}`);
function openImageInTab(imagePath, enablePopup = true) {
console.log(`KikoSaveImage: Attempting to open image: ${imagePath}`);
// Note: enablePopup parameter is kept for compatibility but not used
// since popup now controls viewer visibility, not individual image clicks
const basePath = window.location.origin;
let fullPath;
// Properly encode the filename for URL
const encodedFilename = encodeURIComponent(imagePath);
const encodedSubfolder = subfolder ? encodeURIComponent(subfolder) : '';
if (subfolder) {
fullPath = `${basePath}/api/view?filename=${encodedFilename}&subfolder=${encodedSubfolder}&type=output`;
} else {
fullPath = `${basePath}/api/view?filename=${encodedFilename}&type=output`;
}
const fullPath = `${basePath}/api/view?filename=${encodedFilename}&type=output`;
console.log(`KikoSaveImage: Opening URL: ${fullPath}`);
@@ -385,7 +427,9 @@ class KikoImageViewer extends HTMLElement {
this.selectedImages = new Set();
this.isDragging = false;
this.isMinimized = false;
this.isRolledUp = false;
this.dragOffset = { x: 0, y: 0 };
this.lastHeaderClick = 0;
// Always start at default position
}
@@ -398,6 +442,7 @@ class KikoImageViewer extends HTMLElement {
if (name === 'data' && newValue) {
try {
this.imageData = JSON.parse(newValue);
this.autoUnrollOnNewImages();
this.render();
} catch (e) {
console.error('KikoImageViewer: Failed to parse image data:', e);
@@ -406,16 +451,20 @@ class KikoImageViewer extends HTMLElement {
}
setImageData(data) {
console.log('KikoImageViewer: setImageData called with:', data);
console.log('KikoImageViewer: First image data:', data[0]);
this.imageData = data;
this.autoUnrollOnNewImages();
this.render();
this.setupEventListeners();
}
setupEventListeners() {
// Window dragging functionality
// Window dragging and double-click functionality
const header = this.querySelector('.kiko-viewer-header');
if (header) {
header.addEventListener('mousedown', this.startDrag.bind(this));
header.addEventListener('dblclick', this.handleHeaderDoubleClick.bind(this));
}
// Control buttons
@@ -466,6 +515,11 @@ class KikoImageViewer extends HTMLElement {
}
startDrag(e) {
// Don't start dragging if clicking on control buttons
if (e.target.closest('.kiko-viewer-controls')) {
return;
}
this.isDragging = true;
const rect = this.getBoundingClientRect();
this.dragOffset = {
@@ -507,6 +561,33 @@ class KikoImageViewer extends HTMLElement {
this.classList.remove('dragging');
}
handleHeaderDoubleClick(e) {
// Don't toggle if clicking on control buttons
if (e.target.closest('.kiko-viewer-controls')) {
return;
}
this.toggleRollUp();
}
autoUnrollOnNewImages() {
// Auto-unroll if currently rolled up and we have new image data
if (this.isRolledUp && this.imageData && this.imageData.length > 0) {
this.isRolledUp = false;
this.classList.remove('rolled-up');
}
}
toggleRollUp() {
this.isRolledUp = !this.isRolledUp;
if (this.isRolledUp) {
this.classList.add('rolled-up');
} else {
this.classList.remove('rolled-up');
}
}
toggleMinimize() {
this.isMinimized = !this.isMinimized;
const container = this.querySelector('.kiko-viewer-container');
@@ -623,16 +704,14 @@ class KikoImageViewer extends HTMLElement {
setTimeout(() => {
const imageData = this.imageData[index];
if (imageData) {
openImageInTab(imageData.filename, imageData.subfolder);
openImageInTab(imageData.filename);
}
}, i * 150);
});
}
downloadImage(imageData) {
const imageUrl = imageData.subfolder
? `${window.location.origin}/api/view?filename=${encodeURIComponent(imageData.filename)}&subfolder=${encodeURIComponent(imageData.subfolder)}&type=${imageData.type}`
: `${window.location.origin}/api/view?filename=${encodeURIComponent(imageData.filename)}&type=${imageData.type}`;
const imageUrl = `${window.location.origin}/api/view?filename=${encodeURIComponent(imageData.filename)}&type=${imageData.type}`;
// Create temporary link and trigger download
const link = document.createElement('a');
@@ -701,9 +780,7 @@ class KikoImageViewer extends HTMLElement {
}
createImageItem(data, index) {
const imageUrl = data.subfolder
? `${window.location.origin}/api/view?filename=${encodeURIComponent(data.filename)}&subfolder=${encodeURIComponent(data.subfolder)}&type=${data.type}`
: `${window.location.origin}/api/view?filename=${encodeURIComponent(data.filename)}&type=${data.type}`;
const imageUrl = `${window.location.origin}/api/view?filename=${encodeURIComponent(data.filename)}&type=${data.type}`;
const formatClass = `kiko-format-${data.format.toLowerCase()}`;
@@ -758,8 +835,8 @@ class KikoImageViewer extends HTMLElement {
item.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
console.log(`KikoImageViewer: Opening image ${index}: ${data.filename}`);
openImageInTab(data.filename, data.subfolder);
console.log(`KikoImageViewer: Clicking image ${index}: ${data.filename}`);
openImageInTab(data.filename);
});
}
});
@@ -778,9 +855,7 @@ function createImagePreview(imageData) {
// Create image element
const img = document.createElement('img');
const imagePath = imageData.subfolder
? `/api/view?filename=${imageData.filename}&subfolder=${imageData.subfolder}&type=${imageData.type}`
: `/api/view?filename=${imageData.filename}&type=${imageData.type}`;
const imagePath = `/api/view?filename=${imageData.filename}&type=${imageData.type}`;
img.src = imagePath;
img.alt = imageData.filename;
@@ -826,7 +901,7 @@ function createImagePreview(imageData) {
// Add click handler to open in new tab
container.addEventListener('click', () => {
openImageInTab(imageData.filename, imageData.subfolder);
openImageInTab(imageData.filename);
});
return container;
@@ -942,6 +1017,15 @@ app.registerExtension({
nodeType.prototype.createCustomImageViewer = function(imageData) {
console.log('KikoSaveImage: Creating custom viewer for', imageData.length, 'images');
// Check if popup is enabled for any image (use first image's popup setting)
const popupEnabled = imageData.length > 0 ? imageData[0].popup : true;
console.log('KikoSaveImage: Popup enabled:', popupEnabled);
if (!popupEnabled) {
console.log('KikoSaveImage: Popup disabled, not showing custom viewer');
return;
}
// Check if viewer already exists
let existingViewer = document.querySelector('kiko-image-viewer');
if (existingViewer) {