libreflow.extensions.sk.export-psd-layers 1.1.6__tar.gz → 1.1.8__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (17) hide show
  1. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/CHANGELOG.md +13 -0
  2. {libreflow_extensions_sk_export_psd_layers-1.1.6/src/libreflow.extensions.sk.export_psd_layers.egg-info → libreflow_extensions_sk_export_psd_layers-1.1.8}/PKG-INFO +14 -1
  3. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/extensions/sk/export_psd_layers/__init__.py +333 -54
  4. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/extensions/sk/export_psd_layers/_version.py +3 -3
  5. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8/src/libreflow.extensions.sk.export_psd_layers.egg-info}/PKG-INFO +14 -1
  6. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/MANIFEST.in +0 -0
  7. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/README.md +0 -0
  8. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/setup.cfg +0 -0
  9. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/setup.py +0 -0
  10. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/__init__.py +0 -0
  11. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/extensions/__init__.py +0 -0
  12. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow/extensions/sk/__init__.py +0 -0
  13. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow.extensions.sk.export_psd_layers.egg-info/SOURCES.txt +0 -0
  14. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow.extensions.sk.export_psd_layers.egg-info/dependency_links.txt +0 -0
  15. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow.extensions.sk.export_psd_layers.egg-info/requires.txt +0 -0
  16. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/src/libreflow.extensions.sk.export_psd_layers.egg-info/top_level.txt +0 -0
  17. {libreflow_extensions_sk_export_psd_layers-1.1.6 → libreflow_extensions_sk_export_psd_layers-1.1.8}/versioneer.py +0 -0
@@ -19,6 +19,19 @@ Types of changes
19
19
 
20
20
  ## [Unreleased]
21
21
 
22
+ ## [1.1.8] - 2025-10-07
23
+
24
+ ### Fixed
25
+
26
+ * Export resized to psd canvas - warning message for layers with a bouding box twice as large as the viewbox
27
+
28
+ ## [1.1.7] - 2025-08-19
29
+
30
+ ### Added
31
+
32
+ * Warning message when publishing regarding resolution and colour depth.
33
+ * Message in the window when creating a new working copy.
34
+
22
35
  ## [1.1.6] - 2025-08-07
23
36
 
24
37
  ### Fixed
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libreflow.extensions.sk.export_psd_layers
3
- Version: 1.1.6
3
+ Version: 1.1.8
4
4
  Home-page: https://gitlab.com/lfs.coop/libreflow/libreflow_launcher
5
5
  Author: Thomas Thiebaut
6
6
  Author-email: autor@les-fees-speciales.coop
@@ -50,6 +50,19 @@ Types of changes
50
50
 
51
51
  ## [Unreleased]
52
52
 
53
+ ## [1.1.8] - 2025-10-07
54
+
55
+ ### Fixed
56
+
57
+ * Export resized to psd canvas - warning message for layers with a bouding box twice as large as the viewbox
58
+
59
+ ## [1.1.7] - 2025-08-19
60
+
61
+ ### Added
62
+
63
+ * Warning message when publishing regarding resolution and colour depth.
64
+ * Message in the window when creating a new working copy.
65
+
53
66
  ## [1.1.6] - 2025-08-07
54
67
 
55
68
  ### Fixed
@@ -1,22 +1,26 @@
1
+ import gc
2
+ import json
1
3
  import os
2
4
  import re
3
- import json
4
- import gazu
5
5
  import time
6
6
 
7
+ import gazu
7
8
  from kabaret import flow
8
- from libreflow.flows.default.flow.film import Film
9
9
  from libreflow.baseflow.file import (
10
- GenericRunAction,
11
- TrackedFile,
10
+ CreateWorkingCopyAction,
12
11
  FileRevisionNameChoiceValue,
13
- UploadPNGToKitsu,
14
12
  PublishAndRenderPlayblast,
13
+ PublishFileAction,
14
+ TrackedFile,
15
+ UploadPNGToKitsu,
15
16
  )
17
+ from libreflow.flows.default.flow.film import Film
16
18
  from psd_tools import PSDImage
17
19
 
18
20
 
19
21
  class ExportPSDLayers(flow.Action):
22
+ """Exports Photoshop (PSD/PSB) layers for background layout and color tasks."""
23
+
20
24
  ICON = ("icons.flow", "photoshop")
21
25
 
22
26
  _file = flow.Parent()
@@ -28,6 +32,15 @@ class ExportPSDLayers(flow.Action):
28
32
  revision = flow.Param(None, FileRevisionNameChoiceValue)
29
33
 
30
34
  def allow_context(self, context):
35
+ """Check whether the given context is valid for running the action.
36
+
37
+ Args:
38
+ context: Context object, usually representing the current project/task.
39
+
40
+ Returns:
41
+ bool: True if the action can be executed in this context, False otherwise.
42
+
43
+ """
31
44
  return (
32
45
  context
33
46
  and self._file.format.get() in ["psd", "psb"]
@@ -35,24 +48,45 @@ class ExportPSDLayers(flow.Action):
35
48
  self._file.get_revision_names(
36
49
  sync_status="Available", published_only=True
37
50
  )
38
- ) > 0
51
+ )
52
+ > 0
39
53
  )
40
54
 
41
55
  def needs_dialog(self):
56
+ """Indicate whether this action requires a dialog to be displayed.
57
+
58
+ Returns:
59
+ bool: always True.
60
+
61
+ """
42
62
  msg = ""
43
63
  if self._task.name() == "bg_layout":
44
64
  msg = "<b>Layout : Photoshop Project layers will be exported separately</b>"
45
65
  elif self._task.name() == "bg_color":
46
- msg = "<b>BG Color : Photoshop Project will be exported as a single image</b>"
47
-
66
+ msg = (
67
+ "<b>BG Color : Photoshop Project will be exported as a single image</b>"
68
+ )
69
+
48
70
  self.message.set(msg)
49
71
  return True
50
72
 
51
73
  def get_buttons(self):
74
+ """Return the buttons displayed in the dialog.
75
+
76
+ Returns:
77
+ list[str]: list of button labels, typically ['Export', 'Cancel'].
78
+
79
+ """
52
80
  self.revision.revert_to_default()
53
81
  return ["Export", "Cancel"]
54
82
 
55
83
  def ensure_render_folder(self):
84
+ """Ensure the render output folder exists for the current file.
85
+
86
+ Returns:
87
+ flow.File: The folder object where rendered files will be stored.
88
+
89
+ """
56
90
  folder_name = self._file.complete_name.get()
57
91
  folder_name += "_render"
58
92
 
@@ -65,6 +99,12 @@ class ExportPSDLayers(flow.Action):
65
99
  return self._files[folder_name]
66
100
 
67
101
  def ensure_render_folder_revision(self):
102
+ """Ensure the render folder has the correct revision.
103
+
104
+ Returns:
105
+ flow.Revision: The revision object in the render folder.
106
+
107
+ """
68
108
  folder = self.ensure_render_folder()
69
109
  revision_name = self.revision.get()
70
110
  source_revision = self._file.get_revision(self.revision.get())
@@ -82,37 +122,55 @@ class ExportPSDLayers(flow.Action):
82
122
  self._files.touch()
83
123
 
84
124
  return revision
85
-
125
+
86
126
  def get_default_file(self):
127
+ """Retrieve the default file for exporting a BG color image.
128
+
129
+ Returns:
130
+ flow.File or None: The default file object, or None if not found.
131
+
132
+ """
87
133
  mng = self.root().project().get_task_manager()
88
134
  default_files = mng.default_files.get()
89
135
  for file_name, task_names in default_files.items():
90
- if 'bg_color.png' in file_name:
136
+ if "bg_color.png" in file_name:
91
137
  task = default_files[file_name][0]
92
- file_mapped_name = file_name.replace(".", '_')
138
+ file_mapped_name = file_name.replace(".", "_")
93
139
  break
94
140
 
95
141
  dft_task = mng.default_tasks[task]
96
- if not dft_task.files.has_mapped_name(file_mapped_name): # check default file
142
+ if not dft_task.files.has_mapped_name(file_mapped_name): # check default file
97
143
  # print(f'Scene Builder - default task {task_name} has no default file {filename} -> use default template')
98
144
  return None
99
145
 
100
146
  dft_file = dft_task.files[file_mapped_name]
101
147
  return dft_file
102
-
148
+
103
149
  def _ensure_file(self, name, format, path_format, source_revision):
150
+ """Ensure a file exists in the project with the given name and revision.
151
+
152
+ Args:
153
+ name (str): The base name of the file.
154
+ format (str): The file extension (e.g., "png").
155
+ path_format (str): The default path format for the file.
156
+ source_revision (flow.Revision): The source revision to copy comments from.
157
+
158
+ Returns:
159
+ flow.Revision: The revision object created or retrieved.
160
+
161
+ """
104
162
  mapped_name = "%s_%s" % (name, format)
105
163
 
106
164
  file = None
107
-
165
+
108
166
  if not self._files.has_mapped_name(mapped_name):
109
167
  if format:
110
168
  file = self._files.add_file(
111
- name=name,
112
- extension=format,
113
- tracked=True,
114
- default_path_format=path_format,
115
- )
169
+ name=name,
170
+ extension=format,
171
+ tracked=True,
172
+ default_path_format=path_format,
173
+ )
116
174
  else:
117
175
  file = self._files.add_folder(name, tracked=True)
118
176
  else:
@@ -127,7 +185,7 @@ class ExportPSDLayers(flow.Action):
127
185
  r = file.get_revision(revision_name)
128
186
 
129
187
  r.comment.set(source_revision.comment.get())
130
-
188
+
131
189
  file.ensure_last_revision_oid()
132
190
 
133
191
  r.set_sync_status("Available")
@@ -140,13 +198,22 @@ class ExportPSDLayers(flow.Action):
140
198
  os.remove(img_path)
141
199
 
142
200
  self._files.touch()
143
-
201
+
144
202
  return r
145
203
 
146
204
  def run(self, button):
205
+ """Execute the render action.
206
+
207
+ Args:
208
+ button (str): The label of the button pressed by the user (e.g., 'Export' or 'Cancel').
209
+
210
+ Returns:
211
+ Any: the result of the parent run method if executed, or None if canceled.
212
+
213
+ """
147
214
  if button == "Cancel":
148
215
  return
149
-
216
+
150
217
  session = self.root().session()
151
218
  log_format = "[EXPORT LAYERS] {message}"
152
219
 
@@ -163,23 +230,22 @@ class ExportPSDLayers(flow.Action):
163
230
  # Open photoshop file
164
231
  psb = PSDImage.open(source_revision.get_path())
165
232
 
166
-
167
233
  ############# BG LAYOUT PROCESS #############
168
234
 
169
- if self._task.name() == "bg_layout" :
235
+ if self._task.name() == "bg_layout":
170
236
 
171
237
  render_revision = self.ensure_render_folder_revision()
172
238
  # JSON structure for layers order
173
239
  layers_data = {
174
240
  "from": os.path.basename(source_revision.get_path()),
175
241
  "layers": [],
176
- "hidden_layers": []
242
+ "hidden_layers": [],
177
243
  }
178
244
 
179
245
  # Export image layers
180
246
  if os.path.exists(render_revision.get_path()) is False:
181
247
  os.makedirs(render_revision.get_path())
182
-
248
+
183
249
  folder_name = os.path.basename(render_revision.get_path())
184
250
 
185
251
  # frame_bbox = (0, 0, 0, 0)
@@ -201,22 +267,49 @@ class ExportPSDLayers(flow.Action):
201
267
  # # frame_bbox[2] - 5,
202
268
  # # frame_bbox[3] - 5,
203
269
  # # )
204
-
270
+
205
271
  # print(frame_bbox)
206
272
 
273
+ # Stop exporting action when find layers with a bouding box twice as large as the viewbox
274
+ layers_to_crop = []
275
+ for descendant in psb.descendants():
276
+ if not descendant.is_group():
277
+ v_left, v_top, v_right, v_bottom = psb.viewbox
278
+ v_width, v_height = v_right - v_left, v_bottom - v_top
279
+
280
+ bbox_left, bbox_top, bbox_right, bbox_bottom = descendant.bbox
281
+ bbox_width, bbox_height = (
282
+ bbox_right - bbox_left,
283
+ bbox_bottom - bbox_top,
284
+ )
285
+
286
+ if not (bbox_height <= v_height * 2 and bbox_width <= v_width * 2):
287
+ layers_to_crop.append(descendant.name)
288
+
289
+ if len(layers_to_crop) != 0:
290
+ session.log_warning(
291
+ log_format.format(
292
+ message=f"You need to crop these layers : {layers_to_crop}"
293
+ )
294
+ )
295
+ return None
296
+
207
297
  for layer in reversed(psb):
298
+
208
299
  # Remove invalid characters
209
- layer_name = layer.name.replace(' ','-')
300
+ layer_name = layer.name.replace(" ", "-")
210
301
  match_invalid = re.search(r"[~\"#%&*:<>?/\\{|}]+", layer.name)
211
302
  if match_invalid:
212
- layer_name = layer_name.replace(match_invalid.group(0), '')
303
+ layer_name = layer_name.replace(match_invalid.group(0), "")
213
304
 
214
305
  output_path = os.path.join(
215
306
  render_revision.get_path(),
216
307
  "{folder}-{layer}.png".format(folder=folder_name, layer=layer_name),
217
308
  )
218
309
 
219
- session.log_info(log_format.format(message=f'Exporting layer {layer_name}'))
310
+ session.log_info(
311
+ log_format.format(message=f"Exporting layer {layer_name}")
312
+ )
220
313
 
221
314
  if not layer.visible:
222
315
  layer.visible = True
@@ -227,59 +320,67 @@ class ExportPSDLayers(flow.Action):
227
320
  layer_name
228
321
  )
229
322
 
230
- image = layer.composite(viewport=psb.viewbox)
323
+ image = layer.composite(viewport=psb.viewbox, force=True)
231
324
  image.save(output_path)
325
+ session.log_info(f"Layer {layer_name} exported !")
326
+ gc.collect()
232
327
 
233
-
234
328
  # Export JSON data
235
329
  json_object = json.dumps(layers_data)
236
330
  json_path = os.path.join(render_revision.get_path(), "layers.json")
237
-
238
- session.log_info(log_format.format(message='Saving layers.json'))
331
+
332
+ session.log_info(log_format.format(message="Saving layers.json"))
239
333
  with open(json_path, "w") as outfile:
240
334
  outfile.write(json_object)
241
-
242
- session.log_info(log_format.format(message='Export complete'))
243
-
335
+
336
+ session.log_info(log_format.format(message="Export complete"))
337
+
244
338
  ############# BG COLOR PROCESS #############
245
-
246
- if self._task.name() == "bg_color" :
339
+
340
+ if self._task.name() == "bg_color":
247
341
 
248
342
  default_file = self.get_default_file()
249
343
 
250
344
  if default_file is not None:
251
-
345
+
252
346
  render_revision = self._ensure_file(
253
- name="bg_color",
254
- format="png",
255
- path_format=default_file.path_format.get(),
256
- source_revision=source_revision
257
- )
347
+ name="bg_color",
348
+ format="png",
349
+ path_format=default_file.path_format.get(),
350
+ source_revision=source_revision,
351
+ )
258
352
 
259
353
  output_path = render_revision.get_path()
260
354
 
261
355
  info_layer = psb.find("INFO")
262
- if info_layer : info_layer.visible = False
356
+ if info_layer:
357
+ info_layer.visible = False
263
358
 
264
359
  LO_layer = psb.find("REF_LAYOUT")
265
- if LO_layer : LO_layer.visible = False
360
+ if LO_layer:
361
+ LO_layer.visible = False
266
362
 
267
363
  DES_layer = psb.find("REF_DESIGN")
268
- if DES_layer : DES_layer.visible = False
364
+ if DES_layer:
365
+ DES_layer.visible = False
269
366
 
270
367
  chara_layer = psb.find("character")
271
- if chara_layer : chara_layer.visible = False
368
+ if chara_layer:
369
+ chara_layer.visible = False
272
370
 
273
371
  utils_layer = psb.find("_utils")
274
- if utils_layer : utils_layer.visible = False
372
+ if utils_layer:
373
+ utils_layer.visible = False
275
374
 
276
- image = psb.composite(viewport=psb.viewbox,force=True)
375
+ image = psb.composite(viewport=psb.viewbox, force=True)
277
376
  image.save(output_path)
278
377
 
279
- session.log_info(log_format.format(message='Export complete'))
378
+ session.log_info(log_format.format(message="Export complete"))
280
379
 
281
380
  else:
282
- self.root().session().log_error("[Export PSD] BG Color Image default file do not exist")
381
+ self.root().session().log_error(
382
+ "[Export PSD] BG Color Image default file do not exist"
383
+ )
283
384
 
284
385
  return self.get_result(close=True)
285
386
 
@@ -424,6 +525,10 @@ class ExportPSDLayersBatch(flow.Action):
424
525
  class PublishandExportPSD(PublishAndRenderPlayblast):
425
526
  ICON = ('icons.libreflow', 'publish')
426
527
 
528
+ _shot = flow.Parent(5)
529
+ _sequence = flow.Parent(7)
530
+ _film = flow.Parent(9)
531
+
427
532
  def allow_context(self, context):
428
533
  return (
429
534
  context
@@ -431,7 +536,56 @@ class PublishandExportPSD(PublishAndRenderPlayblast):
431
536
  and self._file.format.get() in ["psd", "psb"]
432
537
  )
433
538
 
539
+ def get_bg_layout(self):
540
+ source_file = None
541
+ shot_entity = (
542
+ self.root()
543
+ .project()
544
+ .films[self._film.name()]
545
+ .sequences[self._sequence.name()]
546
+ .shots[self._shot.name()]
547
+ )
548
+
549
+ bg_layout_task = shot_entity.tasks["bg_layout"]
550
+ if bg_layout_task.files.has_file("bg_layout", "psb"):
551
+ # Get the file path as parent
552
+ source_file = bg_layout_task.files["bg_layout_psb"].get_head_revision()
553
+
554
+ return source_file
555
+
434
556
  def get_buttons(self):
557
+ msg = "<h2>Publish and Export Preview</h2>"
558
+
559
+ if "bg_color" in self._file.name():
560
+ bg_layout_file = self.get_bg_layout()
561
+ bg_layout_psb = PSDImage.open(bg_layout_file.get_path())
562
+ working_copy_path = self._file.get_working_copy().get_path()
563
+ psb = PSDImage.open(working_copy_path)
564
+
565
+ if psb.size != bg_layout_psb.size:
566
+ msg += (f"""<h3><font color=#D66500>
567
+ Does not conform to the size of bg_layout.
568
+ </font></h3>
569
+ <h4><font color=#D66500>
570
+ BG_layout size = {bg_layout_psb.size}
571
+ </font></h4>
572
+ """)
573
+
574
+ if psb.depth != 16:
575
+ msg += (
576
+ "<h3><font color=#D66500>"
577
+ "This file is not in 16-bit format."
578
+ "</font></h3>"
579
+ "<h4><font color=#D66500>"
580
+ "To convert to 16 Bits/Channel, choose `Image > Mode > 16 Bits/Channel`."
581
+ "</font></h4>"
582
+ )
583
+
584
+ self.message.set(msg)
585
+ return ["Cancel"]
586
+
587
+ self.message.set(msg)
588
+
435
589
  return ["Publish and Export Preview", "Cancel"]
436
590
 
437
591
  def _configure_and_render(self, revision_name, upload_after_publish):
@@ -708,6 +862,129 @@ class UploadPSDPreview(UploadPNGToKitsu):
708
862
  rev.set_status("on_kitsu")
709
863
 
710
864
 
865
+ class PublishPSDFile(PublishFileAction):
866
+
867
+ _task = flow.Parent(3)
868
+ _shot = flow.Parent(5)
869
+ _sequence = flow.Parent(7)
870
+ _film = flow.Parent(9)
871
+
872
+ def __init__(self, name, parent):
873
+ super(PublishPSDFile, self).__init__(parent, name)
874
+
875
+ def get_bg_layout(self):
876
+ source_file = None
877
+ shot_entity = (
878
+ self.root()
879
+ .project()
880
+ .films[self._film.name()]
881
+ .sequences[self._sequence.name()]
882
+ .shots[self._shot.name()]
883
+ )
884
+
885
+ bg_layout_task = shot_entity.tasks["bg_layout"]
886
+ if bg_layout_task.files.has_file("bg_layout", "psb"):
887
+ # Get the file path as parent
888
+ source_file = bg_layout_task.files["bg_layout_psb"].get_head_revision()
889
+
890
+ return source_file
891
+
892
+ def get_buttons(self):
893
+ self.check_default_values()
894
+
895
+ msg = "<h2>Publish</h2>"
896
+
897
+ working_copies = self._file.get_working_copies()
898
+ if working_copies:
899
+ user_names = [wc.user.get() for wc in working_copies]
900
+ user_names = ["<b>" + n + "</b>" for n in user_names]
901
+ msg += (
902
+ "<h3><font color=#D66500><br>"
903
+ "This file is currently being edited by one or more users ({})."
904
+ "</font></h3>".format(", ".join(user_names))
905
+ )
906
+
907
+ if "bg_color" in self._file.name():
908
+ bg_layout_file = self.get_bg_layout()
909
+ bg_layout_psb = PSDImage.open(bg_layout_file.get_path())
910
+ working_copy_path = self._file.get_working_copy().get_path()
911
+ psb = PSDImage.open(working_copy_path)
912
+
913
+ if psb.size != bg_layout_psb.size:
914
+ msg += f"""<h3><font color=#D66500>
915
+ Does not conform to the size of bg_layout.
916
+ </font></h3>
917
+ <h4><font color=#D66500>
918
+ BG_layout size = {bg_layout_psb.size}
919
+ </font></h4>
920
+ """
921
+
922
+ if psb.depth != 16:
923
+ msg += (
924
+ "<h3><font color=#D66500>"
925
+ "This file is not in 16-bit format."
926
+ "</font></h3>"
927
+ "<h4><font color=#D66500>"
928
+ "To convert to 16 Bits/Channel, choose `Image > Mode > 16 Bits/Channel`."
929
+ "</font></h4>"
930
+ )
931
+
932
+ self.message.set(msg)
933
+ return ["Cancel"]
934
+
935
+ self.message.set(msg)
936
+
937
+ return ["Publish", "Cancel"]
938
+
939
+ class CreateWorkingCopy(CreateWorkingCopyAction):
940
+ def __init__(self, parent, name):
941
+ super(CreateWorkingCopy, self).__init__(parent, name)
942
+
943
+ def get_buttons(self):
944
+ msg = "<h3>Create a working copy</h3>"
945
+
946
+ if "bg_color" in self._task.name() and "psb" in self._file.name():
947
+ msg += """<h3>
948
+ Remember to change the colour depth to <u>16 bits</u> in Photoshop.
949
+ </h3>
950
+ <h4>
951
+ To convert to 16 Bits/Channel, choose <u>`Image > Mode > 16 Bits/Channel`</u>.
952
+ </h4>"""
953
+
954
+ # Buttons for Use Base File mode
955
+ if self.use_base_file:
956
+ msg += f"<font color=#FFA34D>WARNING: You should start working on this file \
957
+ from the latest version of {self.base_file_name} in {self.from_task} task.</font>"
958
+ self.message.set(msg)
959
+ return ["Create from base file", "Create from scratch", "Cancel"]
960
+
961
+ if self._file.has_working_copy(from_current_user=True):
962
+ msg += "<font color=#FFA34D>WARNING: You already have a working copy to your name. \
963
+ Choosing to create a new one will overwrite your changes.</font>"
964
+
965
+ self.message.set(msg)
966
+
967
+ self.from_revision.revert_to_default()
968
+
969
+ return ["Create", "Create from scratch", "Cancel"]
970
+
971
+
972
+ def publish_psd_file(parent):
973
+ if isinstance(parent, TrackedFile) and "psb" in parent.name():
974
+ r = flow.Child(PublishPSDFile).ui(label="Publish")
975
+ r.name = "publish_action"
976
+ r.index = 25
977
+ return r
978
+
979
+
980
+ def create_working_copy(parent):
981
+ if isinstance(parent, TrackedFile) and "psb" in parent.name():
982
+ r = flow.Child(CreateWorkingCopy).ui(label="Create working copy")
983
+ r.name = "create_working_copy_action"
984
+ r.index = 25
985
+ return r
986
+
987
+
711
988
  def publish_and_export_preview(parent):
712
989
  if isinstance(parent, TrackedFile):
713
990
  r = flow.Child(PublishandExportPSD)
@@ -755,5 +1032,7 @@ def install_extensions(session):
755
1032
  export_psd_preview,
756
1033
  upload_preview,
757
1034
  publish_and_export_preview,
1035
+ publish_psd_file,
1036
+ create_working_copy,
758
1037
  ]
759
1038
  }
@@ -8,11 +8,11 @@ import json
8
8
 
9
9
  version_json = '''
10
10
  {
11
- "date": "2025-08-07T14:51:04+0200",
11
+ "date": "2025-10-07T12:13:47+0200",
12
12
  "dirty": false,
13
13
  "error": null,
14
- "full-revisionid": "fe31644892623f2fd46e335334959c9335952d11",
15
- "version": "1.1.6"
14
+ "full-revisionid": "9b4d99a24c5df5c15e21e86f38c101e653930ad1",
15
+ "version": "1.1.8"
16
16
  }
17
17
  ''' # END VERSION_JSON
18
18
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: libreflow.extensions.sk.export_psd_layers
3
- Version: 1.1.6
3
+ Version: 1.1.8
4
4
  Home-page: https://gitlab.com/lfs.coop/libreflow/libreflow_launcher
5
5
  Author: Thomas Thiebaut
6
6
  Author-email: autor@les-fees-speciales.coop
@@ -50,6 +50,19 @@ Types of changes
50
50
 
51
51
  ## [Unreleased]
52
52
 
53
+ ## [1.1.8] - 2025-10-07
54
+
55
+ ### Fixed
56
+
57
+ * Export resized to psd canvas - warning message for layers with a bouding box twice as large as the viewbox
58
+
59
+ ## [1.1.7] - 2025-08-19
60
+
61
+ ### Added
62
+
63
+ * Warning message when publishing regarding resolution and colour depth.
64
+ * Message in the window when creating a new working copy.
65
+
53
66
  ## [1.1.6] - 2025-08-07
54
67
 
55
68
  ### Fixed