@@ -1172,25 +1172,41 @@ const SettingsForms = {
11721172 </div>
11731173
11741174 <div class="setting-item">
1175- <label for="swaparr_malicious_extensions ">
1175+ <label for="swaparr_malicious_extensions_input ">
11761176 <a href="https://plexguide.github.io/Huntarr.io/apps/swaparr.html#malicious-extensions" class="info-icon" title="File extensions to consider malicious" target="_blank" rel="noopener">
11771177 <i class="fas fa-info-circle"></i>
11781178 </a>
11791179 Malicious File Extensions:
11801180 </label>
1181- <textarea id="swaparr_malicious_extensions" rows="3" placeholder="Enter file extensions separated by commas...">${ ( settings . malicious_extensions || [ '.lnk' , '.exe' , '.bat' , '.cmd' , '.scr' , '.zipx' , '.jar' , '.vbs' ] ) . join ( ', ' ) } </textarea>
1182- <p class="setting-help">File extensions to block (comma-separated). Examples: .lnk, .exe, .bat, .zipx</p>
1181+ <div class="tag-input-container">
1182+ <div class="tag-list" id="swaparr_malicious_extensions_tags"></div>
1183+ <div class="tag-input-wrapper">
1184+ <input type="text" id="swaparr_malicious_extensions_input" placeholder="Type extension and press Enter (e.g. .lnk)" class="tag-input">
1185+ <button type="button" class="tag-add-btn" onclick="addExtensionTag()">
1186+ <i class="fas fa-plus"></i>
1187+ </button>
1188+ </div>
1189+ </div>
1190+ <p class="setting-help">File extensions to block. Type extension and press Enter or click +. Examples: .lnk, .exe, .bat, .zipx</p>
11831191 </div>
11841192
11851193 <div class="setting-item">
1186- <label for="swaparr_suspicious_patterns ">
1194+ <label for="swaparr_suspicious_patterns_input ">
11871195 <a href="https://plexguide.github.io/Huntarr.io/apps/swaparr.html#suspicious-patterns" class="info-icon" title="Suspicious filename patterns" target="_blank" rel="noopener">
11881196 <i class="fas fa-info-circle"></i>
11891197 </a>
11901198 Suspicious Patterns:
11911199 </label>
1192- <textarea id="swaparr_suspicious_patterns" rows="3" placeholder="Enter suspicious patterns separated by commas...">${ ( settings . suspicious_patterns || [ 'password.txt' , 'readme.txt' , 'install.exe' , 'keygen' , 'crack' ] ) . join ( ', ' ) } </textarea>
1193- <p class="setting-help">Filename patterns to block (comma-separated). Examples: password.txt, keygen, crack</p>
1200+ <div class="tag-input-container">
1201+ <div class="tag-list" id="swaparr_suspicious_patterns_tags"></div>
1202+ <div class="tag-input-wrapper">
1203+ <input type="text" id="swaparr_suspicious_patterns_input" placeholder="Type pattern and press Enter (e.g. keygen)" class="tag-input">
1204+ <button type="button" class="tag-add-btn" onclick="addPatternTag()">
1205+ <i class="fas fa-plus"></i>
1206+ </button>
1207+ </div>
1208+ </div>
1209+ <p class="setting-help">Filename patterns to block. Type pattern and press Enter or click +. Examples: password.txt, keygen, crack</p>
11941210 </div>
11951211 </div>
11961212
@@ -1213,6 +1229,9 @@ const SettingsForms = {
12131229 }
12141230 } ) ;
12151231 }
1232+
1233+ // Initialize tag systems
1234+ this . initializeTagSystem ( settings ) ;
12161235
12171236 // Add event listener for global Swaparr enabled toggle to control instance visibility
12181237 const swaparrEnabledToggle = container . querySelector ( '#swaparr_enabled' ) ;
@@ -1230,6 +1249,124 @@ const SettingsForms = {
12301249 }
12311250 } ,
12321251
1252+ // Initialize tag input systems for malicious file detection
1253+ initializeTagSystem : function ( settings ) {
1254+ // Initialize extensions
1255+ const defaultExtensions = [ '.lnk' , '.exe' , '.bat' , '.cmd' , '.scr' , '.pif' , '.com' , '.zipx' , '.jar' , '.vbs' , '.js' , '.jse' , '.wsf' , '.wsh' ] ;
1256+ const extensions = settings . malicious_extensions || defaultExtensions ;
1257+ this . loadTags ( 'swaparr_malicious_extensions_tags' , extensions ) ;
1258+
1259+ // Initialize patterns
1260+ const defaultPatterns = [ 'password.txt' , 'readme.txt' , 'install.exe' , 'setup.exe' , 'keygen' , 'crack' , 'patch.exe' , 'activator' ] ;
1261+ const patterns = settings . suspicious_patterns || defaultPatterns ;
1262+ this . loadTags ( 'swaparr_suspicious_patterns_tags' , patterns ) ;
1263+
1264+ // Add enter key listeners
1265+ const extensionInput = document . getElementById ( 'swaparr_malicious_extensions_input' ) ;
1266+ const patternInput = document . getElementById ( 'swaparr_suspicious_patterns_input' ) ;
1267+
1268+ if ( extensionInput ) {
1269+ extensionInput . addEventListener ( 'keypress' , ( e ) => {
1270+ if ( e . key === 'Enter' ) {
1271+ e . preventDefault ( ) ;
1272+ this . addExtensionTag ( ) ;
1273+ }
1274+ } ) ;
1275+ }
1276+
1277+ if ( patternInput ) {
1278+ patternInput . addEventListener ( 'keypress' , ( e ) => {
1279+ if ( e . key === 'Enter' ) {
1280+ e . preventDefault ( ) ;
1281+ this . addPatternTag ( ) ;
1282+ }
1283+ } ) ;
1284+ }
1285+
1286+ // Make functions globally accessible
1287+ window . addExtensionTag = ( ) => this . addExtensionTag ( ) ;
1288+ window . addPatternTag = ( ) => this . addPatternTag ( ) ;
1289+ } ,
1290+
1291+ // Load tags into a tag list
1292+ loadTags : function ( containerId , tags ) {
1293+ const container = document . getElementById ( containerId ) ;
1294+ if ( ! container ) return ;
1295+
1296+ container . innerHTML = '' ;
1297+ tags . forEach ( tag => {
1298+ this . createTagElement ( container , tag ) ;
1299+ } ) ;
1300+ } ,
1301+
1302+ // Create a tag element
1303+ createTagElement : function ( container , text ) {
1304+ const tagDiv = document . createElement ( 'div' ) ;
1305+ tagDiv . className = 'tag-item' ;
1306+ tagDiv . innerHTML = `
1307+ <span class="tag-text">${ text } </span>
1308+ <button type="button" class="tag-remove" onclick="this.parentElement.remove()">
1309+ <i class="fas fa-times"></i>
1310+ </button>
1311+ ` ;
1312+ container . appendChild ( tagDiv ) ;
1313+ } ,
1314+
1315+ // Add extension tag
1316+ addExtensionTag : function ( ) {
1317+ const input = document . getElementById ( 'swaparr_malicious_extensions_input' ) ;
1318+ const container = document . getElementById ( 'swaparr_malicious_extensions_tags' ) ;
1319+
1320+ if ( ! input || ! container ) return ;
1321+
1322+ let value = input . value . trim ( ) ;
1323+ if ( ! value ) return ;
1324+
1325+ // Auto-add dot if not present for extensions
1326+ if ( ! value . startsWith ( '.' ) ) {
1327+ value = '.' + value ;
1328+ }
1329+
1330+ // Check for duplicates
1331+ const existing = Array . from ( container . querySelectorAll ( '.tag-text' ) ) . map ( el => el . textContent ) ;
1332+ if ( existing . includes ( value ) ) {
1333+ input . value = '' ;
1334+ return ;
1335+ }
1336+
1337+ this . createTagElement ( container , value ) ;
1338+ input . value = '' ;
1339+ } ,
1340+
1341+ // Add pattern tag
1342+ addPatternTag : function ( ) {
1343+ const input = document . getElementById ( 'swaparr_suspicious_patterns_input' ) ;
1344+ const container = document . getElementById ( 'swaparr_suspicious_patterns_tags' ) ;
1345+
1346+ if ( ! input || ! container ) return ;
1347+
1348+ const value = input . value . trim ( ) ;
1349+ if ( ! value ) return ;
1350+
1351+ // Check for duplicates
1352+ const existing = Array . from ( container . querySelectorAll ( '.tag-text' ) ) . map ( el => el . textContent ) ;
1353+ if ( existing . includes ( value ) ) {
1354+ input . value = '' ;
1355+ return ;
1356+ }
1357+
1358+ this . createTagElement ( container , value ) ;
1359+ input . value = '' ;
1360+ } ,
1361+
1362+ // Get tags from a container
1363+ getTagsFromContainer : function ( containerId ) {
1364+ const container = document . getElementById ( containerId ) ;
1365+ if ( ! container ) return [ ] ;
1366+
1367+ return Array . from ( container . querySelectorAll ( '.tag-text' ) ) . map ( el => el . textContent ) ;
1368+ } ,
1369+
12331370 // Generate Hunt Manager placeholder form
12341371 generateHuntingForm : function ( container , settings = { } ) {
12351372 // Add data-app-type attribute to container
@@ -1611,19 +1748,9 @@ const SettingsForms = {
16111748 // Malicious file detection settings
16121749 settings . malicious_file_detection = getInputValue ( '#swaparr_malicious_detection' , false ) ;
16131750
1614- // Parse malicious extensions (comma-separated)
1615- const extensionsText = document . getElementById ( 'swaparr_malicious_extensions' ) ?. value || '' ;
1616- settings . malicious_extensions = extensionsText
1617- . split ( ',' )
1618- . map ( ext => ext . trim ( ) )
1619- . filter ( ext => ext . length > 0 ) ;
1620-
1621- // Parse suspicious patterns (comma-separated)
1622- const patternsText = document . getElementById ( 'swaparr_suspicious_patterns' ) ?. value || '' ;
1623- settings . suspicious_patterns = patternsText
1624- . split ( ',' )
1625- . map ( pattern => pattern . trim ( ) )
1626- . filter ( pattern => pattern . length > 0 ) ;
1751+ // Get tags from tag containers
1752+ settings . malicious_extensions = this . getTagsFromContainer ( 'swaparr_malicious_extensions_tags' ) ;
1753+ settings . suspicious_patterns = this . getTagsFromContainer ( 'swaparr_suspicious_patterns_tags' ) ;
16271754 }
16281755 }
16291756
0 commit comments