paintplus: vendor the app source and rename from EditmaskwithAI
Bring the full EditmaskwithAI application into the repo under paintplus/ (429 files) so the service is self-contained — the installer copies the vendored source to ~/docker/paintplus/src instead of cloning at runtime. Rename to PaintPlus (service + branding; app logic untouched): - services/editmaskwithai.sh -> services/paintplus.sh (register_service paintplus, install_paintplus, ~/docker/paintplus, Caddy paintplus:8000, Authelia option preserved) - container names -> paintplus across docker-compose*.yml; dev network -> paintplus-network - browser <title> -> "PaintPlus - AI Image Editor"; README heading -> PaintPlus with upstream provenance note - README utilities table: editmaskwithai -> paintplus Backend/frontend code (help strings referencing the old container name, the ai_photo_edit.db filename) is intentionally left as-is to avoid touching application logic. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Nb2vJ8W7bHKx1JXVvpCraH
This commit is contained in:
@@ -0,0 +1,20 @@
|
||||
class View_fullScreen_class {
|
||||
|
||||
constructor() {}
|
||||
|
||||
/**
|
||||
* toggle full-screen
|
||||
*/
|
||||
fs() {
|
||||
if (!document.fullscreenElement) {
|
||||
document.documentElement.requestFullscreen();
|
||||
}
|
||||
else {
|
||||
if (document.exitFullscreen) {
|
||||
document.exitFullscreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default View_fullScreen_class;
|
||||
@@ -0,0 +1,48 @@
|
||||
import config from './../../config.js';
|
||||
import Helper_class from './../../libs/helpers.js';
|
||||
import Base_gui_class from './../../core/base-gui.js';
|
||||
|
||||
var instance = null;
|
||||
|
||||
class View_grid_class {
|
||||
|
||||
constructor() {
|
||||
//singleton
|
||||
if (instance) {
|
||||
return instance;
|
||||
}
|
||||
instance = this;
|
||||
|
||||
this.GUI = new Base_gui_class();
|
||||
this.Helper = new Helper_class();
|
||||
|
||||
this.set_events();
|
||||
}
|
||||
|
||||
set_events() {
|
||||
document.addEventListener('keydown', (event) => {
|
||||
var code = event.keyCode;
|
||||
if (this.Helper.is_input(event.target))
|
||||
return;
|
||||
|
||||
if (code == 71 && event.ctrlKey != true && event.metaKey != true) {
|
||||
//G - grid
|
||||
this.grid({visible: !this.GUI.grid});
|
||||
event.preventDefault();
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
grid() {
|
||||
if (this.GUI.grid == false) {
|
||||
this.GUI.grid = true;
|
||||
}
|
||||
else {
|
||||
this.GUI.grid = false;
|
||||
}
|
||||
config.need_render = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default View_grid_class;
|
||||
@@ -0,0 +1,146 @@
|
||||
import config from './../../config.js';
|
||||
import Dialog_class from './../../libs/popup.js';
|
||||
import Helper_class from './../../libs/helpers.js';
|
||||
import Base_layers_class from './../../core/base-layers.js';
|
||||
import alertify from './../../../../node_modules/alertifyjs/build/alertify.min.js';
|
||||
import Tools_settings_class from './../tools/settings.js';
|
||||
import app from './../../app.js';
|
||||
|
||||
class View_guides_class {
|
||||
|
||||
|
||||
constructor() {
|
||||
this.POP = new Dialog_class();
|
||||
this.Base_layers = new Base_layers_class();
|
||||
this.Tools_settings = new Tools_settings_class();
|
||||
this.Helper = new Helper_class();
|
||||
}
|
||||
|
||||
insert() {
|
||||
var _this = this;
|
||||
var units = this.Tools_settings.get_setting('default_units');
|
||||
var resolution = this.Tools_settings.get_setting('resolution');
|
||||
|
||||
//convert units
|
||||
var position = 20;
|
||||
var position = this.Helper.get_user_unit(position, units, resolution);
|
||||
|
||||
var settings = {
|
||||
title: 'Insert guides',
|
||||
params: [
|
||||
{name: "type", title: "Type:", values: ["Vertical", "Horizontal"], value :"Vertical"},
|
||||
{name: "position", title: "Position:", value: position},
|
||||
],
|
||||
on_finish: function (params) {
|
||||
_this.insert_handler(params);
|
||||
},
|
||||
};
|
||||
this.POP.show(settings);
|
||||
}
|
||||
|
||||
insert_handler(data){
|
||||
var type = data.type;
|
||||
var position = parseFloat(data.position);
|
||||
var units = this.Tools_settings.get_setting('default_units');
|
||||
var resolution = this.Tools_settings.get_setting('resolution');
|
||||
|
||||
//convert units
|
||||
position = this.Helper.get_internal_unit(position, units, resolution);
|
||||
|
||||
var x = null;
|
||||
var y = null;
|
||||
if(type == 'Vertical')
|
||||
x = position;
|
||||
if(type == 'Horizontal')
|
||||
y = position;
|
||||
|
||||
//update
|
||||
config.guides.push({x: x, y: y});
|
||||
|
||||
if(config.guides_enabled == false){
|
||||
//was disabled
|
||||
config.guides_enabled = true;
|
||||
this.Helper.setCookie('guides', 1);
|
||||
alertify.warning('Guides enabled.');
|
||||
}
|
||||
|
||||
config.need_render = true;
|
||||
}
|
||||
|
||||
update(){
|
||||
var _this = this;
|
||||
var units = this.Tools_settings.get_setting('default_units');
|
||||
var resolution = this.Tools_settings.get_setting('resolution');
|
||||
|
||||
var params = [];
|
||||
for(var i in config.guides){
|
||||
var guide = config.guides[i];
|
||||
|
||||
//convert units
|
||||
var value = guide.x;
|
||||
var value = this.Helper.get_user_unit(value, units, resolution);
|
||||
|
||||
if(guide.y === null) {
|
||||
params.push({name: i, title: "Vertical:", value: value});
|
||||
}
|
||||
}
|
||||
for(var i in config.guides){
|
||||
var guide = config.guides[i];
|
||||
|
||||
//convert units
|
||||
var value = guide.y;
|
||||
var value = this.Helper.get_user_unit(value, units, resolution);
|
||||
|
||||
if(guide.x === null) {
|
||||
params.push({name: i, title: "Horizontal:", value: value});
|
||||
}
|
||||
}
|
||||
|
||||
var settings = {
|
||||
title: 'Update guides',
|
||||
params: params,
|
||||
on_finish: function (params) {
|
||||
_this.update_handler(params);
|
||||
},
|
||||
};
|
||||
this.POP.show(settings);
|
||||
}
|
||||
|
||||
update_handler(data){
|
||||
var units = this.Tools_settings.get_setting('default_units');
|
||||
var resolution = this.Tools_settings.get_setting('resolution');
|
||||
|
||||
//update
|
||||
for (var i in data) {
|
||||
var key = parseInt(i);
|
||||
var value = parseFloat(data[i]);
|
||||
|
||||
//convert units
|
||||
value = this.Helper.get_internal_unit(value, units, resolution);
|
||||
|
||||
if (config.guides[key].x === null)
|
||||
config.guides[key].y = value;
|
||||
else
|
||||
config.guides[key].x = value;
|
||||
}
|
||||
|
||||
//remove empty
|
||||
for (var i = 0; i < config.guides.length; i++) {
|
||||
if(config.guides[i].x === 0 || config.guides[i].y === 0
|
||||
|| isNaN(config.guides[i].x) || isNaN( config.guides[i].y)){
|
||||
config.guides.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
config.need_render = true;
|
||||
}
|
||||
|
||||
remove(params) {
|
||||
config.guides = [];
|
||||
config.need_render = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default View_guides_class;
|
||||
@@ -0,0 +1,205 @@
|
||||
import config from './../../config.js';
|
||||
import Helper_class from './../../libs/helpers.js';
|
||||
import Base_gui_class from './../../core/base-gui.js';
|
||||
import Base_layers_class from './../../core/base-layers.js';
|
||||
import Tools_settings_class from './../tools/settings.js';
|
||||
|
||||
var instance = null;
|
||||
|
||||
class View_ruler_class {
|
||||
|
||||
constructor() {
|
||||
//singleton
|
||||
if (instance) {
|
||||
return instance;
|
||||
}
|
||||
instance = this;
|
||||
|
||||
this.GUI = new Base_gui_class();
|
||||
this.Base_layers = new Base_layers_class();
|
||||
this.Tools_settings = new Tools_settings_class();
|
||||
this.Helper = new Helper_class();
|
||||
|
||||
this.set_events();
|
||||
}
|
||||
|
||||
set_events() {
|
||||
var _this = this;
|
||||
|
||||
window.addEventListener('resize', function (event) {
|
||||
//resize
|
||||
_this.prepare_ruler();
|
||||
_this.render_ruler();
|
||||
}, false);
|
||||
document.addEventListener('keydown', (event) => {
|
||||
var code = event.code;
|
||||
if (this.Helper.is_input(event.target))
|
||||
return;
|
||||
|
||||
if (event.code == "KeyU" && event.ctrlKey != true && event.metaKey != true) {
|
||||
_this.ruler();
|
||||
event.preventDefault();
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
ruler() {
|
||||
var ruler_left = document.getElementById('ruler_left');
|
||||
var ruler_top = document.getElementById('ruler_top');
|
||||
var middle_area = document.getElementById('middle_area');
|
||||
|
||||
if(config.ruler_active == false){
|
||||
//activate
|
||||
config.ruler_active = true;
|
||||
document.getElementById('middle_area').classList.add('has-ruler');
|
||||
ruler_left.style.display = 'block';
|
||||
ruler_top.style.display = 'block';
|
||||
|
||||
this.prepare_ruler();
|
||||
this.render_ruler();
|
||||
}
|
||||
else{
|
||||
//deactivate
|
||||
config.ruler_active = false;
|
||||
document.getElementById('middle_area').classList.remove('has-ruler');
|
||||
ruler_left.style.display = 'none';
|
||||
ruler_top.style.display = 'none';
|
||||
}
|
||||
|
||||
this.GUI.prepare_canvas();
|
||||
|
||||
config.need_render = true;
|
||||
}
|
||||
|
||||
prepare_ruler(){
|
||||
if(config.ruler_active == false)
|
||||
return;
|
||||
|
||||
var ruler_left = document.getElementById('ruler_left');
|
||||
var ruler_top = document.getElementById('ruler_top');
|
||||
var middle_area = document.getElementById('middle_area');
|
||||
|
||||
var middle_area_width = middle_area.clientWidth;
|
||||
var middle_area_height = middle_area.clientHeight;
|
||||
|
||||
ruler_left.width = 15;
|
||||
ruler_left.height = middle_area_height - 20;
|
||||
|
||||
ruler_top.width = middle_area_width - 20;
|
||||
ruler_top.height = 15;
|
||||
}
|
||||
|
||||
render_ruler(){
|
||||
if(config.ruler_active == false)
|
||||
return;
|
||||
|
||||
var units = this.Tools_settings.get_setting('default_units');
|
||||
var resolution = this.Tools_settings.get_setting('resolution');
|
||||
|
||||
var ruler_left = document.getElementById('ruler_left');
|
||||
var ruler_top = document.getElementById('ruler_top');
|
||||
|
||||
var ctx_left = ruler_left.getContext("2d");
|
||||
var ctx_top = ruler_top.getContext("2d");
|
||||
|
||||
var color = '#111';
|
||||
var size = 15;
|
||||
|
||||
//calc step
|
||||
var step = Math.ceil(10 * config.ZOOM);
|
||||
while (step < 5) {
|
||||
step = step * 2;
|
||||
}
|
||||
while (step > 10) {
|
||||
step = Math.ceil(step / 2);
|
||||
}
|
||||
var step_big = step * 10;
|
||||
|
||||
//calc begin/end point
|
||||
var begin_x = Math.max(0, ruler_top.width / 2 - config.WIDTH * config.ZOOM / 2);
|
||||
var begin_y = Math.max(0, ruler_left.height / 2 - config.HEIGHT * config.ZOOM / 2);
|
||||
|
||||
var end_x = Math.min(ruler_top.width, ruler_top.width / 2 + config.WIDTH * config.ZOOM / 2);
|
||||
var end_y = Math.min(ruler_left.height, ruler_left.height / 2 + config.HEIGHT * config.ZOOM / 2);
|
||||
|
||||
//left
|
||||
ctx_left.strokeStyle = color;
|
||||
ctx_left.lineWidth = 1;
|
||||
ctx_left.font = "11px Arial";
|
||||
|
||||
ctx_left.clearRect(0, 0, ruler_left.width, ruler_left.height);
|
||||
|
||||
ctx_left.beginPath();
|
||||
for (var i = begin_y; i < end_y; i += step) {
|
||||
ctx_left.moveTo(10, i + 0.5);
|
||||
ctx_left.lineTo(size, i + 0.5);
|
||||
}
|
||||
ctx_left.stroke();
|
||||
|
||||
ctx_left.beginPath();
|
||||
for (var i = begin_y; i <= end_y; i += step_big) {
|
||||
ctx_left.moveTo(0, i + 0.5);
|
||||
ctx_left.lineTo(size, i + 0.5);
|
||||
|
||||
var global_pos = this.Base_layers.get_world_coords(0, i - begin_y);
|
||||
var value = this.Helper.get_user_unit(global_pos.y, units, resolution);
|
||||
|
||||
if(units == 'inches'){
|
||||
//more decimals value
|
||||
var text = this.Helper.number_format(value, 1);
|
||||
}
|
||||
else{
|
||||
var text = Math.ceil(value);
|
||||
}
|
||||
text = text.toString();
|
||||
|
||||
//text
|
||||
for (var j = 0; j < text.length; j++) {
|
||||
var letter = text.charAt(j);
|
||||
var line_height = 10;
|
||||
ctx_left.fillText(letter, 1, i + 11 + j * line_height);
|
||||
}
|
||||
}
|
||||
ctx_left.stroke();
|
||||
|
||||
//top
|
||||
ctx_top.strokeStyle = color;
|
||||
ctx_top.lineWidth = 1;
|
||||
ctx_top.font = "11px Arial";
|
||||
|
||||
ctx_top.clearRect(0, 0, ruler_top.width, ruler_top.height);
|
||||
|
||||
ctx_top.beginPath();
|
||||
for (var i = begin_x; i < end_x; i += step) {
|
||||
var y = (i / step_big == parseInt(i / step_big)) ? 0 : step;
|
||||
ctx_top.moveTo(i + 0.5, 10);
|
||||
ctx_top.lineTo(i + 0.5, size);
|
||||
}
|
||||
ctx_top.stroke();
|
||||
|
||||
ctx_top.beginPath();
|
||||
for (var i = begin_x; i <= end_x; i += step_big) {
|
||||
ctx_top.moveTo(i + 0.5, 0);
|
||||
ctx_top.lineTo(i + 0.5, size);
|
||||
|
||||
var global_pos = this.Base_layers.get_world_coords(i - begin_x, 0);
|
||||
var value = this.Helper.get_user_unit(global_pos.x, units, resolution);
|
||||
|
||||
if(units == 'inches'){
|
||||
//more decimals value
|
||||
var text = this.Helper.number_format(value, 1);
|
||||
}
|
||||
else{
|
||||
var text = Math.ceil(value);
|
||||
}
|
||||
text = text.toString();
|
||||
|
||||
//text
|
||||
ctx_top.fillText(text, i + 3, 9);
|
||||
}
|
||||
ctx_top.stroke();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default View_ruler_class;
|
||||
@@ -0,0 +1,26 @@
|
||||
import GUI_preview_class from './../../core/gui/gui-preview.js';
|
||||
|
||||
class View_zoom_class {
|
||||
|
||||
constructor() {
|
||||
this.GUI_preview = new GUI_preview_class();
|
||||
}
|
||||
|
||||
in() {
|
||||
this.GUI_preview.zoom(1);
|
||||
}
|
||||
|
||||
out() {
|
||||
this.GUI_preview.zoom(-1);
|
||||
}
|
||||
|
||||
original() {
|
||||
this.GUI_preview.zoom(100);
|
||||
}
|
||||
|
||||
auto() {
|
||||
this.GUI_preview.zoom_auto();
|
||||
}
|
||||
}
|
||||
|
||||
export default View_zoom_class;
|
||||
Reference in New Issue
Block a user