@mattli/dotmd 0.1.0 → 0.1.1

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 (35) hide show
  1. package/dist/dashboard/_archive/wizard-client.d.ts +2 -0
  2. package/dist/dashboard/_archive/wizard-client.d.ts.map +1 -0
  3. package/dist/dashboard/_archive/wizard-client.js +412 -0
  4. package/dist/dashboard/_archive/wizard-client.js.map +1 -0
  5. package/dist/dashboard/_archive/wizard.d.ts +9 -0
  6. package/dist/dashboard/_archive/wizard.d.ts.map +1 -0
  7. package/dist/dashboard/_archive/wizard.js +317 -0
  8. package/dist/dashboard/_archive/wizard.js.map +1 -0
  9. package/dist/dashboard/help.d.ts +2 -0
  10. package/dist/dashboard/help.d.ts.map +1 -0
  11. package/dist/dashboard/help.js +73 -0
  12. package/dist/dashboard/help.js.map +1 -0
  13. package/dist/dashboard/layout.d.ts.map +1 -1
  14. package/dist/dashboard/layout.js +1 -0
  15. package/dist/dashboard/layout.js.map +1 -1
  16. package/dist/dashboard/server.d.ts.map +1 -1
  17. package/dist/dashboard/server.js +15 -45
  18. package/dist/dashboard/server.js.map +1 -1
  19. package/dist/dashboard/settings-client.d.ts +1 -1
  20. package/dist/dashboard/settings-client.d.ts.map +1 -1
  21. package/dist/dashboard/settings-client.js +367 -162
  22. package/dist/dashboard/settings-client.js.map +1 -1
  23. package/dist/dashboard/settings.d.ts +5 -1
  24. package/dist/dashboard/settings.d.ts.map +1 -1
  25. package/dist/dashboard/settings.js +60 -45
  26. package/dist/dashboard/settings.js.map +1 -1
  27. package/dist/dashboard/wizard-client.d.ts +1 -1
  28. package/dist/dashboard/wizard-client.d.ts.map +1 -1
  29. package/dist/dashboard/wizard-client.js +226 -80
  30. package/dist/dashboard/wizard-client.js.map +1 -1
  31. package/dist/dashboard/wizard.d.ts +2 -2
  32. package/dist/dashboard/wizard.d.ts.map +1 -1
  33. package/dist/dashboard/wizard.js +113 -32
  34. package/dist/dashboard/wizard.js.map +1 -1
  35. package/package.json +1 -1
@@ -1,2 +1,2 @@
1
- export declare const SETTINGS_CLIENT_SCRIPT = "<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n var debounceTimer = null;\n var currentBrowseDir = null;\n\n function escapeHtml(str) {\n var div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n // --- Chip toggling ---\n function initChipToggling(container) {\n if (!container) return;\n container.querySelectorAll('[data-chip]').forEach(function(chip) {\n chip.addEventListener('click', function() {\n var isSelected = chip.getAttribute('data-selected') === 'true';\n chip.setAttribute('data-selected', isSelected ? 'false' : 'true');\n if (isSelected) {\n chip.classList.remove('bg-blue-100', 'text-blue-800', 'border-blue-300');\n chip.classList.add('bg-gray-100', 'text-gray-600', 'border-gray-300');\n } else {\n chip.classList.remove('bg-gray-100', 'text-gray-600', 'border-gray-300');\n chip.classList.add('bg-blue-100', 'text-blue-800', 'border-blue-300');\n }\n scheduleRefresh();\n });\n });\n }\n\n initChipToggling(document.getElementById('roots-list'));\n initChipToggling(document.getElementById('patterns-list'));\n\n // --- Collect selected values ---\n function getSelectedRoots() {\n var values = [];\n document.getElementById('roots-list').querySelectorAll('[data-chip][data-selected=\"true\"]').forEach(function(chip) {\n values.push(chip.getAttribute('data-value'));\n });\n document.getElementById('roots-list').querySelectorAll('[data-custom-value]').forEach(function(tag) {\n values.push(tag.getAttribute('data-custom-value'));\n });\n return values;\n }\n\n function getSelectedPatterns() {\n var values = [];\n document.getElementById('patterns-list').querySelectorAll('[data-chip][data-selected=\"true\"]').forEach(function(chip) {\n values.push(chip.getAttribute('data-value'));\n });\n document.getElementById('patterns-list').querySelectorAll('[data-custom-value]').forEach(function(tag) {\n values.push(tag.getAttribute('data-custom-value'));\n });\n return values;\n }\n\n // --- Auto-refresh file preview ---\n function scheduleRefresh() {\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(refreshPreview, 300);\n }\n\n function refreshPreview() {\n var roots = getSelectedRoots();\n var patterns = getSelectedPatterns();\n var preview = document.getElementById('file-preview');\n\n if (roots.length === 0 || patterns.length === 0) {\n preview.innerHTML = '<p class=\"text-gray-400 text-sm\">Select at least one folder and one pattern.</p>';\n return;\n }\n\n preview.style.minHeight = preview.offsetHeight + 'px';\n\n fetch('/api/setup/discover', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ roots: roots, patterns: patterns })\n })\n .then(function(res) { return res.json(); })\n .then(function(data) {\n renderFilePreview(data.files || []);\n })\n .catch(function() {\n preview.innerHTML = '<p class=\"text-red-500 text-sm\">Error scanning files.</p>';\n });\n }\n\n function renderFilePreview(files) {\n var preview = document.getElementById('file-preview');\n\n if (files.length === 0) {\n preview.innerHTML = '<p class=\"text-gray-400 text-sm\">No matching files found.</p>';\n return;\n }\n\n // Group by root (provided by server)\n var groups = {};\n var rootOrder = [];\n files.forEach(function(f) {\n var root = f.root || 'Other';\n if (!groups[root]) {\n groups[root] = [];\n rootOrder.push(root);\n }\n groups[root].push(f);\n });\n\n var html = '<div class=\"flex gap-3 mb-4\">' +\n '<button type=\"button\" id=\"preview-select-all\" class=\"text-sm text-blue-600 hover:underline\">Select all</button>' +\n '<button type=\"button\" id=\"preview-deselect-all\" class=\"text-sm text-blue-600 hover:underline\">Deselect all</button>' +\n '<span class=\"text-sm text-gray-400\">' + files.length + ' file' + (files.length === 1 ? '' : 's') + '</span>' +\n '</div>';\n\n rootOrder.forEach(function(root) {\n var rootFiles = groups[root];\n if (!rootFiles || rootFiles.length === 0) return;\n\n html += '<div class=\"mb-4\">';\n html += '<h3 class=\"text-sm font-semibold text-gray-500 uppercase tracking-wide mb-2\">' + escapeHtml(root) + '</h3>';\n html += '<div class=\"space-y-1\">';\n\n rootFiles.forEach(function(file) {\n // Show path relative to the root for cleaner display\n var displayPath = file.path;\n if (root !== 'Other' && displayPath.indexOf(root) === 0) {\n displayPath = displayPath.slice(root.length + 1);\n }\n html += '<label class=\"flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-gray-50 cursor-pointer\">' +\n '<input type=\"checkbox\" name=\"files\" value=\"' + escapeHtml(file.path) + '\" checked ' +\n 'class=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500\">' +\n '<span class=\"font-mono text-sm\">' + escapeHtml(displayPath) + '</span>' +\n '</label>';\n });\n\n html += '</div></div>';\n });\n\n preview.innerHTML = html;\n preview.style.minHeight = '';\n\n // Wire up select/deselect all\n var selectAll = document.getElementById('preview-select-all');\n var deselectAll = document.getElementById('preview-deselect-all');\n if (selectAll) {\n selectAll.addEventListener('click', function() {\n preview.querySelectorAll('input[name=\"files\"]').forEach(function(cb) { cb.checked = true; });\n });\n }\n if (deselectAll) {\n deselectAll.addEventListener('click', function() {\n preview.querySelectorAll('input[name=\"files\"]').forEach(function(cb) { cb.checked = false; });\n });\n }\n }\n\n // --- Folder browser ---\n var openBrowserBtn = document.getElementById('open-browser');\n var folderBrowser = document.getElementById('folder-browser');\n var browserPath = document.getElementById('browser-path');\n var browserList = document.getElementById('browser-list');\n var browserUp = document.getElementById('browser-up');\n var browserAdd = document.getElementById('browser-add');\n var rootsList = document.getElementById('roots-list');\n\n if (browserAdd) {\n browserAdd.addEventListener('click', function() {\n if (currentBrowseDir) {\n addCustomChip(rootsList, currentBrowseDir);\n scheduleRefresh();\n }\n });\n }\n\n function addCustomChip(container, value) {\n // Don't add duplicates\n var existing = container.querySelectorAll('[data-custom-value]');\n for (var i = 0; i < existing.length; i++) {\n if (existing[i].getAttribute('data-custom-value') === value) return;\n }\n var chips = container.querySelectorAll('[data-chip]');\n for (var i = 0; i < chips.length; i++) {\n if (chips[i].getAttribute('data-value') === value) return;\n }\n\n var tag = document.createElement('span');\n tag.setAttribute('data-custom-value', value);\n tag.className = 'inline-flex items-center gap-1 px-3 py-1.5 rounded-full text-sm font-medium bg-blue-100 text-blue-800 border border-blue-300';\n tag.innerHTML = escapeHtml(value) + ' <button type=\"button\" class=\"ml-1 text-blue-500 hover:text-blue-700 font-bold\">&times;</button>';\n tag.querySelector('button').addEventListener('click', function() {\n tag.remove();\n scheduleRefresh();\n });\n container.appendChild(tag);\n }\n\n function loadBrowserDir(dir) {\n fetch('/api/setup/browse?dir=' + encodeURIComponent(dir || '~'))\n .then(function(res) { return res.json(); })\n .then(function(data) {\n if (data.error) return;\n currentBrowseDir = data.current;\n if (browserPath) browserPath.textContent = data.current;\n if (browserUp) {\n browserUp.style.display = data.parent ? '' : 'none';\n browserUp.onclick = function() { loadBrowserDir(data.parent); };\n }\n if (!browserList) return;\n browserList.innerHTML = '';\n\n data.dirs.forEach(function(entry) {\n var row = document.createElement('div');\n row.className = 'px-4 py-2 hover:bg-gray-50 cursor-pointer flex items-center gap-2 text-sm';\n row.innerHTML = '<span class=\"text-gray-400\">&#128193;</span> ' + escapeHtml(entry.name);\n row.addEventListener('click', function() {\n loadBrowserDir(entry.path);\n });\n browserList.appendChild(row);\n });\n });\n }\n\n if (openBrowserBtn && folderBrowser) {\n openBrowserBtn.addEventListener('click', function() {\n var isHidden = folderBrowser.classList.contains('hidden');\n if (isHidden) {\n folderBrowser.classList.remove('hidden');\n openBrowserBtn.textContent = 'Hide browser';\n loadBrowserDir('~');\n } else {\n folderBrowser.classList.add('hidden');\n openBrowserBtn.textContent = 'Browse for folder...';\n }\n });\n }\n\n // --- Custom pattern input ---\n var addBtn = document.getElementById('add-custom');\n var customInput = document.getElementById('custom-input');\n var patternsList = document.getElementById('patterns-list');\n\n function addCustomPattern() {\n if (!customInput || !patternsList) return;\n var value = customInput.value.trim();\n if (!value) return;\n addCustomChip(patternsList, value);\n customInput.value = '';\n scheduleRefresh();\n }\n\n if (addBtn) {\n addBtn.addEventListener('click', addCustomPattern);\n }\n if (customInput) {\n customInput.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') {\n e.preventDefault();\n addCustomPattern();\n }\n });\n }\n\n // --- Save ---\n var saveBtn = document.getElementById('save-btn');\n var saveStatus = document.getElementById('save-status');\n\n if (saveBtn) {\n saveBtn.addEventListener('click', function() {\n saveBtn.disabled = true;\n saveBtn.textContent = 'Saving...';\n if (saveStatus) saveStatus.classList.add('hidden');\n\n var roots = getSelectedRoots();\n var patterns = getSelectedPatterns();\n\n var excludedFiles = [];\n document.querySelectorAll('#file-preview input[name=\"files\"]').forEach(function(cb) {\n if (!cb.checked) {\n excludedFiles.push(cb.value);\n }\n });\n\n fetch('/api/setup/confirm', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ roots: roots, patterns: patterns, excludedFiles: excludedFiles })\n })\n .then(function(res) { return res.json(); })\n .then(function(data) {\n saveBtn.disabled = false;\n saveBtn.textContent = 'Save';\n if (data.success && saveStatus) {\n saveStatus.textContent = 'Settings saved. Tracking ' + data.trackedCount + ' file' + (data.trackedCount === 1 ? '' : 's') + '.';\n saveStatus.classList.remove('hidden');\n setTimeout(function() { saveStatus.classList.add('hidden'); }, 3000);\n }\n })\n .catch(function() {\n saveBtn.disabled = false;\n saveBtn.textContent = 'Save';\n alert('Error saving settings. Please try again.');\n });\n });\n }\n\n // --- Initial load ---\n refreshPreview();\n});\n</script>";
1
+ export declare const SETTINGS_CLIENT_SCRIPT = "<script>\ndocument.addEventListener('DOMContentLoaded', function() {\n var debounceTimer = null;\n var currentBrowseDir = null;\n\n function escapeHtml(str) {\n var div = document.createElement('div');\n div.textContent = str;\n return div.innerHTML;\n }\n\n // ===== Folder management =====\n\n var folderList = document.getElementById('folder-list');\n var noFoldersMsg = document.getElementById('no-folders-msg');\n\n function getAddedFolders() {\n var folders = [];\n if (!folderList) return folders;\n folderList.querySelectorAll('[data-folder-chip]').forEach(function(chip) {\n folders.push(chip.getAttribute('data-folder-chip'));\n });\n return folders;\n }\n\n function isFolderAdded(folderPath) {\n var folders = getAddedFolders();\n for (var i = 0; i < folders.length; i++) {\n if (folders[i] === folderPath) return true;\n }\n return false;\n }\n\n function isCoveredByParent(folderPath) {\n var folders = getAddedFolders();\n for (var i = 0; i < folders.length; i++) {\n if (folderPath !== folders[i] && folderPath.indexOf(folders[i] + '/') === 0) return true;\n }\n return false;\n }\n\n function updateNoFoldersMsg() {\n if (!noFoldersMsg) return;\n if (getAddedFolders().length === 0) {\n noFoldersMsg.classList.remove('hidden');\n } else {\n noFoldersMsg.classList.add('hidden');\n }\n }\n\n function removeFolderChip(chip) {\n chip.remove();\n updateNoFoldersMsg();\n scheduleRefresh();\n }\n\n function addFolderChip(folderPath) {\n if (!folderList) return;\n if (isFolderAdded(folderPath)) return;\n\n var tag = document.createElement('span');\n tag.setAttribute('data-folder-chip', folderPath);\n tag.className = 'inline-flex items-center gap-1 px-3 py-1.5 rounded-full text-sm font-medium bg-blue-100 text-blue-800 border border-blue-300';\n tag.innerHTML = escapeHtml(folderPath) + ' <button type=\"button\" class=\"ml-1 text-blue-500 hover:text-blue-700 font-bold\">&times;</button>';\n tag.querySelector('button').addEventListener('click', function() {\n removeFolderChip(tag);\n });\n folderList.appendChild(tag);\n updateNoFoldersMsg();\n scheduleRefresh();\n }\n\n // Wire up x buttons on server-rendered folder chips\n if (folderList) {\n folderList.querySelectorAll('[data-folder-chip] button').forEach(function(btn) {\n btn.addEventListener('click', function() {\n removeFolderChip(btn.parentElement);\n });\n });\n }\n\n // ===== Folder browser =====\n\n var openBrowserBtn = document.getElementById('open-browser');\n var folderBrowser = document.getElementById('folder-browser');\n var browserPath = document.getElementById('browser-path');\n var browserList = document.getElementById('browser-list');\n var browserUp = document.getElementById('browser-up');\n\n function loadBrowserDir(dir) {\n fetch('/api/setup/browse?dir=' + encodeURIComponent(dir || '~'))\n .then(function(res) { return res.json(); })\n .then(function(data) {\n if (data.error) return;\n currentBrowseDir = data.current;\n if (browserPath) browserPath.textContent = data.current;\n if (browserUp) {\n browserUp.style.display = data.parent ? '' : 'none';\n browserUp.onclick = function() { loadBrowserDir(data.parent); };\n }\n if (!browserList) return;\n browserList.innerHTML = '';\n\n data.dirs.forEach(function(entry) {\n var added = isFolderAdded(entry.path);\n var covered = isCoveredByParent(entry.path);\n\n var row = document.createElement('div');\n row.className = 'px-4 py-2 hover:bg-gray-50 flex items-center gap-2 text-sm';\n\n var folderIcon = document.createElement('span');\n folderIcon.innerHTML = '&#128193;';\n folderIcon.className = covered ? 'text-gray-300' : 'text-gray-400';\n\n var nameSpan = document.createElement('span');\n nameSpan.textContent = entry.name;\n nameSpan.className = covered\n ? 'cursor-pointer flex-1 text-gray-300'\n : 'cursor-pointer flex-1 hover:text-blue-600';\n nameSpan.addEventListener('click', function() {\n loadBrowserDir(entry.path);\n });\n\n row.appendChild(folderIcon);\n row.appendChild(nameSpan);\n\n if (added) {\n var removeBtn = document.createElement('button');\n removeBtn.textContent = 'Remove';\n removeBtn.type = 'button';\n removeBtn.className = 'text-red-500 hover:text-red-700 text-xs font-medium flex-shrink-0';\n removeBtn.addEventListener('click', function(e) {\n e.stopPropagation();\n var chips = folderList.querySelectorAll('[data-folder-chip]');\n for (var i = 0; i < chips.length; i++) {\n if (chips[i].getAttribute('data-folder-chip') === entry.path) {\n removeFolderChip(chips[i]);\n break;\n }\n }\n loadBrowserDir(currentBrowseDir);\n });\n row.appendChild(removeBtn);\n } else if (!covered) {\n var addBtn = document.createElement('button');\n addBtn.textContent = 'Add';\n addBtn.type = 'button';\n addBtn.className = 'text-blue-600 hover:text-blue-800 text-xs font-medium flex-shrink-0';\n addBtn.addEventListener('click', function(e) {\n e.stopPropagation();\n addFolderChip(entry.path);\n loadBrowserDir(currentBrowseDir);\n });\n row.appendChild(addBtn);\n }\n\n browserList.appendChild(row);\n });\n });\n }\n\n if (openBrowserBtn && folderBrowser) {\n openBrowserBtn.addEventListener('click', function() {\n var isHidden = folderBrowser.classList.contains('hidden');\n if (isHidden) {\n folderBrowser.classList.remove('hidden');\n openBrowserBtn.textContent = 'Hide browser';\n loadBrowserDir('~');\n } else {\n folderBrowser.classList.add('hidden');\n openBrowserBtn.textContent = 'Browse for folder...';\n }\n });\n }\n\n // ===== Pattern management =====\n\n var patternList = document.getElementById('pattern-list');\n var suggestedPatternsDiv = document.getElementById('suggested-patterns');\n var noPatternsMsg = document.getElementById('no-patterns-msg');\n\n function getSelectedPatterns() {\n var values = [];\n if (!patternList) return values;\n patternList.querySelectorAll('[data-pattern-chip]').forEach(function(chip) {\n values.push(chip.getAttribute('data-pattern-chip'));\n });\n return values;\n }\n\n function updateNoPatternsMsg() {\n if (!noPatternsMsg) return;\n if (getSelectedPatterns().length === 0) {\n noPatternsMsg.classList.remove('hidden');\n } else {\n noPatternsMsg.classList.add('hidden');\n }\n }\n\n function removePatternChip(chip) {\n var value = chip.getAttribute('data-pattern-chip');\n chip.remove();\n // If it's a suggested pattern, unhide it in the suggestions section\n if (suggestedPatternsDiv && value) {\n var suggestions = suggestedPatternsDiv.querySelectorAll('[data-suggested-pattern]');\n for (var i = 0; i < suggestions.length; i++) {\n if (suggestions[i].getAttribute('data-suggested-pattern') === value) {\n suggestions[i].classList.remove('hidden');\n // Show the suggestions section\n var section = document.getElementById('suggestions-section');\n if (section) section.classList.remove('hidden');\n break;\n }\n }\n }\n updateNoPatternsMsg();\n scheduleRefresh();\n }\n\n function addPatternChip(value) {\n if (!patternList) return;\n // Check for duplicates\n var existing = patternList.querySelectorAll('[data-pattern-chip]');\n for (var i = 0; i < existing.length; i++) {\n if (existing[i].getAttribute('data-pattern-chip') === value) return;\n }\n\n var tag = document.createElement('span');\n tag.setAttribute('data-pattern-chip', value);\n tag.className = 'inline-flex items-center gap-1 px-3 py-1.5 rounded-full text-sm font-medium bg-blue-100 text-blue-800 border border-blue-300';\n tag.innerHTML = escapeHtml(value) + ' <button type=\"button\" class=\"ml-1 text-blue-500 hover:text-blue-700 font-bold\">&times;</button>';\n tag.querySelector('button').addEventListener('click', function() {\n removePatternChip(tag);\n });\n patternList.appendChild(tag);\n updateNoPatternsMsg();\n scheduleRefresh();\n }\n\n // Wire up x buttons on server-rendered pattern chips\n if (patternList) {\n patternList.querySelectorAll('[data-pattern-chip] button').forEach(function(btn) {\n btn.addEventListener('click', function() {\n removePatternChip(btn.parentElement);\n });\n });\n }\n\n // Wire up suggested pattern chips \u2014 click to add\n if (suggestedPatternsDiv) {\n suggestedPatternsDiv.querySelectorAll('[data-suggested-pattern]').forEach(function(chip) {\n chip.addEventListener('click', function() {\n var value = chip.getAttribute('data-suggested-pattern');\n addPatternChip(value);\n chip.classList.add('hidden');\n // Hide suggestions section if all hidden\n var anyVisible = false;\n suggestedPatternsDiv.querySelectorAll('[data-suggested-pattern]').forEach(function(s) {\n if (!s.classList.contains('hidden')) anyVisible = true;\n });\n if (!anyVisible) {\n var section = document.getElementById('suggestions-section');\n if (section) section.classList.add('hidden');\n }\n });\n });\n }\n\n // Custom pattern input\n var addCustomBtn = document.getElementById('add-custom');\n var customInput = document.getElementById('custom-input');\n\n function addCustomPattern() {\n if (!customInput) return;\n var value = customInput.value.trim();\n if (!value) return;\n addPatternChip(value);\n customInput.value = '';\n }\n\n if (addCustomBtn) {\n addCustomBtn.addEventListener('click', addCustomPattern);\n }\n if (customInput) {\n customInput.addEventListener('keydown', function(e) {\n if (e.key === 'Enter') {\n e.preventDefault();\n addCustomPattern();\n }\n });\n }\n\n // ===== File preview =====\n\n function scheduleRefresh() {\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(refreshPreview, 300);\n }\n\n function refreshPreview() {\n var roots = getAddedFolders();\n var patterns = getSelectedPatterns();\n var preview = document.getElementById('file-preview');\n\n if (roots.length === 0 || patterns.length === 0) {\n preview.innerHTML = '<p class=\"text-gray-400 text-sm\">Select at least one folder and one pattern.</p>';\n return;\n }\n\n preview.style.minHeight = preview.offsetHeight + 'px';\n\n fetch('/api/setup/discover', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ roots: roots, patterns: patterns })\n })\n .then(function(res) { return res.json(); })\n .then(function(data) {\n renderFilePreview(data.files || []);\n })\n .catch(function() {\n preview.innerHTML = '<p class=\"text-red-500 text-sm\">Error scanning files.</p>';\n });\n }\n\n function renderFilePreview(files) {\n var preview = document.getElementById('file-preview');\n\n if (files.length === 0) {\n preview.innerHTML = '<p class=\"text-gray-400 text-sm\">No matching files found.</p>';\n return;\n }\n\n // Group by root\n var groups = {};\n var rootOrder = [];\n files.forEach(function(f) {\n var root = f.root || 'Other';\n if (!groups[root]) {\n groups[root] = [];\n rootOrder.push(root);\n }\n groups[root].push(f);\n });\n\n var totalFiles = files.length;\n\n var html = '<div class=\"flex gap-3 mb-4 items-center\">' +\n '<button type=\"button\" id=\"preview-select-all\" class=\"text-sm text-blue-600 hover:underline\">Select all</button>' +\n '<button type=\"button\" id=\"preview-deselect-all\" class=\"text-sm text-blue-600 hover:underline\">Deselect all</button>' +\n '<span id=\"file-count\" class=\"text-sm text-gray-400\"></span>' +\n '</div>';\n\n function checkboxHtml(filePath, displayPath) {\n return '<label class=\"flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-gray-50 cursor-pointer\">' +\n '<input type=\"checkbox\" name=\"files\" value=\"' + escapeHtml(filePath) + '\" checked ' +\n 'class=\"rounded border-gray-300 text-blue-600 focus:ring-blue-500\">' +\n '<span class=\"font-mono text-sm\">' + escapeHtml(displayPath) + '</span>' +\n '</label>';\n }\n\n rootOrder.forEach(function(root) {\n var rootFiles = groups[root];\n if (!rootFiles || rootFiles.length === 0) return;\n\n // Group by first subdirectory\n var subGroups = {};\n var subGroupOrder = [];\n\n rootFiles.forEach(function(file) {\n var displayPath = file.path;\n if (root !== 'Other' && displayPath.indexOf(root) === 0) {\n displayPath = displayPath.slice(root.length + 1);\n }\n var firstSlash = displayPath.indexOf('/');\n var groupName = firstSlash === -1 ? '' : displayPath.slice(0, firstSlash);\n if (!subGroups[groupName]) {\n subGroups[groupName] = [];\n subGroupOrder.push(groupName);\n }\n subGroups[groupName].push({ path: file.path, displayPath: displayPath });\n });\n\n var hasSubGroups = subGroupOrder.length > 1 || subGroupOrder[0] !== '';\n\n html += '<div class=\"mb-4\">';\n html += '<h3 class=\"text-sm font-semibold text-gray-500 uppercase tracking-wide mb-2\">' + escapeHtml(root) + '</h3>';\n html += '<div class=\"space-y-1\">';\n\n if (!hasSubGroups) {\n subGroups[''].forEach(function(file) {\n html += checkboxHtml(file.path, file.displayPath);\n });\n } else {\n if (subGroups['']) {\n subGroups[''].forEach(function(file) {\n html += checkboxHtml(file.path, file.displayPath);\n });\n }\n subGroupOrder.forEach(function(groupName) {\n if (groupName === '') return;\n var groupFiles = subGroups[groupName];\n html += '<details class=\"mt-1\">' +\n '<summary class=\"cursor-pointer text-sm text-gray-600 hover:text-gray-800 px-3 py-1.5 rounded-lg hover:bg-gray-50 select-none\">' +\n escapeHtml(groupName) + '/ <span class=\"text-gray-400\">(' + groupFiles.length + ')</span>' +\n '</summary><div class=\"ml-4\">';\n groupFiles.forEach(function(file) {\n html += checkboxHtml(file.path, file.displayPath);\n });\n html += '</div></details>';\n });\n }\n\n html += '</div></div>';\n });\n\n preview.innerHTML = html;\n preview.style.minHeight = '';\n\n function updateFileCount() {\n var checked = preview.querySelectorAll('input[name=\"files\"]:checked').length;\n var countEl = document.getElementById('file-count');\n if (countEl) {\n countEl.textContent = checked + ' of ' + totalFiles + ' file' + (totalFiles === 1 ? '' : 's') + ' selected';\n }\n }\n\n // Listen for checkbox changes\n preview.querySelectorAll('input[name=\"files\"]').forEach(function(cb) {\n cb.addEventListener('change', updateFileCount);\n });\n\n var selectAll = document.getElementById('preview-select-all');\n var deselectAll = document.getElementById('preview-deselect-all');\n if (selectAll) {\n selectAll.addEventListener('click', function() {\n preview.querySelectorAll('input[name=\"files\"]').forEach(function(cb) { cb.checked = true; });\n updateFileCount();\n });\n }\n if (deselectAll) {\n deselectAll.addEventListener('click', function() {\n preview.querySelectorAll('input[name=\"files\"]').forEach(function(cb) { cb.checked = false; });\n updateFileCount();\n });\n }\n\n updateFileCount();\n }\n\n // ===== Save =====\n\n var saveBtn = document.getElementById('save-btn');\n var saveStatus = document.getElementById('save-status');\n\n if (saveBtn) {\n saveBtn.addEventListener('click', function() {\n var roots = getAddedFolders();\n var patterns = getSelectedPatterns();\n\n if (roots.length === 0 || patterns.length === 0) {\n alert('Please select at least one folder and one pattern.');\n return;\n }\n\n saveBtn.disabled = true;\n saveBtn.textContent = 'Saving...';\n if (saveStatus) saveStatus.classList.add('hidden');\n\n var excludedFiles = [];\n document.querySelectorAll('#file-preview input[name=\"files\"]').forEach(function(cb) {\n if (!cb.checked) {\n excludedFiles.push(cb.value);\n }\n });\n\n fetch('/api/setup/confirm', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ roots: roots, patterns: patterns, excludedFiles: excludedFiles })\n })\n .then(function(res) { return res.json(); })\n .then(function(data) {\n if (data.success) {\n var shouldRedirect = saveBtn.getAttribute('data-redirect') === 'true';\n if (shouldRedirect) {\n window.location.href = '/?setup=complete&count=' + data.trackedCount;\n } else {\n saveBtn.disabled = false;\n saveBtn.textContent = 'Save';\n if (saveStatus) {\n saveStatus.textContent = 'Settings saved. Tracking ' + data.trackedCount + ' file' + (data.trackedCount === 1 ? '' : 's') + '.';\n saveStatus.classList.remove('hidden');\n setTimeout(function() { saveStatus.classList.add('hidden'); }, 3000);\n }\n }\n } else {\n saveBtn.disabled = false;\n saveBtn.textContent = saveBtn.getAttribute('data-redirect') === 'true' ? 'Start Tracking' : 'Save';\n alert('Error saving. Please try again.');\n }\n })\n .catch(function() {\n saveBtn.disabled = false;\n saveBtn.textContent = saveBtn.getAttribute('data-redirect') === 'true' ? 'Start Tracking' : 'Save';\n alert('Error saving. Please try again.');\n });\n });\n }\n\n // ===== Initial load =====\n refreshPreview();\n});\n</script>";
2
2
  //# sourceMappingURL=settings-client.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"settings-client.d.ts","sourceRoot":"","sources":["../../src/dashboard/settings-client.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,sxWAoTzB,CAAC"}
1
+ {"version":3,"file":"settings-client.d.ts","sourceRoot":"","sources":["../../src/dashboard/settings-client.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,sBAAsB,uxkBAigBzB,CAAC"}