Skip to content

Commit 3fbbdc4

Browse files
authored
Merge pull request #116 from Pascal-Institute/develop
Version Up
2 parents 69560c4 + 28ffcdd commit 3fbbdc4

File tree

17 files changed

+180
-26
lines changed

17 files changed

+180
-26
lines changed

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,15 @@ npm run build
6666
### 4-1. Availiable file extension
6767

6868
```bash
69-
png svg jpg jpeg webp gif bmp ico tiff tif
69+
png svg jpg jpeg webp gif bmp ico tiff tif avif
7070
```
7171

7272
**Note:** Animated GIF files are now supported! When you load an animated GIF, playback controls appear automatically. See [GIF Animation Documentation](docs/GIF_ANIMATION.md) for details.
7373

7474
### 4-2. Multi-Select Image Panels
7575

7676
**Multiple image panels can be selected simultaneously:**
77+
7778
- **Single Select:** Click on an image panel to select it (clears other selections)
7879
- **Multi-Select:** Hold `Ctrl` key + click to toggle selection of multiple panels
7980
- **Deselect:** `Ctrl` + click on a selected panel to deselect it (all panels can be deselected)

assets/addImage.ico

1.59 KB
Binary file not shown.

css/resize_panel.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1-
#resizeExecuteBtn:hover {
1+
#resizeExecuteBtn:hover,
2+
#resizePixelExecuteBtn:hover {
23
cursor: pointer;
34
}

css/styles.css

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,6 @@ select#extensionComboBox {
8989
transform: translateY(1px);
9090
}
9191

92-
#nameInput {
93-
position: absolute;
94-
top: 8px;
95-
left: 8px;
96-
}
97-
9892
.mainColorBox {
9993
width: 24px;
10094
height: 86px;

imgkit/core/image_layer.js

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class ImageLayer {
118118
// Get UI control elements
119119
this.deleteBtn = this.panel.querySelector(".delete-btn");
120120
this.nameInput = this.panel.querySelector(".name-input");
121+
this.coordText = this.panel.querySelector(".coord-text");
121122
this.infoText = this.panel.querySelector(".info-text");
122123
this.extensionCombo = this.panel.querySelector(".extension-combo");
123124

@@ -172,7 +173,6 @@ class ImageLayer {
172173
};
173174

174175
this.infoText.textContent = `${info.width} x ${info.height}`;
175-
this.history.add(buffer, info, this.extension);
176176
// Extract colors and update color boxes
177177
let extractedColors = colors;
178178
if (!extractedColors) {
@@ -184,6 +184,8 @@ class ImageLayer {
184184
}
185185
}
186186

187+
this.history.add(buffer, info, this.extension, extractedColors);
188+
187189
// Update color box UI
188190
if (Array.isArray(extractedColors) && extractedColors.length > 0) {
189191
extractedColors.forEach((color, idx) => {
@@ -250,7 +252,7 @@ class ImageLayer {
250252

251253
/**
252254
* Restore state from history
253-
* @param {Object} state - State object with buffer, info, extension
255+
* @param {Object} state - State object with buffer, info, extension, colors
254256
*/
255257
restoreFromHistory(state) {
256258
this.buffer = state.buffer;
@@ -267,6 +269,29 @@ class ImageLayer {
267269

268270
this.infoText.textContent = `${state.info.width} x ${state.info.height}`;
269271
this.extensionCombo.value = state.extension;
272+
273+
// Restore color boxes
274+
const colors = state.colors || [];
275+
if (Array.isArray(colors) && colors.length > 0) {
276+
colors.forEach((color, idx) => {
277+
if (this.colorBox.colors[idx]) {
278+
this.colorBox.colors[idx].style.backgroundColor = color;
279+
this.colorBox.colors[idx].title = color;
280+
}
281+
});
282+
283+
// Clear unused color boxes
284+
for (let i = colors.length; i < this.colorBox.colors.length; i++) {
285+
this.colorBox.colors[i].style.backgroundColor = "transparent";
286+
this.colorBox.colors[i].title = "empty";
287+
}
288+
} else {
289+
// No colors - clear all boxes
290+
this.colorBox.colors.forEach((colorDiv) => {
291+
colorDiv.style.backgroundColor = "transparent";
292+
colorDiv.title = "empty";
293+
});
294+
}
270295
}
271296

272297
// --------------------------------------------------------------------------
@@ -295,6 +320,7 @@ class ImageLayer {
295320
this.nameInput,
296321
this.colorBox.container,
297322
this.infoText,
323+
this.coordText,
298324
this.extensionCombo,
299325
].forEach((el) => (el.style.visibility = visibility));
300326
this.visibility = visibility;
@@ -478,6 +504,10 @@ class ImageLayer {
478504
}
479505
}
480506

507+
async applyWatermark({ filePath }) {
508+
await ImageProcessor.applyWatermark(filePath);
509+
}
510+
481511
/**
482512
* Process image with various operations
483513
* Bridges UI to backend operations

imgkit/features/image_layer_events.js

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,17 @@ class ImageLayerEvents {
103103
// Extension change
104104
this.layer.extensionCombo.addEventListener("change", (e) => {
105105
if (this.layer.convertFormat) {
106+
if (e.target.value === "ico") {
107+
if (
108+
this.layer.info.width > 256 ||
109+
this.layer.info.height > 256 ||
110+
this.layer.info.width !== this.layer.info.height
111+
)
112+
ipcRenderer.send("showNotificationREQ", "imgkit-ico-error");
113+
this.layer.extensionCombo.value = this.layer.extension;
114+
return;
115+
}
116+
106117
this.layer.convertFormat(e.target.value);
107118
}
108119
});
@@ -120,6 +131,7 @@ class ImageLayerEvents {
120131
this.setupCropping();
121132
this.setupDrawing();
122133
this.setupMagnifying();
134+
this.setupCoordinateDisplay();
123135

124136
this.setupPanelDragging();
125137
}
@@ -248,7 +260,10 @@ class ImageLayerEvents {
248260
}
249261

250262
// Add one more empty layer for next image
251-
if (this.layer.renderer.currentIndex === this.layer.renderer.imageLayerQueue.length - 1) {
263+
if (
264+
this.layer.renderer.currentIndex ===
265+
this.layer.renderer.imageLayerQueue.length - 1
266+
) {
252267
this.layer.renderer.createDefaultImage();
253268
}
254269
}
@@ -362,7 +377,6 @@ class ImageLayerEvents {
362377
case "hide":
363378
this.layer.hide();
364379
break;
365-
366380
}
367381
});
368382

@@ -594,6 +608,26 @@ class ImageLayerEvents {
594608
}
595609
});
596610
}
611+
612+
/**
613+
* Setup coordinate display functionality
614+
*/
615+
setupCoordinateDisplay() {
616+
// Track mouse position on canvas and display coordinates
617+
this.layer.canvas.addEventListener("mousemove", (e) => {
618+
const rect = this.layer.canvas.getBoundingClientRect();
619+
const x = Math.floor(e.clientX - rect.left);
620+
const y = Math.floor(e.clientY - rect.top);
621+
622+
// Update coordinate text
623+
this.layer.coordText.textContent = `( ${x} , ${y} )`;
624+
});
625+
626+
// Reset coordinate display when mouse leaves canvas
627+
this.layer.canvas.addEventListener("mouseleave", () => {
628+
this.layer.coordText.textContent = "( - , - )";
629+
});
630+
}
597631
}
598632

599633
module.exports = { ImageLayerEvents };

imgkit/features/layer_history.js

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ class LayerHistory {
1515
this.buffers = []; // Array of image buffers
1616
this.infos = []; // Array of image metadata objects
1717
this.extensions = []; // Array of file extensions
18+
this.colors = []; // Array of color arrays
1819
this.index = -1; // Current position in history (-1 = empty)
1920
this.maxSize = maxSize; // Maximum number of history states
2021
}
@@ -24,8 +25,9 @@ class LayerHistory {
2425
* @param {Buffer} buffer - Image buffer
2526
* @param {Object} info - Image metadata (width, height, format, etc.)
2627
* @param {string} extension - File extension (png, jpg, etc.)
28+
* @param {Array<string>} [colors] - Optional array of color strings
2729
*/
28-
add(buffer, info, extension) {
30+
add(buffer, info, extension, colors = []) {
2931
this.index++;
3032

3133
// Remove future history if we're not at the end
@@ -34,20 +36,23 @@ class LayerHistory {
3436
this.buffers = this.buffers.slice(0, this.index);
3537
this.infos = this.infos.slice(0, this.index);
3638
this.extensions = this.extensions.slice(0, this.index);
39+
this.colors = this.colors.slice(0, this.index);
3740
}
3841

3942
// Limit history size (FIFO - remove oldest)
4043
if (this.index >= this.maxSize) {
4144
this.buffers.shift();
4245
this.infos.shift();
4346
this.extensions.shift();
47+
this.colors.shift();
4448
this.index = this.maxSize - 1;
4549
}
4650

4751
// Add new state
4852
this.buffers.push(buffer);
4953
this.infos.push(info);
5054
this.extensions.push(extension);
55+
this.colors.push(colors);
5156
}
5257

5358
/**
@@ -78,7 +83,7 @@ class LayerHistory {
7883

7984
/**
8085
* Get current state from history
81-
* @returns {Object|null} Current state {buffer, info, extension} or null if empty
86+
* @returns {Object|null} Current state {buffer, info, extension, colors} or null if empty
8287
*/
8388
getCurrentState() {
8489
if (this.index < 0 || this.index >= this.buffers.length) {
@@ -89,6 +94,7 @@ class LayerHistory {
8994
buffer: this.buffers[this.index],
9095
info: this.infos[this.index],
9196
extension: this.extensions[this.index],
97+
colors: this.colors[this.index],
9298
};
9399
}
94100

@@ -123,6 +129,7 @@ class LayerHistory {
123129
this.buffers = [];
124130
this.infos = [];
125131
this.extensions = [];
132+
this.colors = [];
126133
this.index = -1;
127134
}
128135

imgkit/imgpanel.css

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,8 @@
7777
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
7878
transition: all 0.2s;
7979
position: relative;
80-
min-width: 200px;
80+
min-width: 192px;
81+
min-height: 192px;
8182
flex-shrink: 0;
8283
outline: none;
8384
}
@@ -114,8 +115,6 @@
114115
}
115116

116117
.img-canvas {
117-
max-width: 600px;
118-
max-height: 600px;
119118
border: 1px solid #ddd;
120119
border-radius: 4px;
121120
cursor: pointer;
@@ -158,6 +157,11 @@
158157
position: absolute;
159158
left: 18px;
160159
top: 8px;
160+
border-top: none;
161+
border-left: none;
162+
border-right: none;
163+
border-bottom: 1px solid #ccc;
164+
outline: none;
161165
}
162166

163167
.color-box-container {
@@ -185,12 +189,10 @@
185189
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
186190
}
187191

192+
.coord-text,
188193
.info-text {
189194
font-size: 13px;
190195
color: #666;
191-
position: absolute;
192-
right: 12px;
193-
bottom: 12px;
194196
}
195197

196198
.extension-combo {
@@ -252,3 +254,13 @@
252254
.context-menu-item:hover {
253255
background: #f5f5f5;
254256
}
257+
258+
.coord-container {
259+
position: absolute;
260+
right: 12px;
261+
bottom: 12px;
262+
display: flex;
263+
flex-direction: row;
264+
align-items: flex-end;
265+
gap: 4px;
266+
}

imgkit/index.html

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
<div id="imgkit-convert" class="imgkit-notification success">
3131
Converted Successfully
3232
</div>
33+
<div id="imgkit-ico-error" class="imgkit-notification error">
34+
Could not convert to ICO format. Keep same dimensions under 256x256
35+
</div>
3336
<div id="imgkit-error" class="imgkit-notification error">ERROR</div>
3437
<div id="imgkit-open-error" class="imgkit-notification error">
3538
Sorry. Couldn't open image...
@@ -55,7 +58,10 @@
5558
id="image-name-input"
5659
value="Untitled"
5760
/>
58-
<h2 class="info-text">0 x 0</h2>
61+
<div class="coord-container">
62+
<h2 class="coord-text">( - , - )</h2>
63+
<h2 class="info-text">0 x 0</h2>
64+
</div>
5965
<select class="extension-combo">
6066
<option value="png">png</option>
6167
<option value="svg">svg</option>
@@ -67,6 +73,7 @@ <h2 class="info-text">0 x 0</h2>
6773
<option value="ico">ico</option>
6874
<option value="tif">tif</option>
6975
<option value="tiff">tiff</option>
76+
<option value="avif">avif</option>
7077
</select>
7178
</div>
7279
</template>

imgkit/processing/image_processor.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const namer = require("color-namer");
77
const { FormatConverter } = require("./format_converter");
88

99
class ImageProcessor {
10+
static filePath = "";
11+
1012
// Delegate to FormatConverter
1113
static async convertFormat(buffer, fromExt, toExt) {
1214
return FormatConverter.convert(buffer, fromExt, toExt);
@@ -20,6 +22,10 @@ class ImageProcessor {
2022
FormatConverter.cleanupTempFiles();
2123
}
2224

25+
static async applyWatermark(filePath) {
26+
this.filePath = filePath;
27+
}
28+
2329
// Image Processing Operations (kept in ImageProcessor)
2430
static async processImage(buffer, options = {}) {
2531
try {
@@ -95,7 +101,8 @@ class ImageProcessor {
95101
}
96102

97103
if (options.composite) {
98-
const watermarkPath = "./assets/icon.png";
104+
const watermarkPath =
105+
this.filePath !== "" ? this.filePath : "./assets/icon.png";
99106

100107
const watermark = await sharp(watermarkPath).resize(32, 32).toBuffer();
101108

0 commit comments

Comments
 (0)