krunakuamar commited on
Commit
ed01507
1 Parent(s): d45f692

Upload 216 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. Dockerfile +22 -0
  2. README.md +66 -0
  3. app.py +322 -0
  4. docker/CPUDockerfile +17 -0
  5. docker/GPUDockerfile +17 -0
  6. lama_cleaner/__init__.py +11 -0
  7. lama_cleaner/__pycache__/__init__.cpython-38.pyc +0 -0
  8. lama_cleaner/__pycache__/const.cpython-38.pyc +0 -0
  9. lama_cleaner/__pycache__/helper.cpython-38.pyc +0 -0
  10. lama_cleaner/__pycache__/interactive_seg.cpython-38.pyc +0 -0
  11. lama_cleaner/__pycache__/model_manager.cpython-38.pyc +0 -0
  12. lama_cleaner/__pycache__/parse_args.cpython-38.pyc +0 -0
  13. lama_cleaner/__pycache__/runtime.cpython-38.pyc +0 -0
  14. lama_cleaner/__pycache__/schema.cpython-38.pyc +0 -0
  15. lama_cleaner/__pycache__/server2.cpython-38.pyc +0 -0
  16. lama_cleaner/app/.env +1 -0
  17. lama_cleaner/app/.eslintrc.json +52 -0
  18. lama_cleaner/app/.gitignore +27 -0
  19. lama_cleaner/app/.prettierrc +6 -0
  20. lama_cleaner/app/LICENSE +201 -0
  21. lama_cleaner/app/build/asset-manifest.json +15 -0
  22. lama_cleaner/app/build/index.html +1 -0
  23. lama_cleaner/app/build/static/css/main.c28d98ca.css +1 -0
  24. lama_cleaner/app/build/static/js/main.1bd455bc.js +0 -0
  25. lama_cleaner/app/build/static/js/main.1bd455bc.js.LICENSE.txt +60 -0
  26. lama_cleaner/app/build/static/media/WorkSans-Black.67c2c5a144333953880b.ttf +0 -0
  27. lama_cleaner/app/build/static/media/WorkSans-Bold.2bea7a7f7d052c74da25.ttf +0 -0
  28. lama_cleaner/app/build/static/media/WorkSans-Regular.bb287b894b27372d8ea7.ttf +0 -0
  29. lama_cleaner/app/build/static/media/WorkSans-SemiBold.1e98db4eb705b586728e.ttf +0 -0
  30. lama_cleaner/app/config/env.js +104 -0
  31. lama_cleaner/app/config/getHttpsConfig.js +66 -0
  32. lama_cleaner/app/config/jest/babelTransform.js +29 -0
  33. lama_cleaner/app/config/jest/cssTransform.js +14 -0
  34. lama_cleaner/app/config/jest/fileTransform.js +40 -0
  35. lama_cleaner/app/config/modules.js +134 -0
  36. lama_cleaner/app/config/paths.js +77 -0
  37. lama_cleaner/app/config/webpack.config.js +761 -0
  38. lama_cleaner/app/config/webpack/persistentCache/createEnvironmentHash.js +9 -0
  39. lama_cleaner/app/config/webpackDevServer.config.js +127 -0
  40. lama_cleaner/app/package.json +180 -0
  41. lama_cleaner/app/public/index.html +33 -0
  42. lama_cleaner/app/scripts/build.js +217 -0
  43. lama_cleaner/app/scripts/start.js +154 -0
  44. lama_cleaner/app/scripts/test.js +52 -0
  45. lama_cleaner/app/src/App.tsx +204 -0
  46. lama_cleaner/app/src/adapters/inpainting.ts +225 -0
  47. lama_cleaner/app/src/components/CoffeeIcon/CoffeeIcon.tsx +66 -0
  48. lama_cleaner/app/src/components/Croper/Croper.scss +167 -0
  49. lama_cleaner/app/src/components/Croper/Croper.tsx +408 -0
  50. lama_cleaner/app/src/components/Editor/Editor.scss +111 -0
Dockerfile ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.8
2
+ # ENV MPLCONFIGDIR=/tmp/matplotlib-399fa59w
3
+ RUN mkdir -m 777 -p /app/
4
+
5
+ RUN mkdir -m 777 -p /.cache/matplotlib
6
+ RUN mkdir -m 777 -p /.cache/huggingface/hub/
7
+ RUN mkdir -m 777 -p /.cache/torch/
8
+ RUN mkdir -m 777 -p /.cache/
9
+ RUN mkdir -m 777 -p /.config/matplotlib/
10
+
11
+
12
+ RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
13
+ COPY ./app.py ./app.py
14
+ COPY ./lama_cleaner ./lama_cleaner
15
+ COPY ./yolov8x-seg.pt ./yolov8x-seg.pt
16
+ COPY ./requirements.txt ./requirements.txt
17
+ RUN pip install -r ./requirements.txt
18
+ ENV HOST="0.0.0.0"
19
+ ENV PORT=7860
20
+
21
+ #ENTRYPOINT python3 app.py --host ${HOST} --port ${PORT}
22
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <h1 align="center" >Remove Objects Server</h1>
2
+
3
+
4
+ <!-- TABLE OF CONTENTS -->
5
+ <details>
6
+ <summary>Table of Contents</summary>
7
+ <ol>
8
+ <li><a href="#about-the-project">About</a></li>
9
+ <li><a href="#built-with">Installation</a></li>
10
+ <li><a href="#usage">Usage</a></li>
11
+ <li><a href="#license">License</a></li>
12
+
13
+ </ol>
14
+ </details>
15
+
16
+ ## About
17
+
18
+
19
+ This is a Python project for removing unwanted objects from images using the inpainting technique. It includes a server implemented with FastAPI and an endpoint for processing images by applying inpainting techniques. This project uses a deep learning library, PyTorch, for training and testing the inpainting model.
20
+
21
+ <p align="center">
22
+ <img src="lama_cleaner_video.gif" />
23
+ </p>
24
+
25
+ ## Installation
26
+
27
+ To install this project, you should first create a virtual environment using the following commands:
28
+
29
+ ```bash
30
+ python3 -m venv venv
31
+ source venv/bin/activate
32
+ ```
33
+ After creating the virtual environment, you can install the required libraries using pip:
34
+
35
+ ```bash
36
+ pip install -r requirements.txt
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ To use this project, first start the server by running main.py:
42
+
43
+ ```bash
44
+ python main.py
45
+ ```
46
+
47
+ After the server has started, you can test following endpoints:
48
+
49
+ - `http://{localhost}:{port}/lama/paint`
50
+ - This endpoint accepts an image file in the `file` parameter and applies inpainting techniques to remove unwanted objects.
51
+
52
+ - `http://{localhost}:{port}/mask`
53
+ - Mask endpoint is used to apply a mask to an image. The route accepts `img` and `mask` as input parameters. Then, it applies a mask on an image.
54
+ - You can use `testX.png` image and `testX_mask.png` mask in image folder for testing.
55
+
56
+ ## License
57
+
58
+ This project is licensed under the MIT License - see the LICENSE file for details.
59
+
60
+
61
+ Other command
62
+
63
+ ```bash
64
+ docker build -t zest .
65
+ ```
66
+
app.py ADDED
@@ -0,0 +1,322 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import base64
2
+ import imghdr
3
+ import os
4
+
5
+ import cv2
6
+ import numpy as np
7
+ import psutil
8
+ import pyinstrument
9
+ import torch
10
+ from ultralytics import YOLO
11
+ from ultralytics.yolo.utils.ops import scale_image
12
+ import asyncio
13
+ from fastapi import FastAPI, File, UploadFile
14
+ from fastapi.middleware.cors import CORSMiddleware
15
+ import uvicorn
16
+ # from mangum import Mangum
17
+
18
+ from argparse import ArgumentParser
19
+
20
+ import lama_cleaner.server2 as server
21
+ from lama_cleaner.helper import (
22
+ load_img,
23
+ )
24
+
25
+ # os.environ["TRANSFORMERS_CACHE"] = "/path/to/writable/directory"
26
+
27
+ app = FastAPI()
28
+ # handler = Mangum(app)
29
+
30
+ origins = ["*"]
31
+
32
+ app.add_middleware(
33
+ CORSMiddleware,
34
+ allow_origins=origins,
35
+ allow_credentials=True,
36
+ allow_methods=["*"],
37
+ allow_headers=["*"],
38
+ )
39
+
40
+ def numpy_to_bytes(image_numpy: np.ndarray, ext: str) -> bytes:
41
+ """
42
+ Args:
43
+ image_numpy: numpy image
44
+ ext: image extension
45
+ Returns:
46
+ image bytes
47
+ """
48
+ data = cv2.imencode(
49
+ f".{ext}",
50
+ image_numpy,
51
+ [int(cv2.IMWRITE_JPEG_QUALITY), 100, int(cv2.IMWRITE_PNG_COMPRESSION), 0],
52
+ )[1].tobytes()
53
+ return data
54
+
55
+
56
+ def get_image_ext(img_bytes):
57
+ """
58
+ Args:
59
+ img_bytes: image bytes
60
+ Returns:
61
+ image extension
62
+ """
63
+ if not img_bytes:
64
+ raise ValueError("Empty input")
65
+ header = img_bytes[:32]
66
+ w = imghdr.what("", header)
67
+ if w is None:
68
+ w = "jpeg"
69
+ return w
70
+
71
+
72
+ def predict_on_image(model, img, conf, retina_masks):
73
+ """
74
+ Args:
75
+ model: YOLOv8 model
76
+ img: image (C, H, W)
77
+ conf: confidence threshold
78
+ retina_masks: use retina masks or not
79
+ Returns:
80
+ boxes: box with xyxy format, (N, 4)
81
+ masks: masks, (N, H, W)
82
+ cls: class of masks, (N, )
83
+ probs: confidence score, (N, 1)
84
+ """
85
+ with torch.no_grad():
86
+ result = model(img, conf=conf, retina_masks=retina_masks, scale=1)[0]
87
+
88
+
89
+ boxes, masks, cls, probs = None, None, None, None
90
+
91
+ if result.boxes.cls.size(0) > 0:
92
+ # detection
93
+ cls = result.boxes.cls.cpu().numpy().astype(np.int32)
94
+ probs = result.boxes.conf.cpu().numpy() # confidence score, (N, 1)
95
+ boxes = result.boxes.xyxy.cpu().numpy() # box with xyxy format, (N, 4)
96
+
97
+ # segmentation
98
+ masks = result.masks.masks.cpu().numpy() # masks, (N, H, W)
99
+ masks = np.transpose(masks, (1, 2, 0)) # masks, (H, W, N)
100
+ # rescale masks to original image
101
+ masks = scale_image(masks.shape[:2], masks, result.masks.orig_shape)
102
+ masks = np.transpose(masks, (2, 0, 1)) # masks, (N, H, W)
103
+
104
+ return boxes, masks, cls, probs
105
+
106
+
107
+ def overlay(image, mask, color, alpha, id, resize=None):
108
+ """Overlays a binary mask on an image.
109
+
110
+ Args:
111
+ image: Image to be overlayed on.
112
+ mask: Binary mask to overlay.
113
+ color: Color to use for the mask.
114
+ alpha: Opacity of the mask.
115
+ id: id of the mask
116
+ resize: Resize the image to this size. If None, no resizing is performed.
117
+
118
+ Returns:
119
+ The overlayed image.
120
+ """
121
+ color = color[::-1]
122
+ colored_mask = np.expand_dims(mask, 0).repeat(3, axis=0)
123
+ colored_mask = np.moveaxis(colored_mask, 0, -1)
124
+ masked = np.ma.MaskedArray(image, mask=colored_mask, fill_value=color)
125
+ image_overlay = masked.filled()
126
+
127
+ imgray = cv2.cvtColor(image_overlay, cv2.COLOR_BGR2GRAY)
128
+
129
+ contour_thickness = 8
130
+ _, thresh = cv2.threshold(imgray, 255, 255, 255)
131
+ contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)
132
+ imgray = cv2.cvtColor(imgray, cv2.COLOR_GRAY2BGR)
133
+ imgray = cv2.drawContours(imgray, contours, -1, (255, 255, 255), contour_thickness)
134
+
135
+ imgray = np.where(imgray.any(-1, keepdims=True), (46, 36, 225), 0)
136
+
137
+ if resize is not None:
138
+ image = cv2.resize(image.transpose(1, 2, 0), resize)
139
+ image_overlay = cv2.resize(image_overlay.transpose(1, 2, 0), resize)
140
+
141
+ return imgray
142
+
143
+
144
+ async def process_mask(idx, mask_i, boxes, probs, yolo_model, blank_image, cls):
145
+ """Process the mask of the image.
146
+
147
+ Args:
148
+ idx: index of the mask
149
+ mask_i: mask of the image
150
+ boxes: box with xyxy format, (N, 4)
151
+ probs: confidence score, (N, 1)
152
+ yolo_model: YOLOv8 model
153
+ blank_image: blank image
154
+ cls: class of masks, (N, )
155
+
156
+ Returns:
157
+ dictionary_seg: dictionary of the mask of the image
158
+ """
159
+ dictionary_seg = {}
160
+ maskwith_back = overlay(blank_image, mask_i, color=(255, 155, 155), alpha=0.5, id=idx)
161
+
162
+ alpha = np.sum(maskwith_back, axis=-1) > 0
163
+ alpha = np.uint8(alpha * 255)
164
+ maskwith_back = np.dstack((maskwith_back, alpha))
165
+
166
+ imgencode = await asyncio.get_running_loop().run_in_executor(None, cv2.imencode, '.png', maskwith_back)
167
+ mask = base64.b64encode(imgencode[1]).decode('utf-8')
168
+
169
+ dictionary_seg["confi"] = f'{probs[idx] * 100:.2f}'
170
+ dictionary_seg["boxe"] = [int(item) for item in list(boxes[idx])]
171
+ dictionary_seg["mask"] = mask
172
+ dictionary_seg["cls"] = str(yolo_model.names[cls[idx]])
173
+
174
+ return dictionary_seg
175
+
176
+
177
+ @app.post("/api/mask")
178
+ async def detect_mask(file: UploadFile = File()):
179
+ """
180
+ Detects masks in an image uploaded via a POST request and returns a JSON response containing the details of the detected masks.
181
+
182
+ Args:
183
+ None
184
+
185
+ Parameters:
186
+ - file: a file object containing the input image
187
+
188
+ Returns:
189
+ A JSON response containing the details of the detected masks:
190
+ - code: 200 if objects were detected, 500 if no objects were detected
191
+ - msg: a message indicating whether objects were detected or not
192
+ - data: a list of dictionaries, where each dictionary contains the following keys:
193
+ - confi: the confidence level of the detected object
194
+ - boxe: a list containing the coordinates of the bounding box of the detected object
195
+ - mask: the mask of the detected object encoded in base64
196
+ - cls: the class of the detected object
197
+
198
+ Raises:
199
+ 500: No objects detected
200
+ """
201
+ file = await file.read()
202
+
203
+ img, _ = load_img(file)
204
+
205
+ # predict by YOLOv8
206
+ boxes, masks, cls, probs = predict_on_image(yolo_model, img, conf=0.55, retina_masks=True)
207
+
208
+ if boxes.size == 0:
209
+ return {'code': 500, 'msg': 'No objects detected'}
210
+
211
+ # overlay masks on original image
212
+ blank_image = np.zeros(img.shape, dtype=np.uint8)
213
+
214
+ data = []
215
+
216
+ coroutines = [process_mask(idx, mask_i, boxes, probs, yolo_model, blank_image, cls) for idx, mask_i in enumerate(masks)]
217
+ results = await asyncio.gather(*coroutines)
218
+
219
+ for result in results:
220
+ data.append(result)
221
+
222
+ return {'code': 200, 'msg': "object detected", 'data': data}
223
+
224
+
225
+ @app.post("/api/lama/paint")
226
+ async def paint(img: UploadFile = File(), mask: UploadFile = File()):
227
+ """
228
+ Endpoint to process an image with a given mask using the server's process function.
229
+
230
+ Route: '/api/lama/paint'
231
+ Method: POST
232
+
233
+ Parameters:
234
+ img: The input image file (JPEG or PNG format).
235
+ mask: The mask file (JPEG or PNG format).
236
+ Returns:
237
+ A JSON object containing the processed image in base64 format under the "image" key.
238
+ """
239
+
240
+ img = await img.read()
241
+ mask = await mask.read()
242
+
243
+ profiler = pyinstrument.Profiler()
244
+ profiler.start()
245
+
246
+ x = {"image": server.process(img, mask)}
247
+
248
+ profiler.stop()
249
+ print("--------START------")
250
+ os.system("nvidia-smi")
251
+ print(profiler.output_text(unicode=True, color=True))
252
+
253
+ process = psutil.Process()
254
+ memory_info = process.memory_info()
255
+ cpu_percent = psutil.cpu_percent()
256
+
257
+ print("Memory usage: {} MB".format(memory_info.rss / 1048576))
258
+ print("CPU usage: {}%".format(cpu_percent))
259
+ print("--------END------")
260
+
261
+ return x
262
+
263
+
264
+ @app.post("/api/lama/model")
265
+ def switch_model(new_name: str):
266
+ return server.switch_model(new_name)
267
+
268
+
269
+ @app.get("/api/lama/model")
270
+ def current_model():
271
+ return server.current_model()
272
+
273
+
274
+ @app.get("/api/lama/switchmode")
275
+ def get_is_disable_model_switch():
276
+ return server.get_is_disable_model_switch()
277
+
278
+ @app.on_event("startup")
279
+ def init_data():
280
+ model_device = "cuda"
281
+ if not torch.cuda.is_available():
282
+ print("CUDA is not available, switching to CPU")
283
+ model_device = "cpu"
284
+ global yolo_model
285
+ yolo_model = YOLO('yolov8x-seg.pt')
286
+ yolo_model.to(model_device)
287
+ print(f"YOLO model yolov8x-seg.pt loaded.")
288
+ server.initModel()
289
+
290
+ def create_app(args):
291
+ """
292
+ Creates the FastAPI app and adds the endpoints.
293
+
294
+ Args:
295
+ args: The arguments.
296
+ """
297
+ uvicorn.run("app:app", host=args.host, port=args.port, reload=args.reload)
298
+
299
+
300
+ if __name__ == "__main__":
301
+ parser = ArgumentParser()
302
+ parser.add_argument('--model_name', type=str, default='lama', help='Model name')
303
+ parser.add_argument('--host', type=str, default='0.0.0.0')
304
+ parser.add_argument('--port', type=int, default=5000)
305
+ parser.add_argument('--reload', type=bool, default=True)
306
+ parser.add_argument('--model_device', type=str, default='cuda', help='Model device')
307
+ parser.add_argument('--disable_model_switch', type=bool, default=False, help='Disable model switch')
308
+ parser.add_argument('--gui', type=bool, default=False, help='Enable GUI')
309
+ parser.add_argument('--cpu_offload', type=bool, default=False, help='Enable CPU offload')
310
+ parser.add_argument('--disable_nsfw', type=bool, default=False, help='Disable NSFW')
311
+ parser.add_argument('--enable_xformers', type=bool, default=False, help='Enable xformers')
312
+ parser.add_argument('--hf_access_token', type=str, default='', help='Hugging Face access token')
313
+ parser.add_argument('--local_files_only', type=bool, default=False, help='Enable local files only')
314
+ parser.add_argument('--no_half', type=bool, default=False, help='Disable half')
315
+ parser.add_argument('--sd_cpu_textencoder', type=bool, default=False, help='Enable CPU text encoder')
316
+ parser.add_argument('--sd_disable_nsfw', type=bool, default=False, help='Disable NSFW')
317
+ parser.add_argument('--sd_enable_xformers', type=bool, default=False, help='Enable xformers')
318
+ parser.add_argument('--sd_run_local', type=bool, default=False, help='Enable local files only')
319
+
320
+ args = parser.parse_args()
321
+ create_app(args)
322
+
docker/CPUDockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM python:3.7.4-slim-buster
2
+
3
+ RUN apt-get update && apt-get install -y --no-install-recommends \
4
+ software-properties-common \
5
+ libsm6 libxext6 ffmpeg libfontconfig1 libxrender1 libgl1-mesa-glx \
6
+ curl
7
+
8
+ RUN pip install --upgrade pip && \
9
+ pip install torch==1.13.1 --extra-index-url https://download.pytorch.org/whl/cpu
10
+
11
+ ARG version
12
+
13
+ RUN pip install lama-cleaner==$version
14
+
15
+ EXPOSE 8080
16
+
17
+ CMD ["bash"]
docker/GPUDockerfile ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ FROM nvidia/cuda:11.7.1-runtime-ubuntu20.04
2
+
3
+ RUN apt-get update && apt-get install -y --no-install-recommends \
4
+ software-properties-common \
5
+ libsm6 libxext6 ffmpeg libfontconfig1 libxrender1 libgl1-mesa-glx \
6
+ curl python3-pip
7
+
8
+ RUN pip3 install --upgrade pip && \
9
+ pip3 install torch==1.13.1 xformers==0.0.16rc425
10
+
11
+ ARG version
12
+
13
+ RUN pip3 install lama-cleaner==$version
14
+
15
+ EXPOSE 8080
16
+
17
+ CMD ["bash"]
lama_cleaner/__init__.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import warnings
2
+ warnings.simplefilter("ignore", UserWarning)
3
+
4
+ from lama_cleaner.parse_args import parse_args
5
+
6
+ def entry_point():
7
+ args = parse_args()
8
+ # To make os.environ["XDG_CACHE_HOME"] = args.model_cache_dir works for diffusers
9
+ # https://github.com/huggingface/diffusers/blob/be99201a567c1ccd841dc16fb24e88f7f239c187/src/diffusers/utils/constants.py#L18
10
+ from lama_cleaner.server import main
11
+ main(args)
lama_cleaner/__pycache__/__init__.cpython-38.pyc ADDED
Binary file (462 Bytes). View file
 
lama_cleaner/__pycache__/const.cpython-38.pyc ADDED
Binary file (1.77 kB). View file
 
lama_cleaner/__pycache__/helper.cpython-38.pyc ADDED
Binary file (5.44 kB). View file
 
lama_cleaner/__pycache__/interactive_seg.cpython-38.pyc ADDED
Binary file (6.75 kB). View file
 
lama_cleaner/__pycache__/model_manager.cpython-38.pyc ADDED
Binary file (2.26 kB). View file
 
lama_cleaner/__pycache__/parse_args.cpython-38.pyc ADDED
Binary file (4.27 kB). View file
 
lama_cleaner/__pycache__/runtime.cpython-38.pyc ADDED
Binary file (1.33 kB). View file
 
lama_cleaner/__pycache__/schema.cpython-38.pyc ADDED
Binary file (2.4 kB). View file
 
lama_cleaner/__pycache__/server2.cpython-38.pyc ADDED
Binary file (5.77 kB). View file
 
lama_cleaner/app/.env ADDED
@@ -0,0 +1 @@
 
 
1
+ REACT_APP_INPAINTING_URL=""
lama_cleaner/app/.eslintrc.json ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "extends": [
3
+ "airbnb",
4
+ "airbnb/hooks",
5
+ "plugin:@typescript-eslint/recommended",
6
+ "prettier",
7
+ "plugin:prettier/recommended"
8
+ ],
9
+ "plugins": ["@typescript-eslint", "react", "react-hooks", "prettier"],
10
+ "parser": "@typescript-eslint/parser",
11
+ "parserOptions": {
12
+ "ecmaFeatures": {
13
+ "jsx": true
14
+ },
15
+ "ecmaVersion": 2018,
16
+ "sourceType": "module",
17
+ "project": "./tsconfig.json"
18
+ },
19
+ "rules": {
20
+ "jsx-a11y/click-events-have-key-events": 0,
21
+ "react/jsx-props-no-spreading": 0,
22
+ "import/no-unresolved": 0,
23
+ "react/jsx-no-bind": "off",
24
+ "react/jsx-filename-extension": [
25
+ 1,
26
+ {
27
+ "extensions": [".ts", ".tsx"]
28
+ }
29
+ ],
30
+ "prettier/prettier": [
31
+ "error",
32
+ {
33
+ "singleQuote": true,
34
+ "arrowParens": "avoid",
35
+ "endOfLine": "auto"
36
+ }
37
+ ],
38
+ "consistent-return": "off",
39
+ "no-use-before-define": "off",
40
+ "import/extensions": "off",
41
+ "react/prop-types": 0,
42
+ "react/require-default-props": "off",
43
+ "no-shadow": "off",
44
+ "@typescript-eslint/ban-ts-comment": "off",
45
+ "@typescript-eslint/no-shadow": ["error"],
46
+ "@typescript-eslint/no-explicit-any": "off",
47
+ "@typescript-eslint/explicit-function-return-type": "off",
48
+ "@typescript-eslint/explicit-module-boundary-types": "off",
49
+ "react-hooks/rules-of-hooks": "error",
50
+ "react-hooks/exhaustive-deps": "warn"
51
+ }
52
+ }
lama_cleaner/app/.gitignore ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2
+
3
+ # dependencies
4
+ /node_modules
5
+ /.pnp
6
+ .pnp.js
7
+
8
+ # testing
9
+ /coverage
10
+
11
+ # production
12
+
13
+ # misc
14
+ .DS_Store
15
+ .env.local
16
+ .env.development.local
17
+ .env.test.local
18
+ .env.production.local
19
+
20
+ npm-debug.log*
21
+ yarn-debug.log*
22
+ yarn-error.log*
23
+
24
+ # Tailwind processed CSS
25
+ index.css
26
+
27
+ .firebase
lama_cleaner/app/.prettierrc ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ {
2
+ "singleQuote": true,
3
+ "semi": false,
4
+ "trailingComma": "es5",
5
+ "arrowParens": "avoid"
6
+ }
lama_cleaner/app/LICENSE ADDED
@@ -0,0 +1,201 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
lama_cleaner/app/build/asset-manifest.json ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "files": {
3
+ "main.css": "/static/css/main.c28d98ca.css",
4
+ "main.js": "/static/js/main.1bd455bc.js",
5
+ "static/media/WorkSans-SemiBold.ttf": "/static/media/WorkSans-SemiBold.1e98db4eb705b586728e.ttf",
6
+ "static/media/WorkSans-Bold.ttf": "/static/media/WorkSans-Bold.2bea7a7f7d052c74da25.ttf",
7
+ "static/media/WorkSans-Regular.ttf": "/static/media/WorkSans-Regular.bb287b894b27372d8ea7.ttf",
8
+ "static/media/WorkSans-Black.ttf": "/static/media/WorkSans-Black.67c2c5a144333953880b.ttf",
9
+ "index.html": "/index.html"
10
+ },
11
+ "entrypoints": [
12
+ "static/css/main.c28d98ca.css",
13
+ "static/js/main.1bd455bc.js"
14
+ ]
15
+ }
lama_cleaner/app/build/index.html ADDED
@@ -0,0 +1 @@
 
 
1
+ <!doctype html><html lang="en"><head><meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate"/><meta http-equiv="Pragma" content="no-cache"/><meta http-equiv="Expires" content="0"/><meta charset="utf-8"/><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=0"/><meta name="theme-color" content="#ffffff"/><title>lama-cleaner - Image inpainting powered by SOTA AI model</title><script defer="defer" src="/static/js/main.1bd455bc.js"></script><link href="/static/css/main.c28d98ca.css" rel="stylesheet"></head><body><noscript>You need to enable JavaScript to run this app.</noscript><div id="root"></div></body></html>
lama_cleaner/app/build/static/css/main.c28d98ca.css ADDED
@@ -0,0 +1 @@
 
 
1
+ :root{--blackA1:rgba(0,0,0,.012);--blackA2:rgba(0,0,0,.027);--blackA3:rgba(0,0,0,.047);--blackA4:rgba(0,0,0,.071);--blackA5:rgba(0,0,0,.09);--blackA6:rgba(0,0,0,.114);--blackA7:rgba(0,0,0,.141);--blackA8:rgba(0,0,0,.22);--blackA9:rgba(0,0,0,.439);--blackA10:rgba(0,0,0,.478);--blackA11:rgba(0,0,0,.565);--blackA12:rgba(0,0,0,.91);--mauve1:#fdfcfd;--mauve2:#f9f8f9;--mauve3:#f4f2f4;--mauve4:#eeedef;--mauve5:#e9e8ea;--mauve6:#e4e2e4;--mauve7:#dcdbdd;--mauve8:#c8c7cb;--mauve9:#908e96;--mauve10:#86848d;--mauve11:#6f6e77;--mauve12:#1a1523;--violet1:#fdfcfe;--violet2:#fbfaff;--violet3:#f5f2ff;--violet4:#ede9fe;--violet5:#e4defc;--violet6:#d7cff9;--violet7:#c4b8f3;--violet8:#aa99ec;--violet9:#6e56cf;--violet10:#644fc1;--violet11:#5746af;--violet12:#20134b;--page-bg:#fff;--page-bg-light:hsla(0,0%,100%,.5);--page-text-color:#040404;--yellow-accent:#fc0;--yellow-accent-light:#ffcc0055;--link-color:#000;--border-color:#eff1f4;--border-color-light:hsla(240,9%,43%,.5);--tooltip-bg:#e6e6ea;--tooltip-text-color:#000;--error-color:#ef4444;--success-color:#10b981;--editor-toolkit-bg:hsla(0,0%,100%,.5);--editor-options-bg:#e6e6ea;--options-text-color:var(--page-text-color);--editor-size-border-color:var(--border-color);--editor-toolkit-panel-border:0;--modal-bg:var(--page-bg);--modal-text-color:#000;--modal-hotkey-border-color:#000;--model-mask-bg:rgba(209,213,219,.4);--text-color:#040404;--text-color-gray:#6b6f76;--btn-text-color:var(--text-color);--btn-text-hover-color:#040404;--btn-border-color:#646478;--btn-primary-hover-bg:var(--yellow-accent);--animation-pulsing-bg:hsla(0,0%,100%,.5);--switch-root-background-color:#dfe1e4;--switch-thumb-color:var(--page-bg);--switch-thumb-checked-color:var(--page-bg);--slider-background-color:var(--switch-root-background-color);--tooltip-bg:var(--page-bg);--badge-background-color:#f1f3f5;--badge-color:#687076;--box-shadow:inset 0 0.5px hsla(0,0%,100%,.1),inset 0 1px 5px #f8f9fa,0px 0px 0px 0.5px #c1c8cd,0px 2px 1px -1px #c1c8cd,0 1px #c1c8cd;--croper-bg:rgba(0,0,0,.5);--tabs-active-color:#f0f3f9}@font-face{font-family:WorkSans;src:url(/static/media/WorkSans-Regular.bb287b894b27372d8ea7.ttf)}@font-face{font-family:WorkSans-Semibold;src:url(/static/media/WorkSans-SemiBold.1e98db4eb705b586728e.ttf)}@font-face{font-family:WorkSans-Bold;src:url(/static/media/WorkSans-Bold.2bea7a7f7d052c74da25.ttf)}@font-face{font-family:WorkSans-Black;src:url(/static/media/WorkSans-Black.67c2c5a144333953880b.ttf)}[data-theme=dark]{--page-bg:#040404;--page-bg-light:#04040488;--page-text-color:#f9f9f9;--yellow-accent:#fc0;--yellow-accent-light:#ffcc0055;--link-color:var(--yellow-accent);--border-color:#1e1e1e;--border-color-light:#666;--tooltip-bg:#212121;--tooltip-text-color:#d2d2d2;--editor-toolkit-bg:rgba(0,0,0,.5);--editor-options-bg:#212121;--options-text-color:var(--page-text-color);--editor-size-border-color:var(--yellow-accent);--editor-toolkit-panel-border:1px solid hsla(240,9%,43%,.4);--modal-bg:var(--page-bg);--modal-text-color:var(--page-text-color);--modal-hotkey-border-color:var(--page-text-color);--model-mask-bg:rgba(76,76,87,.4);--text-color:#fff;--text-color-gray:#c3c4c6;--btn-text-color:var(--text-color);--btn-text-hover-color:var(--page-bg);--btn-border-color:var(--yellow-accent);--btn-primary-hover-bg:var(--yellow-accent);--animation-pulsing-bg:#f0f0ff;--switch-root-background-color:#3c3f44;--switch-thumb-color:#1f2023;--switch-thumb-checked-color:#fff;--slider-background-color:var(--switch-root-background-color);--badge-background-color:#202425;--badge-color:#9ba1a6;--box-shadow:inset 0 0.5px hsla(0,0%,100%,.1),inset 0 1px 5px #1a1d1e,0px 0px 0px 0.5px #4c5155,0px 2px 1px -1px #4c5155,0 1px #4c5155;--croper-bg:rgba(0,0,0,.5);--tabs-active-color:#272831}@supports (color:hsl(0 0% 0%/0)){[data-theme=dark]{--tooltip-bg:#202425}}@-webkit-keyframes pulsing{0%{opacity:1}50%{background-color:hsla(0,0%,100%,.5);background-color:var(--animation-pulsing-bg);opacity:.75}to{opacity:1}}@keyframes pulsing{0%{opacity:1}50%{background-color:hsla(0,0%,100%,.5);background-color:var(--animation-pulsing-bg);opacity:.75}to{opacity:1}}@-webkit-keyframes opacityReveal{0%{opacity:0}to{opacity:1}}@keyframes opacityReveal{0%{opacity:0}to{opacity:1}}@-webkit-keyframes slideDown{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes slideDown{0%{-webkit-transform:translateY(-100%);transform:translateY(-100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes slideUp{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes slideUp{0%{-webkit-transform:translateY(100%);transform:translateY(100%)}to{-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes slideIn{0%{-webkit-transform:translateX(calc(100% + 25px));transform:translateX(calc(100% + 25px))}to{-webkit-transform:translateX(0);transform:translateX(0)}}@keyframes slideIn{0%{-webkit-transform:translateX(calc(100% + 25px));transform:translateX(calc(100% + 25px))}to{-webkit-transform:translateX(0);transform:translateX(0)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}to{-webkit-transform:rotate(1turn);transform:rotate(1turn)}}@-webkit-keyframes slideUpAndFade{0%{opacity:0;-webkit-transform:translateY(2px);transform:translateY(2px)}to{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes slideUpAndFade{0%{opacity:0;-webkit-transform:translateY(2px);transform:translateY(2px)}to{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@-webkit-keyframes slideDownAndFade{0%{opacity:0;-webkit-transform:translateY(-2px);transform:translateY(-2px)}to{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}@keyframes slideDownAndFade{0%{opacity:0;-webkit-transform:translateY(-2px);transform:translateY(-2px)}to{opacity:1;-webkit-transform:translateY(0);transform:translateY(0)}}.lama-cleaner{background-color:#fff;background-color:var(--page-bg);color:#040404;color:var(--page-text-color);display:grid;grid-template-areas:"main-content";height:100vh;transition-duration:.2s;transition-property:background-color,color;transition-timing-function:repeat(2,ease-out);width:100vw}a{color:inherit;text-decoration:inherit}input:disabled{color:#6b6f76;color:var(--text-color-gray)}.editor-container{align-items:center;display:flex;height:100vh;justify-content:center;width:100vw}.react-transform-wrapper{display:grid!important;height:100%!important;width:100%!important}.editor-canvas-container{grid-row-gap:1rem;display:grid;grid-template-areas:"editor-content";row-gap:1rem}.editor-canvas{grid-area:editor-content;z-index:2}.original-image-container{display:grid;grid-area:editor-content;grid-template-areas:"original-image-content";pointer-events:none}.original-image-container img{grid-area:original-image-content}.original-image-container .editor-slider{background-color:#fc0;background-color:var(--yellow-accent);grid-area:original-image-content;height:100%;justify-self:end;transition:all .3s cubic-bezier(.4,0,.2,1);width:6px;z-index:2}.editor-canvas-loading{-webkit-animation:pulsing .75s infinite;animation:pulsing .75s infinite;pointer-events:none}.editor-toolkit-panel{grid-column-gap:2rem;align-items:center;-webkit-animation:slideUp .2s ease-out;animation:slideUp .2s ease-out;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background-color:hsla(0,0%,100%,.5);background-color:var(--page-bg-light);border:0;border:var(--editor-toolkit-panel-border);border-radius:3rem;bottom:.5rem;box-shadow:0 0 0 1px rgba(0,0,0,.102),0 3px 16px rgba(0,0,0,.078),0 2px 6px 1px rgba(0,0,0,.09);-webkit-column-gap:2rem;column-gap:2rem;display:grid;grid-template-areas:"toolkit-size-selector toolkit-brush-slider toolkit-btns";justify-content:center;padding:.6rem 3rem;position:fixed}@media screen and (max-width:767px){.editor-toolkit-panel{grid-template-areas:"toolkit-size-selector toolkit-size-selector" "toolkit-brush-slider toolkit-brush-slider" "toolkit-btns toolkit-btns";justify-items:center;padding:1rem 2rem;row-gap:2rem}}.editor-toolkit-panel .eyeicon-active{background-color:#fc0;background-color:var(--yellow-accent);color:#040404;color:var(--btn-text-hover-color)}.editor-brush-slider{grid-column-gap:1rem;align-items:center;-webkit-column-gap:1rem;column-gap:1rem;display:grid;grid-area:toolkit-brush-slider;grid-template-columns:repeat(2,-webkit-max-content);grid-template-columns:repeat(2,max-content);height:-webkit-max-content;height:max-content;-webkit-user-select:none;user-select:none}.editor-brush-slider input[type=range]{-webkit-appearance:none;appearance:none;background:transparent;border-color:transparent;color:transparent;cursor:pointer;width:100%}.editor-brush-slider input[type=range]:focus{outline:none}.editor-brush-slider input[type=range]::-webkit-slider-thumb{-webkit-appearance:none;background:#fc0;background:var(--yellow-accent);border:1px solid #000;border-radius:50%;height:1.2rem;margin-top:-.5rem;width:1.2rem;z-index:2}.editor-brush-slider input[type=range]::-webkit-slider-runnable-track{background:#dfe1e4;background:var(--slider-background-color);border-radius:2rem;height:.2rem}.editor-brush-slider input[type=range]::-moz-range-track{background:#dfe1e4;background:var(--slider-background-color);border-radius:2rem}.editor-brush-slider input[type=range]::-moz-range-progress{background:#fc0;background:var(--yellow-accent)}.editor-toolkit-btns{grid-column-gap:1rem;-webkit-column-gap:1rem;column-gap:1rem;display:grid;grid-area:toolkit-btns;grid-auto-flow:column}.brush-shape{background-color:rgba(255,204,0,.733);border:1px solid #fc0;border:1px solid var(--yellow-accent);border-radius:50%;pointer-events:none;position:absolute}.file-manager-modal{color:#040404;color:var(--text-color);height:90%;width:80%}.react-photo-album.react-photo-album--columns{height:80vh}.react-photo-album--photo{border:1px solid transparent;border-radius:8px;transition:visibility .25s ease-in,-webkit-transform .25s;transition:transform .25s,visibility .25s ease-in;transition:transform .25s,visibility .25s ease-in,-webkit-transform .25s;-webkit-user-select:none;user-select:none}.react-photo-album--photo:hover{border:1px solid #eff1f4;border:1px solid var(--border-color);-webkit-transform:scale(1.03);transform:scale(1.03)}.ScrollAreaRoot{--scrollbar-size:10px;border-radius:4px;overflow:hidden}.ScrollAreaViewport{border-radius:inherit;height:100%;width:100%}.ScrollAreaScrollbar{display:flex;padding:2px;touch-action:none;transition:background .16s ease-out;-webkit-user-select:none;user-select:none}.ScrollAreaScrollbar:hover{background:var(--blackA8)}.ScrollAreaScrollbar[data-orientation=vertical]{width:var(--scrollbar-size)}.ScrollAreaScrollbar[data-orientation=horizontal]{flex-direction:column;height:var(--scrollbar-size)}.ScrollAreaThumb{background:var(--mauve10);border-radius:var(--scrollbar-size);flex:1 1;position:relative}.ScrollAreaThumb:before{content:"";height:100%;left:50%;min-height:44px;min-width:44px;position:absolute;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);width:100%}.ScrollAreaCorner{background:var(--blackA8)}.file-search-input{border:1px solid #eff1f4;border:1px solid var(--border-color);border-radius:12px;height:32px;padding-left:30px;width:250px}.sort-btn-inactive svg{opacity:.5}button,fieldset,input{all:unset}.TabsRoot{align-self:flex-start;background-color:#fff;background-color:var(--page-bg);display:flex;flex-direction:column;gap:8px}.TabsList{border:1px solid #eff1f4;border:1px solid var(--border-color);border-radius:12px;flex-direction:row;gap:6px;padding:4px}.TabsList,.TabsTrigger{background-color:#fff;background-color:var(--page-bg);display:flex;justify-content:flex-start}.TabsTrigger{align-items:center;border-radius:8px;color:#000;color:var(--modal-text-color);font-family:inherit;font-size:15px;line-height:1;padding:8px;-webkit-user-select:none;user-select:none}.TabsTrigger:hover,.TabsTrigger[data-state=active]{background-color:#f0f3f9;background-color:var(--tabs-active-color)}.TabsTrigger:focus{position:relative}.TabsContent{background-color:#fff;background-color:var(--page-bg);outline:none;width:100%}.TabsContent[data-state=active]{display:flex;flex-direction:column;gap:14px}.landing-page{grid-row-gap:2rem;display:grid;grid-auto-rows:-webkit-max-content;grid-auto-rows:max-content;justify-items:center;place-self:center;row-gap:2rem}@media screen and (max-width:767px){.landing-page{padding:1rem}}.landing-page h1{font-size:1.4rem;text-align:center}@media screen and (max-width:767px){.landing-page h1{font-size:1.2rem}}.landing-page a{color:#000;color:var(--link-color)}.landing-file-selector{display:grid}header{align-items:center;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background-color:hsla(0,0%,100%,.5);background-color:var(--page-bg-light);border-bottom:1px solid hsla(240,9%,43%,.2);display:flex;height:60px;justify-content:space-between;padding:1rem 1.5rem;position:absolute;top:0;width:100%;z-index:20}.shortcuts{z-index:1}.header-icons-wrapper{gap:12px}.header-icons,.header-icons-wrapper{align-items:center;display:flex;justify-content:center;justify-self:end}.header-icons{gap:6px}.mask-preview{border:1px solid #eff1f4;border:1px solid var(--border-color);border-radius:8px;margin-left:20px;margin-top:30px;max-height:400px;max-width:400px}.prompt-wrapper{display:flex;gap:12px}.prompt-wrapper input{all:unset;border-radius:.5rem;border-width:0;min-width:600px;outline:1px solid #eff1f4;outline:1px solid var(--border-color);padding:0 .8rem}.prompt-wrapper input:focus-visible{border-width:0;outline:1px solid #fc0;outline:1px solid var(--yellow-accent)}.theme-toggle-ui{transition:all .2s ease-in;-webkit-user-select:none;user-select:none;z-index:10}.theme-toggle-ui .theme-btn{align-items:center;cursor:pointer;display:flex;justify-content:center;outline:none}.theme-toggle-ui .theme-btn svg{height:22px;width:22px}.modal-shortcuts{background-color:#fff;background-color:var(--modal-bg);box-shadow:0 0 20px rgba(0,0,40,.2);color:#000;color:var(--modal-text-color);grid-area:main-content}@media screen and (max-width:767px){.modal-shortcuts{-webkit-animation:slideDown .2s ease-out;animation:slideDown .2s ease-out;display:grid;height:auto;width:100%}}.shortcut-options{grid-row-gap:1rem;display:grid;row-gap:1rem}.shortcut-options .shortcut-option{grid-column-gap:6rem;align-items:center;-webkit-column-gap:6rem;column-gap:6rem;display:grid;grid-template-columns:repeat(2,auto)}@media screen and (max-width:767px){.shortcut-options .shortcut-option{-webkit-column-gap:0;column-gap:0;row-gap:.6rem}}.shortcut-options .shortcut-key{background-color:#fff;background-color:var(--page-bg);border-radius:6px;box-shadow:inset 0 .5px hsla(0,0%,100%,.1),inset 0 1px 5px #f8f9fa,0 0 0 .5px #c1c8cd,0 2px 1px -1px #c1c8cd,0 1px #c1c8cd;box-shadow:var(--box-shadow);box-sizing:border-box;color:#000;color:var(--modal-text-color);font-family:inherit;font-weight:400;justify-self:end;line-height:1.5;padding-left:.5rem;padding-right:.5rem;text-shadow:0 0 1px hsla(0,0%,100%,.5);-webkit-user-select:none;user-select:none;white-space:nowrap;width:-webkit-max-content;width:max-content}@media screen and (max-width:767px){.shortcut-options .shortcut-key{padding:.2rem .4rem}}.shortcut-options .shortcut-description{font-size:.95rem;justify-self:start;text-align:left}@media screen and (max-width:767px){.shortcut-options .shortcut-description{justify-self:start;text-align:left;width:auto}}.setting-block,.setting-block .option-desc{display:flex;flex-direction:column}.setting-block .option-desc{border:1px solid #eff1f4;border:1px solid var(--border-color);border-radius:.3rem;color:#6b6f76;color:var(--text-color-gray);gap:8px;margin-top:12px;padding:1rem}.setting-block .option-desc .sub-setting-block{color:#040404;color:var(--text-color)}.setting-block .option-desc svg{color:#6b6f76;color:var(--text-color-gray)}.setting-block-content{align-items:center;display:flex;gap:12rem;justify-content:space-between}.setting-block-content-v{align-items:flex-start;display:flex;flex-direction:column;gap:1rem;justify-content:flex-start}.setting-block-content-title{align-items:center;display:flex;flex-direction:row;gap:8px;justify-content:center}.setting-block-desc{color:#6b6f76;color:var(--text-color-gray);font-size:1rem;margin-top:8px}.hd-setting-block .inline-tip{color:#040404;color:var(--text-color);cursor:pointer;display:inline}.model-desc-link{border-radius:999px;color:#687076;color:var(--badge-color);display:flex;justify-items:center;padding-left:5px;padding-right:5px;text-decoration:none}.modal-setting{background-color:#fff;background-color:var(--modal-bg);box-shadow:0 0 20px rgba(0,0,40,.2);color:#000;color:var(--modal-text-color);min-height:420px;width:680px}@media screen and (max-width:767px){.modal-setting{-webkit-animation:slideDown .2s ease-out;animation:slideDown .2s ease-out;display:grid;height:auto;margin-top:-11rem;width:100%}}.folder-path-block{display:flex;flex-direction:column;gap:12px}.folder-path{border-radius:6px;border-width:0;outline:1px solid #eff1f4;outline:1px solid var(--border-color);padding:.3rem .5rem;width:95%}.folder-path:focus-visible{border-width:0;outline:1px solid #fc0;outline:1px solid var(--yellow-accent)}.side-panel{border-color:#eff1f4;border-color:var(--border-color);border-radius:.8rem;border-style:solid;border-width:1px;padding:.3rem;position:absolute;right:1.5rem;top:68px;z-index:4}.side-panel-trigger{border:0;font-family:WorkSans,sans-serif;font-size:16px}.side-panel-content{background-color:#fff;background-color:var(--page-bg);border-color:#eff1f4;border-color:var(--border-color);border-radius:.8rem;border-style:solid;border-width:1px;color:#040404;color:var(--text-color);display:flex;flex-direction:column;font-family:WorkSans,sans-serif;font-size:14px;gap:12px;padding:1rem;position:relative;right:1.5rem;top:1rem;z-index:9}.side-panel-content .setting-block-content{gap:1rem}.negative-prompt{all:unset;border-radius:.5rem;border-width:0;max-width:200px;min-height:150px;outline:1px solid #eff1f4;outline:1px solid var(--border-color);padding:12px .8rem;width:100%}.negative-prompt:focus-visible{border-width:0;outline:1px solid #fc0;outline:1px solid var(--yellow-accent)}.negative-prompt:-webkit-input-placeholder{padding-top:10px}.negative-prompt:-moz-input-placeholder{padding-top:10px}.negative-prompt:-ms-input-placeholder{padding-top:10px}.resize-title-tile{color:#6b6f76;color:var(--text-color-gray);font-size:.5rem;width:86px}.crop-border{outline-color:#fc0;outline-color:var(--yellow-accent);outline-style:dashed}.info-bar{align-items:center;background-color:#fff;background-color:var(--page-bg);border:0;border:var(--editor-toolkit-panel-border);border-radius:9999px;box-shadow:0 0 0 1px rgba(0,0,0,.102),0 3px 16px rgba(0,0,0,.078),0 2px 6px 1px rgba(0,0,0,.09);color:#040404;color:var(--text-color);display:flex;font-size:1rem;gap:12px;justify-content:center;padding:.2rem .8rem;pointer-events:auto;position:absolute}.info-bar:hover{cursor:move}.croper-wrapper{height:100%;overflow:hidden;position:absolute;width:100%}.croper,.croper-wrapper{pointer-events:none;z-index:2}.croper{bottom:0;box-shadow:0 0 0 9999px rgba(0,0,0,.5);left:0;position:relative;right:0;top:0}.drag-bar{pointer-events:auto;position:absolute}.drag-bar.ord-top{cursor:ns-resize;height:12px;left:0;margin-top:-6px;top:0;width:100%}.drag-bar.ord-right{cursor:ew-resize;height:100%;margin-right:-6px;right:0;top:0;width:12px}.drag-bar.ord-bottom{bottom:0;cursor:ns-resize;height:12px;left:0;margin-bottom:-6px;width:100%}.drag-bar.ord-left{cursor:ew-resize;height:100%;left:0;margin-left:-6px;top:0;width:12px}.drag-handle{background-color:#ffcc0055;background-color:var(--yellow-accent-light);border:2px solid #fc0;border:2px solid var(--yellow-accent);content:"";display:block;height:12px;pointer-events:auto;position:absolute;width:12px;z-index:4}.drag-handle:hover{background-color:#fc0;background-color:var(--yellow-accent)}.drag-handle.ord-topleft{cursor:nw-resize;left:-7px;top:-7px}.drag-handle.ord-topright{cursor:ne-resize;right:-7px;top:-7px}.drag-handle.ord-bottomright{bottom:-7px;cursor:se-resize;right:-7px}.drag-handle.ord-bottomleft{bottom:-7px;cursor:sw-resize;left:-7px}.drag-handle.ord-bottom,.drag-handle.ord-top{cursor:ns-resize;left:calc(50% - 6px)}.drag-handle.ord-top{top:-7px}.drag-handle.ord-bottom{bottom:-7px}.drag-handle.ord-left,.drag-handle.ord-right{cursor:ew-resize;top:calc(50% - 6px)}.drag-handle.ord-left{left:-7px}.drag-handle.ord-right{right:-7px}.interactive-seg-wrapper{height:100%;overflow:hidden;pointer-events:none;position:absolute;width:100%;z-index:2}.interactive-seg-wrapper .click-item{border-radius:50%;height:8px;position:absolute;width:8px}.interactive-seg-wrapper .click-item-positive{background-color:rgba(21,215,121,.936);outline:6px solid rgba(98,255,179,.31)}.interactive-seg-wrapper .click-item-negative{background-color:rgba(237,49,55,.942);outline:6px solid rgba(255,89,95,.31)}.interactive-seg-confirm-actions{background-color:#fff;background-color:var(--page-bg);border-color:#eff1f4;border-color:var(--border-color);border-radius:16px;border-style:solid;border-width:1px;padding:8px;position:absolute;top:68px;z-index:5}.interactive-seg-confirm-actions .action-buttons{align-items:center;display:flex;gap:8px;justify-content:center}@-webkit-keyframes pulse{to{box-shadow:0 0 0 14px rgba(21,215,121,0)}}@keyframes pulse{to{box-shadow:0 0 0 14px rgba(21,215,121,0)}}.interactive-seg-cursor{-webkit-animation:pulse 1.5s cubic-bezier(.66,0,0,1) infinite;animation:pulse 1.5s cubic-bezier(.66,0,0,1) infinite;background-color:rgba(21,215,121,.936);border-radius:50%;box-shadow:0 0 0 0 rgba(21,215,121,.936);color:rgba(234,255,240,.98);height:20px;pointer-events:none;position:absolute;width:20px}.file-select-label{border:2px dashed #eff1f4;border:2px dashed var(--border-color);border-radius:.5rem;cursor:pointer;display:grid;min-width:600px}@media screen and (max-width:767px){.file-select-label{min-width:300px}}.file-select-label .file-select-label-hover,.file-select-label:hover{background-color:#fc0;background-color:var(--yellow-accent);color:#000}.file-select-container{display:grid;height:100%;padding:4rem;width:100%}.file-select-container input{display:none}.file-select-message{font-family:WorkSans;text-align:center}.btn-primary{grid-column-gap:1rem;background-color:#fff;background-color:var(--page-bg);border-radius:.5rem;color:#040404;color:var(--btn-text-color);-webkit-column-gap:1rem;column-gap:1rem;cursor:pointer;display:grid;font-family:WorkSans,sans-serif;grid-auto-flow:column;padding:.5rem;place-items:center;width:-webkit-max-content;width:max-content;z-index:1}.btn-primary:hover{background-color:#fc0;background-color:var(--btn-primary-hover-bg);color:#040404;color:var(--btn-text-hover-color)}.btn-primary svg{height:auto;width:20px}.btn-primary-disabled{background-color:#fff;background-color:var(--page-bg);opacity:.5;pointer-events:none}.btn-border{border-color:#646478;border-color:var(--btn-border-color);border-style:solid;border-width:1px}.modal-mask{-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);background-color:rgba(209,213,219,.4);background-color:var(--model-mask-bg);inset:0;position:fixed;z-index:9998}@media(prefers-reduced-motion:no-preference){.modal-mask{-webkit-animation:opacityReveal .15s cubic-bezier(.16,1,.3,1) forwards;animation:opacityReveal .15s cubic-bezier(.16,1,.3,1) forwards}}@-webkit-keyframes contentShow{0%{opacity:0;-webkit-transform:translate(-50%,-48%) scale(.96);transform:translate(-50%,-48%) scale(.96)}to{opacity:1;-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}}@keyframes contentShow{0%{opacity:0;-webkit-transform:translate(-50%,-48%) scale(.96);transform:translate(-50%,-48%) scale(.96)}to{opacity:1;-webkit-transform:translate(-50%,-50%) scale(1);transform:translate(-50%,-50%) scale(1)}}.modal{background-color:#fff;background-color:var(--page-bg);border-radius:.95rem;display:flex;flex-direction:column;gap:16px;left:50%;padding:25px;place-self:center;position:fixed;top:50%;-webkit-transform:translate(-50%,-50%);transform:translate(-50%,-50%);z-index:9999}.modal:focus{outline:none}.modal .modal-header{align-items:center;display:grid;grid-template-columns:repeat(2,auto)}.modal .modal-header .btn-primary{justify-self:end}@media(prefers-reduced-motion:no-preference){.modal{-webkit-animation:contentShow .15s cubic-bezier(.16,1,.3,1) forwards;animation:contentShow .15s cubic-bezier(.16,1,.3,1) forwards}}.select-trigger{all:unset;align-items:center;background-color:#fff;background-color:var(--page-bg);border:1px solid #eff1f4;border:1px solid var(--border-color);border-radius:.5rem;color:#040404;color:var(--options-text-color);display:inline-flex;gap:8px;height:32px;justify-content:space-between;padding:0 .8rem}.select-trigger svg{height:1rem;margin-top:.25rem;width:1rem}.select-trigger:hover{border-color:#fc0;border-color:var(--yellow-accent)}.select-trigger:disabled{border-color:#eff1f4;border-color:var(--border-color);color:#eff1f4;color:var(--border-color)}.select-content{background-color:#fff;background-color:var(--page-bg);border-radius:.5rem;overflow:hidden}.select-viewport{border:1px solid #eff1f4;border:1px solid var(--border-color);border-radius:.5rem;padding:5px}.select-item{all:unset;align-items:center;background-color:#fff;background-color:var(--page-bg);border-radius:.5rem;color:#040404;color:var(--options-text-color);display:flex;padding:6px 6px 6px 25px;position:relative;-webkit-user-select:none;user-select:none}.select-item:focus{background-color:#fc0;background-color:var(--yellow-accent);color:#040404;color:var(--btn-text-hover-color)}.select-item-indicator{align-items:center;display:inline-flex;justify-content:center;left:0;padding-right:4px;position:absolute;width:25px}.switch-root{-webkit-tap-highlight-color:rgba(0,0,0,0);all:"unset";background-color:#dfe1e4;background-color:var(--switch-root-background-color);border:none;border-radius:9999px;height:25px;position:relative;transition:background-color .1s;width:42px}.switch-root:focus-visible{outline:none}.switch-root[data-state=checked]{background-color:#fc0;background-color:var(--yellow-accent)}.switch-thumb{background-color:#fff;background-color:var(--switch-thumb-color);border-radius:9999px;display:block;height:17px;-webkit-transform:translateX(4px);transform:translateX(4px);transition:-webkit-transform .1s;transition:transform .1s;transition:transform .1s,-webkit-transform .1s;width:17px;will-change:transform}.switch-thumb[data-state=checked]{background-color:#fff;background-color:var(--switch-thumb-checked-color);outline:1px solid hsla(240,9%,43%,.5);-webkit-transform:translateX(21px);transform:translateX(21px)}.number-input{all:unset;border-radius:.5rem;flex:1 0 auto;height:32px;outline:1px solid #eff1f4;outline:1px solid var(--border-color);padding:0 .8rem;text-align:right}.number-input:focus-visible{outline:1px solid #fc0;outline:1px solid var(--yellow-accent)}.number-input:disabled{color:#eff1f4;color:var(--border-color)}.toast-viewpoint{bottom:48px;display:flex;flex-direction:row;gap:10px;margin:0;max-width:100vw;padding:25px;position:fixed;right:1.5rem;z-index:999999}.toast-viewpoint:focus-visible{outline:none}.toast-root{align-items:center;background-color:#fff;background-color:var(--page-bg);border:1px solid hsla(240,9%,43%,.5);border:1px solid var(--border-color-light);border-radius:.6rem;display:flex;gap:12px;padding:15px}.toast-root[data-state=open]{-webkit-animation:slideIn .15s cubic-bezier(.16,1,.3,1);animation:slideIn .15s cubic-bezier(.16,1,.3,1)}.toast-root[data-state=close]{-webkit-animation:opacityReveal .1s ease-in forwards;animation:opacityReveal .1s ease-in forwards}.toast-root[data-state=cancel]{-webkit-animation:transform .1s ease-out;animation:transform .1s ease-out;-webkit-transform:translateX(0);transform:translateX(0)}.toast-root.error{border:1px solid #ef4444;border:1px solid var(--error-color)}.toast-root.success{border:1px solid #10b981;border:1px solid var(--success-color)}.error-icon{color:#ef4444;color:var(--error-color);height:24px;width:24px}.success-icon{color:#10b981;color:var(--success-color);height:24px;width:24px}.loading-icon{-webkit-animation-duration:1.5s;animation-duration:1.5s;-webkit-animation-iteration-count:infinite;animation-iteration-count:infinite;-webkit-animation-name:spin;animation-name:spin;-webkit-animation-timing-function:linear;animation-timing-function:linear;-webkit-transform-origin:center center;transform-origin:center center}.loading-icon,.toast-desc,.toast-icon{align-items:center;display:flex}.toast-desc{color:#040404;color:var(--text-color);margin:0;min-width:240px}.tooltip-trigger{align-items:center;display:flex;justify-content:center}.tooltip-content{background-color:#fff;background-color:var(--tooltip-bg);border-radius:4px;box-shadow:0 10px 38px -10px rgba(14,18,22,.35),0 10px 20px -15px rgba(14,18,22,.2);color:#000;color:var(--tooltip-text-color);padding:10px 15px}@media(prefers-reduced-motion:no-preference){.tooltip-content{-webkit-animation-duration:.4s;animation-duration:.4s;-webkit-animation-fill-mode:forwards;animation-fill-mode:forwards;-webkit-animation-timing-function:cubic-bezier(.16,1,.3,1);animation-timing-function:cubic-bezier(.16,1,.3,1);will-change:transform,opacity}.tooltip-content[data-state=delayed-open][data-side=top]{-webkit-animation-name:slideDownAndFade;animation-name:slideDownAndFade}.tooltip-content[data-state=delayed-open][data-side=bottom]{-webkit-animation-name:slideUpAndFade;animation-name:slideUpAndFade}}.tooltip-arrow{fill:#fff;fill:var(--tooltip-bg)}*,:after,:before{box-sizing:border-box;margin:0;padding:0}body,html{font-family:WorkSans,sans-serif}
lama_cleaner/app/build/static/js/main.1bd455bc.js ADDED
The diff for this file is too large to render. See raw diff
 
lama_cleaner/app/build/static/js/main.1bd455bc.js.LICENSE.txt ADDED
@@ -0,0 +1,60 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /*
2
+ object-assign
3
+ (c) Sindre Sorhus
4
+ @license MIT
5
+ */
6
+
7
+ /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */
8
+
9
+ /**
10
+ * @license
11
+ * Lodash <https://lodash.com/>
12
+ * Copyright OpenJS Foundation and other contributors <https://openjsf.org/>
13
+ * Released under MIT license <https://lodash.com/license>
14
+ * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
15
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
16
+ */
17
+
18
+ /** @license React v0.20.2
19
+ * scheduler.production.min.js
20
+ *
21
+ * Copyright (c) Facebook, Inc. and its affiliates.
22
+ *
23
+ * This source code is licensed under the MIT license found in the
24
+ * LICENSE file in the root directory of this source tree.
25
+ */
26
+
27
+ /** @license React v17.0.2
28
+ * react-dom.production.min.js
29
+ *
30
+ * Copyright (c) Facebook, Inc. and its affiliates.
31
+ *
32
+ * This source code is licensed under the MIT license found in the
33
+ * LICENSE file in the root directory of this source tree.
34
+ */
35
+
36
+ /** @license React v17.0.2
37
+ * react-jsx-runtime.production.min.js
38
+ *
39
+ * Copyright (c) Facebook, Inc. and its affiliates.
40
+ *
41
+ * This source code is licensed under the MIT license found in the
42
+ * LICENSE file in the root directory of this source tree.
43
+ */
44
+
45
+ /** @license React v17.0.2
46
+ * react.production.min.js
47
+ *
48
+ * Copyright (c) Facebook, Inc. and its affiliates.
49
+ *
50
+ * This source code is licensed under the MIT license found in the
51
+ * LICENSE file in the root directory of this source tree.
52
+ */
53
+
54
+ /**!
55
+ * FlexSearch.js v0.7.21 (Bundle)
56
+ * Copyright 2018-2021 Nextapps GmbH
57
+ * Author: Thomas Wilkerling
58
+ * Licence: Apache-2.0
59
+ * https://github.com/nextapps-de/flexsearch
60
+ */
lama_cleaner/app/build/static/media/WorkSans-Black.67c2c5a144333953880b.ttf ADDED
Binary file (192 kB). View file
 
lama_cleaner/app/build/static/media/WorkSans-Bold.2bea7a7f7d052c74da25.ttf ADDED
Binary file (193 kB). View file
 
lama_cleaner/app/build/static/media/WorkSans-Regular.bb287b894b27372d8ea7.ttf ADDED
Binary file (192 kB). View file
 
lama_cleaner/app/build/static/media/WorkSans-SemiBold.1e98db4eb705b586728e.ttf ADDED
Binary file (193 kB). View file
 
lama_cleaner/app/config/env.js ADDED
@@ -0,0 +1,104 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const paths = require('./paths');
6
+
7
+ // Make sure that including paths.js after env.js will read .env variables.
8
+ delete require.cache[require.resolve('./paths')];
9
+
10
+ const NODE_ENV = process.env.NODE_ENV;
11
+ if (!NODE_ENV) {
12
+ throw new Error(
13
+ 'The NODE_ENV environment variable is required but was not specified.'
14
+ );
15
+ }
16
+
17
+ // https://github.com/bkeepers/dotenv#what-other-env-files-can-i-use
18
+ const dotenvFiles = [
19
+ `${paths.dotenv}.${NODE_ENV}.local`,
20
+ // Don't include `.env.local` for `test` environment
21
+ // since normally you expect tests to produce the same
22
+ // results for everyone
23
+ NODE_ENV !== 'test' && `${paths.dotenv}.local`,
24
+ `${paths.dotenv}.${NODE_ENV}`,
25
+ paths.dotenv,
26
+ ].filter(Boolean);
27
+
28
+ // Load environment variables from .env* files. Suppress warnings using silent
29
+ // if this file is missing. dotenv will never modify any environment variables
30
+ // that have already been set. Variable expansion is supported in .env files.
31
+ // https://github.com/motdotla/dotenv
32
+ // https://github.com/motdotla/dotenv-expand
33
+ dotenvFiles.forEach(dotenvFile => {
34
+ if (fs.existsSync(dotenvFile)) {
35
+ require('dotenv-expand')(
36
+ require('dotenv').config({
37
+ path: dotenvFile,
38
+ })
39
+ );
40
+ }
41
+ });
42
+
43
+ // We support resolving modules according to `NODE_PATH`.
44
+ // This lets you use absolute paths in imports inside large monorepos:
45
+ // https://github.com/facebook/create-react-app/issues/253.
46
+ // It works similar to `NODE_PATH` in Node itself:
47
+ // https://nodejs.org/api/modules.html#modules_loading_from_the_global_folders
48
+ // Note that unlike in Node, only *relative* paths from `NODE_PATH` are honored.
49
+ // Otherwise, we risk importing Node.js core modules into an app instead of webpack shims.
50
+ // https://github.com/facebook/create-react-app/issues/1023#issuecomment-265344421
51
+ // We also resolve them to make sure all tools using them work consistently.
52
+ const appDirectory = fs.realpathSync(process.cwd());
53
+ process.env.NODE_PATH = (process.env.NODE_PATH || '')
54
+ .split(path.delimiter)
55
+ .filter(folder => folder && !path.isAbsolute(folder))
56
+ .map(folder => path.resolve(appDirectory, folder))
57
+ .join(path.delimiter);
58
+
59
+ // Grab NODE_ENV and REACT_APP_* environment variables and prepare them to be
60
+ // injected into the application via DefinePlugin in webpack configuration.
61
+ const REACT_APP = /^REACT_APP_/i;
62
+
63
+ function getClientEnvironment(publicUrl) {
64
+ const raw = Object.keys(process.env)
65
+ .filter(key => REACT_APP.test(key))
66
+ .reduce(
67
+ (env, key) => {
68
+ env[key] = process.env[key];
69
+ return env;
70
+ },
71
+ {
72
+ // Useful for determining whether we’re running in production mode.
73
+ // Most importantly, it switches React into the correct mode.
74
+ NODE_ENV: process.env.NODE_ENV || 'development',
75
+ // Useful for resolving the correct path to static assets in `public`.
76
+ // For example, <img src={process.env.PUBLIC_URL + '/img/logo.png'} />.
77
+ // This should only be used as an escape hatch. Normally you would put
78
+ // images into the `src` and `import` them in code to get their paths.
79
+ PUBLIC_URL: publicUrl,
80
+ // We support configuring the sockjs pathname during development.
81
+ // These settings let a developer run multiple simultaneous projects.
82
+ // They are used as the connection `hostname`, `pathname` and `port`
83
+ // in webpackHotDevClient. They are used as the `sockHost`, `sockPath`
84
+ // and `sockPort` options in webpack-dev-server.
85
+ WDS_SOCKET_HOST: process.env.WDS_SOCKET_HOST,
86
+ WDS_SOCKET_PATH: process.env.WDS_SOCKET_PATH,
87
+ WDS_SOCKET_PORT: process.env.WDS_SOCKET_PORT,
88
+ // Whether or not react-refresh is enabled.
89
+ // It is defined here so it is available in the webpackHotDevClient.
90
+ FAST_REFRESH: process.env.FAST_REFRESH !== 'false',
91
+ }
92
+ );
93
+ // Stringify all values so we can feed into webpack DefinePlugin
94
+ const stringified = {
95
+ 'process.env': Object.keys(raw).reduce((env, key) => {
96
+ env[key] = JSON.stringify(raw[key]);
97
+ return env;
98
+ }, {}),
99
+ };
100
+
101
+ return { raw, stringified };
102
+ }
103
+
104
+ module.exports = getClientEnvironment;
lama_cleaner/app/config/getHttpsConfig.js ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const crypto = require('crypto');
6
+ const chalk = require('react-dev-utils/chalk');
7
+ const paths = require('./paths');
8
+
9
+ // Ensure the certificate and key provided are valid and if not
10
+ // throw an easy to debug error
11
+ function validateKeyAndCerts({ cert, key, keyFile, crtFile }) {
12
+ let encrypted;
13
+ try {
14
+ // publicEncrypt will throw an error with an invalid cert
15
+ encrypted = crypto.publicEncrypt(cert, Buffer.from('test'));
16
+ } catch (err) {
17
+ throw new Error(
18
+ `The certificate "${chalk.yellow(crtFile)}" is invalid.\n${err.message}`
19
+ );
20
+ }
21
+
22
+ try {
23
+ // privateDecrypt will throw an error with an invalid key
24
+ crypto.privateDecrypt(key, encrypted);
25
+ } catch (err) {
26
+ throw new Error(
27
+ `The certificate key "${chalk.yellow(keyFile)}" is invalid.\n${
28
+ err.message
29
+ }`
30
+ );
31
+ }
32
+ }
33
+
34
+ // Read file and throw an error if it doesn't exist
35
+ function readEnvFile(file, type) {
36
+ if (!fs.existsSync(file)) {
37
+ throw new Error(
38
+ `You specified ${chalk.cyan(
39
+ type
40
+ )} in your env, but the file "${chalk.yellow(file)}" can't be found.`
41
+ );
42
+ }
43
+ return fs.readFileSync(file);
44
+ }
45
+
46
+ // Get the https config
47
+ // Return cert files if provided in env, otherwise just true or false
48
+ function getHttpsConfig() {
49
+ const { SSL_CRT_FILE, SSL_KEY_FILE, HTTPS } = process.env;
50
+ const isHttps = HTTPS === 'true';
51
+
52
+ if (isHttps && SSL_CRT_FILE && SSL_KEY_FILE) {
53
+ const crtFile = path.resolve(paths.appPath, SSL_CRT_FILE);
54
+ const keyFile = path.resolve(paths.appPath, SSL_KEY_FILE);
55
+ const config = {
56
+ cert: readEnvFile(crtFile, 'SSL_CRT_FILE'),
57
+ key: readEnvFile(keyFile, 'SSL_KEY_FILE'),
58
+ };
59
+
60
+ validateKeyAndCerts({ ...config, keyFile, crtFile });
61
+ return config;
62
+ }
63
+ return isHttps;
64
+ }
65
+
66
+ module.exports = getHttpsConfig;
lama_cleaner/app/config/jest/babelTransform.js ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ const babelJest = require('babel-jest').default;
4
+
5
+ const hasJsxRuntime = (() => {
6
+ if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
7
+ return false;
8
+ }
9
+
10
+ try {
11
+ require.resolve('react/jsx-runtime');
12
+ return true;
13
+ } catch (e) {
14
+ return false;
15
+ }
16
+ })();
17
+
18
+ module.exports = babelJest.createTransformer({
19
+ presets: [
20
+ [
21
+ require.resolve('babel-preset-react-app'),
22
+ {
23
+ runtime: hasJsxRuntime ? 'automatic' : 'classic',
24
+ },
25
+ ],
26
+ ],
27
+ babelrc: false,
28
+ configFile: false,
29
+ });
lama_cleaner/app/config/jest/cssTransform.js ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ // This is a custom Jest transformer turning style imports into empty objects.
4
+ // http://facebook.github.io/jest/docs/en/webpack.html
5
+
6
+ module.exports = {
7
+ process() {
8
+ return 'module.exports = {};';
9
+ },
10
+ getCacheKey() {
11
+ // The output is always the same.
12
+ return 'cssTransform';
13
+ },
14
+ };
lama_cleaner/app/config/jest/fileTransform.js ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const camelcase = require('camelcase');
5
+
6
+ // This is a custom Jest transformer turning file imports into filenames.
7
+ // http://facebook.github.io/jest/docs/en/webpack.html
8
+
9
+ module.exports = {
10
+ process(src, filename) {
11
+ const assetFilename = JSON.stringify(path.basename(filename));
12
+
13
+ if (filename.match(/\.svg$/)) {
14
+ // Based on how SVGR generates a component name:
15
+ // https://github.com/smooth-code/svgr/blob/01b194cf967347d43d4cbe6b434404731b87cf27/packages/core/src/state.js#L6
16
+ const pascalCaseFilename = camelcase(path.parse(filename).name, {
17
+ pascalCase: true,
18
+ });
19
+ const componentName = `Svg${pascalCaseFilename}`;
20
+ return `const React = require('react');
21
+ module.exports = {
22
+ __esModule: true,
23
+ default: ${assetFilename},
24
+ ReactComponent: React.forwardRef(function ${componentName}(props, ref) {
25
+ return {
26
+ $$typeof: Symbol.for('react.element'),
27
+ type: 'svg',
28
+ ref: ref,
29
+ key: null,
30
+ props: Object.assign({}, props, {
31
+ children: ${assetFilename}
32
+ })
33
+ };
34
+ }),
35
+ };`;
36
+ }
37
+
38
+ return `module.exports = ${assetFilename};`;
39
+ },
40
+ };
lama_cleaner/app/config/modules.js ADDED
@@ -0,0 +1,134 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+ const paths = require('./paths');
6
+ const chalk = require('react-dev-utils/chalk');
7
+ const resolve = require('resolve');
8
+
9
+ /**
10
+ * Get additional module paths based on the baseUrl of a compilerOptions object.
11
+ *
12
+ * @param {Object} options
13
+ */
14
+ function getAdditionalModulePaths(options = {}) {
15
+ const baseUrl = options.baseUrl;
16
+
17
+ if (!baseUrl) {
18
+ return '';
19
+ }
20
+
21
+ const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
22
+
23
+ // We don't need to do anything if `baseUrl` is set to `node_modules`. This is
24
+ // the default behavior.
25
+ if (path.relative(paths.appNodeModules, baseUrlResolved) === '') {
26
+ return null;
27
+ }
28
+
29
+ // Allow the user set the `baseUrl` to `appSrc`.
30
+ if (path.relative(paths.appSrc, baseUrlResolved) === '') {
31
+ return [paths.appSrc];
32
+ }
33
+
34
+ // If the path is equal to the root directory we ignore it here.
35
+ // We don't want to allow importing from the root directly as source files are
36
+ // not transpiled outside of `src`. We do allow importing them with the
37
+ // absolute path (e.g. `src/Components/Button.js`) but we set that up with
38
+ // an alias.
39
+ if (path.relative(paths.appPath, baseUrlResolved) === '') {
40
+ return null;
41
+ }
42
+
43
+ // Otherwise, throw an error.
44
+ throw new Error(
45
+ chalk.red.bold(
46
+ "Your project's `baseUrl` can only be set to `src` or `node_modules`." +
47
+ ' Create React App does not support other values at this time.'
48
+ )
49
+ );
50
+ }
51
+
52
+ /**
53
+ * Get webpack aliases based on the baseUrl of a compilerOptions object.
54
+ *
55
+ * @param {*} options
56
+ */
57
+ function getWebpackAliases(options = {}) {
58
+ const baseUrl = options.baseUrl;
59
+
60
+ if (!baseUrl) {
61
+ return {};
62
+ }
63
+
64
+ const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
65
+
66
+ if (path.relative(paths.appPath, baseUrlResolved) === '') {
67
+ return {
68
+ src: paths.appSrc,
69
+ };
70
+ }
71
+ }
72
+
73
+ /**
74
+ * Get jest aliases based on the baseUrl of a compilerOptions object.
75
+ *
76
+ * @param {*} options
77
+ */
78
+ function getJestAliases(options = {}) {
79
+ const baseUrl = options.baseUrl;
80
+
81
+ if (!baseUrl) {
82
+ return {};
83
+ }
84
+
85
+ const baseUrlResolved = path.resolve(paths.appPath, baseUrl);
86
+
87
+ if (path.relative(paths.appPath, baseUrlResolved) === '') {
88
+ return {
89
+ '^src/(.*)$': '<rootDir>/src/$1',
90
+ };
91
+ }
92
+ }
93
+
94
+ function getModules() {
95
+ // Check if TypeScript is setup
96
+ const hasTsConfig = fs.existsSync(paths.appTsConfig);
97
+ const hasJsConfig = fs.existsSync(paths.appJsConfig);
98
+
99
+ if (hasTsConfig && hasJsConfig) {
100
+ throw new Error(
101
+ 'You have both a tsconfig.json and a jsconfig.json. If you are using TypeScript please remove your jsconfig.json file.'
102
+ );
103
+ }
104
+
105
+ let config;
106
+
107
+ // If there's a tsconfig.json we assume it's a
108
+ // TypeScript project and set up the config
109
+ // based on tsconfig.json
110
+ if (hasTsConfig) {
111
+ const ts = require(resolve.sync('typescript', {
112
+ basedir: paths.appNodeModules,
113
+ }));
114
+ config = ts.readConfigFile(paths.appTsConfig, ts.sys.readFile).config;
115
+ // Otherwise we'll check if there is jsconfig.json
116
+ // for non TS projects.
117
+ } else if (hasJsConfig) {
118
+ config = require(paths.appJsConfig);
119
+ }
120
+
121
+ config = config || {};
122
+ const options = config.compilerOptions || {};
123
+
124
+ const additionalModulePaths = getAdditionalModulePaths(options);
125
+
126
+ return {
127
+ additionalModulePaths: additionalModulePaths,
128
+ webpackAliases: getWebpackAliases(options),
129
+ jestAliases: getJestAliases(options),
130
+ hasTsConfig,
131
+ };
132
+ }
133
+
134
+ module.exports = getModules();
lama_cleaner/app/config/paths.js ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ const path = require('path');
4
+ const fs = require('fs');
5
+ const getPublicUrlOrPath = require('react-dev-utils/getPublicUrlOrPath');
6
+
7
+ // Make sure any symlinks in the project folder are resolved:
8
+ // https://github.com/facebook/create-react-app/issues/637
9
+ const appDirectory = fs.realpathSync(process.cwd());
10
+ const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11
+
12
+ // We use `PUBLIC_URL` environment variable or "homepage" field to infer
13
+ // "public path" at which the app is served.
14
+ // webpack needs to know it to put the right <script> hrefs into HTML even in
15
+ // single-page apps that may serve index.html for nested URLs like /todos/42.
16
+ // We can't use a relative path in HTML because we don't want to load something
17
+ // like /todos/42/static/js/bundle.7289d.js. We have to know the root.
18
+ const publicUrlOrPath = getPublicUrlOrPath(
19
+ process.env.NODE_ENV === 'development',
20
+ require(resolveApp('package.json')).homepage,
21
+ process.env.PUBLIC_URL
22
+ );
23
+
24
+ const buildPath = process.env.BUILD_PATH || 'build';
25
+
26
+ const moduleFileExtensions = [
27
+ 'web.mjs',
28
+ 'mjs',
29
+ 'web.js',
30
+ 'js',
31
+ 'web.ts',
32
+ 'ts',
33
+ 'web.tsx',
34
+ 'tsx',
35
+ 'json',
36
+ 'web.jsx',
37
+ 'jsx',
38
+ ];
39
+
40
+ // Resolve file paths in the same order as webpack
41
+ const resolveModule = (resolveFn, filePath) => {
42
+ const extension = moduleFileExtensions.find(extension =>
43
+ fs.existsSync(resolveFn(`${filePath}.${extension}`))
44
+ );
45
+
46
+ if (extension) {
47
+ return resolveFn(`${filePath}.${extension}`);
48
+ }
49
+
50
+ return resolveFn(`${filePath}.js`);
51
+ };
52
+
53
+ // config after eject: we're in ./config/
54
+ module.exports = {
55
+ dotenv: resolveApp('.env'),
56
+ appPath: resolveApp('.'),
57
+ appBuild: resolveApp(buildPath),
58
+ appPublic: resolveApp('public'),
59
+ appHtml: resolveApp('public/index.html'),
60
+ appIndexJs: resolveModule(resolveApp, 'src/index'),
61
+ appPackageJson: resolveApp('package.json'),
62
+ appSrc: resolveApp('src'),
63
+ appTsConfig: resolveApp('tsconfig.json'),
64
+ appJsConfig: resolveApp('jsconfig.json'),
65
+ yarnLockFile: resolveApp('yarn.lock'),
66
+ testsSetup: resolveModule(resolveApp, 'src/setupTests'),
67
+ proxySetup: resolveApp('src/setupProxy.js'),
68
+ appNodeModules: resolveApp('node_modules'),
69
+ appWebpackCache: resolveApp('node_modules/.cache'),
70
+ appTsBuildInfoFile: resolveApp('node_modules/.cache/tsconfig.tsbuildinfo'),
71
+ swSrc: resolveModule(resolveApp, 'src/service-worker'),
72
+ publicUrlOrPath,
73
+ };
74
+
75
+
76
+
77
+ module.exports.moduleFileExtensions = moduleFileExtensions;
lama_cleaner/app/config/webpack.config.js ADDED
@@ -0,0 +1,761 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict'
2
+
3
+ const fs = require('fs')
4
+ const path = require('path')
5
+ const webpack = require('webpack')
6
+ const resolve = require('resolve')
7
+ const HtmlWebpackPlugin = require('html-webpack-plugin')
8
+ const CaseSensitivePathsPlugin = require('case-sensitive-paths-webpack-plugin')
9
+ const InlineChunkHtmlPlugin = require('react-dev-utils/InlineChunkHtmlPlugin')
10
+ const TerserPlugin = require('terser-webpack-plugin')
11
+ const MiniCssExtractPlugin = require('mini-css-extract-plugin')
12
+ const CssMinimizerPlugin = require('css-minimizer-webpack-plugin')
13
+ const { WebpackManifestPlugin } = require('webpack-manifest-plugin')
14
+ const InterpolateHtmlPlugin = require('react-dev-utils/InterpolateHtmlPlugin')
15
+ const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
16
+ const ModuleScopePlugin = require('react-dev-utils/ModuleScopePlugin')
17
+ const getCSSModuleLocalIdent = require('react-dev-utils/getCSSModuleLocalIdent')
18
+ const ESLintPlugin = require('eslint-webpack-plugin')
19
+ const paths = require('./paths')
20
+ const modules = require('./modules')
21
+ const getClientEnvironment = require('./env')
22
+ const ModuleNotFoundPlugin = require('react-dev-utils/ModuleNotFoundPlugin')
23
+ const ForkTsCheckerWebpackPlugin =
24
+ process.env.TSC_COMPILE_ON_ERROR === 'true'
25
+ ? require('react-dev-utils/ForkTsCheckerWarningWebpackPlugin')
26
+ : require('react-dev-utils/ForkTsCheckerWebpackPlugin')
27
+ const ReactRefreshWebpackPlugin = require('@pmmmwh/react-refresh-webpack-plugin')
28
+
29
+ const createEnvironmentHash = require('./webpack/persistentCache/createEnvironmentHash')
30
+
31
+ // Source maps are resource heavy and can cause out of memory issue for large source files.
32
+ const shouldUseSourceMap = process.env.GENERATE_SOURCEMAP !== 'false'
33
+
34
+ const reactRefreshRuntimeEntry = require.resolve('react-refresh/runtime')
35
+ const reactRefreshWebpackPluginRuntimeEntry = require.resolve(
36
+ '@pmmmwh/react-refresh-webpack-plugin'
37
+ )
38
+ const babelRuntimeEntry = require.resolve('babel-preset-react-app')
39
+ const babelRuntimeEntryHelpers = require.resolve(
40
+ '@babel/runtime/helpers/esm/assertThisInitialized',
41
+ { paths: [babelRuntimeEntry] }
42
+ )
43
+ const babelRuntimeRegenerator = require.resolve('@babel/runtime/regenerator', {
44
+ paths: [babelRuntimeEntry],
45
+ })
46
+
47
+ // Some apps do not need the benefits of saving a web request, so not inlining the chunk
48
+ // makes for a smoother build process.
49
+ const shouldInlineRuntimeChunk = process.env.INLINE_RUNTIME_CHUNK !== 'false'
50
+
51
+ const emitErrorsAsWarnings = process.env.ESLINT_NO_DEV_ERRORS === 'true'
52
+ const disableESLintPlugin = process.env.DISABLE_ESLINT_PLUGIN === 'true'
53
+
54
+ const imageInlineSizeLimit = parseInt(
55
+ process.env.IMAGE_INLINE_SIZE_LIMIT || '10000'
56
+ )
57
+
58
+ // Check if TypeScript is setup
59
+ const useTypeScript = fs.existsSync(paths.appTsConfig)
60
+
61
+ // Check if Tailwind config exists
62
+ const useTailwind = fs.existsSync(
63
+ path.join(paths.appPath, 'tailwind.config.js')
64
+ )
65
+
66
+ // Get the path to the uncompiled service worker (if it exists).
67
+ const swSrc = paths.swSrc
68
+
69
+ // style files regexes
70
+ const cssRegex = /\.css$/
71
+ const cssModuleRegex = /\.module\.css$/
72
+ const sassRegex = /\.(scss|sass)$/
73
+ const sassModuleRegex = /\.module\.(scss|sass)$/
74
+
75
+ const hasJsxRuntime = (() => {
76
+ if (process.env.DISABLE_NEW_JSX_TRANSFORM === 'true') {
77
+ return false
78
+ }
79
+
80
+ try {
81
+ require.resolve('react/jsx-runtime')
82
+ return true
83
+ } catch (e) {
84
+ return false
85
+ }
86
+ })()
87
+
88
+ // This is the production and development configuration.
89
+ // It is focused on developer experience, fast rebuilds, and a minimal bundle.
90
+ module.exports = function (webpackEnv) {
91
+ const isEnvDevelopment = webpackEnv === 'development'
92
+ const isEnvProduction = webpackEnv === 'production'
93
+
94
+ // Variable used for enabling profiling in Production
95
+ // passed into alias object. Uses a flag if passed into the build command
96
+ const isEnvProductionProfile =
97
+ isEnvProduction && process.argv.includes('--profile')
98
+
99
+ // We will provide `paths.publicUrlOrPath` to our app
100
+ // as %PUBLIC_URL% in `index.html` and `process.env.PUBLIC_URL` in JavaScript.
101
+ // Omit trailing slash as %PUBLIC_URL%/xyz looks better than %PUBLIC_URL%xyz.
102
+ // Get environment variables to inject into our app.
103
+ const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1))
104
+
105
+ const shouldUseReactRefresh = env.raw.FAST_REFRESH
106
+
107
+ // common function to get style loaders
108
+ const getStyleLoaders = (cssOptions, preProcessor) => {
109
+ const loaders = [
110
+ isEnvDevelopment && require.resolve('style-loader'),
111
+ isEnvProduction && {
112
+ loader: MiniCssExtractPlugin.loader,
113
+ // css is located in `static/css`, use '../../' to locate index.html folder
114
+ // in production `paths.publicUrlOrPath` can be a relative path
115
+ options: paths.publicUrlOrPath.startsWith('.')
116
+ ? { publicPath: '../../' }
117
+ : {},
118
+ },
119
+ {
120
+ loader: require.resolve('css-loader'),
121
+ options: cssOptions,
122
+ },
123
+ {
124
+ // Options for PostCSS as we reference these options twice
125
+ // Adds vendor prefixing based on your specified browser support in
126
+ // package.json
127
+ loader: require.resolve('postcss-loader'),
128
+ options: {
129
+ postcssOptions: {
130
+ // Necessary for external CSS imports to work
131
+ // https://github.com/facebook/create-react-app/issues/2677
132
+ ident: 'postcss',
133
+ config: false,
134
+ plugins: !useTailwind
135
+ ? [
136
+ 'postcss-flexbugs-fixes',
137
+ [
138
+ 'postcss-preset-env',
139
+ {
140
+ autoprefixer: {
141
+ flexbox: 'no-2009',
142
+ },
143
+ stage: 3,
144
+ },
145
+ ],
146
+ // Adds PostCSS Normalize as the reset css with default options,
147
+ // so that it honors browserslist config in package.json
148
+ // which in turn let's users customize the target behavior as per their needs.
149
+ 'postcss-normalize',
150
+ ]
151
+ : [
152
+ 'tailwindcss',
153
+ 'postcss-flexbugs-fixes',
154
+ [
155
+ 'postcss-preset-env',
156
+ {
157
+ autoprefixer: {
158
+ flexbox: 'no-2009',
159
+ },
160
+ stage: 3,
161
+ },
162
+ ],
163
+ ],
164
+ },
165
+ sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
166
+ },
167
+ },
168
+ ].filter(Boolean)
169
+ if (preProcessor) {
170
+ loaders.push(
171
+ {
172
+ loader: require.resolve('resolve-url-loader'),
173
+ options: {
174
+ sourceMap: isEnvProduction ? shouldUseSourceMap : isEnvDevelopment,
175
+ root: paths.appSrc,
176
+ },
177
+ },
178
+ {
179
+ loader: require.resolve(preProcessor),
180
+ options: {
181
+ sourceMap: true,
182
+ },
183
+ }
184
+ )
185
+ }
186
+ return loaders
187
+ }
188
+
189
+ return {
190
+ target: ['browserslist'],
191
+ // Webpack noise constrained to errors and warnings
192
+ stats: 'errors-warnings',
193
+ mode: isEnvProduction ? 'production' : isEnvDevelopment && 'development',
194
+ // Stop compilation early in production
195
+ bail: isEnvProduction,
196
+ devtool: isEnvProduction
197
+ ? shouldUseSourceMap
198
+ ? 'source-map'
199
+ : false
200
+ : isEnvDevelopment && 'cheap-module-source-map',
201
+ // These are the "entry points" to our application.
202
+ // This means they will be the "root" imports that are included in JS bundle.
203
+ entry: paths.appIndexJs,
204
+ output: {
205
+ // The build folder.
206
+ path: paths.appBuild,
207
+ // Add /* filename */ comments to generated require()s in the output.
208
+ pathinfo: isEnvDevelopment,
209
+ // There will be one main bundle, and one file per asynchronous chunk.
210
+ // In development, it does not produce real files.
211
+ filename: isEnvProduction
212
+ ? 'static/js/[name].[contenthash:8].js'
213
+ : isEnvDevelopment && 'static/js/bundle.js',
214
+ // There are also additional JS chunk files if you use code splitting.
215
+ chunkFilename: isEnvProduction
216
+ ? 'static/js/[name].[contenthash:8].chunk.js'
217
+ : isEnvDevelopment && 'static/js/[name].chunk.js',
218
+ assetModuleFilename: 'static/media/[name].[hash][ext]',
219
+ // webpack uses `publicPath` to determine where the app is being served from.
220
+ // It requires a trailing slash, or the file assets will get an incorrect path.
221
+ // We inferred the "public path" (such as / or /my-project) from homepage.
222
+ publicPath: paths.publicUrlOrPath,
223
+ // Point sourcemap entries to original disk location (format as URL on Windows)
224
+ devtoolModuleFilenameTemplate: isEnvProduction
225
+ ? info =>
226
+ path
227
+ .relative(paths.appSrc, info.absoluteResourcePath)
228
+ .replace(/\\/g, '/')
229
+ : isEnvDevelopment &&
230
+ (info => path.resolve(info.absoluteResourcePath).replace(/\\/g, '/')),
231
+ },
232
+ cache: {
233
+ type: 'filesystem',
234
+ version: createEnvironmentHash(env.raw),
235
+ cacheDirectory: paths.appWebpackCache,
236
+ store: 'pack',
237
+ buildDependencies: {
238
+ defaultWebpack: ['webpack/lib/'],
239
+ config: [__filename],
240
+ tsconfig: [paths.appTsConfig, paths.appJsConfig].filter(f =>
241
+ fs.existsSync(f)
242
+ ),
243
+ },
244
+ },
245
+ infrastructureLogging: {
246
+ level: 'none',
247
+ },
248
+ optimization: {
249
+ minimize: isEnvProduction,
250
+ minimizer: [
251
+ // This is only used in production mode
252
+ new TerserPlugin({
253
+ terserOptions: {
254
+ parse: {
255
+ // We want terser to parse ecma 8 code. However, we don't want it
256
+ // to apply any minification steps that turns valid ecma 5 code
257
+ // into invalid ecma 5 code. This is why the 'compress' and 'output'
258
+ // sections only apply transformations that are ecma 5 safe
259
+ // https://github.com/facebook/create-react-app/pull/4234
260
+ ecma: 8,
261
+ },
262
+ compress: {
263
+ ecma: 5,
264
+ warnings: false,
265
+ // Disabled because of an issue with Uglify breaking seemingly valid code:
266
+ // https://github.com/facebook/create-react-app/issues/2376
267
+ // Pending further investigation:
268
+ // https://github.com/mishoo/UglifyJS2/issues/2011
269
+ comparisons: false,
270
+ // Disabled because of an issue with Terser breaking valid code:
271
+ // https://github.com/facebook/create-react-app/issues/5250
272
+ // Pending further investigation:
273
+ // https://github.com/terser-js/terser/issues/120
274
+ inline: 2,
275
+ },
276
+ mangle: {
277
+ safari10: true,
278
+ },
279
+ // Added for profiling in devtools
280
+ keep_classnames: isEnvProductionProfile,
281
+ keep_fnames: isEnvProductionProfile,
282
+ output: {
283
+ ecma: 5,
284
+ comments: false,
285
+ // Turned on because emoji and regex is not minified properly using default
286
+ // https://github.com/facebook/create-react-app/issues/2488
287
+ ascii_only: true,
288
+ },
289
+ },
290
+ }),
291
+ // This is only used in production mode
292
+ new CssMinimizerPlugin(),
293
+ ],
294
+ },
295
+ resolve: {
296
+ // This allows you to set a fallback for where webpack should look for modules.
297
+ // We placed these paths second because we want `node_modules` to "win"
298
+ // if there are any conflicts. This matches Node resolution mechanism.
299
+ // https://github.com/facebook/create-react-app/issues/253
300
+ modules: ['node_modules', paths.appNodeModules].concat(
301
+ modules.additionalModulePaths || []
302
+ ),
303
+ // These are the reasonable defaults supported by the Node ecosystem.
304
+ // We also include JSX as a common component filename extension to support
305
+ // some tools, although we do not recommend using it, see:
306
+ // https://github.com/facebook/create-react-app/issues/290
307
+ // `web` extension prefixes have been added for better support
308
+ // for React Native Web.
309
+ extensions: paths.moduleFileExtensions
310
+ .map(ext => `.${ext}`)
311
+ .filter(ext => useTypeScript || !ext.includes('ts')),
312
+ alias: {
313
+ // Support React Native Web
314
+ // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
315
+ 'react-native': 'react-native-web',
316
+ // Allows for better profiling with ReactDevTools
317
+ ...(isEnvProductionProfile && {
318
+ 'react-dom$': 'react-dom/profiling',
319
+ 'scheduler/tracing': 'scheduler/tracing-profiling',
320
+ }),
321
+ ...(modules.webpackAliases || {}),
322
+ },
323
+ plugins: [
324
+ // Prevents users from importing files from outside of src/ (or node_modules/).
325
+ // This often causes confusion because we only process files within src/ with babel.
326
+ // To fix this, we prevent you from importing files out of src/ -- if you'd like to,
327
+ // please link the files into your node_modules/ and let module-resolution kick in.
328
+ // Make sure your source files are compiled, as they will not be processed in any way.
329
+ new ModuleScopePlugin(paths.appSrc, [
330
+ paths.appPackageJson,
331
+ reactRefreshRuntimeEntry,
332
+ reactRefreshWebpackPluginRuntimeEntry,
333
+ babelRuntimeEntry,
334
+ babelRuntimeEntryHelpers,
335
+ babelRuntimeRegenerator,
336
+ ]),
337
+ ],
338
+ },
339
+ module: {
340
+ strictExportPresence: true,
341
+ rules: [
342
+ {
343
+ test: /\.m?js/,
344
+ resolve: {
345
+ fullySpecified: false,
346
+ },
347
+ },
348
+ // Handle node_modules packages that contain sourcemaps
349
+ shouldUseSourceMap && {
350
+ enforce: 'pre',
351
+ exclude: /@babel(?:\/|\\{1,2})runtime/,
352
+ test: /\.(js|mjs|jsx|ts|tsx|css)$/,
353
+ loader: require.resolve('source-map-loader'),
354
+ },
355
+ {
356
+ // "oneOf" will traverse all following loaders until one will
357
+ // match the requirements. When no loader matches it will fall
358
+ // back to the "file" loader at the end of the loader list.
359
+ oneOf: [
360
+ // TODO: Merge this config once `image/avif` is in the mime-db
361
+ // https://github.com/jshttp/mime-db
362
+ {
363
+ test: [/\.avif$/],
364
+ type: 'asset',
365
+ mimetype: 'image/avif',
366
+ parser: {
367
+ dataUrlCondition: {
368
+ maxSize: imageInlineSizeLimit,
369
+ },
370
+ },
371
+ },
372
+ // "url" loader works like "file" loader except that it embeds assets
373
+ // smaller than specified limit in bytes as data URLs to avoid requests.
374
+ // A missing `test` is equivalent to a match.
375
+ {
376
+ test: [/\.bmp$/, /\.gif$/, /\.jpe?g$/, /\.png$/],
377
+ type: 'asset',
378
+ parser: {
379
+ dataUrlCondition: {
380
+ maxSize: imageInlineSizeLimit,
381
+ },
382
+ },
383
+ },
384
+ {
385
+ test: /\.svg$/,
386
+ use: [
387
+ {
388
+ loader: require.resolve('@svgr/webpack'),
389
+ options: {
390
+ prettier: false,
391
+ svgo: false,
392
+ svgoConfig: {
393
+ plugins: [{ removeViewBox: false }],
394
+ },
395
+ titleProp: true,
396
+ ref: true,
397
+ },
398
+ },
399
+ {
400
+ loader: require.resolve('file-loader'),
401
+ options: {
402
+ name: 'static/media/[name].[hash].[ext]',
403
+ },
404
+ },
405
+ ],
406
+ issuer: {
407
+ and: [/\.(ts|tsx|js|jsx|md|mdx)$/],
408
+ },
409
+ },
410
+ // Process application JS with Babel.
411
+ // The preset includes JSX, Flow, TypeScript, and some ESnext features.
412
+ {
413
+ test: /\.(js|mjs|jsx|ts|tsx)$/,
414
+ include: paths.appSrc,
415
+ loader: require.resolve('babel-loader'),
416
+ options: {
417
+ customize: require.resolve(
418
+ 'babel-preset-react-app/webpack-overrides'
419
+ ),
420
+ presets: [
421
+ [
422
+ require.resolve('babel-preset-react-app'),
423
+ {
424
+ runtime: hasJsxRuntime ? 'automatic' : 'classic',
425
+ },
426
+ ],
427
+ ],
428
+
429
+ plugins: [
430
+ isEnvDevelopment &&
431
+ shouldUseReactRefresh &&
432
+ require.resolve('react-refresh/babel'),
433
+ ].filter(Boolean),
434
+ // This is a feature of `babel-loader` for webpack (not Babel itself).
435
+ // It enables caching results in ./node_modules/.cache/babel-loader/
436
+ // directory for faster rebuilds.
437
+ cacheDirectory: true,
438
+ // See #6846 for context on why cacheCompression is disabled
439
+ cacheCompression: false,
440
+ compact: isEnvProduction,
441
+ },
442
+ },
443
+ // Process any JS outside of the app with Babel.
444
+ // Unlike the application JS, we only compile the standard ES features.
445
+ {
446
+ test: /\.(js|mjs)$/,
447
+ exclude: /@babel(?:\/|\\{1,2})runtime/,
448
+ loader: require.resolve('babel-loader'),
449
+ options: {
450
+ babelrc: false,
451
+ configFile: false,
452
+ compact: false,
453
+ presets: [
454
+ [
455
+ require.resolve('babel-preset-react-app/dependencies'),
456
+ { helpers: true },
457
+ ],
458
+ ],
459
+ cacheDirectory: true,
460
+ // See #6846 for context on why cacheCompression is disabled
461
+ cacheCompression: false,
462
+
463
+ // Babel sourcemaps are needed for debugging into node_modules
464
+ // code. Without the options below, debuggers like VSCode
465
+ // show incorrect code and set breakpoints on the wrong lines.
466
+ sourceMaps: shouldUseSourceMap,
467
+ inputSourceMap: shouldUseSourceMap,
468
+ },
469
+ },
470
+ // "postcss" loader applies autoprefixer to our CSS.
471
+ // "css" loader resolves paths in CSS and adds assets as dependencies.
472
+ // "style" loader turns CSS into JS modules that inject <style> tags.
473
+ // In production, we use MiniCSSExtractPlugin to extract that CSS
474
+ // to a file, but in development "style" loader enables hot editing
475
+ // of CSS.
476
+ // By default we support CSS Modules with the extension .module.css
477
+ {
478
+ test: cssRegex,
479
+ exclude: cssModuleRegex,
480
+ use: getStyleLoaders({
481
+ importLoaders: 1,
482
+ sourceMap: isEnvProduction
483
+ ? shouldUseSourceMap
484
+ : isEnvDevelopment,
485
+ modules: {
486
+ mode: 'icss',
487
+ },
488
+ }),
489
+ // Don't consider CSS imports dead code even if the
490
+ // containing package claims to have no side effects.
491
+ // Remove this when webpack adds a warning or an error for this.
492
+ // See https://github.com/webpack/webpack/issues/6571
493
+ sideEffects: true,
494
+ },
495
+ // Adds support for CSS Modules (https://github.com/css-modules/css-modules)
496
+ // using the extension .module.css
497
+ {
498
+ test: cssModuleRegex,
499
+ use: getStyleLoaders({
500
+ importLoaders: 1,
501
+ sourceMap: isEnvProduction
502
+ ? shouldUseSourceMap
503
+ : isEnvDevelopment,
504
+ modules: {
505
+ mode: 'local',
506
+ getLocalIdent: getCSSModuleLocalIdent,
507
+ },
508
+ }),
509
+ },
510
+ // Opt-in support for SASS (using .scss or .sass extensions).
511
+ // By default we support SASS Modules with the
512
+ // extensions .module.scss or .module.sass
513
+ {
514
+ test: sassRegex,
515
+ exclude: sassModuleRegex,
516
+ use: getStyleLoaders(
517
+ {
518
+ importLoaders: 3,
519
+ sourceMap: isEnvProduction
520
+ ? shouldUseSourceMap
521
+ : isEnvDevelopment,
522
+ modules: {
523
+ mode: 'icss',
524
+ },
525
+ },
526
+ 'sass-loader'
527
+ ),
528
+ // Don't consider CSS imports dead code even if the
529
+ // containing package claims to have no side effects.
530
+ // Remove this when webpack adds a warning or an error for this.
531
+ // See https://github.com/webpack/webpack/issues/6571
532
+ sideEffects: true,
533
+ },
534
+ // Adds support for CSS Modules, but using SASS
535
+ // using the extension .module.scss or .module.sass
536
+ {
537
+ test: sassModuleRegex,
538
+ use: getStyleLoaders(
539
+ {
540
+ importLoaders: 3,
541
+ sourceMap: isEnvProduction
542
+ ? shouldUseSourceMap
543
+ : isEnvDevelopment,
544
+ modules: {
545
+ mode: 'local',
546
+ getLocalIdent: getCSSModuleLocalIdent,
547
+ },
548
+ },
549
+ 'sass-loader'
550
+ ),
551
+ },
552
+ // "file" loader makes sure those assets get served by WebpackDevServer.
553
+ // When you `import` an asset, you get its (virtual) filename.
554
+ // In production, they would get copied to the `build` folder.
555
+ // This loader doesn't use a "test" so it will catch all modules
556
+ // that fall through the other loaders.
557
+ {
558
+ // Exclude `js` files to keep "css" loader working as it injects
559
+ // its runtime that would otherwise be processed through "file" loader.
560
+ // Also exclude `html` and `json` extensions so they get processed
561
+ // by webpacks internal loaders.
562
+ exclude: [/^$/, /\.(js|mjs|jsx|ts|tsx)$/, /\.html$/, /\.json$/],
563
+ type: 'asset/resource',
564
+ },
565
+ // ** STOP ** Are you adding a new loader?
566
+ // Make sure to add the new loader(s) before the "file" loader.
567
+ ],
568
+ },
569
+ ].filter(Boolean),
570
+ },
571
+ plugins: [
572
+ // Generates an `index.html` file with the <script> injected.
573
+ new HtmlWebpackPlugin(
574
+ Object.assign(
575
+ {},
576
+ {
577
+ inject: true,
578
+ template: paths.appHtml,
579
+ },
580
+ isEnvProduction
581
+ ? {
582
+ minify: {
583
+ removeComments: true,
584
+ collapseWhitespace: true,
585
+ removeRedundantAttributes: true,
586
+ useShortDoctype: true,
587
+ removeEmptyAttributes: true,
588
+ removeStyleLinkTypeAttributes: true,
589
+ keepClosingSlash: true,
590
+ minifyJS: true,
591
+ minifyCSS: true,
592
+ minifyURLs: true,
593
+ },
594
+ }
595
+ : undefined
596
+ )
597
+ ),
598
+ // Inlines the webpack runtime script. This script is too small to warrant
599
+ // a network request.
600
+ // https://github.com/facebook/create-react-app/issues/5358
601
+ isEnvProduction &&
602
+ shouldInlineRuntimeChunk &&
603
+ new InlineChunkHtmlPlugin(HtmlWebpackPlugin, [/runtime-.+[.]js/]),
604
+ // Makes some environment variables available in index.html.
605
+ // The public URL is available as %PUBLIC_URL% in index.html, e.g.:
606
+ // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
607
+ // It will be an empty string unless you specify "homepage"
608
+ // in `package.json`, in which case it will be the pathname of that URL.
609
+ new InterpolateHtmlPlugin(HtmlWebpackPlugin, env.raw),
610
+ // This gives some necessary context to module not found errors, such as
611
+ // the requesting resource.
612
+ new ModuleNotFoundPlugin(paths.appPath),
613
+ // Makes some environment variables available to the JS code, for example:
614
+ // if (process.env.NODE_ENV === 'production') { ... }. See `./env.js`.
615
+ // It is absolutely essential that NODE_ENV is set to production
616
+ // during a production build.
617
+ // Otherwise React will be compiled in the very slow development mode.
618
+ new webpack.DefinePlugin(env.stringified),
619
+ // Experimental hot reloading for React .
620
+ // https://github.com/facebook/react/tree/main/packages/react-refresh
621
+ isEnvDevelopment &&
622
+ shouldUseReactRefresh &&
623
+ new ReactRefreshWebpackPlugin({
624
+ overlay: false,
625
+ }),
626
+ // Watcher doesn't work well if you mistype casing in a path so we use
627
+ // a plugin that prints an error when you attempt to do this.
628
+ // See https://github.com/facebook/create-react-app/issues/240
629
+ isEnvDevelopment && new CaseSensitivePathsPlugin(),
630
+ isEnvProduction &&
631
+ new MiniCssExtractPlugin({
632
+ // Options similar to the same options in webpackOptions.output
633
+ // both options are optional
634
+ filename: 'static/css/[name].[contenthash:8].css',
635
+ chunkFilename: 'static/css/[name].[contenthash:8].chunk.css',
636
+ }),
637
+ // Generate an asset manifest file with the following content:
638
+ // - "files" key: Mapping of all asset filenames to their corresponding
639
+ // output file so that tools can pick it up without having to parse
640
+ // `index.html`
641
+ // - "entrypoints" key: Array of files which are included in `index.html`,
642
+ // can be used to reconstruct the HTML if necessary
643
+ new WebpackManifestPlugin({
644
+ fileName: 'asset-manifest.json',
645
+ publicPath: paths.publicUrlOrPath,
646
+ generate: (seed, files, entrypoints) => {
647
+ const manifestFiles = files.reduce((manifest, file) => {
648
+ manifest[file.name] = file.path
649
+ return manifest
650
+ }, seed)
651
+ const entrypointFiles = entrypoints.main.filter(
652
+ fileName => !fileName.endsWith('.map')
653
+ )
654
+
655
+ return {
656
+ files: manifestFiles,
657
+ entrypoints: entrypointFiles,
658
+ }
659
+ },
660
+ }),
661
+ // Moment.js is an extremely popular library that bundles large locale files
662
+ // by default due to how webpack interprets its code. This is a practical
663
+ // solution that requires the user to opt into importing specific locales.
664
+ // https://github.com/jmblog/how-to-optimize-momentjs-with-webpack
665
+ // You can remove this if you don't use Moment.js:
666
+ new webpack.IgnorePlugin({
667
+ resourceRegExp: /^\.\/locale$/,
668
+ contextRegExp: /moment$/,
669
+ }),
670
+ // Generate a service worker script that will precache, and keep up to date,
671
+ // the HTML & assets that are part of the webpack build.
672
+ isEnvProduction &&
673
+ fs.existsSync(swSrc) &&
674
+ new WorkboxWebpackPlugin.InjectManifest({
675
+ swSrc,
676
+ dontCacheBustURLsMatching: /\.[0-9a-f]{8}\./,
677
+ exclude: [/\.map$/, /asset-manifest\.json$/, /LICENSE/],
678
+ // Bump up the default maximum size (2mb) that's precached,
679
+ // to make lazy-loading failure scenarios less likely.
680
+ // See https://github.com/cra-template/pwa/issues/13#issuecomment-722667270
681
+ maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
682
+ }),
683
+ // TypeScript type checking
684
+ useTypeScript &&
685
+ new ForkTsCheckerWebpackPlugin({
686
+ async: isEnvDevelopment,
687
+ typescript: {
688
+ typescriptPath: resolve.sync('typescript', {
689
+ basedir: paths.appNodeModules,
690
+ }),
691
+ configOverwrite: {
692
+ compilerOptions: {
693
+ sourceMap: isEnvProduction
694
+ ? shouldUseSourceMap
695
+ : isEnvDevelopment,
696
+ skipLibCheck: true,
697
+ inlineSourceMap: false,
698
+ declarationMap: false,
699
+ noEmit: true,
700
+ incremental: true,
701
+ tsBuildInfoFile: paths.appTsBuildInfoFile,
702
+ },
703
+ },
704
+ context: paths.appPath,
705
+ diagnosticOptions: {
706
+ syntactic: true,
707
+ },
708
+ mode: 'write-references',
709
+ // profile: true,
710
+ },
711
+ issue: {
712
+ // This one is specifically to match during CI tests,
713
+ // as micromatch doesn't match
714
+ // '../cra-template-typescript/template/src/App.tsx'
715
+ // otherwise.
716
+ include: [
717
+ { file: '../**/src/**/*.{ts,tsx}' },
718
+ { file: '**/src/**/*.{ts,tsx}' },
719
+ ],
720
+ exclude: [
721
+ { file: '**/src/**/__tests__/**' },
722
+ { file: '**/src/**/?(*.){spec|test}.*' },
723
+ { file: '**/src/setupProxy.*' },
724
+ { file: '**/src/setupTests.*' },
725
+ ],
726
+ },
727
+ logger: {
728
+ infrastructure: 'silent',
729
+ },
730
+ }),
731
+ !disableESLintPlugin &&
732
+ new ESLintPlugin({
733
+ // Plugin options
734
+ extensions: ['js', 'mjs', 'jsx', 'ts', 'tsx'],
735
+ formatter: require.resolve('react-dev-utils/eslintFormatter'),
736
+ eslintPath: require.resolve('eslint'),
737
+ failOnError: !(isEnvDevelopment && emitErrorsAsWarnings),
738
+ context: paths.appSrc,
739
+ cache: true,
740
+ cacheLocation: path.resolve(
741
+ paths.appNodeModules,
742
+ '.cache/.eslintcache'
743
+ ),
744
+ // ESLint class options
745
+ cwd: paths.appPath,
746
+ resolvePluginsRelativeTo: __dirname,
747
+ baseConfig: {
748
+ extends: [require.resolve('eslint-config-react-app/base')],
749
+ rules: {
750
+ ...(!hasJsxRuntime && {
751
+ 'react/react-in-jsx-scope': 'error',
752
+ }),
753
+ },
754
+ },
755
+ }),
756
+ ].filter(Boolean),
757
+ // Turn off performance processing because we utilize
758
+ // our own hints via the FileSizeReporter
759
+ performance: false,
760
+ }
761
+ }
lama_cleaner/app/config/webpack/persistentCache/createEnvironmentHash.js ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+ const { createHash } = require('crypto');
3
+
4
+ module.exports = env => {
5
+ const hash = createHash('md5');
6
+ hash.update(JSON.stringify(env));
7
+
8
+ return hash.digest('hex');
9
+ };
lama_cleaner/app/config/webpackDevServer.config.js ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ const fs = require('fs');
4
+ const evalSourceMapMiddleware = require('react-dev-utils/evalSourceMapMiddleware');
5
+ const noopServiceWorkerMiddleware = require('react-dev-utils/noopServiceWorkerMiddleware');
6
+ const ignoredFiles = require('react-dev-utils/ignoredFiles');
7
+ const redirectServedPath = require('react-dev-utils/redirectServedPathMiddleware');
8
+ const paths = require('./paths');
9
+ const getHttpsConfig = require('./getHttpsConfig');
10
+
11
+ const host = process.env.HOST || '0.0.0.0';
12
+ const sockHost = process.env.WDS_SOCKET_HOST;
13
+ const sockPath = process.env.WDS_SOCKET_PATH; // default: '/ws'
14
+ const sockPort = process.env.WDS_SOCKET_PORT;
15
+
16
+ module.exports = function (proxy, allowedHost) {
17
+ const disableFirewall =
18
+ !proxy || process.env.DANGEROUSLY_DISABLE_HOST_CHECK === 'true';
19
+ return {
20
+ // WebpackDevServer 2.4.3 introduced a security fix that prevents remote
21
+ // websites from potentially accessing local content through DNS rebinding:
22
+ // https://github.com/webpack/webpack-dev-server/issues/887
23
+ // https://medium.com/webpack/webpack-dev-server-middleware-security-issues-1489d950874a
24
+ // However, it made several existing use cases such as development in cloud
25
+ // environment or subdomains in development significantly more complicated:
26
+ // https://github.com/facebook/create-react-app/issues/2271
27
+ // https://github.com/facebook/create-react-app/issues/2233
28
+ // While we're investigating better solutions, for now we will take a
29
+ // compromise. Since our WDS configuration only serves files in the `public`
30
+ // folder we won't consider accessing them a vulnerability. However, if you
31
+ // use the `proxy` feature, it gets more dangerous because it can expose
32
+ // remote code execution vulnerabilities in backends like Django and Rails.
33
+ // So we will disable the host check normally, but enable it if you have
34
+ // specified the `proxy` setting. Finally, we let you override it if you
35
+ // really know what you're doing with a special environment variable.
36
+ // Note: ["localhost", ".localhost"] will support subdomains - but we might
37
+ // want to allow setting the allowedHosts manually for more complex setups
38
+ allowedHosts: disableFirewall ? 'all' : [allowedHost],
39
+ headers: {
40
+ 'Access-Control-Allow-Origin': '*',
41
+ 'Access-Control-Allow-Methods': '*',
42
+ 'Access-Control-Allow-Headers': '*',
43
+ },
44
+ // Enable gzip compression of generated files.
45
+ compress: true,
46
+ static: {
47
+ // By default WebpackDevServer serves physical files from current directory
48
+ // in addition to all the virtual build products that it serves from memory.
49
+ // This is confusing because those files won’t automatically be available in
50
+ // production build folder unless we copy them. However, copying the whole
51
+ // project directory is dangerous because we may expose sensitive files.
52
+ // Instead, we establish a convention that only files in `public` directory
53
+ // get served. Our build script will copy `public` into the `build` folder.
54
+ // In `index.html`, you can get URL of `public` folder with %PUBLIC_URL%:
55
+ // <link rel="icon" href="%PUBLIC_URL%/favicon.ico">
56
+ // In JavaScript code, you can access it with `process.env.PUBLIC_URL`.
57
+ // Note that we only recommend to use `public` folder as an escape hatch
58
+ // for files like `favicon.ico`, `manifest.json`, and libraries that are
59
+ // for some reason broken when imported through webpack. If you just want to
60
+ // use an image, put it in `src` and `import` it from JavaScript instead.
61
+ directory: paths.appPublic,
62
+ publicPath: [paths.publicUrlOrPath],
63
+ // By default files from `contentBase` will not trigger a page reload.
64
+ watch: {
65
+ // Reportedly, this avoids CPU overload on some systems.
66
+ // https://github.com/facebook/create-react-app/issues/293
67
+ // src/node_modules is not ignored to support absolute imports
68
+ // https://github.com/facebook/create-react-app/issues/1065
69
+ ignored: ignoredFiles(paths.appSrc),
70
+ },
71
+ },
72
+ client: {
73
+ webSocketURL: {
74
+ // Enable custom sockjs pathname for websocket connection to hot reloading server.
75
+ // Enable custom sockjs hostname, pathname and port for websocket connection
76
+ // to hot reloading server.
77
+ hostname: sockHost,
78
+ pathname: sockPath,
79
+ port: sockPort,
80
+ },
81
+ overlay: {
82
+ errors: true,
83
+ warnings: false,
84
+ },
85
+ },
86
+ devMiddleware: {
87
+ // It is important to tell WebpackDevServer to use the same "publicPath" path as
88
+ // we specified in the webpack config. When homepage is '.', default to serving
89
+ // from the root.
90
+ // remove last slash so user can land on `/test` instead of `/test/`
91
+ publicPath: paths.publicUrlOrPath.slice(0, -1),
92
+ },
93
+
94
+ https: getHttpsConfig(),
95
+ host,
96
+ historyApiFallback: {
97
+ // Paths with dots should still use the history fallback.
98
+ // See https://github.com/facebook/create-react-app/issues/387.
99
+ disableDotRule: true,
100
+ index: paths.publicUrlOrPath,
101
+ },
102
+ // `proxy` is run between `before` and `after` `webpack-dev-server` hooks
103
+ proxy,
104
+ onBeforeSetupMiddleware(devServer) {
105
+ // Keep `evalSourceMapMiddleware`
106
+ // middlewares before `redirectServedPath` otherwise will not have any effect
107
+ // This lets us fetch source contents from webpack for the error overlay
108
+ devServer.app.use(evalSourceMapMiddleware(devServer));
109
+
110
+ if (fs.existsSync(paths.proxySetup)) {
111
+ // This registers user provided middleware for proxy reasons
112
+ require(paths.proxySetup)(devServer.app);
113
+ }
114
+ },
115
+ onAfterSetupMiddleware(devServer) {
116
+ // Redirect to `PUBLIC_URL` or `homepage` from `package.json` if url not match
117
+ devServer.app.use(redirectServedPath(paths.publicUrlOrPath));
118
+
119
+ // This service worker file is effectively a 'no-op' that will reset any
120
+ // previous service worker registered for the same host:port combination.
121
+ // We do this in development to avoid hitting the production cache if
122
+ // it used the same host and port.
123
+ // https://github.com/facebook/create-react-app/issues/2272#issuecomment-302832432
124
+ devServer.app.use(noopServiceWorkerMiddleware(paths.publicUrlOrPath));
125
+ },
126
+ };
127
+ };
lama_cleaner/app/package.json ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "lama-cleaner",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "proxy": "http://localhost:8080",
6
+ "dependencies": {
7
+ "@babel/core": "^7.16.0",
8
+ "@heroicons/react": "^2.0.0",
9
+ "@pmmmwh/react-refresh-webpack-plugin": "^0.5.3",
10
+ "@radix-ui/colors": "^0.1.8",
11
+ "@radix-ui/react-dialog": "0.1.8-rc.25",
12
+ "@radix-ui/react-icons": "^1.1.1",
13
+ "@radix-ui/react-popover": "^1.0.0",
14
+ "@radix-ui/react-scroll-area": "^1.0.2",
15
+ "@radix-ui/react-select": "0.1.2-rc.27",
16
+ "@radix-ui/react-switch": "^0.1.5",
17
+ "@radix-ui/react-tabs": "^1.0.1",
18
+ "@radix-ui/react-toast": "^0.1.1",
19
+ "@radix-ui/react-tooltip": "^0.1.7",
20
+ "@svgr/webpack": "^5.5.0",
21
+ "@testing-library/jest-dom": "^5.14.1",
22
+ "@testing-library/react": "^12.1.2",
23
+ "@testing-library/user-event": "^13.5.0",
24
+ "@types/flexsearch": "^0.7.3",
25
+ "@types/jest": "^27.0.2",
26
+ "@types/lodash": "^4.14.182",
27
+ "@types/node": "^16.11.1",
28
+ "@types/react": "^17.0.30",
29
+ "@types/react-dom": "^17.0.9",
30
+ "babel-jest": "^27.4.2",
31
+ "babel-loader": "^8.2.3",
32
+ "babel-plugin-named-asset-import": "^0.3.8",
33
+ "babel-preset-react-app": "^10.0.1",
34
+ "bfj": "^7.0.2",
35
+ "browserslist": "^4.18.1",
36
+ "camelcase": "^6.2.1",
37
+ "case-sensitive-paths-webpack-plugin": "^2.4.0",
38
+ "cross-env": "7.x",
39
+ "css-loader": "^6.5.1",
40
+ "css-minimizer-webpack-plugin": "^3.2.0",
41
+ "dotenv": "^10.0.0",
42
+ "dotenv-expand": "^5.1.0",
43
+ "eslint": "^8.3.0",
44
+ "eslint-config-react-app": "^7.0.1",
45
+ "eslint-webpack-plugin": "^3.1.1",
46
+ "file-loader": "^6.2.0",
47
+ "flexsearch": "0.7.21",
48
+ "fs-extra": "^10.0.0",
49
+ "hacktimer": "^1.1.3",
50
+ "html-webpack-plugin": "^5.5.0",
51
+ "identity-obj-proxy": "^3.0.0",
52
+ "jest": "^27.4.3",
53
+ "jest-resolve": "^27.4.2",
54
+ "jest-watch-typeahead": "^1.0.0",
55
+ "lodash": "^4.17.21",
56
+ "mini-css-extract-plugin": "^2.4.5",
57
+ "mitt": "^3.0.0",
58
+ "nanoid": "^4.0.0",
59
+ "npm-run-all": "4.x",
60
+ "postcss": "^8.4.4",
61
+ "postcss-flexbugs-fixes": "^5.0.2",
62
+ "postcss-loader": "^6.2.1",
63
+ "postcss-normalize": "^10.0.1",
64
+ "postcss-preset-env": "^7.0.1",
65
+ "prompts": "^2.4.2",
66
+ "react": "^17.0.2",
67
+ "react-app-polyfill": "^3.0.0",
68
+ "react-dev-utils": "^12.0.1",
69
+ "react-dom": "^17.0.2",
70
+ "react-feather": "^2.0.10",
71
+ "react-hotkeys-hook": "^3.4.7",
72
+ "react-photo-album": "^2.0.0",
73
+ "react-refresh": "^0.11.0",
74
+ "react-use": "^17.3.1",
75
+ "react-zoom-pan-pinch": "^2.1.3",
76
+ "recoil": "^0.6.1",
77
+ "resolve": "^1.20.0",
78
+ "resolve-url-loader": "^4.0.0",
79
+ "sass-loader": "^12.3.0",
80
+ "semver": "^7.3.5",
81
+ "socket.io-client": "^4.5.2",
82
+ "source-map-loader": "^3.0.0",
83
+ "style-loader": "^3.3.1",
84
+ "tailwindcss": "^3.0.2",
85
+ "terser-webpack-plugin": "^5.2.5",
86
+ "typescript": "4.x",
87
+ "webpack": "^5.64.4",
88
+ "webpack-dev-server": "^4.6.0",
89
+ "webpack-manifest-plugin": "^4.0.2",
90
+ "workbox-webpack-plugin": "^6.4.1"
91
+ },
92
+ "scripts": {
93
+ "start": "cross-env GENERATE_SOURCEMAP=false node scripts/start.js",
94
+ "build": "cross-env GENERATE_SOURCEMAP=false node scripts/build.js",
95
+ "test": "node scripts/test.js"
96
+ },
97
+ "eslintConfig": {
98
+ "extends": "react-app"
99
+ },
100
+ "browserslist": {
101
+ "production": [
102
+ ">0.2%",
103
+ "not dead",
104
+ "not op_mini all"
105
+ ],
106
+ "development": [
107
+ "last 1 chrome version",
108
+ "last 1 firefox version",
109
+ "last 1 safari version"
110
+ ]
111
+ },
112
+ "devDependencies": {
113
+ "@typescript-eslint/eslint-plugin": "^5.1.0",
114
+ "eslint-config-airbnb": "^18.2.1",
115
+ "eslint-config-prettier": "^8.3.0",
116
+ "eslint-plugin-import": "^2.25.2",
117
+ "eslint-plugin-jsx-a11y": "^6.4.1",
118
+ "eslint-plugin-prettier": "^4.0.0",
119
+ "eslint-plugin-react": "^7.27.1",
120
+ "eslint-plugin-react-hooks": "^4.3.0",
121
+ "prettier": "^2.4.1",
122
+ "sass": "^1.49.9"
123
+ },
124
+ "jest": {
125
+ "roots": [
126
+ "<rootDir>/src"
127
+ ],
128
+ "collectCoverageFrom": [
129
+ "src/**/*.{js,jsx,ts,tsx}",
130
+ "!src/**/*.d.ts"
131
+ ],
132
+ "setupFiles": [
133
+ "react-app-polyfill/jsdom"
134
+ ],
135
+ "setupFilesAfterEnv": [
136
+ "<rootDir>/src/setupTests.ts"
137
+ ],
138
+ "testMatch": [
139
+ "<rootDir>/src/**/__tests__/**/*.{js,jsx,ts,tsx}",
140
+ "<rootDir>/src/**/*.{spec,test}.{js,jsx,ts,tsx}"
141
+ ],
142
+ "testEnvironment": "jsdom",
143
+ "transform": {
144
+ "^.+\\.(js|jsx|mjs|cjs|ts|tsx)$": "<rootDir>/config/jest/babelTransform.js",
145
+ "^.+\\.css$": "<rootDir>/config/jest/cssTransform.js",
146
+ "^(?!.*\\.(js|jsx|mjs|cjs|ts|tsx|css|json)$)": "<rootDir>/config/jest/fileTransform.js"
147
+ },
148
+ "transformIgnorePatterns": [
149
+ "[/\\\\]node_modules[/\\\\].+\\.(js|jsx|mjs|cjs|ts|tsx)$",
150
+ "^.+\\.module\\.(css|sass|scss)$"
151
+ ],
152
+ "modulePaths": [],
153
+ "moduleNameMapper": {
154
+ "^react-native$": "react-native-web",
155
+ "^.+\\.module\\.(css|sass|scss)$": "identity-obj-proxy"
156
+ },
157
+ "moduleFileExtensions": [
158
+ "web.js",
159
+ "js",
160
+ "web.ts",
161
+ "ts",
162
+ "web.tsx",
163
+ "tsx",
164
+ "json",
165
+ "web.jsx",
166
+ "jsx",
167
+ "node"
168
+ ],
169
+ "watchPlugins": [
170
+ "jest-watch-typeahead/filename",
171
+ "jest-watch-typeahead/testname"
172
+ ],
173
+ "resetMocks": true
174
+ },
175
+ "babel": {
176
+ "presets": [
177
+ "react-app"
178
+ ]
179
+ }
180
+ }
lama_cleaner/app/public/index.html ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta
5
+ http-equiv="Cache-Control"
6
+ content="no-cache, no-store, must-revalidate"
7
+ />
8
+ <meta http-equiv="Pragma" content="no-cache" />
9
+ <meta http-equiv="Expires" content="0" />
10
+
11
+ <meta charset="utf-8" />
12
+ <meta
13
+ name="viewport"
14
+ content="width=device-width, initial-scale=1, maximum-scale=1.0, user-scalable=0"
15
+ />
16
+
17
+ <meta name="theme-color" content="#ffffff" />
18
+ <!--
19
+ Notice the use of %PUBLIC_URL% in the tags above.
20
+ It will be replaced with the URL of the `public` folder during the build.
21
+ Only files inside the `public` folder can be referenced from the HTML.
22
+
23
+ Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
24
+ work correctly both with client-side routing and a non-root public URL.
25
+ Learn how to configure a non-root public URL by running `npm run build`.
26
+ -->
27
+ <title>lama-cleaner - Image inpainting powered by SOTA AI model</title>
28
+ </head>
29
+ <body>
30
+ <noscript>You need to enable JavaScript to run this app.</noscript>
31
+ <div id="root"></div>
32
+ </body>
33
+ </html>
lama_cleaner/app/scripts/build.js ADDED
@@ -0,0 +1,217 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ // Do this as the first thing so that any code reading it knows the right env.
4
+ process.env.BABEL_ENV = 'production';
5
+ process.env.NODE_ENV = 'production';
6
+
7
+ // Makes the script crash on unhandled rejections instead of silently
8
+ // ignoring them. In the future, promise rejections that are not handled will
9
+ // terminate the Node.js process with a non-zero exit code.
10
+ process.on('unhandledRejection', err => {
11
+ throw err;
12
+ });
13
+
14
+ // Ensure environment variables are read.
15
+ require('../config/env');
16
+
17
+ const path = require('path');
18
+ const chalk = require('react-dev-utils/chalk');
19
+ const fs = require('fs-extra');
20
+ const bfj = require('bfj');
21
+ const webpack = require('webpack');
22
+ const configFactory = require('../config/webpack.config');
23
+ const paths = require('../config/paths');
24
+ const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
25
+ const formatWebpackMessages = require('react-dev-utils/formatWebpackMessages');
26
+ const printHostingInstructions = require('react-dev-utils/printHostingInstructions');
27
+ const FileSizeReporter = require('react-dev-utils/FileSizeReporter');
28
+ const printBuildError = require('react-dev-utils/printBuildError');
29
+
30
+ const measureFileSizesBeforeBuild =
31
+ FileSizeReporter.measureFileSizesBeforeBuild;
32
+ const printFileSizesAfterBuild = FileSizeReporter.printFileSizesAfterBuild;
33
+ const useYarn = fs.existsSync(paths.yarnLockFile);
34
+
35
+ // These sizes are pretty large. We'll warn for bundles exceeding them.
36
+ const WARN_AFTER_BUNDLE_GZIP_SIZE = 512 * 1024;
37
+ const WARN_AFTER_CHUNK_GZIP_SIZE = 1024 * 1024;
38
+
39
+ const isInteractive = process.stdout.isTTY;
40
+
41
+ // Warn and crash if required files are missing
42
+ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
43
+ process.exit(1);
44
+ }
45
+
46
+ const argv = process.argv.slice(2);
47
+ const writeStatsJson = argv.indexOf('--stats') !== -1;
48
+
49
+ // Generate configuration
50
+ const config = configFactory('production');
51
+
52
+ // We require that you explicitly set browsers and do not fall back to
53
+ // browserslist defaults.
54
+ const { checkBrowsers } = require('react-dev-utils/browsersHelper');
55
+ checkBrowsers(paths.appPath, isInteractive)
56
+ .then(() => {
57
+ // First, read the current file sizes in build directory.
58
+ // This lets us display how much they changed later.
59
+ return measureFileSizesBeforeBuild(paths.appBuild);
60
+ })
61
+ .then(previousFileSizes => {
62
+ // Remove all content but keep the directory so that
63
+ // if you're in it, you don't end up in Trash
64
+ fs.emptyDirSync(paths.appBuild);
65
+ // Merge with the public folder
66
+ copyPublicFolder();
67
+ // Start the webpack build
68
+ return build(previousFileSizes);
69
+ })
70
+ .then(
71
+ ({ stats, previousFileSizes, warnings }) => {
72
+ if (warnings.length) {
73
+ console.log(chalk.yellow('Compiled with warnings.\n'));
74
+ console.log(warnings.join('\n\n'));
75
+ console.log(
76
+ '\nSearch for the ' +
77
+ chalk.underline(chalk.yellow('keywords')) +
78
+ ' to learn more about each warning.'
79
+ );
80
+ console.log(
81
+ 'To ignore, add ' +
82
+ chalk.cyan('// eslint-disable-next-line') +
83
+ ' to the line before.\n'
84
+ );
85
+ } else {
86
+ console.log(chalk.green('Compiled successfully.\n'));
87
+ }
88
+
89
+ console.log('File sizes after gzip:\n');
90
+ printFileSizesAfterBuild(
91
+ stats,
92
+ previousFileSizes,
93
+ paths.appBuild,
94
+ WARN_AFTER_BUNDLE_GZIP_SIZE,
95
+ WARN_AFTER_CHUNK_GZIP_SIZE
96
+ );
97
+ console.log();
98
+
99
+ const appPackage = require(paths.appPackageJson);
100
+ const publicUrl = paths.publicUrlOrPath;
101
+ const publicPath = config.output.publicPath;
102
+ const buildFolder = path.relative(process.cwd(), paths.appBuild);
103
+ printHostingInstructions(
104
+ appPackage,
105
+ publicUrl,
106
+ publicPath,
107
+ buildFolder,
108
+ useYarn
109
+ );
110
+ },
111
+ err => {
112
+ const tscCompileOnError = process.env.TSC_COMPILE_ON_ERROR === 'true';
113
+ if (tscCompileOnError) {
114
+ console.log(
115
+ chalk.yellow(
116
+ 'Compiled with the following type errors (you may want to check these before deploying your app):\n'
117
+ )
118
+ );
119
+ printBuildError(err);
120
+ } else {
121
+ console.log(chalk.red('Failed to compile.\n'));
122
+ printBuildError(err);
123
+ process.exit(1);
124
+ }
125
+ }
126
+ )
127
+ .catch(err => {
128
+ if (err && err.message) {
129
+ console.log(err.message);
130
+ }
131
+ process.exit(1);
132
+ });
133
+
134
+ // Create the production build and print the deployment instructions.
135
+ function build(previousFileSizes) {
136
+ console.log('Creating an optimized production build...');
137
+
138
+ const compiler = webpack(config);
139
+ return new Promise((resolve, reject) => {
140
+ compiler.run((err, stats) => {
141
+ let messages;
142
+ if (err) {
143
+ if (!err.message) {
144
+ return reject(err);
145
+ }
146
+
147
+ let errMessage = err.message;
148
+
149
+ // Add additional information for postcss errors
150
+ if (Object.prototype.hasOwnProperty.call(err, 'postcssNode')) {
151
+ errMessage +=
152
+ '\nCompileError: Begins at CSS selector ' +
153
+ err['postcssNode'].selector;
154
+ }
155
+
156
+ messages = formatWebpackMessages({
157
+ errors: [errMessage],
158
+ warnings: [],
159
+ });
160
+ } else {
161
+ messages = formatWebpackMessages(
162
+ stats.toJson({ all: false, warnings: true, errors: true })
163
+ );
164
+ }
165
+ if (messages.errors.length) {
166
+ // Only keep the first error. Others are often indicative
167
+ // of the same problem, but confuse the reader with noise.
168
+ if (messages.errors.length > 1) {
169
+ messages.errors.length = 1;
170
+ }
171
+ return reject(new Error(messages.errors.join('\n\n')));
172
+ }
173
+ if (
174
+ process.env.CI &&
175
+ (typeof process.env.CI !== 'string' ||
176
+ process.env.CI.toLowerCase() !== 'false') &&
177
+ messages.warnings.length
178
+ ) {
179
+ // Ignore sourcemap warnings in CI builds. See #8227 for more info.
180
+ const filteredWarnings = messages.warnings.filter(
181
+ w => !/Failed to parse source map/.test(w)
182
+ );
183
+ if (filteredWarnings.length) {
184
+ console.log(
185
+ chalk.yellow(
186
+ '\nTreating warnings as errors because process.env.CI = true.\n' +
187
+ 'Most CI servers set it automatically.\n'
188
+ )
189
+ );
190
+ return reject(new Error(filteredWarnings.join('\n\n')));
191
+ }
192
+ }
193
+
194
+ const resolveArgs = {
195
+ stats,
196
+ previousFileSizes,
197
+ warnings: messages.warnings,
198
+ };
199
+
200
+ if (writeStatsJson) {
201
+ return bfj
202
+ .write(paths.appBuild + '/bundle-stats.json', stats.toJson())
203
+ .then(() => resolve(resolveArgs))
204
+ .catch(error => reject(new Error(error)));
205
+ }
206
+
207
+ return resolve(resolveArgs);
208
+ });
209
+ });
210
+ }
211
+
212
+ function copyPublicFolder() {
213
+ fs.copySync(paths.appPublic, paths.appBuild, {
214
+ dereference: true,
215
+ filter: file => file !== paths.appHtml,
216
+ });
217
+ }
lama_cleaner/app/scripts/start.js ADDED
@@ -0,0 +1,154 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ // Do this as the first thing so that any code reading it knows the right env.
4
+ process.env.BABEL_ENV = 'development';
5
+ process.env.NODE_ENV = 'development';
6
+
7
+ // Makes the script crash on unhandled rejections instead of silently
8
+ // ignoring them. In the future, promise rejections that are not handled will
9
+ // terminate the Node.js process with a non-zero exit code.
10
+ process.on('unhandledRejection', err => {
11
+ throw err;
12
+ });
13
+
14
+ // Ensure environment variables are read.
15
+ require('../config/env');
16
+
17
+ const fs = require('fs');
18
+ const chalk = require('react-dev-utils/chalk');
19
+ const webpack = require('webpack');
20
+ const WebpackDevServer = require('webpack-dev-server');
21
+ const clearConsole = require('react-dev-utils/clearConsole');
22
+ const checkRequiredFiles = require('react-dev-utils/checkRequiredFiles');
23
+ const {
24
+ choosePort,
25
+ createCompiler,
26
+ prepareProxy,
27
+ prepareUrls,
28
+ } = require('react-dev-utils/WebpackDevServerUtils');
29
+ const openBrowser = require('react-dev-utils/openBrowser');
30
+ const semver = require('semver');
31
+ const paths = require('../config/paths');
32
+ const configFactory = require('../config/webpack.config');
33
+ const createDevServerConfig = require('../config/webpackDevServer.config');
34
+ const getClientEnvironment = require('../config/env');
35
+ const react = require(require.resolve('react', { paths: [paths.appPath] }));
36
+
37
+ const env = getClientEnvironment(paths.publicUrlOrPath.slice(0, -1));
38
+ const useYarn = fs.existsSync(paths.yarnLockFile);
39
+ const isInteractive = process.stdout.isTTY;
40
+
41
+ // Warn and crash if required files are missing
42
+ if (!checkRequiredFiles([paths.appHtml, paths.appIndexJs])) {
43
+ process.exit(1);
44
+ }
45
+
46
+ // Tools like Cloud9 rely on this.
47
+ const DEFAULT_PORT = parseInt(process.env.PORT, 10) || 3000;
48
+ const HOST = process.env.HOST || '0.0.0.0';
49
+
50
+ if (process.env.HOST) {
51
+ console.log(
52
+ chalk.cyan(
53
+ `Attempting to bind to HOST environment variable: ${chalk.yellow(
54
+ chalk.bold(process.env.HOST)
55
+ )}`
56
+ )
57
+ );
58
+ console.log(
59
+ `If this was unintentional, check that you haven't mistakenly set it in your shell.`
60
+ );
61
+ console.log(
62
+ `Learn more here: ${chalk.yellow('https://cra.link/advanced-config')}`
63
+ );
64
+ console.log();
65
+ }
66
+
67
+ // We require that you explicitly set browsers and do not fall back to
68
+ // browserslist defaults.
69
+ const { checkBrowsers } = require('react-dev-utils/browsersHelper');
70
+ checkBrowsers(paths.appPath, isInteractive)
71
+ .then(() => {
72
+ // We attempt to use the default port but if it is busy, we offer the user to
73
+ // run on a different port. `choosePort()` Promise resolves to the next free port.
74
+ return choosePort(HOST, DEFAULT_PORT);
75
+ })
76
+ .then(port => {
77
+ if (port == null) {
78
+ // We have not found a port.
79
+ return;
80
+ }
81
+
82
+ const config = configFactory('development');
83
+ const protocol = process.env.HTTPS === 'true' ? 'https' : 'http';
84
+ const appName = require(paths.appPackageJson).name;
85
+
86
+ const useTypeScript = fs.existsSync(paths.appTsConfig);
87
+ const urls = prepareUrls(
88
+ protocol,
89
+ HOST,
90
+ port,
91
+ paths.publicUrlOrPath.slice(0, -1)
92
+ );
93
+ // Create a webpack compiler that is configured with custom messages.
94
+ const compiler = createCompiler({
95
+ appName,
96
+ config,
97
+ urls,
98
+ useYarn,
99
+ useTypeScript,
100
+ webpack,
101
+ });
102
+ // Load proxy config
103
+ const proxySetting = require(paths.appPackageJson).proxy;
104
+ const proxyConfig = prepareProxy(
105
+ proxySetting,
106
+ paths.appPublic,
107
+ paths.publicUrlOrPath
108
+ );
109
+ // Serve webpack assets generated by the compiler over a web server.
110
+ const serverConfig = {
111
+ ...createDevServerConfig(proxyConfig, urls.lanUrlForConfig),
112
+ host: HOST,
113
+ port,
114
+ };
115
+ const devServer = new WebpackDevServer(serverConfig, compiler);
116
+ // Launch WebpackDevServer.
117
+ devServer.startCallback(() => {
118
+ if (isInteractive) {
119
+ clearConsole();
120
+ }
121
+
122
+ if (env.raw.FAST_REFRESH && semver.lt(react.version, '16.10.0')) {
123
+ console.log(
124
+ chalk.yellow(
125
+ `Fast Refresh requires React 16.10 or higher. You are using React ${react.version}.`
126
+ )
127
+ );
128
+ }
129
+
130
+ console.log(chalk.cyan('Starting the development server...\n'));
131
+ openBrowser(urls.localUrlForBrowser);
132
+ });
133
+
134
+ ['SIGINT', 'SIGTERM'].forEach(function (sig) {
135
+ process.on(sig, function () {
136
+ devServer.close();
137
+ process.exit();
138
+ });
139
+ });
140
+
141
+ if (process.env.CI !== 'true') {
142
+ // Gracefully exit when stdin ends
143
+ process.stdin.on('end', function () {
144
+ devServer.close();
145
+ process.exit();
146
+ });
147
+ }
148
+ })
149
+ .catch(err => {
150
+ if (err && err.message) {
151
+ console.log(err.message);
152
+ }
153
+ process.exit(1);
154
+ });
lama_cleaner/app/scripts/test.js ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ // Do this as the first thing so that any code reading it knows the right env.
4
+ process.env.BABEL_ENV = 'test';
5
+ process.env.NODE_ENV = 'test';
6
+ process.env.PUBLIC_URL = '';
7
+
8
+ // Makes the script crash on unhandled rejections instead of silently
9
+ // ignoring them. In the future, promise rejections that are not handled will
10
+ // terminate the Node.js process with a non-zero exit code.
11
+ process.on('unhandledRejection', err => {
12
+ throw err;
13
+ });
14
+
15
+ // Ensure environment variables are read.
16
+ require('../config/env');
17
+
18
+ const jest = require('jest');
19
+ const execSync = require('child_process').execSync;
20
+ let argv = process.argv.slice(2);
21
+
22
+ function isInGitRepository() {
23
+ try {
24
+ execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
25
+ return true;
26
+ } catch (e) {
27
+ return false;
28
+ }
29
+ }
30
+
31
+ function isInMercurialRepository() {
32
+ try {
33
+ execSync('hg --cwd . root', { stdio: 'ignore' });
34
+ return true;
35
+ } catch (e) {
36
+ return false;
37
+ }
38
+ }
39
+
40
+ // Watch unless on CI or explicitly running all tests
41
+ if (
42
+ !process.env.CI &&
43
+ argv.indexOf('--watchAll') === -1 &&
44
+ argv.indexOf('--watchAll=false') === -1
45
+ ) {
46
+ // https://github.com/facebook/create-react-app/issues/5210
47
+ const hasSourceControl = isInGitRepository() || isInMercurialRepository();
48
+ argv.push(hasSourceControl ? '--watch' : '--watchAll');
49
+ }
50
+
51
+
52
+ jest.run(argv);
lama_cleaner/app/src/App.tsx ADDED
@@ -0,0 +1,204 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useCallback, useEffect, useMemo } from 'react'
2
+ import { useRecoilState, useSetRecoilState } from 'recoil'
3
+ import { nanoid } from 'nanoid'
4
+ import useInputImage from './hooks/useInputImage'
5
+ import { themeState } from './components/Header/ThemeChanger'
6
+ import Workspace from './components/Workspace'
7
+ import {
8
+ enableFileManagerState,
9
+ fileState,
10
+ isDisableModelSwitchState,
11
+ toastState,
12
+ } from './store/Atoms'
13
+ import { keepGUIAlive } from './utils'
14
+ import Header from './components/Header/Header'
15
+ import useHotKey from './hooks/useHotkey'
16
+ import {
17
+ getEnableFileManager,
18
+ getIsDisableModelSwitch,
19
+ isDesktop,
20
+ } from './adapters/inpainting'
21
+
22
+ const SUPPORTED_FILE_TYPE = [
23
+ 'image/jpeg',
24
+ 'image/png',
25
+ 'image/webp',
26
+ 'image/bmp',
27
+ 'image/tiff',
28
+ ]
29
+
30
+ function App() {
31
+ const [file, setFile] = useRecoilState(fileState)
32
+ const [theme, setTheme] = useRecoilState(themeState)
33
+ const setToastState = useSetRecoilState(toastState)
34
+ const userInputImage = useInputImage()
35
+ const setIsDisableModelSwitch = useSetRecoilState(isDisableModelSwitchState)
36
+ const setEnableFileManager = useSetRecoilState(enableFileManagerState)
37
+
38
+ // Set Input Image
39
+ useEffect(() => {
40
+ setFile(userInputImage)
41
+ }, [userInputImage, setFile])
42
+
43
+ // Keeping GUI Window Open
44
+ useEffect(() => {
45
+ const fetchData = async () => {
46
+ const isRunDesktop = await isDesktop().then(res => res.text())
47
+ if (isRunDesktop === 'True') {
48
+ keepGUIAlive()
49
+ }
50
+ }
51
+ fetchData()
52
+ }, [])
53
+
54
+ useEffect(() => {
55
+ const fetchData = async () => {
56
+ const isDisable: string = await getIsDisableModelSwitch().then(res =>
57
+ res.text()
58
+ )
59
+ setIsDisableModelSwitch(isDisable === 'true')
60
+ }
61
+
62
+ fetchData()
63
+
64
+ const fetchData2 = async () => {
65
+ const isEnabled = await getEnableFileManager().then(res => res.text())
66
+ setEnableFileManager(isEnabled === 'true')
67
+ }
68
+ fetchData2()
69
+ }, [setEnableFileManager, setIsDisableModelSwitch])
70
+
71
+ // Dark Mode Hotkey
72
+ useHotKey(
73
+ 'shift+d',
74
+ () => {
75
+ const newTheme = theme === 'light' ? 'dark' : 'light'
76
+ setTheme(newTheme)
77
+ },
78
+ {},
79
+ [theme]
80
+ )
81
+
82
+ useEffect(() => {
83
+ document.body.setAttribute('data-theme', theme)
84
+ }, [theme])
85
+
86
+ const workspaceId = useMemo(() => {
87
+ return nanoid()
88
+ }, [file])
89
+
90
+ ///
91
+
92
+ const [isDragging, setIsDragging] = React.useState(false)
93
+ const dragCounter = React.useRef(0)
94
+
95
+ const handleDrag = React.useCallback(event => {
96
+ event.preventDefault()
97
+ event.stopPropagation()
98
+ }, [])
99
+
100
+ const handleDragIn = React.useCallback(event => {
101
+ event.preventDefault()
102
+ event.stopPropagation()
103
+ dragCounter.current += 1
104
+ if (event.dataTransfer.items && event.dataTransfer.items.length > 0) {
105
+ setIsDragging(true)
106
+ }
107
+ }, [])
108
+
109
+ const handleDragOut = React.useCallback(event => {
110
+ event.preventDefault()
111
+ event.stopPropagation()
112
+ dragCounter.current -= 1
113
+ if (dragCounter.current > 0) return
114
+ setIsDragging(false)
115
+ }, [])
116
+
117
+ const handleDrop = React.useCallback(
118
+ event => {
119
+ event.preventDefault()
120
+ event.stopPropagation()
121
+ setIsDragging(false)
122
+ if (event.dataTransfer.files && event.dataTransfer.files.length > 0) {
123
+ if (event.dataTransfer.files.length > 1) {
124
+ setToastState({
125
+ open: true,
126
+ desc: 'Please drag and drop only one file',
127
+ state: 'error',
128
+ duration: 3000,
129
+ })
130
+ } else {
131
+ const dragFile = event.dataTransfer.files[0]
132
+ const fileType = dragFile.type
133
+ if (SUPPORTED_FILE_TYPE.includes(fileType)) {
134
+ setFile(dragFile)
135
+ } else {
136
+ setToastState({
137
+ open: true,
138
+ desc: 'Please drag and drop an image file',
139
+ state: 'error',
140
+ duration: 3000,
141
+ })
142
+ }
143
+ }
144
+ event.dataTransfer.clearData()
145
+ }
146
+ },
147
+ [setToastState, setFile]
148
+ )
149
+
150
+ const onPaste = useCallback((event: any) => {
151
+ // TODO: when sd side panel open, ctrl+v not work
152
+ // https://htmldom.dev/paste-an-image-from-the-clipboard/
153
+ if (!event.clipboardData) {
154
+ return
155
+ }
156
+ const clipboardItems = event.clipboardData.items
157
+ const items: DataTransferItem[] = [].slice
158
+ .call(clipboardItems)
159
+ .filter((item: DataTransferItem) => {
160
+ // Filter the image items only
161
+ return item.type.indexOf('image') !== -1
162
+ })
163
+
164
+ if (items.length === 0) {
165
+ return
166
+ }
167
+
168
+ event.preventDefault()
169
+ event.stopPropagation()
170
+
171
+ // TODO: add confirm dialog
172
+
173
+ const item = items[0]
174
+ // Get the blob of image
175
+ const blob = item.getAsFile()
176
+ if (blob) {
177
+ setFile(blob)
178
+ }
179
+ }, [])
180
+
181
+ React.useEffect(() => {
182
+ window.addEventListener('dragenter', handleDragIn)
183
+ window.addEventListener('dragleave', handleDragOut)
184
+ window.addEventListener('dragover', handleDrag)
185
+ window.addEventListener('drop', handleDrop)
186
+ window.addEventListener('paste', onPaste)
187
+ return function cleanUp() {
188
+ window.removeEventListener('dragenter', handleDragIn)
189
+ window.removeEventListener('dragleave', handleDragOut)
190
+ window.removeEventListener('dragover', handleDrag)
191
+ window.removeEventListener('drop', handleDrop)
192
+ window.removeEventListener('paste', onPaste)
193
+ }
194
+ })
195
+
196
+ return (
197
+ <div className="lama-cleaner">
198
+ <Header />
199
+ <Workspace key={workspaceId} />
200
+ </div>
201
+ )
202
+ }
203
+
204
+ export default App
lama_cleaner/app/src/adapters/inpainting.ts ADDED
@@ -0,0 +1,225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { Rect, Settings } from '../store/Atoms'
2
+ import { dataURItoBlob, srcToFile } from '../utils'
3
+
4
+ export const API_ENDPOINT = `${process.env.REACT_APP_INPAINTING_URL}`
5
+
6
+ export default async function inpaint(
7
+ imageFile: File,
8
+ settings: Settings,
9
+ croperRect: Rect,
10
+ prompt?: string,
11
+ negativePrompt?: string,
12
+ sizeLimit?: string,
13
+ seed?: number,
14
+ maskBase64?: string,
15
+ customMask?: File,
16
+ paintByExampleImage?: File
17
+ ) {
18
+ // 1080, 2000, Original
19
+ const fd = new FormData()
20
+ fd.append('image', imageFile)
21
+ if (maskBase64 !== undefined) {
22
+ fd.append('mask', dataURItoBlob(maskBase64))
23
+ } else if (customMask !== undefined) {
24
+ fd.append('mask', customMask)
25
+ }
26
+
27
+ const hdSettings = settings.hdSettings[settings.model]
28
+ fd.append('ldmSteps', settings.ldmSteps.toString())
29
+ fd.append('ldmSampler', settings.ldmSampler.toString())
30
+ fd.append('zitsWireframe', settings.zitsWireframe.toString())
31
+ fd.append('hdStrategy', hdSettings.hdStrategy)
32
+ fd.append('hdStrategyCropMargin', hdSettings.hdStrategyCropMargin.toString())
33
+ fd.append(
34
+ 'hdStrategyCropTrigerSize',
35
+ hdSettings.hdStrategyCropTrigerSize.toString()
36
+ )
37
+ fd.append(
38
+ 'hdStrategyResizeLimit',
39
+ hdSettings.hdStrategyResizeLimit.toString()
40
+ )
41
+
42
+ fd.append('prompt', prompt === undefined ? '' : prompt)
43
+ fd.append(
44
+ 'negativePrompt',
45
+ negativePrompt === undefined ? '' : negativePrompt
46
+ )
47
+ fd.append('croperX', croperRect.x.toString())
48
+ fd.append('croperY', croperRect.y.toString())
49
+ fd.append('croperHeight', croperRect.height.toString())
50
+ fd.append('croperWidth', croperRect.width.toString())
51
+ fd.append('useCroper', settings.showCroper ? 'true' : 'false')
52
+
53
+ fd.append('sdMaskBlur', settings.sdMaskBlur.toString())
54
+ fd.append('sdStrength', settings.sdStrength.toString())
55
+ fd.append('sdSteps', settings.sdSteps.toString())
56
+ fd.append('sdGuidanceScale', settings.sdGuidanceScale.toString())
57
+ fd.append('sdSampler', settings.sdSampler.toString())
58
+ fd.append('sdSeed', seed ? seed.toString() : '-1')
59
+ fd.append('sdMatchHistograms', settings.sdMatchHistograms ? 'true' : 'false')
60
+ fd.append('sdScale', (settings.sdScale / 100).toString())
61
+
62
+ fd.append('cv2Radius', settings.cv2Radius.toString())
63
+ fd.append('cv2Flag', settings.cv2Flag.toString())
64
+
65
+ fd.append('paintByExampleSteps', settings.paintByExampleSteps.toString())
66
+ fd.append(
67
+ 'paintByExampleGuidanceScale',
68
+ settings.paintByExampleGuidanceScale.toString()
69
+ )
70
+ fd.append('paintByExampleSeed', seed ? seed.toString() : '-1')
71
+ fd.append(
72
+ 'paintByExampleMaskBlur',
73
+ settings.paintByExampleMaskBlur.toString()
74
+ )
75
+ fd.append(
76
+ 'paintByExampleMatchHistograms',
77
+ settings.paintByExampleMatchHistograms ? 'true' : 'false'
78
+ )
79
+ // TODO: resize image's shortest_edge to 224 before pass to backend, save network time?
80
+ // https://huggingface.co/docs/transformers/model_doc/clip#transformers.CLIPImageProcessor
81
+ if (paintByExampleImage) {
82
+ fd.append('paintByExampleImage', paintByExampleImage)
83
+ }
84
+
85
+ if (sizeLimit === undefined) {
86
+ fd.append('sizeLimit', '1080')
87
+ } else {
88
+ fd.append('sizeLimit', sizeLimit)
89
+ }
90
+
91
+ try {
92
+ const res = await fetch(`${API_ENDPOINT}/inpaint`, {
93
+ method: 'POST',
94
+ body: fd,
95
+ })
96
+ if (res.ok) {
97
+ const blob = await res.blob()
98
+ const newSeed = res.headers.get('x-seed')
99
+ return { blob: URL.createObjectURL(blob), seed: newSeed }
100
+ }
101
+ const errMsg = await res.text()
102
+ throw new Error(errMsg)
103
+ } catch (error) {
104
+ throw new Error(`Something went wrong: ${error}`)
105
+ }
106
+ }
107
+
108
+ export function getIsDisableModelSwitch() {
109
+ return fetch(`${API_ENDPOINT}/is_disable_model_switch`, {
110
+ method: 'GET',
111
+ })
112
+ }
113
+
114
+ export function getEnableFileManager() {
115
+ return fetch(`${API_ENDPOINT}/is_enable_file_manager`, {
116
+ method: 'GET',
117
+ })
118
+ }
119
+
120
+ export function switchModel(name: string) {
121
+ const fd = new FormData()
122
+ fd.append('name', name)
123
+ return fetch(`${API_ENDPOINT}/model`, {
124
+ method: 'POST',
125
+ body: fd,
126
+ })
127
+ }
128
+
129
+ export function currentModel() {
130
+ return fetch(`${API_ENDPOINT}/model`, {
131
+ method: 'GET',
132
+ })
133
+ }
134
+
135
+ export function isDesktop() {
136
+ return fetch(`${API_ENDPOINT}/is_desktop`, {
137
+ method: 'GET',
138
+ })
139
+ }
140
+
141
+ export function modelDownloaded(name: string) {
142
+ return fetch(`${API_ENDPOINT}/model_downloaded/${name}`, {
143
+ method: 'GET',
144
+ })
145
+ }
146
+
147
+ export async function postInteractiveSeg(
148
+ imageFile: File,
149
+ maskFile: File | null,
150
+ clicks: number[][]
151
+ ) {
152
+ const fd = new FormData()
153
+ fd.append('image', imageFile)
154
+ fd.append('clicks', JSON.stringify(clicks))
155
+ if (maskFile !== null) {
156
+ fd.append('mask', maskFile)
157
+ }
158
+
159
+ try {
160
+ const res = await fetch(`${API_ENDPOINT}/interactive_seg`, {
161
+ method: 'POST',
162
+ body: fd,
163
+ })
164
+ if (res.ok) {
165
+ const blob = await res.blob()
166
+ return { blob: URL.createObjectURL(blob) }
167
+ }
168
+ const errMsg = await res.text()
169
+ throw new Error(errMsg)
170
+ } catch (error) {
171
+ throw new Error(`Something went wrong: ${error}`)
172
+ }
173
+ }
174
+
175
+ export async function getMediaFile(tab: string, filename: string) {
176
+ const res = await fetch(
177
+ `${API_ENDPOINT}/media/${tab}/${encodeURIComponent(filename)}`,
178
+ {
179
+ method: 'GET',
180
+ }
181
+ )
182
+ if (res.ok) {
183
+ const blob = await res.blob()
184
+ const file = new File([blob], filename)
185
+ return file
186
+ }
187
+ const errMsg = await res.text()
188
+ throw new Error(errMsg)
189
+ }
190
+
191
+ export async function getMedias(tab: string) {
192
+ const res = await fetch(`${API_ENDPOINT}/medias/${tab}`, {
193
+ method: 'GET',
194
+ })
195
+ if (res.ok) {
196
+ const filenames = await res.json()
197
+ return filenames
198
+ }
199
+ const errMsg = await res.text()
200
+ throw new Error(errMsg)
201
+ }
202
+
203
+ export async function downloadToOutput(
204
+ image: HTMLImageElement,
205
+ filename: string,
206
+ mimeType: string
207
+ ) {
208
+ const file = await srcToFile(image.src, filename, mimeType)
209
+ const fd = new FormData()
210
+ fd.append('image', file)
211
+ fd.append('filename', filename)
212
+
213
+ try {
214
+ const res = await fetch(`${API_ENDPOINT}/save_image`, {
215
+ method: 'POST',
216
+ body: fd,
217
+ })
218
+ if (!res.ok) {
219
+ const errMsg = await res.text()
220
+ throw new Error(errMsg)
221
+ }
222
+ } catch (error) {
223
+ throw new Error(`Something went wrong: ${error}`)
224
+ }
225
+ }
lama_cleaner/app/src/components/CoffeeIcon/CoffeeIcon.tsx ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react'
2
+ import { Coffee } from 'react-feather'
3
+ import Button from '../shared/Button'
4
+ import Modal from '../shared/Modal'
5
+
6
+ const CoffeeIcon = () => {
7
+ const [show, setShow] = useState(false)
8
+ const onClick = () => {
9
+ setShow(true)
10
+ }
11
+
12
+ return (
13
+ <div>
14
+ <Button
15
+ onClick={onClick}
16
+ toolTip="Buy me a coffee"
17
+ style={{ border: 0 }}
18
+ icon={<Coffee />}
19
+ />
20
+ <Modal
21
+ onClose={() => setShow(false)}
22
+ title="Buy Me a Coffee"
23
+ className="modal-setting"
24
+ show={show}
25
+ showCloseIcon={false}
26
+ >
27
+ <h4 style={{ lineHeight: '24px' }}>
28
+ Hi there, If you found my project is useful, and want to help keep it
29
+ alive please consider donating! Thank you for your support!
30
+ </h4>
31
+ <div
32
+ style={{
33
+ display: 'flex',
34
+ width: '100%',
35
+ justifyContent: 'flex-end',
36
+ alignItems: 'center',
37
+ gap: '12px',
38
+ }}
39
+ >
40
+ <Button onClick={() => setShow(false)}> No thanks </Button>
41
+ <a
42
+ href="https://ko-fi.com/Z8Z1CZJGY"
43
+ target="_blank"
44
+ rel="noreferrer"
45
+ >
46
+ <Button border onClick={() => setShow(false)}>
47
+ <div
48
+ style={{
49
+ display: 'flex',
50
+ justifyContent: 'center',
51
+ alignItems: 'center',
52
+ gap: '8px',
53
+ }}
54
+ >
55
+ Sure
56
+ <Coffee />
57
+ </div>
58
+ </Button>
59
+ </a>
60
+ </div>
61
+ </Modal>
62
+ </div>
63
+ )
64
+ }
65
+
66
+ export default CoffeeIcon
lama_cleaner/app/src/components/Croper/Croper.scss ADDED
@@ -0,0 +1,167 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @use 'sass:math';
2
+
3
+ $drag-handle-shortside: 12px;
4
+ $drag-handle-longside: 40px;
5
+ $drag-bar-size: 12px;
6
+
7
+ $half-handle-shortside: math.div($drag-handle-shortside, 2);
8
+ $half-handle-longside: math.div($drag-handle-longside, 2);
9
+ $half-drag-bar-size: math.div($drag-bar-size, 2);
10
+
11
+ .crop-border {
12
+ outline-color: var(--yellow-accent);
13
+ outline-style: dashed;
14
+ }
15
+
16
+ .info-bar {
17
+ position: absolute;
18
+ pointer-events: auto;
19
+ font-size: 1rem;
20
+ padding: 0.2rem 0.8rem;
21
+ display: flex;
22
+ align-items: center;
23
+ justify-content: center;
24
+ gap: 12px;
25
+ color: var(--text-color);
26
+ background-color: var(--page-bg);
27
+ border-radius: 9999px;
28
+
29
+ border: var(--editor-toolkit-panel-border);
30
+ box-shadow: 0 0 0 1px #0000001a, 0 3px 16px #00000014, 0 2px 6px 1px #00000017;
31
+
32
+ &:hover {
33
+ cursor: move;
34
+ }
35
+ }
36
+
37
+ .croper-wrapper {
38
+ position: absolute;
39
+ height: 100%;
40
+ width: 100%;
41
+ z-index: 2;
42
+ overflow: hidden;
43
+ pointer-events: none;
44
+ }
45
+
46
+ .croper {
47
+ position: relative;
48
+ top: 0;
49
+ bottom: 0;
50
+ left: 0;
51
+ right: 0;
52
+ z-index: 2;
53
+ pointer-events: none;
54
+
55
+ // display: flex;
56
+ // flex-direction: column;
57
+ // align-items: center;
58
+
59
+ box-shadow: 0 0 0 9999px rgba(0, 0, 0, 0.5);
60
+ }
61
+
62
+ .drag-bar {
63
+ position: absolute;
64
+ pointer-events: auto;
65
+ // display: none;
66
+
67
+ &.ord-top {
68
+ top: 0;
69
+ left: 0;
70
+ width: 100%;
71
+ height: $drag-bar-size;
72
+ margin-top: -$half-drag-bar-size;
73
+ cursor: ns-resize;
74
+ }
75
+ &.ord-right {
76
+ right: 0;
77
+ top: 0;
78
+ width: $drag-bar-size;
79
+ height: 100%;
80
+ margin-right: -$half-drag-bar-size;
81
+ cursor: ew-resize;
82
+ }
83
+ &.ord-bottom {
84
+ bottom: 0;
85
+ left: 0;
86
+ width: 100%;
87
+ height: $drag-bar-size;
88
+ margin-bottom: -$half-drag-bar-size;
89
+ cursor: ns-resize;
90
+ }
91
+ &.ord-left {
92
+ top: 0;
93
+ left: 0;
94
+ width: $drag-bar-size;
95
+ height: 100%;
96
+ margin-left: -$half-drag-bar-size;
97
+ cursor: ew-resize;
98
+ }
99
+ }
100
+
101
+ .drag-handle {
102
+ width: $drag-handle-shortside;
103
+ height: $drag-handle-shortside;
104
+ z-index: 4;
105
+ position: absolute;
106
+ display: block;
107
+ content: '';
108
+ border: 2px solid var(--yellow-accent);
109
+ background-color: var(--yellow-accent-light);
110
+ pointer-events: auto;
111
+
112
+ &:hover {
113
+ background-color: var(--yellow-accent);
114
+ }
115
+
116
+ &.ord-topleft {
117
+ cursor: nw-resize;
118
+ top: (-$half-handle-shortside)-1px;
119
+ left: (-$half-handle-shortside)-1px;
120
+ }
121
+
122
+ &.ord-topright {
123
+ cursor: ne-resize;
124
+ top: -($half-handle-shortside)-1px;
125
+ right: -($half-handle-shortside)-1px;
126
+ }
127
+
128
+ &.ord-bottomright {
129
+ cursor: se-resize;
130
+ bottom: -($half-handle-shortside)-1px;
131
+ right: -($half-handle-shortside)-1px;
132
+ }
133
+
134
+ &.ord-bottomleft {
135
+ cursor: sw-resize;
136
+ bottom: -($half-handle-shortside)-1px;
137
+ left: -($half-handle-shortside)-1px;
138
+ }
139
+
140
+ &.ord-top,
141
+ &.ord-bottom {
142
+ left: calc(50% - $half-handle-shortside);
143
+ cursor: ns-resize;
144
+ }
145
+
146
+ &.ord-top {
147
+ top: (-$half-handle-shortside)-1px;
148
+ }
149
+
150
+ &.ord-bottom {
151
+ bottom: -($half-handle-shortside)-1px;
152
+ }
153
+
154
+ &.ord-left,
155
+ &.ord-right {
156
+ top: calc(50% - $half-handle-shortside);
157
+ cursor: ew-resize;
158
+ }
159
+
160
+ &.ord-left {
161
+ left: (-$half-handle-shortside)-1px;
162
+ }
163
+
164
+ &.ord-right {
165
+ right: -($half-handle-shortside)-1px;
166
+ }
167
+ }
lama_cleaner/app/src/components/Croper/Croper.tsx ADDED
@@ -0,0 +1,408 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useState } from 'react'
2
+ import { useRecoilState, useRecoilValue } from 'recoil'
3
+ import {
4
+ croperHeight,
5
+ croperWidth,
6
+ croperX,
7
+ croperY,
8
+ isInpaintingState,
9
+ } from '../../store/Atoms'
10
+
11
+ const DOC_MOVE_OPTS = { capture: true, passive: false }
12
+
13
+ const DRAG_HANDLE_BORDER = 2
14
+ const DRAG_HANDLE_SHORT = 12
15
+ const DRAG_HANDLE_LONG = 40
16
+
17
+ interface EVData {
18
+ initX: number
19
+ initY: number
20
+ initHeight: number
21
+ initWidth: number
22
+ startResizeX: number
23
+ startResizeY: number
24
+ ord: string // top/right/bottom/left
25
+ }
26
+
27
+ interface Props {
28
+ maxHeight: number
29
+ maxWidth: number
30
+ scale: number
31
+ minHeight: number
32
+ minWidth: number
33
+ show: boolean
34
+ }
35
+
36
+ const clamp = (
37
+ newPos: number,
38
+ newLength: number,
39
+ oldPos: number,
40
+ oldLength: number,
41
+ minLength: number,
42
+ maxLength: number
43
+ ) => {
44
+ if (newPos !== oldPos && newLength === oldLength) {
45
+ if (newPos < 0) {
46
+ return [0, oldLength]
47
+ }
48
+ if (newPos + newLength > maxLength) {
49
+ return [maxLength - oldLength, oldLength]
50
+ }
51
+ } else {
52
+ if (newLength < minLength) {
53
+ if (newPos === oldPos) {
54
+ return [newPos, minLength]
55
+ }
56
+ return [newPos + newLength - minLength, minLength]
57
+ }
58
+ if (newPos < 0) {
59
+ return [0, newPos + newLength]
60
+ }
61
+ if (newPos + newLength > maxLength) {
62
+ return [newPos, maxLength - newPos]
63
+ }
64
+ }
65
+
66
+ return [newPos, newLength]
67
+ }
68
+
69
+ const Croper = (props: Props) => {
70
+ const { minHeight, minWidth, maxHeight, maxWidth, scale, show } = props
71
+ const [x, setX] = useRecoilState(croperX)
72
+ const [y, setY] = useRecoilState(croperY)
73
+ const [height, setHeight] = useRecoilState(croperHeight)
74
+ const [width, setWidth] = useRecoilState(croperWidth)
75
+ const isInpainting = useRecoilValue(isInpaintingState)
76
+
77
+ const [isResizing, setIsResizing] = useState(false)
78
+ const [isMoving, setIsMoving] = useState(false)
79
+
80
+ useEffect(() => {
81
+ setX(Math.round((maxWidth - 512) / 2))
82
+ setY(Math.round((maxHeight - 512) / 2))
83
+ }, [maxHeight, maxWidth])
84
+
85
+ const [evData, setEVData] = useState<EVData>({
86
+ initX: 0,
87
+ initY: 0,
88
+ initHeight: 0,
89
+ initWidth: 0,
90
+ startResizeX: 0,
91
+ startResizeY: 0,
92
+ ord: 'top',
93
+ })
94
+
95
+ const onDragFocus = () => {
96
+ console.log('focus')
97
+ }
98
+
99
+ const clampLeftRight = (newX: number, newWidth: number) => {
100
+ return clamp(newX, newWidth, x, width, minWidth, maxWidth)
101
+ }
102
+
103
+ const clampTopBottom = (newY: number, newHeight: number) => {
104
+ return clamp(newY, newHeight, y, height, minHeight, maxHeight)
105
+ }
106
+
107
+ const onPointerMove = (e: PointerEvent) => {
108
+ if (isInpainting) {
109
+ return
110
+ }
111
+ const curX = e.clientX
112
+ const curY = e.clientY
113
+
114
+ const offsetY = Math.round((curY - evData.startResizeY) / scale)
115
+ const offsetX = Math.round((curX - evData.startResizeX) / scale)
116
+
117
+ const moveTop = () => {
118
+ const newHeight = evData.initHeight - offsetY
119
+ const newY = evData.initY + offsetY
120
+ const [clampedY, clampedHeight] = clampTopBottom(newY, newHeight)
121
+ setHeight(clampedHeight)
122
+ setY(clampedY)
123
+ }
124
+
125
+ const moveBottom = () => {
126
+ const newHeight = evData.initHeight + offsetY
127
+ const [clampedY, clampedHeight] = clampTopBottom(evData.initY, newHeight)
128
+ setHeight(clampedHeight)
129
+ setY(clampedY)
130
+ }
131
+
132
+ const moveLeft = () => {
133
+ const newWidth = evData.initWidth - offsetX
134
+ const newX = evData.initX + offsetX
135
+ const [clampedX, clampedWidth] = clampLeftRight(newX, newWidth)
136
+ setWidth(clampedWidth)
137
+ setX(clampedX)
138
+ }
139
+
140
+ const moveRight = () => {
141
+ const newWidth = evData.initWidth + offsetX
142
+ const [clampedX, clampedWidth] = clampLeftRight(evData.initX, newWidth)
143
+ setWidth(clampedWidth)
144
+ setX(clampedX)
145
+ }
146
+
147
+ if (isResizing) {
148
+ switch (evData.ord) {
149
+ case 'topleft': {
150
+ moveTop()
151
+ moveLeft()
152
+ break
153
+ }
154
+ case 'topright': {
155
+ moveTop()
156
+ moveRight()
157
+ break
158
+ }
159
+ case 'bottomleft': {
160
+ moveBottom()
161
+ moveLeft()
162
+ break
163
+ }
164
+ case 'bottomright': {
165
+ moveBottom()
166
+ moveRight()
167
+ break
168
+ }
169
+ case 'top': {
170
+ moveTop()
171
+ break
172
+ }
173
+ case 'right': {
174
+ moveRight()
175
+ break
176
+ }
177
+ case 'bottom': {
178
+ moveBottom()
179
+ break
180
+ }
181
+ case 'left': {
182
+ moveLeft()
183
+ break
184
+ }
185
+
186
+ default:
187
+ break
188
+ }
189
+ }
190
+
191
+ if (isMoving) {
192
+ const newX = evData.initX + offsetX
193
+ const newY = evData.initY + offsetY
194
+ const [clampedX, clampedWidth] = clampLeftRight(newX, evData.initWidth)
195
+ const [clampedY, clampedHeight] = clampTopBottom(newY, evData.initHeight)
196
+ setWidth(clampedWidth)
197
+ setHeight(clampedHeight)
198
+ setX(clampedX)
199
+ setY(clampedY)
200
+ }
201
+ }
202
+
203
+ const onPointerDone = (e: PointerEvent) => {
204
+ if (isResizing) {
205
+ setIsResizing(false)
206
+ }
207
+
208
+ if (isMoving) {
209
+ setIsMoving(false)
210
+ }
211
+ }
212
+
213
+ useEffect(() => {
214
+ if (isResizing || isMoving) {
215
+ document.addEventListener('pointermove', onPointerMove, DOC_MOVE_OPTS)
216
+ document.addEventListener('pointerup', onPointerDone, DOC_MOVE_OPTS)
217
+ document.addEventListener('pointercancel', onPointerDone, DOC_MOVE_OPTS)
218
+ return () => {
219
+ document.removeEventListener(
220
+ 'pointermove',
221
+ onPointerMove,
222
+ DOC_MOVE_OPTS
223
+ )
224
+ document.removeEventListener('pointerup', onPointerDone, DOC_MOVE_OPTS)
225
+ document.removeEventListener(
226
+ 'pointercancel',
227
+ onPointerDone,
228
+ DOC_MOVE_OPTS
229
+ )
230
+ }
231
+ }
232
+ }, [isResizing, isMoving, width, height, evData])
233
+
234
+ const onCropPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
235
+ const { ord } = (e.target as HTMLElement).dataset
236
+ if (ord) {
237
+ setIsResizing(true)
238
+ setEVData({
239
+ initX: x,
240
+ initY: y,
241
+ initHeight: height,
242
+ initWidth: width,
243
+ startResizeX: e.clientX,
244
+ startResizeY: e.clientY,
245
+ ord,
246
+ })
247
+ }
248
+ }
249
+
250
+ const createCropSelection = () => {
251
+ return (
252
+ <div
253
+ className="drag-elements"
254
+ onFocus={onDragFocus}
255
+ onPointerDown={onCropPointerDown}
256
+ >
257
+ <div
258
+ className="drag-bar ord-top"
259
+ data-ord="top"
260
+ style={{ transform: `scale(${1 / scale})` }}
261
+ />
262
+ <div
263
+ className="drag-bar ord-right"
264
+ data-ord="right"
265
+ style={{ transform: `scale(${1 / scale})` }}
266
+ />
267
+ <div
268
+ className="drag-bar ord-bottom"
269
+ data-ord="bottom"
270
+ style={{ transform: `scale(${1 / scale})` }}
271
+ />
272
+ <div
273
+ className="drag-bar ord-left"
274
+ data-ord="left"
275
+ style={{ transform: `scale(${1 / scale})` }}
276
+ />
277
+
278
+ <div
279
+ className="drag-handle ord-topleft"
280
+ data-ord="topleft"
281
+ aria-label="topleft"
282
+ tabIndex={0}
283
+ role="button"
284
+ style={{ transform: `scale(${1 / scale})` }}
285
+ />
286
+
287
+ <div
288
+ className="drag-handle ord-topright"
289
+ data-ord="topright"
290
+ aria-label="topright"
291
+ tabIndex={0}
292
+ role="button"
293
+ style={{ transform: `scale(${1 / scale})` }}
294
+ />
295
+
296
+ <div
297
+ className="drag-handle ord-bottomleft"
298
+ data-ord="bottomleft"
299
+ aria-label="bottomleft"
300
+ tabIndex={0}
301
+ role="button"
302
+ style={{ transform: `scale(${1 / scale})` }}
303
+ />
304
+
305
+ <div
306
+ className="drag-handle ord-bottomright"
307
+ data-ord="bottomright"
308
+ aria-label="bottomright"
309
+ tabIndex={0}
310
+ role="button"
311
+ style={{ transform: `scale(${1 / scale})` }}
312
+ />
313
+
314
+ <div
315
+ className="drag-handle ord-top"
316
+ data-ord="top"
317
+ aria-label="top"
318
+ tabIndex={0}
319
+ role="button"
320
+ style={{ transform: `scale(${1 / scale})` }}
321
+ />
322
+ <div
323
+ className="drag-handle ord-right"
324
+ data-ord="right"
325
+ aria-label="right"
326
+ tabIndex={0}
327
+ role="button"
328
+ style={{ transform: `scale(${1 / scale})` }}
329
+ />
330
+ <div
331
+ className="drag-handle ord-bottom"
332
+ data-ord="bottom"
333
+ aria-label="bottom"
334
+ tabIndex={0}
335
+ role="button"
336
+ style={{ transform: `scale(${1 / scale})` }}
337
+ />
338
+ <div
339
+ className="drag-handle ord-left"
340
+ data-ord="left"
341
+ aria-label="left"
342
+ tabIndex={0}
343
+ role="button"
344
+ style={{ transform: `scale(${1 / scale})` }}
345
+ />
346
+ </div>
347
+ )
348
+ }
349
+
350
+ const onInfoBarPointerDown = (e: React.PointerEvent<HTMLDivElement>) => {
351
+ setIsMoving(true)
352
+ setEVData({
353
+ initX: x,
354
+ initY: y,
355
+ initHeight: height,
356
+ initWidth: width,
357
+ startResizeX: e.clientX,
358
+ startResizeY: e.clientY,
359
+ ord: '',
360
+ })
361
+ }
362
+
363
+ const createInfoBar = () => {
364
+ return (
365
+ <div
366
+ className="info-bar"
367
+ onPointerDown={onInfoBarPointerDown}
368
+ style={{
369
+ transform: `scale(${1 / scale})`,
370
+ top: `${10 / scale}px`,
371
+ left: `${10 / scale}px`,
372
+ }}
373
+ >
374
+ <div className="crop-size">
375
+ {width} x {height}
376
+ </div>
377
+ </div>
378
+ )
379
+ }
380
+
381
+ const createBorder = () => {
382
+ return (
383
+ <div
384
+ className="crop-border"
385
+ style={{
386
+ height,
387
+ width,
388
+ outlineWidth: `${DRAG_HANDLE_BORDER / scale}px`,
389
+ }}
390
+ />
391
+ )
392
+ }
393
+
394
+ return (
395
+ <div
396
+ className="croper-wrapper"
397
+ style={{ visibility: show ? 'visible' : 'hidden' }}
398
+ >
399
+ <div className="croper" style={{ height, width, left: x, top: y }}>
400
+ {createBorder()}
401
+ {createInfoBar()}
402
+ {createCropSelection()}
403
+ </div>
404
+ </div>
405
+ )
406
+ }
407
+
408
+ export default Croper
lama_cleaner/app/src/components/Editor/Editor.scss ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ @use '../../styles/Mixins' as *;
2
+
3
+ .editor-container {
4
+ display: flex;
5
+ width: 100vw;
6
+ height: 100vh;
7
+ justify-content: center;
8
+ align-items: center;
9
+ }
10
+
11
+ .react-transform-wrapper {
12
+ display: grid !important;
13
+ width: 100% !important;
14
+ height: 100% !important;
15
+ }
16
+
17
+ .editor-canvas-container {
18
+ display: grid;
19
+ grid-template-areas: 'editor-content';
20
+ row-gap: 1rem;
21
+ }
22
+
23
+ .editor-canvas {
24
+ grid-area: editor-content;
25
+ z-index: 2;
26
+ }
27
+
28
+ .original-image-container {
29
+ grid-area: editor-content;
30
+ pointer-events: none;
31
+ display: grid;
32
+ grid-template-areas: 'original-image-content';
33
+
34
+ img {
35
+ grid-area: original-image-content;
36
+ }
37
+
38
+ .editor-slider {
39
+ grid-area: original-image-content;
40
+ height: 100%;
41
+ width: 6px;
42
+ justify-self: end;
43
+ background-color: var(--yellow-accent);
44
+ transition: all 300ms cubic-bezier(0.4, 0, 0.2, 1);
45
+ z-index: 2;
46
+ }
47
+ }
48
+
49
+ .editor-canvas-loading {
50
+ pointer-events: none;
51
+ animation: pulsing 750ms infinite;
52
+ }
53
+
54
+ .editor-toolkit-panel {
55
+ position: fixed;
56
+ bottom: 0.5rem;
57
+ border-radius: 3rem;
58
+ padding: 0.6rem 3rem;
59
+ display: grid;
60
+ grid-template-areas: 'toolkit-size-selector toolkit-brush-slider toolkit-btns';
61
+ column-gap: 2rem;
62
+ align-items: center;
63
+ justify-content: center;
64
+ backdrop-filter: blur(12px);
65
+ background-color: var(--page-bg-light);
66
+ animation: slideUp 0.2s ease-out;
67
+ border: var(--editor-toolkit-panel-border);
68
+ box-shadow: 0 0 0 1px #0000001a, 0 3px 16px #00000014, 0 2px 6px 1px #00000017;
69
+
70
+ @include mobile {
71
+ padding: 1rem 2rem;
72
+ grid-template-areas:
73
+ 'toolkit-size-selector toolkit-size-selector'
74
+ 'toolkit-brush-slider toolkit-brush-slider'
75
+ 'toolkit-btns toolkit-btns';
76
+ row-gap: 2rem;
77
+ justify-items: center;
78
+ }
79
+
80
+ .eyeicon-active {
81
+ background-color: var(--yellow-accent);
82
+ color: var(--btn-text-hover-color);
83
+ }
84
+ }
85
+
86
+ .editor-brush-slider {
87
+ grid-area: toolkit-brush-slider;
88
+ user-select: none;
89
+ display: grid;
90
+ grid-template-columns: repeat(2, max-content);
91
+ height: max-content;
92
+ column-gap: 1rem;
93
+ align-items: center;
94
+
95
+ @include slider-bar;
96
+ }
97
+
98
+ .editor-toolkit-btns {
99
+ grid-area: toolkit-btns;
100
+ display: grid;
101
+ grid-auto-flow: column;
102
+ column-gap: 1rem;
103
+ }
104
+
105
+ .brush-shape {
106
+ position: absolute;
107
+ border-radius: 50%;
108
+ background-color: #ffcc00bb;
109
+ border: 1px solid var(--yellow-accent);
110
+ pointer-events: none;
111
+ }