coveragebook_components 0.5.0 → 0.5.2

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/build/coco/app.css +28 -28
  3. data/app/assets/build/coco/app.js +8113 -974
  4. data/app/assets/build/coco/book.css +3 -0
  5. data/app/assets/build/coco/book.js +37 -4
  6. data/app/assets/js/base/polyfills/request-submit.js +39 -0
  7. data/app/assets/js/base/setup.js +2 -0
  8. data/app/assets/js/helpers/path.js +9 -1
  9. data/app/assets/js/helpers/screenshot.js +20 -4
  10. data/app/components/coco/app/blocks/slide_editor/slide_editor.css +8 -11
  11. data/app/components/coco/app/blocks/slide_editor/slide_editor.html.erb +155 -196
  12. data/app/components/coco/app/blocks/slide_editor/slide_editor.js +92 -70
  13. data/app/components/coco/app/blocks/slide_editor/slide_editor.rb +36 -85
  14. data/app/components/coco/app/elements/alert/alert.css +1 -1
  15. data/app/components/coco/app/elements/button/button.css +11 -11
  16. data/app/components/coco/app/elements/color_picker_button/color_picker_button.html.erb +17 -16
  17. data/app/components/coco/app/elements/color_picker_button/color_picker_button.js +0 -8
  18. data/app/components/coco/app/elements/color_picker_button/color_picker_button.rb +2 -15
  19. data/app/components/coco/app/elements/image_picker/image_picker.html.erb +4 -4
  20. data/app/components/coco/app/elements/image_picker/image_picker.js +18 -6
  21. data/app/components/coco/app/elements/image_picker/image_picker.rb +2 -11
  22. data/app/components/coco/app/elements/image_picker_button/image_picker_button.html.erb +9 -8
  23. data/app/components/coco/app/elements/image_picker_button/image_picker_button.js +10 -23
  24. data/app/components/coco/app/elements/image_picker_button/image_picker_button.rb +19 -35
  25. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.html.erb +7 -4
  26. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.js +9 -3
  27. data/app/components/coco/app/elements/seamless_textarea/seamless_textarea.rb +5 -20
  28. data/app/components/coco/base/button/button.css +3 -3
  29. data/app/components/coco/base/button/button.js +1 -3
  30. data/app/components/coco/base/image_uploader/image_uploader.html.erb +3 -2
  31. data/app/components/coco/base/image_uploader/image_uploader.rb +2 -12
  32. data/app/components/coco/book/blocks/slides/media_slide/media_slide.css +1 -1
  33. data/app/components/coco/component.rb +1 -0
  34. data/app/components/coco/concerns/has_name.rb +1 -1
  35. data/app/components/coco/concerns/translatable.rb +18 -0
  36. data/config/credentials/production.key +1 -0
  37. data/config/credentials/production.yml.enc +1 -0
  38. data/config/icons.json +886 -0
  39. data/config/locales/coco.en.yml +22 -0
  40. data/config/tailwind.app.config.cjs +21 -0
  41. data/config/tailwind.base.config.cjs +79 -0
  42. data/config/tailwind.book.config.cjs +20 -0
  43. data/config/tokens.cjs +229 -0
  44. data/config/utils.cjs +16 -0
  45. data/lib/coco.rb +1 -1
  46. metadata +13 -6
  47. data/app/assets/build/coco/app.dev.css +0 -3427
  48. data/app/assets/build/coco/app.dev.js +0 -24891
  49. data/app/assets/build/coco/book.dev.css +0 -992
  50. data/app/assets/build/coco/book.dev.js +0 -19840
@@ -893,8 +893,11 @@ select {
893
893
  font-size: var(--text-font-size);
894
894
  }
895
895
  [data-coco][data-component="book-media-slide"] img {
896
+ height: 100%;
896
897
  width: 100%;
897
898
  border-radius: 0.375rem;
899
+ -o-object-fit: contain;
900
+ object-fit: contain;
898
901
  }
899
902
  .static {
900
903
  position: static;
@@ -11636,6 +11636,40 @@ ${t4.join("\n")}`);
11636
11636
  // ../../../node_modules/@hotwired/turbo-rails/app/javascript/turbo/index.js
11637
11637
  addEventListener("turbo:before-fetch-request", encodeMethodIntoRequestBody);
11638
11638
 
11639
+ // base/polyfills/request-submit.js
11640
+ (function(prototype) {
11641
+ if (typeof prototype.requestSubmit == "function")
11642
+ return;
11643
+ prototype.requestSubmit = function(submitter) {
11644
+ if (submitter) {
11645
+ validateSubmitter(submitter, this);
11646
+ submitter.click();
11647
+ } else {
11648
+ submitter = document.createElement("input");
11649
+ submitter.type = "submit";
11650
+ submitter.hidden = true;
11651
+ this.appendChild(submitter);
11652
+ submitter.click();
11653
+ this.removeChild(submitter);
11654
+ }
11655
+ };
11656
+ function validateSubmitter(submitter, form) {
11657
+ submitter instanceof HTMLElement || raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
11658
+ submitter.type == "submit" || raise(TypeError, "The specified element is not a submit button");
11659
+ submitter.form == form || raise(
11660
+ DOMException,
11661
+ "The specified element is not owned by this form element",
11662
+ "NotFoundError"
11663
+ );
11664
+ }
11665
+ function raise(errorConstructor, message, name) {
11666
+ throw new errorConstructor(
11667
+ "Failed to execute 'requestSubmit' on 'HTMLFormElement': " + message + ".",
11668
+ name
11669
+ );
11670
+ }
11671
+ })(HTMLFormElement.prototype);
11672
+
11639
11673
  // ../../../node_modules/alpinejs/dist/module.esm.js
11640
11674
  var flushPending = false;
11641
11675
  var flushing = false;
@@ -19579,10 +19613,8 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
19579
19613
  this.collapsed = false;
19580
19614
  return;
19581
19615
  }
19582
- this.setTooltipText();
19583
19616
  this.$root.setAttribute("data-collapsed", collapsed ? "true" : "false");
19584
19617
  });
19585
- this.$watch("$options.state", () => this.setTooltipText());
19586
19618
  },
19587
19619
  setTooltipText() {
19588
19620
  if (this.disabled) {
@@ -19685,6 +19717,7 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
19685
19717
  root: {
19686
19718
  "x-options": "options",
19687
19719
  "x-tooltip": "tooltipText",
19720
+ "x-effect": "setTooltipText",
19688
19721
  "@confirmation:confirm": "approveAndRun",
19689
19722
  "@confirmation:cancel": "cancelConfirmation",
19690
19723
  "@dropdown:show": "setState('active')",
@@ -19802,14 +19835,14 @@ ${expression ? 'Expression: "' + expression + '"\n\n' : ""}`, el);
19802
19835
  };
19803
19836
  });
19804
19837
 
19805
- // import-glob:/home/runner/work/coco/coco/app/assets/js/base|@baseComponents/**/*.js
19838
+ // import-glob:/Users/mark/Projects/coveragebook/coco/app/assets/js/base|@baseComponents/**/*.js
19806
19839
  var modules = [button_exports, dropdown_exports, icon_exports, image_uploader_exports];
19807
19840
  var __default = modules;
19808
19841
 
19809
19842
  // base/components.js
19810
19843
  var components_default = registerComponents(__default);
19811
19844
 
19812
- // import-glob:/home/runner/work/coco/coco/app/assets/js/book|@bookComponents/**/*.js
19845
+ // import-glob:/Users/mark/Projects/coveragebook/coco/app/assets/js/book|@bookComponents/**/*.js
19813
19846
  var modules2 = [];
19814
19847
  var __default2 = modules2;
19815
19848
 
@@ -0,0 +1,39 @@
1
+ (function (prototype) {
2
+ if (typeof prototype.requestSubmit == "function") return;
3
+
4
+ prototype.requestSubmit = function (submitter) {
5
+ if (submitter) {
6
+ validateSubmitter(submitter, this);
7
+ submitter.click();
8
+ } else {
9
+ submitter = document.createElement("input");
10
+ submitter.type = "submit";
11
+ submitter.hidden = true;
12
+ this.appendChild(submitter);
13
+ submitter.click();
14
+ this.removeChild(submitter);
15
+ }
16
+ };
17
+
18
+ function validateSubmitter(submitter, form) {
19
+ submitter instanceof HTMLElement ||
20
+ raise(TypeError, "parameter 1 is not of type 'HTMLElement'");
21
+ submitter.type == "submit" ||
22
+ raise(TypeError, "The specified element is not a submit button");
23
+ submitter.form == form ||
24
+ raise(
25
+ DOMException,
26
+ "The specified element is not owned by this form element",
27
+ "NotFoundError"
28
+ );
29
+ }
30
+
31
+ function raise(errorConstructor, message, name) {
32
+ throw new errorConstructor(
33
+ "Failed to execute 'requestSubmit' on 'HTMLFormElement': " +
34
+ message +
35
+ ".",
36
+ name
37
+ );
38
+ }
39
+ })(HTMLFormElement.prototype);
@@ -2,6 +2,8 @@ import "container-query-polyfill";
2
2
  import "alpine-turbo-drive-adapter";
3
3
  import * as Turbo from "@hotwired/turbo-rails";
4
4
 
5
+ import "./polyfills/request-submit";
6
+
5
7
  import Alpine from "@libs/alpine";
6
8
 
7
9
  document.addEventListener("DOMContentLoaded", () => {
@@ -2,4 +2,12 @@ function stripExtension(filename) {
2
2
  return filename.substring(0, filename.lastIndexOf(".")) || filename;
3
3
  }
4
4
 
5
- export { stripExtension };
5
+ function dirname(path) {
6
+ return path ? path.match(/.*\//) : path;
7
+ }
8
+
9
+ function basename(path) {
10
+ return path ? path.replace(/.*\//, "") : path;
11
+ }
12
+
13
+ export { stripExtension, dirname, basename };
@@ -1,8 +1,13 @@
1
- import { toBlob } from "html-to-image";
1
+ import html2canvas from "html2canvas";
2
2
  import { stripExtension } from "./path";
3
3
  import { isObject } from "./lang";
4
4
  import { blobToFile } from "./file";
5
5
 
6
+ const screenshotDefaultOptions = {
7
+ useCORS: true,
8
+ logging: false,
9
+ };
10
+
6
11
  function generateScreenshotFilename(name = null, ext = null) {
7
12
  name = [stripExtension(name || "screenshot"), new Date().getTime()].join("-");
8
13
  return ext ? `${name}.${ext}` : name;
@@ -16,11 +21,22 @@ async function captureElementScreenshot(el, filename, opts = {}) {
16
21
  opts = filename;
17
22
  }
18
23
 
19
- filename = generateScreenshotFilename(filename, "jpg");
24
+ const screenshotFilename = generateScreenshotFilename(filename, "jpg");
25
+ const screenshotOptions = {
26
+ ...screenshotDefaultOptions,
27
+ ...opts,
28
+ };
20
29
 
21
- const imageBlob = await toBlob(el, opts);
30
+ const canvas = await html2canvas(el, screenshotOptions);
31
+ const imageBlob = await new Promise((resolve, reject) => {
32
+ canvas.toBlob((result) => {
33
+ return result
34
+ ? resolve(result)
35
+ : reject(new Error("Screenshot could not be created"));
36
+ });
37
+ });
22
38
 
23
- return blobToFile(imageBlob, filename, "image/jpg");
39
+ return blobToFile(imageBlob, screenshotFilename, "image/jpg");
24
40
  }
25
41
 
26
42
  export { captureElementScreenshot };
@@ -8,8 +8,12 @@
8
8
  @apply mb-4 xl:mb-6;
9
9
  }
10
10
 
11
+ .editor-form form {
12
+ @apply hidden;
13
+ }
14
+
11
15
  .editor-slide {
12
- @apply shadow-lg;
16
+ @apply shadow-md;
13
17
 
14
18
  textarea {
15
19
  @apply flex-none w-full;
@@ -20,16 +24,12 @@
20
24
  @apply cursor-not-allowed;
21
25
  }
22
26
 
23
- .editor-thumbnail-file {
24
- @apply hidden pointer-events-none;
25
- }
26
-
27
- .editor-screenshot-wrapper {
27
+ .editor-thumbnail-wrapper {
28
28
  @apply w-0 overflow-hidden relative;
29
29
  }
30
30
 
31
- .editor-screenshot {
32
- @apply absolute w-[800px] top-0 z-[-1000] pointer-events-none;
31
+ .editor-thumbnail {
32
+ @apply absolute w-[500px] top-0 z-[-1000] pointer-events-none;
33
33
  }
34
34
 
35
35
  .editor-icon-bg-color {
@@ -37,9 +37,6 @@
37
37
  stroke-width: 2px;
38
38
  }
39
39
 
40
- .editor-icon-text-color {
41
- }
42
-
43
40
  [data-component="app-seamless-textarea"] {
44
41
  textarea {
45
42
  @apply outline outline-1 outline-transparent outline-offset-0;
@@ -1,222 +1,169 @@
1
1
  <%= render component_tag(x: {
2
2
  data: x_data("appSlideEditor", alpine_props),
3
- ":class": "{ready}",
4
- "@direct-upload:error": "directUploadError",
3
+ ":class": "{ready}"
5
4
  }) do %>
6
- <%= form_with(**form_args(
7
- class: "editor-form",
8
- "x-ref": "form",
9
- "@turbo:submit-end": "submitEnd",
10
- "@turbo:fetch-request-error": "submitError",
11
- "@direct-uploads:end": "save"
12
- )) do |f| %>
13
- <%= f.hidden_field(layout_name, **layout_options, "x-model": "layout") if layout? %>
14
- <%= f.file_field(thumbnail_name, accept: "image/*", class: "editor-thumbnail-file", "x-ref": "thumbnail") if thumbnail? %>
15
-
16
- <div class="editor-toolbar">
17
- <%= coco_toolbar do |toolbar| %>
18
- <% toolbar.with_section do |section| %>
19
- <% if bg_color? %>
20
- <% section.with_color_picker_button(
21
- input_name: bg_color_name,
22
- selected: bg_color_value,
23
- form_builder: f,
24
- wrapper: {
25
- data: {
26
- role: "bg-color-picker"
27
- },
28
- x: {
29
- modelable: "selectedColor",
30
- model: "bgColor"
31
- }
5
+ <div class="editor-toolbar">
6
+ <%= coco_toolbar do |toolbar| %>
7
+ <% toolbar.with_section do |section| %>
8
+ <% if bg_color? %>
9
+ <% section.with_color_picker_button(
10
+ **@bg_color_options.to_h,
11
+ wrapper: {
12
+ data: {
13
+ role: "bg-color-picker"
14
+ },
15
+ x: {
16
+ "@color-picker:change": "bgColor = $event.detail.color"
32
17
  }
33
- ) do |button| %>
34
- <% button.with_picker %>
35
- <% button.with_text { "Background color" } %>
36
- <% button.with_icon do %>
37
- <svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" class="editor-icon-bg-color">
38
- <path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z"></path>
39
- </svg>
40
- <% end %>
18
+ }
19
+ ) do |button| %>
20
+ <% button.with_picker %>
21
+ <% button.with_text { tt("bg_color_picker.label") } %>
22
+ <% button.with_icon do %>
23
+ <svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg" class="editor-icon-bg-color">
24
+ <path d="M12 22a7 7 0 0 0 7-7c0-2-1-3.9-3-5.5s-3.5-4-4-6.5c-.5 2.5-2 4.9-4 6.5C6 11.1 5 13 5 15a7 7 0 0 0 7 7z"></path>
25
+ </svg>
41
26
  <% end %>
42
27
  <% end %>
43
-
44
- <% if bg_image? %>
45
- <% section.with_image_picker_button(
46
- icon: :image_plus,
47
- input_name: bg_image_name,
48
- src: bg_image_value,
49
- direct_upload: bg_image_options[:direct_upload],
50
- form_builder: f,
51
- wrapper: {
52
- data: {
53
- role: "bg-image-picker"
54
- },
55
- x: {
56
- modelable: "selectedImage",
57
- model: "bgImage"
58
- }
59
- }
60
- ) do |button| %>
61
- <% button.with_picker do |picker| %>
62
- <% picker.with_blank_state_text do %>
63
- Drag a jpg, png or gif onto the slide area...or...
64
- <% end %>
65
- <% end %>
28
+ <% end %>
66
29
 
67
- Background image
30
+ <% if bg_image? %>
31
+ <% section.with_image_picker_button(
32
+ icon: :image_plus,
33
+ src: bg_image_src,
34
+ wrapper: {
35
+ data: {
36
+ role: "bg-image-picker"
37
+ },
38
+ x: {
39
+ "@image-picker:change": "bgImage = $event.detail.image"
40
+ }
41
+ }
42
+ ) do |button| %>
43
+ <% button.with_picker do |picker| %>
44
+ <% picker.with_blank_state_text { tt("bg_image_picker.blank_state") } %>
68
45
  <% end %>
46
+
47
+ <%= tt("bg_image_picker.label") %>
69
48
  <% end %>
49
+ <% end %>
70
50
 
71
- <% if text_color? %>
72
- <% section.with_color_picker_button(
73
- input_name: text_color_name,
74
- selected: text_color_value,
75
- form_builder: f,
76
- wrapper: {
77
- data: {
78
- role: "text-color-picker"
79
- },
80
- x: {
81
- modelable: "selectedColor",
82
- model: "textColor"
83
- }
51
+ <% if text_color? %>
52
+ <% section.with_color_picker_button(
53
+ **@text_color_options.to_h,
54
+ wrapper: {
55
+ data: {
56
+ role: "text-color-picker"
57
+ },
58
+ x: {
59
+ "@color-picker:change": "textColor = $event.detail.color"
84
60
  }
85
- ) do |button| %>
86
- <% button.with_picker %>
87
- <% button.with_text { "Text color" } %>
88
- <% button.with_icon do %>
89
- <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="editor-icon-text-color">
90
- <path d="M4.99994 13.3333L9.99994 3.33334L14.9999 13.3333" stroke="#111827" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
91
- <path d="M6.66656 10H13.3332" stroke="#111827" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
92
- <rect x="2.25" y="15.25" width="15.5" height="2.5" rx="1.25" fill="currentColor" stroke="#111827" stroke-width="0.5"/>
93
- </svg>
94
- <% end %>
61
+ }
62
+ ) do |button| %>
63
+ <% button.with_picker %>
64
+ <% button.with_text { tt("text_color_picker.label") } %>
65
+ <% button.with_icon do %>
66
+ <svg viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg">
67
+ <path d="M4.99994 13.3333L9.99994 3.33334L14.9999 13.3333" stroke="#111827" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
68
+ <path d="M6.66656 10H13.3332" stroke="#111827" stroke-width="1.66667" stroke-linecap="round" stroke-linejoin="round"/>
69
+ <rect x="2.25" y="15.25" width="15.5" height="2.5" rx="1.25" fill="currentColor" stroke="#111827" stroke-width="0.5"/>
70
+ </svg>
95
71
  <% end %>
96
72
  <% end %>
97
73
  <% end %>
74
+ <% end %>
98
75
 
99
- <% toolbar.with_section do |section| %>
100
- <% section.with_button(
101
- icon: :undo_2,
102
- click: "history.undo",
103
- tooltip: "Undo",
104
- theme: :text_secondary,
105
- disabled: true,
106
- x: { "effect": "disabled = !history.undoable" },
107
- data: {
108
- role: "undo"
109
- }
110
- ) %>
76
+ <% toolbar.with_section do |section| %>
77
+ <% section.with_button(
78
+ icon: :undo_2,
79
+ click: "history.undo",
80
+ tooltip: tt("undo_button.tooltip"),
81
+ theme: :text_secondary,
82
+ disabled: true,
83
+ x: { "effect": "disabled = saving || !history.undoable" },
84
+ data: {
85
+ role: "undo"
86
+ }
87
+ ) %>
111
88
 
112
- <% section.with_button(
113
- icon: :redo_2,
114
- click: "history.redo",
115
- tooltip: "Redo",
116
- theme: :text_secondary,
117
- disabled: true,
118
- x: { "effect": "disabled = !history.redoable" },
119
- data: {
120
- role: "redo"
121
- }
122
- ) %>
123
-
124
- <% section.with_button(
125
- theme: :positive,
126
- icon: :check,
127
- collapsible: false,
128
- type: "submit",
129
- disabled: true,
130
- data: {
131
- role: "save"
132
- },
133
- x: {
134
- "@click.stop.prevent": "if(!loading) save()",
135
- "effect": "loading = saving; disabled = !history.undoable",
136
- ":class": "{'editor-saving': saving}"
137
- }
138
- ) do |button| %>
139
- <% button.with_state(:loading, text: "Saving...") %>
140
- Save
141
- <% end %>
89
+ <% section.with_button(
90
+ icon: :redo_2,
91
+ click: "history.redo",
92
+ tooltip: tt("redo_button.tooltip"),
93
+ theme: :text_secondary,
94
+ disabled: true,
95
+ x: { "effect": "disabled = saving || !history.redoable" },
96
+ data: {
97
+ role: "redo"
98
+ }
99
+ ) %>
100
+
101
+ <% section.with_button(
102
+ theme: :positive,
103
+ icon: :check,
104
+ collapsible: false,
105
+ type: "submit",
106
+ disabled: true,
107
+ data: {
108
+ role: "save"
109
+ },
110
+ x: {
111
+ "@click.stop.prevent": "if(!loading) save()",
112
+ "effect": "loading = saving; disabled = !history.undoable",
113
+ ":class": "{'editor-saving': saving}"
114
+ }
115
+ ) do |button| %>
116
+ <% button.with_state(:loading, text: tt("save_button.saving_label")) %>
117
+ <%= tt("save_button.label") %>
142
118
  <% end %>
143
- <% end %>
144
- </div>
145
-
146
- <%# Editable slide %>
147
- <div
148
- class="editor-slide"
149
- x-ref="slide"
150
- @dragenter.stop.prevent="dragging = true"
151
- @dragover.stop.prevent="dragging = true"
152
- @dragleave.stop.prevent="dragging = false"
153
- @drop.stop.prevent="handleImageDrop($event)">
154
- <%= coco_editable_slide(
155
- **slide_args,
156
- data: {
157
- role: "slide"
158
- },
159
- x: {
160
- ":style": "slideStyles",
161
- ":class": "slideClasses",
162
- }
163
- ) do |slide| %>
164
- <% if title? %>
165
- <% slide.with_title do %>
166
- <%= coco_seamless_textarea(
167
- name: title_name,
168
- multiline: false,
169
- form_builder: f,
170
- **title_options,
171
- wrapper: {
172
- data: {
173
- role: "title-editor"
174
- },
175
- x: {
176
- modelable: "value",
177
- model: "title"
178
- }
179
- }
180
- ) %>
181
- <% end %>
119
+ <% end %>
120
+ <% end %>
121
+ </div>
122
+
123
+ <%# Editable slide %>
124
+ <div
125
+ class="editor-slide"
126
+ x-ref="slide"
127
+ @dragenter.stop.prevent="dragging = true"
128
+ @dragover.stop.prevent="dragging = true"
129
+ @dragleave.stop.prevent="dragging = false"
130
+ @drop.stop.prevent="handleImageDrop($event)">
131
+ <%= coco_editable_slide(
132
+ **slide_args,
133
+ data: {
134
+ role: "slide"
135
+ },
136
+ x: {
137
+ bind: "slide"
138
+ }
139
+ ) do |slide| %>
140
+ <% if title? %>
141
+ <% slide.with_title do %>
142
+ <%= title %>
182
143
  <% end %>
144
+ <% end %>
183
145
 
184
- <% if text_1? %>
185
- <% slide.with_text_1 do %>
186
- <%= coco_seamless_textarea(
187
- name: text_1_name,
188
- multiline: true,
189
- form_builder: f,
190
- **text_1_options,
191
- wrapper: {
192
- data: {
193
- role: "text-1-editor"
194
- },
195
- x: {
196
- modelable: "value",
197
- model: "text1"
198
- }
199
- }
200
- ) %>
201
- <% end %>
146
+ <% if text_1? %>
147
+ <% slide.with_text_1 do %>
148
+ <%= text_1 %>
202
149
  <% end %>
203
150
  <% end %>
204
- </div>
205
-
206
- <% if content %>
207
- <%# Any additional fields required %>
208
- <div class="editor-additional-content">
209
- <%= content %>
210
- </div>
211
151
  <% end %>
212
-
213
- <% end %>
214
-
215
- <% if thumbnail? %>
152
+ </div>
153
+
154
+ <% if generate_thumbnail? %>
216
155
  <%# Slide used for thumbnail generation %>
217
- <div class="editor-screenshot-wrapper">
218
- <div x-ref="screenshot" class="editor-screenshot">
219
- <%= coco_editable_slide(**slide_args) do |slide| %>
156
+ <div class="editor-thumbnail-wrapper">
157
+ <div x-ref="thumbnail" class="editor-thumbnail">
158
+ <%= coco_editable_slide(
159
+ **slide_args,
160
+ data: {
161
+ role: "slide-thumbnail"
162
+ },
163
+ x: {
164
+ bind: "slide"
165
+ }
166
+ ) do |slide| %>
220
167
  <% if title? %>
221
168
  <% slide.with_title do %>
222
169
  <span x-text="title"></span>
@@ -232,4 +179,16 @@
232
179
  </div>
233
180
  </div>
234
181
  <% end %>
182
+
183
+ <% if content %>
184
+ <div
185
+ class="editor-form"
186
+ x-ref="form"
187
+ @turbo:submit-end="submitSuccess"
188
+ @turbo:fetch-request-error="submitError"
189
+ @direct-upload:error="directUploadError"
190
+ @direct-uploads:end="save">
191
+ <%= content %>
192
+ </div>
193
+ <% end %>
235
194
  <% end %>