@mattli/dotmd 0.1.0

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 (78) hide show
  1. package/README.md +77 -0
  2. package/dist/cli/commands/init.d.ts +4 -0
  3. package/dist/cli/commands/init.d.ts.map +1 -0
  4. package/dist/cli/commands/init.js +23 -0
  5. package/dist/cli/commands/init.js.map +1 -0
  6. package/dist/cli/commands/install-hook.d.ts +2 -0
  7. package/dist/cli/commands/install-hook.d.ts.map +1 -0
  8. package/dist/cli/commands/install-hook.js +31 -0
  9. package/dist/cli/commands/install-hook.js.map +1 -0
  10. package/dist/cli/commands/scan.d.ts +5 -0
  11. package/dist/cli/commands/scan.d.ts.map +1 -0
  12. package/dist/cli/commands/scan.js +75 -0
  13. package/dist/cli/commands/scan.js.map +1 -0
  14. package/dist/cli/commands/serve.d.ts +4 -0
  15. package/dist/cli/commands/serve.d.ts.map +1 -0
  16. package/dist/cli/commands/serve.js +8 -0
  17. package/dist/cli/commands/serve.js.map +1 -0
  18. package/dist/cli/commands/status.d.ts +2 -0
  19. package/dist/cli/commands/status.d.ts.map +1 -0
  20. package/dist/cli/commands/status.js +70 -0
  21. package/dist/cli/commands/status.js.map +1 -0
  22. package/dist/cli/index.d.ts +3 -0
  23. package/dist/cli/index.d.ts.map +1 -0
  24. package/dist/cli/index.js +38 -0
  25. package/dist/cli/index.js.map +1 -0
  26. package/dist/config/defaults.d.ts +13 -0
  27. package/dist/config/defaults.d.ts.map +1 -0
  28. package/dist/config/defaults.js +36 -0
  29. package/dist/config/defaults.js.map +1 -0
  30. package/dist/config/loader.d.ts +4 -0
  31. package/dist/config/loader.d.ts.map +1 -0
  32. package/dist/config/loader.js +28 -0
  33. package/dist/config/loader.js.map +1 -0
  34. package/dist/dashboard/layout.d.ts +6 -0
  35. package/dist/dashboard/layout.d.ts.map +1 -0
  36. package/dist/dashboard/layout.js +47 -0
  37. package/dist/dashboard/layout.js.map +1 -0
  38. package/dist/dashboard/server.d.ts +5 -0
  39. package/dist/dashboard/server.d.ts.map +1 -0
  40. package/dist/dashboard/server.js +305 -0
  41. package/dist/dashboard/server.js.map +1 -0
  42. package/dist/dashboard/settings-client.d.ts +2 -0
  43. package/dist/dashboard/settings-client.d.ts.map +1 -0
  44. package/dist/dashboard/settings-client.js +310 -0
  45. package/dist/dashboard/settings-client.js.map +1 -0
  46. package/dist/dashboard/settings.d.ts +3 -0
  47. package/dist/dashboard/settings.d.ts.map +1 -0
  48. package/dist/dashboard/settings.js +99 -0
  49. package/dist/dashboard/settings.js.map +1 -0
  50. package/dist/dashboard/views.d.ts +8 -0
  51. package/dist/dashboard/views.d.ts.map +1 -0
  52. package/dist/dashboard/views.js +694 -0
  53. package/dist/dashboard/views.js.map +1 -0
  54. package/dist/dashboard/wizard-client.d.ts +2 -0
  55. package/dist/dashboard/wizard-client.d.ts.map +1 -0
  56. package/dist/dashboard/wizard-client.js +266 -0
  57. package/dist/dashboard/wizard-client.js.map +1 -0
  58. package/dist/dashboard/wizard.d.ts +9 -0
  59. package/dist/dashboard/wizard.d.ts.map +1 -0
  60. package/dist/dashboard/wizard.js +236 -0
  61. package/dist/dashboard/wizard.js.map +1 -0
  62. package/dist/scanner/git.d.ts +8 -0
  63. package/dist/scanner/git.d.ts.map +1 -0
  64. package/dist/scanner/git.js +34 -0
  65. package/dist/scanner/git.js.map +1 -0
  66. package/dist/scanner/index.d.ts +10 -0
  67. package/dist/scanner/index.d.ts.map +1 -0
  68. package/dist/scanner/index.js +60 -0
  69. package/dist/scanner/index.js.map +1 -0
  70. package/dist/storage/db.d.ts +3 -0
  71. package/dist/storage/db.d.ts.map +1 -0
  72. package/dist/storage/db.js +52 -0
  73. package/dist/storage/db.js.map +1 -0
  74. package/dist/storage/snapshots.d.ts +43 -0
  75. package/dist/storage/snapshots.d.ts.map +1 -0
  76. package/dist/storage/snapshots.js +102 -0
  77. package/dist/storage/snapshots.js.map +1 -0
  78. package/package.json +60 -0
@@ -0,0 +1,310 @@
1
+ export const SETTINGS_CLIENT_SCRIPT = `<script>
2
+ document.addEventListener('DOMContentLoaded', function() {
3
+ var debounceTimer = null;
4
+ var currentBrowseDir = null;
5
+
6
+ function escapeHtml(str) {
7
+ var div = document.createElement('div');
8
+ div.textContent = str;
9
+ return div.innerHTML;
10
+ }
11
+
12
+ // --- Chip toggling ---
13
+ function initChipToggling(container) {
14
+ if (!container) return;
15
+ container.querySelectorAll('[data-chip]').forEach(function(chip) {
16
+ chip.addEventListener('click', function() {
17
+ var isSelected = chip.getAttribute('data-selected') === 'true';
18
+ chip.setAttribute('data-selected', isSelected ? 'false' : 'true');
19
+ if (isSelected) {
20
+ chip.classList.remove('bg-blue-100', 'text-blue-800', 'border-blue-300');
21
+ chip.classList.add('bg-gray-100', 'text-gray-600', 'border-gray-300');
22
+ } else {
23
+ chip.classList.remove('bg-gray-100', 'text-gray-600', 'border-gray-300');
24
+ chip.classList.add('bg-blue-100', 'text-blue-800', 'border-blue-300');
25
+ }
26
+ scheduleRefresh();
27
+ });
28
+ });
29
+ }
30
+
31
+ initChipToggling(document.getElementById('roots-list'));
32
+ initChipToggling(document.getElementById('patterns-list'));
33
+
34
+ // --- Collect selected values ---
35
+ function getSelectedRoots() {
36
+ var values = [];
37
+ document.getElementById('roots-list').querySelectorAll('[data-chip][data-selected="true"]').forEach(function(chip) {
38
+ values.push(chip.getAttribute('data-value'));
39
+ });
40
+ document.getElementById('roots-list').querySelectorAll('[data-custom-value]').forEach(function(tag) {
41
+ values.push(tag.getAttribute('data-custom-value'));
42
+ });
43
+ return values;
44
+ }
45
+
46
+ function getSelectedPatterns() {
47
+ var values = [];
48
+ document.getElementById('patterns-list').querySelectorAll('[data-chip][data-selected="true"]').forEach(function(chip) {
49
+ values.push(chip.getAttribute('data-value'));
50
+ });
51
+ document.getElementById('patterns-list').querySelectorAll('[data-custom-value]').forEach(function(tag) {
52
+ values.push(tag.getAttribute('data-custom-value'));
53
+ });
54
+ return values;
55
+ }
56
+
57
+ // --- Auto-refresh file preview ---
58
+ function scheduleRefresh() {
59
+ clearTimeout(debounceTimer);
60
+ debounceTimer = setTimeout(refreshPreview, 300);
61
+ }
62
+
63
+ function refreshPreview() {
64
+ var roots = getSelectedRoots();
65
+ var patterns = getSelectedPatterns();
66
+ var preview = document.getElementById('file-preview');
67
+
68
+ if (roots.length === 0 || patterns.length === 0) {
69
+ preview.innerHTML = '<p class="text-gray-400 text-sm">Select at least one folder and one pattern.</p>';
70
+ return;
71
+ }
72
+
73
+ preview.style.minHeight = preview.offsetHeight + 'px';
74
+
75
+ fetch('/api/setup/discover', {
76
+ method: 'POST',
77
+ headers: { 'Content-Type': 'application/json' },
78
+ body: JSON.stringify({ roots: roots, patterns: patterns })
79
+ })
80
+ .then(function(res) { return res.json(); })
81
+ .then(function(data) {
82
+ renderFilePreview(data.files || []);
83
+ })
84
+ .catch(function() {
85
+ preview.innerHTML = '<p class="text-red-500 text-sm">Error scanning files.</p>';
86
+ });
87
+ }
88
+
89
+ function renderFilePreview(files) {
90
+ var preview = document.getElementById('file-preview');
91
+
92
+ if (files.length === 0) {
93
+ preview.innerHTML = '<p class="text-gray-400 text-sm">No matching files found.</p>';
94
+ return;
95
+ }
96
+
97
+ // Group by root (provided by server)
98
+ var groups = {};
99
+ var rootOrder = [];
100
+ files.forEach(function(f) {
101
+ var root = f.root || 'Other';
102
+ if (!groups[root]) {
103
+ groups[root] = [];
104
+ rootOrder.push(root);
105
+ }
106
+ groups[root].push(f);
107
+ });
108
+
109
+ var html = '<div class="flex gap-3 mb-4">' +
110
+ '<button type="button" id="preview-select-all" class="text-sm text-blue-600 hover:underline">Select all</button>' +
111
+ '<button type="button" id="preview-deselect-all" class="text-sm text-blue-600 hover:underline">Deselect all</button>' +
112
+ '<span class="text-sm text-gray-400">' + files.length + ' file' + (files.length === 1 ? '' : 's') + '</span>' +
113
+ '</div>';
114
+
115
+ rootOrder.forEach(function(root) {
116
+ var rootFiles = groups[root];
117
+ if (!rootFiles || rootFiles.length === 0) return;
118
+
119
+ html += '<div class="mb-4">';
120
+ html += '<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-2">' + escapeHtml(root) + '</h3>';
121
+ html += '<div class="space-y-1">';
122
+
123
+ rootFiles.forEach(function(file) {
124
+ // Show path relative to the root for cleaner display
125
+ var displayPath = file.path;
126
+ if (root !== 'Other' && displayPath.indexOf(root) === 0) {
127
+ displayPath = displayPath.slice(root.length + 1);
128
+ }
129
+ html += '<label class="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-gray-50 cursor-pointer">' +
130
+ '<input type="checkbox" name="files" value="' + escapeHtml(file.path) + '" checked ' +
131
+ 'class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">' +
132
+ '<span class="font-mono text-sm">' + escapeHtml(displayPath) + '</span>' +
133
+ '</label>';
134
+ });
135
+
136
+ html += '</div></div>';
137
+ });
138
+
139
+ preview.innerHTML = html;
140
+ preview.style.minHeight = '';
141
+
142
+ // Wire up select/deselect all
143
+ var selectAll = document.getElementById('preview-select-all');
144
+ var deselectAll = document.getElementById('preview-deselect-all');
145
+ if (selectAll) {
146
+ selectAll.addEventListener('click', function() {
147
+ preview.querySelectorAll('input[name="files"]').forEach(function(cb) { cb.checked = true; });
148
+ });
149
+ }
150
+ if (deselectAll) {
151
+ deselectAll.addEventListener('click', function() {
152
+ preview.querySelectorAll('input[name="files"]').forEach(function(cb) { cb.checked = false; });
153
+ });
154
+ }
155
+ }
156
+
157
+ // --- Folder browser ---
158
+ var openBrowserBtn = document.getElementById('open-browser');
159
+ var folderBrowser = document.getElementById('folder-browser');
160
+ var browserPath = document.getElementById('browser-path');
161
+ var browserList = document.getElementById('browser-list');
162
+ var browserUp = document.getElementById('browser-up');
163
+ var browserAdd = document.getElementById('browser-add');
164
+ var rootsList = document.getElementById('roots-list');
165
+
166
+ if (browserAdd) {
167
+ browserAdd.addEventListener('click', function() {
168
+ if (currentBrowseDir) {
169
+ addCustomChip(rootsList, currentBrowseDir);
170
+ scheduleRefresh();
171
+ }
172
+ });
173
+ }
174
+
175
+ function addCustomChip(container, value) {
176
+ // Don't add duplicates
177
+ var existing = container.querySelectorAll('[data-custom-value]');
178
+ for (var i = 0; i < existing.length; i++) {
179
+ if (existing[i].getAttribute('data-custom-value') === value) return;
180
+ }
181
+ var chips = container.querySelectorAll('[data-chip]');
182
+ for (var i = 0; i < chips.length; i++) {
183
+ if (chips[i].getAttribute('data-value') === value) return;
184
+ }
185
+
186
+ var tag = document.createElement('span');
187
+ tag.setAttribute('data-custom-value', value);
188
+ 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';
189
+ tag.innerHTML = escapeHtml(value) + ' <button type="button" class="ml-1 text-blue-500 hover:text-blue-700 font-bold">&times;</button>';
190
+ tag.querySelector('button').addEventListener('click', function() {
191
+ tag.remove();
192
+ scheduleRefresh();
193
+ });
194
+ container.appendChild(tag);
195
+ }
196
+
197
+ function loadBrowserDir(dir) {
198
+ fetch('/api/setup/browse?dir=' + encodeURIComponent(dir || '~'))
199
+ .then(function(res) { return res.json(); })
200
+ .then(function(data) {
201
+ if (data.error) return;
202
+ currentBrowseDir = data.current;
203
+ if (browserPath) browserPath.textContent = data.current;
204
+ if (browserUp) {
205
+ browserUp.style.display = data.parent ? '' : 'none';
206
+ browserUp.onclick = function() { loadBrowserDir(data.parent); };
207
+ }
208
+ if (!browserList) return;
209
+ browserList.innerHTML = '';
210
+
211
+ data.dirs.forEach(function(entry) {
212
+ var row = document.createElement('div');
213
+ row.className = 'px-4 py-2 hover:bg-gray-50 cursor-pointer flex items-center gap-2 text-sm';
214
+ row.innerHTML = '<span class="text-gray-400">&#128193;</span> ' + escapeHtml(entry.name);
215
+ row.addEventListener('click', function() {
216
+ loadBrowserDir(entry.path);
217
+ });
218
+ browserList.appendChild(row);
219
+ });
220
+ });
221
+ }
222
+
223
+ if (openBrowserBtn && folderBrowser) {
224
+ openBrowserBtn.addEventListener('click', function() {
225
+ var isHidden = folderBrowser.classList.contains('hidden');
226
+ if (isHidden) {
227
+ folderBrowser.classList.remove('hidden');
228
+ openBrowserBtn.textContent = 'Hide browser';
229
+ loadBrowserDir('~');
230
+ } else {
231
+ folderBrowser.classList.add('hidden');
232
+ openBrowserBtn.textContent = 'Browse for folder...';
233
+ }
234
+ });
235
+ }
236
+
237
+ // --- Custom pattern input ---
238
+ var addBtn = document.getElementById('add-custom');
239
+ var customInput = document.getElementById('custom-input');
240
+ var patternsList = document.getElementById('patterns-list');
241
+
242
+ function addCustomPattern() {
243
+ if (!customInput || !patternsList) return;
244
+ var value = customInput.value.trim();
245
+ if (!value) return;
246
+ addCustomChip(patternsList, value);
247
+ customInput.value = '';
248
+ scheduleRefresh();
249
+ }
250
+
251
+ if (addBtn) {
252
+ addBtn.addEventListener('click', addCustomPattern);
253
+ }
254
+ if (customInput) {
255
+ customInput.addEventListener('keydown', function(e) {
256
+ if (e.key === 'Enter') {
257
+ e.preventDefault();
258
+ addCustomPattern();
259
+ }
260
+ });
261
+ }
262
+
263
+ // --- Save ---
264
+ var saveBtn = document.getElementById('save-btn');
265
+ var saveStatus = document.getElementById('save-status');
266
+
267
+ if (saveBtn) {
268
+ saveBtn.addEventListener('click', function() {
269
+ saveBtn.disabled = true;
270
+ saveBtn.textContent = 'Saving...';
271
+ if (saveStatus) saveStatus.classList.add('hidden');
272
+
273
+ var roots = getSelectedRoots();
274
+ var patterns = getSelectedPatterns();
275
+
276
+ var excludedFiles = [];
277
+ document.querySelectorAll('#file-preview input[name="files"]').forEach(function(cb) {
278
+ if (!cb.checked) {
279
+ excludedFiles.push(cb.value);
280
+ }
281
+ });
282
+
283
+ fetch('/api/setup/confirm', {
284
+ method: 'POST',
285
+ headers: { 'Content-Type': 'application/json' },
286
+ body: JSON.stringify({ roots: roots, patterns: patterns, excludedFiles: excludedFiles })
287
+ })
288
+ .then(function(res) { return res.json(); })
289
+ .then(function(data) {
290
+ saveBtn.disabled = false;
291
+ saveBtn.textContent = 'Save';
292
+ if (data.success && saveStatus) {
293
+ saveStatus.textContent = 'Settings saved. Tracking ' + data.trackedCount + ' file' + (data.trackedCount === 1 ? '' : 's') + '.';
294
+ saveStatus.classList.remove('hidden');
295
+ setTimeout(function() { saveStatus.classList.add('hidden'); }, 3000);
296
+ }
297
+ })
298
+ .catch(function() {
299
+ saveBtn.disabled = false;
300
+ saveBtn.textContent = 'Save';
301
+ alert('Error saving settings. Please try again.');
302
+ });
303
+ });
304
+ }
305
+
306
+ // --- Initial load ---
307
+ refreshPreview();
308
+ });
309
+ </script>`;
310
+ //# sourceMappingURL=settings-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings-client.js","sourceRoot":"","sources":["../../src/dashboard/settings-client.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAoT5B,CAAC"}
@@ -0,0 +1,3 @@
1
+ import { DotmdConfig } from "../config/defaults.js";
2
+ export declare function settingsPage(config: DotmdConfig, suggestedRoots: string[], suggestedPatterns: string[]): string;
3
+ //# sourceMappingURL=settings.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/dashboard/settings.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAwBpD,wBAAgB,YAAY,CAC1B,MAAM,EAAE,WAAW,EACnB,cAAc,EAAE,MAAM,EAAE,EACxB,iBAAiB,EAAE,MAAM,EAAE,GAC1B,MAAM,CAiFR"}
@@ -0,0 +1,99 @@
1
+ function escapeHtml(str) {
2
+ return str
3
+ .replace(/&/g, "&amp;")
4
+ .replace(/</g, "&lt;")
5
+ .replace(/>/g, "&gt;")
6
+ .replace(/"/g, "&quot;");
7
+ }
8
+ function chipHtml(value, label, selected) {
9
+ const selectedClasses = selected
10
+ ? "bg-blue-100 text-blue-800 border-blue-300"
11
+ : "bg-gray-100 text-gray-600 border-gray-300";
12
+ return `<span data-chip data-value="${escapeHtml(value)}" data-selected="${selected}"
13
+ class="inline-flex items-center px-3 py-1.5 rounded-full text-sm font-medium border cursor-pointer select-none transition-colors ${selectedClasses}">
14
+ ${escapeHtml(label)}
15
+ </span>`;
16
+ }
17
+ export function settingsPage(config, suggestedRoots, suggestedPatterns) {
18
+ // Roots: show current config roots as selected, suggested ones not in config as deselected
19
+ const configRootSet = new Set(config.scan_roots);
20
+ const allRoots = [...config.scan_roots];
21
+ for (const r of suggestedRoots) {
22
+ if (!configRootSet.has(r))
23
+ allRoots.push(r);
24
+ }
25
+ const rootChips = allRoots
26
+ .map((r) => chipHtml(r, r, configRootSet.has(r)))
27
+ .join("\n ");
28
+ // Patterns: show current config patterns as selected, suggested ones not in config as deselected
29
+ const configPatternSet = new Set(config.patterns);
30
+ const allPatterns = [...config.patterns];
31
+ for (const p of suggestedPatterns) {
32
+ if (!configPatternSet.has(p))
33
+ allPatterns.push(p);
34
+ }
35
+ const patternChips = allPatterns
36
+ .map((p) => chipHtml(p, p, configPatternSet.has(p)))
37
+ .join("\n ");
38
+ return `
39
+ <div class="max-w-3xl mx-auto">
40
+ <h1 class="text-2xl font-bold mb-6">Settings</h1>
41
+
42
+ <!-- Folders -->
43
+ <section class="mb-8">
44
+ <h2 class="text-lg font-semibold mb-2">Folders</h2>
45
+ <p class="text-gray-500 text-sm mb-4">Directories to scan for instruction files. Click to toggle.</p>
46
+ <div id="roots-list" class="flex flex-wrap gap-2 mb-4">
47
+ ${rootChips}
48
+ </div>
49
+ <button id="open-browser" type="button"
50
+ class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-medium hover:bg-gray-200 transition-colors border border-gray-300">
51
+ Browse for folder...
52
+ </button>
53
+ <div id="folder-browser" class="hidden mt-3 bg-white border border-gray-200 rounded-lg overflow-hidden">
54
+ <div id="browser-header" class="px-4 py-2 bg-gray-50 border-b border-gray-200 flex items-center justify-between text-sm">
55
+ <div class="flex items-center gap-2">
56
+ <button id="browser-up" type="button" class="text-blue-600 hover:underline font-medium">Up</button>
57
+ <span id="browser-path" class="text-gray-500 font-mono text-xs"></span>
58
+ </div>
59
+ <button id="browser-add" type="button" class="px-3 py-1 bg-blue-600 text-white rounded text-xs font-medium hover:bg-blue-700">Add this folder</button>
60
+ </div>
61
+ <div id="browser-list" class="max-h-64 overflow-y-auto divide-y divide-gray-100"></div>
62
+ </div>
63
+ </section>
64
+
65
+ <!-- Patterns -->
66
+ <section class="mb-8">
67
+ <h2 class="text-lg font-semibold mb-2">Patterns</h2>
68
+ <p class="text-gray-500 text-sm mb-4">Filename patterns to match within your folders. Click to toggle.</p>
69
+ <div id="patterns-list" class="flex flex-wrap gap-2 mb-4">
70
+ ${patternChips}
71
+ </div>
72
+ <div class="flex gap-2">
73
+ <input id="custom-input" type="text" placeholder="*.md"
74
+ class="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:border-blue-400">
75
+ <button id="add-custom" type="button"
76
+ class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg text-sm font-medium hover:bg-gray-200 transition-colors border border-gray-300">Add</button>
77
+ </div>
78
+ </section>
79
+
80
+ <!-- File Preview -->
81
+ <section class="mb-8">
82
+ <h2 class="text-lg font-semibold mb-2">Tracked Files</h2>
83
+ <p class="text-gray-500 text-sm mb-4">Files matching your current folders and patterns. Uncheck any you don't want to track.</p>
84
+ <div id="file-preview" class="bg-white border border-gray-200 rounded-lg p-4 min-h-[100px]">
85
+ <p class="text-gray-400 text-sm">Loading...</p>
86
+ </div>
87
+ </section>
88
+
89
+ <!-- Save -->
90
+ <div class="flex items-center gap-4">
91
+ <button id="save-btn" type="button"
92
+ class="px-6 py-2.5 bg-blue-600 text-white rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors">
93
+ Save
94
+ </button>
95
+ <span id="save-status" class="text-sm text-green-600 hidden">Settings saved.</span>
96
+ </div>
97
+ </div>`;
98
+ }
99
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/dashboard/settings.ts"],"names":[],"mappings":"AAGA,SAAS,UAAU,CAAC,GAAW;IAC7B,OAAO,GAAG;SACP,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC;SACtB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC;SACrB,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;AAC7B,CAAC;AAED,SAAS,QAAQ,CACf,KAAa,EACb,KAAa,EACb,QAAiB;IAEjB,MAAM,eAAe,GAAG,QAAQ;QAC9B,CAAC,CAAC,2CAA2C;QAC7C,CAAC,CAAC,2CAA2C,CAAC;IAChD,OAAO,+BAA+B,UAAU,CAAC,KAAK,CAAC,oBAAoB,QAAQ;uIACkD,eAAe;MAChJ,UAAU,CAAC,KAAK,CAAC;UACb,CAAC;AACX,CAAC;AAED,MAAM,UAAU,YAAY,CAC1B,MAAmB,EACnB,cAAwB,EACxB,iBAA2B;IAE3B,2FAA2F;IAC3F,MAAM,aAAa,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;IACjD,MAAM,QAAQ,GAAG,CAAC,GAAG,MAAM,CAAC,UAAU,CAAC,CAAC;IACxC,KAAK,MAAM,CAAC,IAAI,cAAc,EAAE,CAAC;QAC/B,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC9C,CAAC;IACD,MAAM,SAAS,GAAG,QAAQ;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SAChD,IAAI,CAAC,YAAY,CAAC,CAAC;IAEtB,iGAAiG;IACjG,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAClD,MAAM,WAAW,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC;IACzC,KAAK,MAAM,CAAC,IAAI,iBAAiB,EAAE,CAAC;QAClC,IAAI,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC;YAAE,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpD,CAAC;IACD,MAAM,YAAY,GAAG,WAAW;SAC7B,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;SACnD,IAAI,CAAC,YAAY,CAAC,CAAC;IAEtB,OAAO;;;;;;;;;YASG,SAAS;;;;;;;;;;;;;;;;;;;;;;;YAuBT,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;WA2Bb,CAAC;AACZ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { TrackedFile, Snapshot } from "../storage/snapshots.js";
2
+ export declare function fileListPage(files: TrackedFile[], lastUpdated: Map<number, string>, unseenChanges: Set<number>): string;
3
+ export declare function fileDetailPage(file: TrackedFile, snapshots: Snapshot[], hasMore?: boolean): string;
4
+ export declare function timelinePage(changes: (Snapshot & {
5
+ path: string;
6
+ category: string;
7
+ })[], unseenChanges?: Set<number>, hasMore?: boolean): string;
8
+ //# sourceMappingURL=views.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"views.d.ts","sourceRoot":"","sources":["../../src/dashboard/views.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAC;AAkQhE,wBAAgB,YAAY,CAC1B,KAAK,EAAE,WAAW,EAAE,EACpB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GACzB,MAAM,CAyGR;AAED,wBAAgB,cAAc,CAC5B,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,QAAQ,EAAE,EACrB,OAAO,GAAE,OAAe,GACvB,MAAM,CA6LR;AAED,wBAAgB,YAAY,CAC1B,OAAO,EAAE,CAAC,QAAQ,GAAG;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,EAAE,EAC1D,aAAa,GAAE,GAAG,CAAC,MAAM,CAAa,EACtC,OAAO,GAAE,OAAe,GACvB,MAAM,CAwLR"}