fix: file browser deletion bug + parent directory

Underscore matters! - fixed both bugs for the browser Extra: - style for toasts quickfix generic modals

Alessandro committed Nov 28, 2024 at 01:13 UTC bb980ea6b93a074b24cf86c54b0be69596b34cb1
9 files changed +154 -73
python/helpers/file_browser.py
+10 -2
@@ -133,8 +133,16 @@ class FileBrowser:
133 # Get parent directory path if not at root
134 parent_path = ""
135 if current_path:
136 - parent = (Path(current_path).parent)
137 - parent_path = str(parent) if parent != Path(".") else ""
136 + try:
137 + # Get the absolute path of current directory
138 + current_abs = (self.base_dir / current_path).resolve()
139 +
140 + # parent_path is empty only if we're already at root
141 + if str(current_abs) != str(self.base_dir):
142 + parent_path = str(Path(current_path).parent)
143 +
144 + except Exception as e:
145 + parent_path = ""
146
147 return {
148 "entries": all_entries,
webui/css/file_browser.css
+8 -2
@@ -123,8 +123,14 @@
123 background-color: var(--color-secondary-dark);
124 }
125
126 -.current-path {
127 - font-family: monospace;
126 +#current-path {
127 + opacity: 0.9;
128 +}
129 +
130 +#path-text {
131 + font-family: 'Roboto Mono', monospace;
132 + font-optical-sizing: auto;
133 + -webkit-font-optical-sizing: auto;
134 opacity: 0.9;
135 }
136
webui/css/modals.css
+20 -16
@@ -40,16 +40,10 @@
40 }
41
42 .modal-header h2 {
43 - color: var(--color-primary);
43 font-size: var(--font-size-large);
44 margin: 0;
45 }
46
48 -.modal-header > svg {
49 - fill: var(--color-primary);
50 - max-height: 30px !important;
51 -}
52 -
47 .modal-subheader {
48 padding: 0.7rem 1.5rem;
49 display: inline;
@@ -220,34 +214,44 @@ h2 {
214 }
215
216 @media (max-width: 768px) {
217 + .modal-header {
218 + padding-left: 1.1rem;
219 + text-wrap: nowrap;
220 + }
221 .modal-content {
224 - padding: 1rem;
225 - overflow-y: auto;
226 - flex-grow: 1;
222 + padding: 0.5rem 0.5rem 0rem 0.5rem;
223 + overflow-y: auto;
224 + flex-grow: 1;
225 }
226 .modal-footer {
229 - padding: var(--spacing-md) 0rem var(--spacing-md) 0 !important;
227 + padding: var(--spacing-sm) 0rem var(--spacing-sm) 0 !important;
228 }
229 .section {
232 - margin-bottom: 1.5rem;
233 - padding: 1rem;
234 - border: 1px solid var(--color-border);
235 - border-radius: 0.5rem;
230 + margin-bottom: 1.5rem;
231 + padding: 1rem;
232 + border: 1px solid var(--color-border);
233 + border-radius: 0.5rem;
234 }
235 #buttons-container {
236 margin: 0 auto;
237 }
238 + .btn {
239 + padding: 0.5rem 1.7rem;
240 + }
241 }
242
243 @media (max-width: 540px) {
243 - .modal-header {
244 - text-wrap: nowrap;
244 + .modal-header h2 {
245 + font-size: var(--font-size-normal);
246 + margin: 0;
247 }
248 #buttons-container {
249 max-height: 50px;
250 }
251 .btn {
252 text-wrap: wrap;
253 + font-size: var(--font-size-small);
254 + padding: 0.5rem 0.85rem;
255 }
256 .btn-upload {
257 margin: 0 auto;
webui/css/settings.css
+4
@@ -223,6 +223,10 @@ nav ul li a img {
223 background-color: var(--color-secondary-light);
224 }
225
226 +.light-mode textarea:focus {
227 + background-color: #dddddd;
228 +}
229 +
230 @media (max-width: 768px) {
231 .field-control {
232 width: 100%;
webui/css/toast.css
+70 -38
@@ -1,43 +1,75 @@
1 #toast {
2 - /* position: fixed;
3 - bottom: 20px;
4 - left: 50%;
5 - transform: translateX(-50%); */
6 - margin: 0.5em;
7 - background-color: #333;
8 - color: #fff;
9 - padding: 0.3em;
10 - border-radius: 0.3125em;
11 - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
12 - display: none;
13 - align-items: center;
14 - z-index: 9999;
2 + position: relative;
3 + width: 98.5%;
4 + bottom: var(--spacing-xs);
5 + left: var(--spacing-xs);
6 + background-color: #333;
7 + color: #fff;
8 + padding: 0.3em;
9 + border-radius: 0.3125em;
10 + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
11 + display: none;
12 + align-items: center;
13 + z-index: 9999;
14 +}
15 +
16 +#toast.show {
17 + display: flex;
18 + animation: toastIn 0.3s ease-out forwards;
19 +}
20 +
21 +#toast.hide {
22 + animation: toastOut 0.3s ease-in forwards;
23 +}
24 +
25 +#toast.toast--success {
26 + background-color: #4CAF50;
27 +}
28 +
29 +#toast.toast--error {
30 + background-color: #731811;
31 +}
32 +
33 +#toast.toast--info {
34 + background-color: #2196F3;
35 +}
36 +
37 +.toast__message {
38 + margin-left: var(--spacing-xs);
39 + margin-right: 16px;
40 + flex-grow: 1;
41 +}
42 +
43 +.toast__close,
44 +.toast__copy {
45 + background-color: transparent;
46 + border: none;
47 + color: #fff;
48 + cursor: pointer;
49 + font-size: 16px;
50 + margin-left: 8px;
51 +}
52 +
53 +/* Animations */
54 +
55 +@keyframes toastIn {
56 + from {
57 + opacity: 0;
58 + transform: translateY(20px);
59 }
16 -
17 - #toast.toast--success {
18 - background-color: #4CAF50;
60 + to {
61 + opacity: 1;
62 + transform: translateY(0);
63 }
20 -
21 - #toast.toast--error {
22 - background-color: #731811;
64 +}
65 +
66 +@keyframes toastOut {
67 + from {
68 + opacity: 1;
69 + transform: translateY(0);
70 }
24 -
25 - #toast.toast--info {
26 - background-color: #2196F3;
71 + to {
72 + opacity: 0;
73 + transform: translateY(20px);
74 }
28 -
29 - .toast__message {
30 - margin-left: var(--spacing-xs);
31 - margin-right: 16px;
32 - flex-grow: 1;
33 - }
34 -
35 - .toast__close,
36 - .toast__copy {
37 - background-color: transparent;
38 - border: none;
39 - color: #fff;
40 - cursor: pointer;
41 - font-size: 16px;
42 - margin-left: 8px;
43 - }
\ No newline at end of file
75 +}
\ No newline at end of file
webui/index.html
+4 -2
@@ -195,7 +195,7 @@
195 </div>
196 <div id="toast" class="toast">
197 <div class="toast__message"></div>
198 - <button class="toast__copy">Copy</button>
198 + <button class="toast__copy" style="display: none;">Copy</button>
199 <button class="toast__close">Close</button>
200 </div>
201 <div id="progress-bar-box" x-data="{ isSpeaking: false }" x-init="
@@ -538,7 +538,9 @@
538 Up
539 </button>
540
541 - <span class="current-path" x-text="browser.currentPath || 'Current Path: /'"></span>
541 + <div id="current-path">
542 + <span id="path-text" x-text="browser.currentPath"></span>
543 + </div>
544 </div>
545
546 <div class="files-list">
webui/index.js
+34 -12
@@ -652,21 +652,31 @@ function removeClassFromElement(element, className) {
652
653 function toast(text, type = 'info') {
654 const toast = document.getElementById('toast');
655 -
655 +
656 // Update the toast content and type
657 - toast.querySelector('#toast .toast__message').textContent = text;
657 + toast.querySelector('.toast__message').textContent = text;
658 toast.className = `toast toast--${type}`;
659 +
660 + // Remove any existing animation classes
661 + toast.classList.remove('show', 'hide');
662 +
663 + // Show the toast and trigger animation
664 toast.style.display = 'flex';
660 -
665 + // Force a reflow to ensure the animation triggers
666 + void toast.offsetWidth;
667 + toast.classList.add('show');
668 +
669 + // Show/hide copy button based on toast type
670 + const copyButton = toast.querySelector('.toast__copy');
671 + copyButton.style.display = type === 'error' ? 'inline-block' : 'none';
672 +
673 // Add the close button event listener
662 - const closeButton = toast.querySelector('#toast .toast__close');
674 + const closeButton = toast.querySelector('.toast__close');
675 closeButton.onclick = () => {
664 - toast.style.display = 'none';
665 - clearTimeout(toast.timeoutId);
676 + hideToast();
677 };
667 -
678 +
679 // Add the copy button event listener
669 - const copyButton = toast.querySelector('#toast .toast__copy');
680 copyButton.onclick = () => {
681 navigator.clipboard.writeText(text);
682 copyButton.textContent = 'Copied!';
@@ -674,15 +684,27 @@ function toast(text, type = 'info') {
684 copyButton.textContent = 'Copy';
685 }, 2000);
686 };
677 -
687 +
688 // Clear any existing timeout
689 clearTimeout(toast.timeoutId);
680 -
681 - // Automatically close the toast after 5 seconds
690 +
691 + // Automatically close the toast after 10 seconds
692 toast.timeoutId = setTimeout(() => {
683 - toast.style.display = 'none';
693 + hideToast();
694 }, 10000);
695 }
696 +
697 + function hideToast() {
698 + const toast = document.getElementById('toast');
699 + toast.classList.remove('show');
700 + toast.classList.add('hide');
701 +
702 + // Remove the element from display after animation completes
703 + setTimeout(() => {
704 + toast.style.display = 'none';
705 + toast.classList.remove('hide');
706 + }, 300); // Match this with animation duration
707 +}
708
709 function scrollChanged(isAtBottom) {
710 const inputAS = Alpine.$data(autoScrollSwitch);
webui/js/file_browser.js
+1 -1
@@ -110,7 +110,7 @@ const fileBrowserModalProxy = {
110 }
111
112 try {
113 - const response = await fetch('/deleteWorkDirFile', {
113 + const response = await fetch('/delete_work_dir_file', {
114 method: 'POST',
115 headers: {
116 'Content-Type': 'application/json',
webui/js/modal.js
+3
@@ -1,6 +1,9 @@
1 const genericModalProxy = {
2 isOpen: false,
3 isLoading: false,
4 + title: '',
5 + description: '',
6 + html: '',
7
8 async openModal(title, description, html) {
9 const modalEl = document.getElementById('genericModal');