hyde_admin 0.0.13 → 0.0.14

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.
@@ -1,58 +1,67 @@
1
- <h2><%= EscapeUtils.escape_html t.send(@type_file).capitalize %></h2>
1
+ <div id="preview-wrapper">
2
+ <div id="editor-panel">
3
+ <h2><%= CGI.escapeHTML t.send(@type_file).capitalize %></h2>
2
4
 
3
5
  <% file_params = (!@new_record ? "?file=#{@file}" : "") %>
4
6
 
5
- <form action="/<%= @type_file %><%= file_params %>" method="post">
7
+ <form action="/<%= @type_file %><%= file_params %>" method="post" data-autosave-key="hyde_autosave:<%= @new_record ? "new:#{@type_file}" : @file %>">
8
+ <div id="autosave-banner" class="alert alert-warning d-none mb-3" role="alert">
9
+ <i class="fa-solid fa-clock-rotate-left"></i>
10
+ <span id="autosave-banner-text"></span>
11
+ <button type="button" class="btn btn-sm btn-warning ms-2" id="autosave-restore">Restore</button>
12
+ <button type="button" class="btn btn-sm btn-outline-secondary ms-2" id="autosave-discard">Discard</button>
13
+ </div>
6
14
  <% if !@new_record %>
7
15
  <div class="mb-3">
8
- <label for="i-path" class="form-label"><%= EscapeUtils.escape_html t.path.capitalize %></label>
16
+ <label for="i-path" class="form-label"><%= CGI.escapeHTML t.path.capitalize %></label>
9
17
  <div class="input-group">
10
18
  <span class="input-group-text">
11
- <i class="fas fa-calendar-alt" id="btn-date-path" title="<%= EscapeUtils.escape_html t.change_date_path %>"></i>
19
+ <i class="fas fa-calendar-alt" id="btn-date-path" title="<%= CGI.escapeHTML t.change_date_path %>"></i>
12
20
  </span>
13
21
  <span class="input-group-text">
14
- <i class="fas fa-sync-alt" id="btn-title-path" title="<%= EscapeUtils.escape_html t.change_title_path %>"></i>
22
+ <i class="fas fa-sync-alt" id="btn-title-path" title="<%= CGI.escapeHTML t.change_title_path %>"></i>
15
23
  </span>
16
- <input type="text" class="form-control" value="<%= @file %>" name="new_file" id="i-path">
24
+ <input type="text" class="form-control" value="<%= CGI.escapeHTML(@file) %>" name="new_file" id="i-path">
17
25
  </div>
18
- <div id="i-path-help" class="form-text"><%= EscapeUtils.escape_html t.help_path %></div>
26
+ <div id="i-path-help" class="form-text"><%= CGI.escapeHTML t.help_path %></div>
19
27
  </div>
20
28
  <% end %>
21
29
 
22
30
  <div class="mb-3">
23
- <label for="i-title" class="form-label"><%= EscapeUtils.escape_html t.title.capitalize %></label>
24
- <input type="text" value="<%= @headers.delete('title') %>" class="form-control" name="title" id="i-title">
31
+ <label for="i-title" class="form-label"><%= CGI.escapeHTML t.title.capitalize %></label>
32
+ <input type="text" value="<%= CGI.escapeHTML(@headers.delete('title').to_s) %>" class="form-control" name="title" id="i-title">
25
33
  </div>
26
34
  <div class="mb-3">
27
- <label for="i-date" class="form-label"><%= EscapeUtils.escape_html t.date.capitalize %></label>
35
+ <label for="i-date" class="form-label"><%= CGI.escapeHTML t.date.capitalize %></label>
28
36
  <div class="input-group">
29
37
  <span class="input-group-text">
30
- <i class="fas fa-calendar-day" id="btn-date-today" title="<%= EscapeUtils.escape_html t.set_date_today %>"></i>
38
+ <i class="fas fa-calendar-day" id="btn-date-today" title="<%= CGI.escapeHTML t.set_date_today %>"></i>
31
39
  </span>
32
- <input type="text" value="<%= @headers.delete('date') || Time.now.strftime('%Y-%m-%d %H:%M:%S %z') %>" class="form-control" name="date" id="i-date">
40
+ <input type="text" value="<%= CGI.escapeHTML((@headers.delete('date') || Time.now.strftime('%Y-%m-%d %H:%M:%S %z')).to_s) %>" class="form-control" name="date" id="i-date">
33
41
  </div>
34
42
  </div>
35
43
  <div class="mb-3">
36
- <label for="i-tags" class="form-label"><%= EscapeUtils.escape_html t.tags.capitalize %></label>
37
- <input type="text" value="<%= Mid.extract_tags(@headers.delete('tags')).join(',') %>" class="form-control" name="tags" id="i-tags">
38
- <div id="i-tags-help" class="form-text"><%= EscapeUtils.escape_html t.help_tags.capitalize %></div>
44
+ <label for="i-tags" class="form-label"><%= CGI.escapeHTML t.tags.capitalize %></label>
45
+ <input type="text" value="<%= CGI.escapeHTML(Mid.extract_tags(@headers.delete('tags')).join(',')) %>" class="form-control" name="tags" id="i-tags">
46
+ <div id="i-tags-help" class="form-text"><%= CGI.escapeHTML t.help_tags.capitalize %></div>
39
47
  </div>
40
48
 
41
49
  <% if @type_file == 'drafts' %>
42
50
  <div class="mb-3 form-check">
43
51
  <input type="checkbox" class="form-check-input" name="publish" value="publish" id="i-publish">
44
- <label class="form-check-label" for="i-publish"><%= EscapeUtils.escape_html t.publish.capitalize %></label>
52
+ <label class="form-check-label" for="i-publish"><%= CGI.escapeHTML t.publish.capitalize %></label>
45
53
  </div>
46
54
  <% end %>
47
55
 
48
56
  <% selected_layout = @headers.delete('layout') %>
49
57
  <% selected_layout = @hyde_parameters['default_layout'] if selected_layout.nil? || selected_layout.empty? %>
58
+ <% available_layouts = Dir.glob(File.join(Dir.pwd, '_layouts', '*')).map { |f| File.basename(f, File.extname(f)) } %>
59
+ <% available_layouts.unshift(selected_layout) unless selected_layout.nil? || available_layouts.include?(selected_layout) %>
50
60
  <% if @hyde_parameters['display_layout'].to_s == 'true' %>
51
61
  <div class="mb-3">
52
- <label for="i-layout" class="form-label"><%= EscapeUtils.escape_html t.layout.capitalize %></label>
62
+ <label for="i-layout" class="form-label"><%= CGI.escapeHTML t.layout.capitalize %></label>
53
63
  <select class="form-select" aria-label="Choice layout" name="layout">
54
- <% Dir.glob(File.join(Dir.pwd, '_layouts', '*')).each do |f| %>
55
- <% layout_loop = File.basename(f, File.extname(f)) %>
64
+ <% available_layouts.each do |layout_loop| %>
56
65
  <option <%= (layout_loop == selected_layout ? 'selected' : '') %> value="<%= layout_loop %>"><%= layout_loop %></option>
57
66
  <% end %>
58
67
  </select>
@@ -65,7 +74,7 @@
65
74
  <% selected_format = ".#{@hyde_parameters['default_format']}" if selected_format.empty? %>
66
75
  <% if @hyde_parameters['display_format'].to_s == 'true' %>
67
76
  <div class="mb-3">
68
- <label for="i-format" class="form-label"><%= EscapeUtils.escape_html t.format.capitalize %></label>
77
+ <label for="i-format" class="form-label"><%= CGI.escapeHTML t.format.capitalize %></label>
69
78
  <select class="form-select" id="select-format" aria-label="Choice format" name="format">
70
79
  <% { 'html' => 'html', 'markdown' => 'md' }.each do |k, v| %>
71
80
  <option <%= (selected_format == ".#{v}" ? 'selected' : '') %> value="<%= v %>"><%= k %></option>
@@ -81,78 +90,166 @@
81
90
 
82
91
  <% @headers.each do |k,v| %>
83
92
  <div class="mb-3">
84
- <label for="i-<%= k %>" class="form-label"><%= k %></label>
85
- <input type="text" value="<%= v %>" class="form-control" name="<%= k %>" id="i-<%= k %>">
93
+ <label for="i-<%= CGI.escapeHTML(k) %>" class="form-label"><%= CGI.escapeHTML(k) %></label>
94
+ <input type="text" value="<%= CGI.escapeHTML(v.to_s) %>" class="form-control" name="<%= CGI.escapeHTML(k) %>" id="i-<%= CGI.escapeHTML(k) %>">
86
95
  </div>
87
96
  <% end %>
88
97
 
89
- <a href="#" class="btn btn-secondary mb-2" id="add-header"><i class="fas fa-plus"></i> <%= EscapeUtils.escape_html t.add_header.capitalize %></a>
98
+ <a href="#" class="btn btn-secondary mb-2" id="add-header"><i class="fas fa-plus"></i> <%= CGI.escapeHTML t.add_header.capitalize %></a>
90
99
  <div class="mb-3 custom-headers"></div>
91
100
 
92
101
  <div class="mb-3 template-header" style="display: none">
93
102
  <div class="row">
94
- <div class="col-sm-4"><input type="text" class="col-sm-6 form-control header-name" name="" placeholder="<%= EscapeUtils.escape_html t.header_name.capitalize %>"></div>
95
- <div class="col-sm-8"><input type="text" class="col-sm-6 form-control header-value" name="" placeholder="<%= EscapeUtils.escape_html t.header_value.capitalize %>"></div>
103
+ <div class="col-sm-4"><input type="text" class="col-sm-6 form-control header-name" name="" placeholder="<%= CGI.escapeHTML t.header_name.capitalize %>"></div>
104
+ <div class="col-sm-8"><input type="text" class="col-sm-6 form-control header-value" name="" placeholder="<%= CGI.escapeHTML t.header_value.capitalize %>"></div>
96
105
  </div>
97
106
  </div>
98
107
 
99
108
  <script type="text/javascript" charset="utf-8">
100
109
  window.custom_headers_i = 0;
101
- $(document).on('click', '#add-header', function(){
102
- $('.template-header').clone().appendTo('.custom-headers');
103
- let elt = $('.custom-headers .template-header');
104
- elt.find('.header-name').attr('name', 'header' + window.custom_headers_i + '[]');
105
- elt.find('.header-value').attr('name', 'header' + window.custom_headers_i + '[]');
106
- elt.show();
107
- elt.removeClass('template-header');
110
+ document.addEventListener('click', function(e){
111
+ if(!e.target.closest('#add-header')) return;
112
+ e.preventDefault();
113
+ var tpl = document.querySelector('.template-header');
114
+ var clone = tpl.cloneNode(true);
115
+ clone.querySelector('.header-name').setAttribute('name', 'header' + window.custom_headers_i + '[]');
116
+ clone.querySelector('.header-value').setAttribute('name', 'header' + window.custom_headers_i + '[]');
117
+ clone.style.display = '';
118
+ clone.classList.remove('template-header');
119
+ document.querySelector('.custom-headers').appendChild(clone);
108
120
  window.custom_headers_i += 1;
109
- return false;
110
121
  });
111
122
  </script>
112
123
 
113
124
  <div class="mb-3">
114
- <label for="i-content" class="form-label"><%= EscapeUtils.escape_html t.content.capitalize %> <a href="https://jekyllrb.com/docs/liquid/" class="text-secondary" target="_blank"><i class="fas fa-question-circle"></i></a></label>
125
+ <label for="i-content" class="form-label"><%= CGI.escapeHTML t.content.capitalize %> <a href="https://jekyllrb.com/docs/liquid/" class="text-secondary" target="_blank"><i class="fas fa-question-circle"></i></a></label>
115
126
 
116
127
  <% path = File.join(Pathname.new(Mid.gem_source_path), 'admin_views', 'editor_html.erb') %>
117
128
  <%= ERB.new(File.read(path)).result(binding) %>
118
129
 
119
- <textarea class="form-control text-editor" id="i-content" rows="3" name="content"><%= @content %></textarea>
130
+ <textarea class="form-control text-editor" id="i-content" rows="3" name="content"><%= CGI.escapeHTML(@content.to_s) %></textarea>
120
131
  </div>
121
- <button type="submit" class="btn btn-primary"><%= EscapeUtils.escape_html t.submit.capitalize %></button>
132
+ <button type="submit" class="btn btn-primary"><%= CGI.escapeHTML t.submit.capitalize %></button>
133
+ <span id="autosave-indicator"><i class="fa-solid fa-check"></i> Saved</span>
122
134
  </form>
123
135
 
124
136
  <script type="text/javascript" charset="utf-8">
125
- $(document).on('click', '#select-format', function(){
126
- let ext_mode = { md: "markdown", html: "htmlmixed"};
127
- console.log($('#select-format').val());
128
- window.myCodeMirror.setOption("mode", ext_mode[$('#select-format').val()]);
129
- window.mode_markdown = ($('#select-format').val() == 'md');
130
- return true;
131
- });
132
- $(document).on('click', '#btn-date-path', function(){
133
- $.post( "/ajax/update_path_date", { date: $('#i-date').val(), path: $('#i-path').val() })
134
- .done(function( data ) {
135
- $('#i-path').val(data);
136
- });
137
- return false;
138
- });
139
- $(document).on('click', '#btn-title-path', function(){
140
- $.post( "/ajax/update_path_title", { title: $('#i-title').val(), path: $('#i-path').val() })
141
- .done(function( data ) {
142
- $('#i-path').val(data);
143
- });
144
- return false;
137
+ document.addEventListener('change', function(e){
138
+ if(!e.target.closest('#select-format')) return;
139
+ var ext_mode = { md: "markdown", html: "htmlmixed"};
140
+ var val = document.getElementById('select-format').value;
141
+ window.myCodeMirror.setOption("mode", ext_mode[val]);
142
+ window.mode_markdown = (val == 'md');
145
143
  });
146
144
 
147
- $(document).on('click', '#btn-date-today', function(){
148
- $.post( "/ajax/update_date_today", {})
149
- .done(function( data ) {
150
- $('#i-date').val(data);
151
- });
152
- return false;
145
+ function postForm(url, params) {
146
+ var body = new URLSearchParams(params);
147
+ return fetch(url, { method: 'POST', body: body }).then(function(r){ return r.text(); });
148
+ }
149
+
150
+ document.addEventListener('click', function(e){
151
+ if(e.target.closest('#btn-date-path')){
152
+ e.preventDefault();
153
+ postForm("/ajax/update_path_date", {
154
+ date: document.getElementById('i-date').value,
155
+ path: document.getElementById('i-path').value
156
+ }).then(function(data){ document.getElementById('i-path').value = data; });
157
+ return;
158
+ }
159
+ if(e.target.closest('#btn-title-path')){
160
+ e.preventDefault();
161
+ postForm("/ajax/update_path_title", {
162
+ title: document.getElementById('i-title').value,
163
+ path: document.getElementById('i-path').value
164
+ }).then(function(data){ document.getElementById('i-path').value = data; });
165
+ return;
166
+ }
167
+ if(e.target.closest('#btn-date-today')){
168
+ e.preventDefault();
169
+ postForm("/ajax/update_date_today", {})
170
+ .then(function(data){ document.getElementById('i-date').value = data; });
171
+ return;
172
+ }
153
173
  });
154
174
 
155
175
  <% path = File.join(Pathname.new(Mid.gem_source_path), 'admin_views', 'editor_js.erb') %>
156
176
  <%= ERB.new(File.read(path)).result(binding) %>
157
177
 
178
+ // Preview functionality
179
+ document.addEventListener('click', function(e) {
180
+ if (e.target.closest('#btn-preview')) {
181
+ e.preventDefault();
182
+ var wrapper = document.getElementById('preview-wrapper');
183
+ var panel = document.getElementById('preview-panel');
184
+
185
+ document.getElementById('waiting').style.display = 'block';
186
+
187
+ var content = window.myCodeMirror ? window.myCodeMirror.getValue() : document.getElementById('i-content').value;
188
+ var layoutEl = document.querySelector('select[name="layout"]') || document.querySelector('input[name="layout"]');
189
+ var formatEl = document.getElementById('select-format') || document.querySelector('input[name="format"]');
190
+
191
+ var body = new URLSearchParams({
192
+ content: content,
193
+ title: (document.getElementById('i-title') || {}).value || '',
194
+ date: (document.getElementById('i-date') || {}).value || '',
195
+ tags: (document.getElementById('i-tags') || {}).value || '',
196
+ layout: layoutEl ? layoutEl.value : '',
197
+ format: formatEl ? formatEl.value : 'md'
198
+ });
199
+
200
+ fetch('/ajax/preview', { method: 'POST', body: body })
201
+ .then(function(r) { return r.text(); })
202
+ .then(function(url) {
203
+ document.getElementById('preview-iframe').src = url;
204
+ panel.classList.remove('d-none');
205
+ wrapper.classList.add('preview-active');
206
+ document.querySelector('main').classList.add('preview-active-main');
207
+ document.getElementById('waiting').style.display = 'none';
208
+ setTimeout(function() {
209
+ if (window.myCodeMirror) window.myCodeMirror.refresh();
210
+ }, 100);
211
+ })
212
+ .catch(function() {
213
+ document.getElementById('waiting').style.display = 'none';
214
+ });
215
+ return;
216
+ }
217
+
218
+ if (e.target.closest('#btn-refresh-preview')) {
219
+ e.preventDefault();
220
+ document.getElementById('btn-preview').click();
221
+ return;
222
+ }
223
+
224
+ if (e.target.closest('#btn-close-preview')) {
225
+ e.preventDefault();
226
+ var wrapper = document.getElementById('preview-wrapper');
227
+ var panel = document.getElementById('preview-panel');
228
+ panel.classList.add('d-none');
229
+ wrapper.classList.remove('preview-active');
230
+ document.querySelector('main').classList.remove('preview-active-main');
231
+ setTimeout(function() {
232
+ if (window.myCodeMirror) window.myCodeMirror.refresh();
233
+ }, 100);
234
+ return;
235
+ }
236
+ });
237
+
158
238
  </script>
239
+ </div><!-- /editor-panel -->
240
+
241
+ <div id="preview-panel" class="d-none">
242
+ <div class="preview-panel-header d-flex align-items-center justify-content-between p-2 bg-light border-bottom">
243
+ <span><i class="fas fa-eye"></i> <%= CGI.escapeHTML t.editor_preview %></span>
244
+ <div>
245
+ <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-refresh-preview" title="<%= CGI.escapeHTML t.preview_refresh %>">
246
+ <i class="fas fa-sync-alt"></i>
247
+ </button>
248
+ <button type="button" class="btn btn-sm btn-outline-danger" id="btn-close-preview" title="<%= CGI.escapeHTML t.preview_close %>">
249
+ <i class="fas fa-times"></i>
250
+ </button>
251
+ </div>
252
+ </div>
253
+ <iframe id="preview-iframe" src="about:blank"></iframe>
254
+ </div>
255
+ </div><!-- /preview-wrapper -->
@@ -1,33 +1,33 @@
1
- <h2><%= EscapeUtils.escape_html t.send(@type_file).capitalize %> &nbsp; <a href="/<%= @type_file %>/new" class="btn btn-secondary btn-sm"><i class="fas fa-plus"></i> <%= EscapeUtils.escape_html t.new.capitalize %></a></h2>
1
+ <h2><%= CGI.escapeHTML t.send(@type_file).capitalize %> &nbsp; <a href="/<%= @type_file %>/new" class="btn btn-secondary btn-sm"><i class="fas fa-plus"></i> <%= CGI.escapeHTML t.new.capitalize %></a></h2>
2
2
 
3
3
  <div class="table-responsive">
4
- <table class="table table-striped table-sm">
4
+ <table class="table table-striped table-sm table-bordered">
5
5
  <tr>
6
6
  <th>
7
- <%= EscapeUtils.escape_html t.file.capitalize %>
7
+ <%= CGI.escapeHTML t.file.capitalize %>
8
8
  </th>
9
9
  <th>
10
- <%= EscapeUtils.escape_html t.edit.capitalize %>
10
+ <%= CGI.escapeHTML t.edit.capitalize %>
11
11
  </th>
12
12
  <th>
13
- <%= EscapeUtils.escape_html t.delete.capitalize %>
13
+ <%= CGI.escapeHTML t.delete.capitalize %>
14
14
  </th>
15
15
  </tr>
16
16
  <% @files.each do |f| %>
17
- <% edit_path = "/#{@type_file}?file=#{f}" %>
17
+ <% edit_path = "/#{@type_file}?file=#{CGI.escape(f)}" %>
18
18
  <tr>
19
19
  <td>
20
20
  <a href="<%= edit_path %>" class="link-dark">
21
- <%= f.gsub(File.join(Dir.pwd, ''),'') %>
21
+ <%= CGI.escapeHTML(f.gsub(File.join(Dir.pwd, ''),'')) %>
22
22
  </a>
23
23
  </td>
24
24
  <td>
25
- <a href="<%= edit_path %>" class="btn btn-default"><i class="fa fa-edit" title="<%= EscapeUtils.escape_html t.edit.capitalize %>"></i></a>
25
+ <a href="<%= edit_path %>" class="btn btn-default"><i class="fa fa-edit" title="<%= CGI.escapeHTML t.edit.capitalize %>"></i></a>
26
26
  </td>
27
27
  <td>
28
- <form method="post" action="/<%= @type_file %>/delete" class="inline form-confirm" data-confirm="<%= EscapeUtils.escape_html t.are_you_sure %>">
28
+ <form method="post" action="/<%= @type_file %>/delete" class="inline form-confirm" data-confirm="<%= CGI.escapeHTML t.are_you_sure %>">
29
29
  <input type="hidden" name="file" value="<%= f %>">
30
- <button type="submit" class="btn btn-default"><i class="fa fa-trash" title="<%= EscapeUtils.escape_html t.delete.capitalize %>"></i></button>
30
+ <button type="submit" class="btn btn-default"><i class="fa fa-trash" title="<%= CGI.escapeHTML t.delete.capitalize %>"></i></button>
31
31
  </form>
32
32
  </td>
33
33
  </tr>
@@ -0,0 +1,42 @@
1
+ <h2><i class="fa-solid fa-magnifying-glass"></i> <%= CGI.escapeHTML t.global_search.capitalize %></h2>
2
+
3
+ <form action="/search" method="get" class="mb-4">
4
+ <div class="input-group">
5
+ <input type="text" name="q" value="<%= CGI.escapeHTML(@query) %>" class="form-control" placeholder="<%= CGI.escapeHTML t.global_search_placeholder %>" autofocus>
6
+ <button class="btn btn-primary" type="submit"><%= CGI.escapeHTML t.search.capitalize %></button>
7
+ </div>
8
+ </form>
9
+
10
+ <% if @query.length >= 2 %>
11
+ <p class="text-muted"><%= @results.size %> <%= CGI.escapeHTML t.global_search_results_for %> "<%= CGI.escapeHTML(@query) %>"</p>
12
+
13
+ <% if @results.empty? %>
14
+ <p><%= CGI.escapeHTML t.global_search_no_results.capitalize %></p>
15
+ <% else %>
16
+ <% type_icons = { 'posts' => 'fa-file-lines', 'pages' => 'fa-file', 'drafts' => 'fa-file-pen', 'files' => 'fa-copy' } %>
17
+ <% @results.group_by { |r| r[:type] }.each do |type, results| %>
18
+ <h5 class="mt-4 mb-3"><i class="fa-solid <%= type_icons[type] || 'fa-file' %>"></i> <%= CGI.escapeHTML t.send(type).capitalize %></h5>
19
+ <div class="list-group mb-3">
20
+ <% results.each do |r| %>
21
+ <a href="<%= r[:edit_url] %>" class="list-group-item list-group-item-action">
22
+ <div class="d-flex justify-content-between align-items-start">
23
+ <div>
24
+ <strong><%= CGI.escapeHTML(r[:title].to_s) %></strong>
25
+ <br><small class="text-muted"><%= CGI.escapeHTML(r[:relative]) %></small>
26
+ <% if r[:tags] && !r[:tags].empty? %>
27
+ <br>
28
+ <% r[:tags].split(',').each do |tag| %>
29
+ <span class="badge bg-secondary"><%= CGI.escapeHTML(tag.strip) %></span>
30
+ <% end %>
31
+ <% end %>
32
+ <% if r[:excerpt] %>
33
+ <br><small class="text-muted fst-italic"><%= CGI.escapeHTML(r[:excerpt]) %></small>
34
+ <% end %>
35
+ </div>
36
+ </div>
37
+ </a>
38
+ <% end %>
39
+ </div>
40
+ <% end %>
41
+ <% end %>
42
+ <% end %>
@@ -33,16 +33,16 @@
33
33
  <%= t.pictures_has_been_uploaded.capitalize %> : <%= @filenames.join(', ') %>
34
34
  <% end %>
35
35
 
36
- <form enctype="multipart/form-data" method="post" action="/upload_image" class="row g-3">
37
- <div class="col-10">
36
+ <form enctype="multipart/form-data" method="post" action="/upload_image">
37
+ <div class="input-group mb-3">
38
38
  <input type="file" name="files[]" multiple class="form-control">
39
- </div>
40
- <div class="col-2">
41
39
  <button class="btn btn-primary"><%= t.upload.capitalize %></button>
42
40
  </div>
41
+
42
+ <a class="btn btn-secondary btn-return-to-editor"><%= t.quit.capitalize %></a>
43
43
  </form>
44
44
 
45
- <button class="btn btn-secondary btn-return-to-editor fixed-bottom mb-4 ms-4"><%= t.quit.capitalize %></button>
45
+
46
46
  </div>
47
47
  </div>
48
48
  </body>