Prechádzať zdrojové kódy

fix(ux): Make it easier to add file extensions to an app in bulk (#1205)

* Add support for pasting multiple file extensions at once in Dev Center

* Fix bulk file extension paste functionality in Dev Center

* fix: add try-catch around tippy

* Fix issues with bulk file extension pasting and add comma key support

* Fix issue with duplicate red tags appearing temporarily

* Implement robust bulk file extension paste functionality in Dev Center

* Fix security issue with JSON.stringify to properly escape < characters
Eric Dubé 1 mesiac pred
rodič
commit
1266ef2179
1 zmenil súbory, kde vykonal 99 pridanie a 3 odobranie
  1. 99 3
      src/dev-center/js/dev-center.js

+ 99 - 3
src/dev-center/js/dev-center.js

@@ -569,7 +569,8 @@ function generate_edit_app_section(app) {
 
 
                 <label for="edit-app-filetype-associations">File Associations</label>
                 <label for="edit-app-filetype-associations">File Associations</label>
                <p style="margin-top: 10px; font-size:13px;">A list of file type specifiers. For example if you include <code>.txt</code> your apps could be opened when a user clicks on a TXT file.</p>
                <p style="margin-top: 10px; font-size:13px;">A list of file type specifiers. For example if you include <code>.txt</code> your apps could be opened when a user clicks on a TXT file.</p>
-               <textarea id="edit-app-filetype-associations"  placeholder=".txt  .jpg    application/json">${JSON.stringify(app.filetype_associations.map(item => ({ "value": item })), null, app.filetype_associations.length)}</textarea>
+               <p style="margin-top: 5px; font-size:13px;">You can paste multiple extensions at once (comma, space, or tab separated) or press comma to add each extension.</p>
+               <textarea id="edit-app-filetype-associations"  placeholder="Paste multiple extensions like: .txt, .doc, .pdf, application/json">${JSON.stringify(app.filetype_associations.map(item => ({ "value": item })), null, app.filetype_associations.length).replace(/</g, '\\u003c')}</textarea>
 
 
                 <h3 style="font-size: 23px; border-bottom: 1px solid #EEE; margin-top: 50px; margin-bottom: 0px;">Window</h3>
                 <h3 style="font-size: 23px; border-bottom: 1px solid #EEE; margin-top: 50px; margin-bottom: 0px;">Window</h3>
                 <div>
                 <div>
@@ -816,7 +817,8 @@ async function edit_app_section(cur_app_name, tab = 'deploy') {
     const filetype_association_input = document.querySelector('textarea[id=edit-app-filetype-associations]');
     const filetype_association_input = document.querySelector('textarea[id=edit-app-filetype-associations]');
     let tagify = new Tagify(filetype_association_input, {
     let tagify = new Tagify(filetype_association_input, {
         pattern: /\.(?:[a-z0-9]+)|(?:[a-z]+\/(?:[a-z0-9.-]+|\*))/,
         pattern: /\.(?:[a-z0-9]+)|(?:[a-z]+\/(?:[a-z0-9.-]+|\*))/,
-        delimiters: ", ",
+        delimiters: ",",  // Use comma as delimiter
+        duplicates: false, // Prevent duplicate tags
         enforceWhitelist: false,
         enforceWhitelist: false,
         dropdown : {
         dropdown : {
             // show the dropdown immediately on focus (0 character typed)
             // show the dropdown immediately on focus (0 character typed)
@@ -1072,7 +1074,101 @@ async function edit_app_section(cur_app_name, tab = 'deploy') {
     // Focus on the first input
     // Focus on the first input
     $('#edit-app-title').focus();
     $('#edit-app-title').focus();
 
 
-    activate_tippy();
+    try {
+        activate_tippy();
+    } catch (e) {
+        console.log('no tippy:', e);
+    }
+
+    // Custom function to handle bulk pasting of file extensions
+    if (tagify) {
+        // Create a completely separate paste handler
+        const handleBulkPaste = function(e) {
+            const clipboardData = e.clipboardData || window.clipboardData;
+            if (!clipboardData) return;
+            
+            const pastedText = clipboardData.getData('text');
+            if (!pastedText) return;
+            
+            // Check if the pasted text contains delimiters
+            if (/[,;\t\s]/.test(pastedText)) {
+                e.stopPropagation();
+                e.preventDefault();
+                
+                // Process the pasted text to extract extensions
+                const extensions = pastedText.split(/[,;\t\s]+/)
+                    .map(ext => ext.trim())
+                    .filter(ext => ext && (ext.startsWith('.') || ext.includes('/')));
+                
+                if (extensions.length > 0) {
+                    // Get existing values to prevent duplicates
+                    const existingValues = tagify.value.map(tag => tag.value);
+                    
+                    // Only add extensions that don't already exist
+                    const newExtensions = extensions.filter(ext => !existingValues.includes(ext));
+                    
+                    if (newExtensions.length > 0) {
+                        // Add the new tags
+                        tagify.addTags(newExtensions);
+                        
+                        // Update the UI
+                        setTimeout(() => {
+                            toggleSaveButton();
+                            toggleResetButton();
+                        }, 10);
+                    }
+                }
+                
+                // Clear the input element to prevent any text concatenation
+                setTimeout(() => {
+                    if (tagify.DOM.input) {
+                        tagify.DOM.input.textContent = '';
+                    }
+                }, 10);
+            }
+        };
+        
+        // Add the paste handler directly to the tagify wrapper element
+        const tagifyWrapper = tagify.DOM.scope;
+        if (tagifyWrapper) {
+            tagifyWrapper.addEventListener('paste', handleBulkPaste, true);
+        }
+        
+        // Also add it to the input element for better coverage
+        if (tagify.DOM.input) {
+            tagify.DOM.input.addEventListener('paste', handleBulkPaste, true);
+        }
+        
+        // Add a comma key handler to support adding tags with comma
+        tagify.DOM.input.addEventListener('keydown', function(e) {
+            if (e.key === ',' && tagify.DOM.input.textContent.trim()) {
+                e.preventDefault();
+                
+                const text = tagify.DOM.input.textContent.trim();
+                
+                // Only add valid extensions
+                if ((text.startsWith('.') || text.includes('/')) && 
+                    tagify.settings.pattern.test(text)) {
+                    
+                    // Check for duplicates
+                    const existingValues = tagify.value.map(tag => tag.value);
+                    
+                    if (!existingValues.includes(text)) {
+                        tagify.addTags([text]);
+                        
+                        // Update UI
+                        setTimeout(() => {
+                            toggleSaveButton();
+                            toggleResetButton();
+                        }, 10);
+                    }
+                    
+                    // Always clear the input
+                    tagify.DOM.input.textContent = '';
+                }
+            }
+        });
+    }
 }
 }
 
 
 $('.jip-submit-btn').on('click', async function (e) {
 $('.jip-submit-btn').on('click', async function (e) {