@mattli/dotmd 0.1.0 → 0.1.2
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.
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +12 -7
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/serve.d.ts.map +1 -1
- package/dist/cli/commands/serve.js +18 -1
- package/dist/cli/commands/serve.js.map +1 -1
- package/dist/cli/commands/status.d.ts.map +1 -1
- package/dist/cli/commands/status.js +10 -5
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/dashboard/_archive/wizard-client.d.ts +2 -0
- package/dist/dashboard/_archive/wizard-client.d.ts.map +1 -0
- package/dist/dashboard/_archive/wizard-client.js +412 -0
- package/dist/dashboard/_archive/wizard-client.js.map +1 -0
- package/dist/dashboard/_archive/wizard.d.ts +9 -0
- package/dist/dashboard/_archive/wizard.d.ts.map +1 -0
- package/dist/dashboard/_archive/wizard.js +317 -0
- package/dist/dashboard/_archive/wizard.js.map +1 -0
- package/dist/dashboard/help.d.ts +2 -0
- package/dist/dashboard/help.d.ts.map +1 -0
- package/dist/dashboard/help.js +73 -0
- package/dist/dashboard/help.js.map +1 -0
- package/dist/dashboard/layout.d.ts.map +1 -1
- package/dist/dashboard/layout.js +1 -0
- package/dist/dashboard/layout.js.map +1 -1
- package/dist/dashboard/server.d.ts +1 -1
- package/dist/dashboard/server.d.ts.map +1 -1
- package/dist/dashboard/server.js +39 -51
- package/dist/dashboard/server.js.map +1 -1
- package/dist/dashboard/settings-client.d.ts +1 -1
- package/dist/dashboard/settings-client.d.ts.map +1 -1
- package/dist/dashboard/settings-client.js +367 -162
- package/dist/dashboard/settings-client.js.map +1 -1
- package/dist/dashboard/settings.d.ts +5 -1
- package/dist/dashboard/settings.d.ts.map +1 -1
- package/dist/dashboard/settings.js +60 -45
- package/dist/dashboard/settings.js.map +1 -1
- package/dist/dashboard/wizard-client.d.ts +1 -1
- package/dist/dashboard/wizard-client.d.ts.map +1 -1
- package/dist/dashboard/wizard-client.js +226 -80
- package/dist/dashboard/wizard-client.js.map +1 -1
- package/dist/dashboard/wizard.d.ts +2 -2
- package/dist/dashboard/wizard.d.ts.map +1 -1
- package/dist/dashboard/wizard.js +113 -32
- package/dist/dashboard/wizard.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
import os from "os";
|
|
2
|
+
import { expandHome } from "../config/defaults.js";
|
|
3
|
+
function escapeHtml(str) {
|
|
4
|
+
return str
|
|
5
|
+
.replace(/&/g, "&")
|
|
6
|
+
.replace(/</g, "<")
|
|
7
|
+
.replace(/>/g, ">")
|
|
8
|
+
.replace(/"/g, """);
|
|
9
|
+
}
|
|
10
|
+
function displayPath(p) {
|
|
11
|
+
return p.replace(os.homedir(), "~");
|
|
12
|
+
}
|
|
13
|
+
function stepIndicator(current) {
|
|
14
|
+
const steps = ["Folders", "Patterns", "Preview"];
|
|
15
|
+
return `<div class="flex items-center justify-center gap-2 mb-8">
|
|
16
|
+
${steps
|
|
17
|
+
.map((label, i) => {
|
|
18
|
+
const num = i + 1;
|
|
19
|
+
const isActive = num === current;
|
|
20
|
+
const isDone = num < current;
|
|
21
|
+
const circleClass = isActive
|
|
22
|
+
? "bg-blue-600 text-white"
|
|
23
|
+
: isDone
|
|
24
|
+
? "bg-blue-200 text-blue-800"
|
|
25
|
+
: "bg-gray-200 text-gray-500";
|
|
26
|
+
const labelClass = isActive
|
|
27
|
+
? "text-blue-600 font-semibold"
|
|
28
|
+
: "text-gray-400";
|
|
29
|
+
const connector = i < steps.length - 1
|
|
30
|
+
? `<div class="w-12 h-px ${num < current ? "bg-blue-300" : "bg-gray-200"}"></div>`
|
|
31
|
+
: "";
|
|
32
|
+
return `<div class="flex items-center gap-2">
|
|
33
|
+
<span class="w-7 h-7 rounded-full flex items-center justify-center text-sm font-medium ${circleClass}">${num}</span>
|
|
34
|
+
<span class="text-sm ${labelClass}">${label}</span>
|
|
35
|
+
</div>${connector}`;
|
|
36
|
+
})
|
|
37
|
+
.join("")}
|
|
38
|
+
</div>`;
|
|
39
|
+
}
|
|
40
|
+
function chipHtml(value, label, selected) {
|
|
41
|
+
const selectedClasses = selected
|
|
42
|
+
? "bg-blue-100 text-blue-800 border-blue-300"
|
|
43
|
+
: "bg-gray-100 text-gray-600 border-gray-300";
|
|
44
|
+
return `<span data-chip data-value="${escapeHtml(value)}" data-selected="${selected}"
|
|
45
|
+
class="inline-flex items-center px-3 py-1.5 rounded-full text-sm font-medium border cursor-pointer select-none transition-colors ${selectedClasses}">
|
|
46
|
+
${escapeHtml(label)}
|
|
47
|
+
</span>`;
|
|
48
|
+
}
|
|
49
|
+
function folderChipHtml(value, label) {
|
|
50
|
+
return `<span data-folder-chip="${escapeHtml(value)}"
|
|
51
|
+
class="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">
|
|
52
|
+
${escapeHtml(label)} <button type="button" class="ml-1 text-blue-500 hover:text-blue-700 font-bold">×</button>
|
|
53
|
+
</span>`;
|
|
54
|
+
}
|
|
55
|
+
function patternChipHtml(value) {
|
|
56
|
+
return `<span data-pattern-chip="${escapeHtml(value)}"
|
|
57
|
+
class="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">
|
|
58
|
+
${escapeHtml(value)} <button type="button" class="ml-1 text-blue-500 hover:text-blue-700 font-bold">×</button>
|
|
59
|
+
</span>`;
|
|
60
|
+
}
|
|
61
|
+
function customChipHtml(value, label) {
|
|
62
|
+
return `<span data-custom-value="${escapeHtml(value)}"
|
|
63
|
+
class="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">
|
|
64
|
+
${escapeHtml(label)} <button type="button" class="ml-1 text-blue-500 hover:text-blue-700 font-bold">×</button>
|
|
65
|
+
</span>`;
|
|
66
|
+
}
|
|
67
|
+
export function setupFoldersPage(existingDirs, preservedRoots) {
|
|
68
|
+
const hasPreserved = preservedRoots && preservedRoots.length > 0;
|
|
69
|
+
const selectedRoots = hasPreserved ? preservedRoots : existingDirs;
|
|
70
|
+
const chips = selectedRoots
|
|
71
|
+
.map((root) => folderChipHtml(root, root))
|
|
72
|
+
.join("\n ");
|
|
73
|
+
return `
|
|
74
|
+
<div class="max-w-2xl mx-auto">
|
|
75
|
+
${stepIndicator(1)}
|
|
76
|
+
<h1 class="text-2xl font-bold mb-2">Where are your projects?</h1>
|
|
77
|
+
<p class="text-gray-500 mb-6">Select the folders where dotmd should look for instruction files.</p>
|
|
78
|
+
|
|
79
|
+
<div class="mb-6">
|
|
80
|
+
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-3">Folders</h2>
|
|
81
|
+
<div id="folder-list" class="flex flex-wrap gap-2">
|
|
82
|
+
${chips}
|
|
83
|
+
</div>
|
|
84
|
+
<p id="no-folders-msg" class="text-gray-400 text-sm mt-2 ${selectedRoots.length > 0 ? "hidden" : ""}">No folders selected. Use the browser below to add folders.</p>
|
|
85
|
+
</div>
|
|
86
|
+
|
|
87
|
+
<div class="mb-8">
|
|
88
|
+
<button id="open-browser" type="button"
|
|
89
|
+
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">
|
|
90
|
+
Browse for folder...
|
|
91
|
+
</button>
|
|
92
|
+
<div id="folder-browser" class="hidden mt-3 bg-white border border-gray-200 rounded-lg overflow-hidden">
|
|
93
|
+
<div id="browser-header" class="px-4 py-2 bg-gray-50 border-b border-gray-200 flex items-center gap-2 text-sm">
|
|
94
|
+
<button id="browser-up" type="button" class="text-blue-600 hover:underline font-medium">Up</button>
|
|
95
|
+
<span id="browser-path" class="text-gray-500 font-mono text-xs"></span>
|
|
96
|
+
</div>
|
|
97
|
+
<div id="browser-list" class="max-h-64 overflow-y-auto divide-y divide-gray-100"></div>
|
|
98
|
+
</div>
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
<div class="flex justify-end">
|
|
102
|
+
<button id="next-btn" data-step="1" type="button"
|
|
103
|
+
class="px-6 py-2.5 bg-blue-600 text-white rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors">
|
|
104
|
+
Next
|
|
105
|
+
</button>
|
|
106
|
+
</div>
|
|
107
|
+
</div>`;
|
|
108
|
+
}
|
|
109
|
+
export function setupPatternsPage(selectedRoots, suggestedPatterns, selectedPatterns) {
|
|
110
|
+
const selectedSet = new Set(selectedPatterns);
|
|
111
|
+
// Selected patterns — shown as removable chips
|
|
112
|
+
const selectedChips = selectedPatterns
|
|
113
|
+
.map((p) => patternChipHtml(p))
|
|
114
|
+
.join("\n ");
|
|
115
|
+
// Unselected suggested patterns — shown as addable chips
|
|
116
|
+
const unselectedSuggested = suggestedPatterns.filter((p) => !selectedSet.has(p));
|
|
117
|
+
const suggestedChips = unselectedSuggested
|
|
118
|
+
.map((p) => `<span data-suggested-pattern="${escapeHtml(p)}"
|
|
119
|
+
class="inline-flex items-center gap-1 px-3 py-1.5 rounded-full text-sm font-medium bg-gray-100 text-gray-600 border border-gray-300 cursor-pointer hover:bg-gray-200 transition-colors select-none">
|
|
120
|
+
+ ${escapeHtml(p)}
|
|
121
|
+
</span>`)
|
|
122
|
+
.join("\n ");
|
|
123
|
+
const rootsParam = selectedRoots
|
|
124
|
+
.map((r) => `roots=${encodeURIComponent(r)}`)
|
|
125
|
+
.join("&");
|
|
126
|
+
return `
|
|
127
|
+
<div class="max-w-2xl mx-auto">
|
|
128
|
+
${stepIndicator(2)}
|
|
129
|
+
<h1 class="text-2xl font-bold mb-2">What files should we look for?</h1>
|
|
130
|
+
<p class="text-gray-500 mb-6">Select the filename patterns to search for in your selected folders.</p>
|
|
131
|
+
|
|
132
|
+
<div class="mb-4">
|
|
133
|
+
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-3">Patterns</h2>
|
|
134
|
+
<div id="pattern-list" class="flex flex-wrap gap-2">
|
|
135
|
+
${selectedChips}
|
|
136
|
+
</div>
|
|
137
|
+
<p id="no-patterns-msg" class="text-gray-400 text-sm mt-2 ${selectedPatterns.length > 0 ? "hidden" : ""}">No patterns selected.</p>
|
|
138
|
+
</div>
|
|
139
|
+
|
|
140
|
+
${unselectedSuggested.length > 0 ? `<div class="mb-4">
|
|
141
|
+
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-3">Suggestions</h2>
|
|
142
|
+
<div id="suggested-patterns" class="flex flex-wrap gap-2">
|
|
143
|
+
${suggestedChips}
|
|
144
|
+
</div>
|
|
145
|
+
</div>` : ""}
|
|
146
|
+
|
|
147
|
+
<div class="mb-8">
|
|
148
|
+
<div class="flex gap-2">
|
|
149
|
+
<input id="custom-input" type="text" placeholder="rules/*.md"
|
|
150
|
+
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:border-blue-400">
|
|
151
|
+
<button id="add-custom" type="button"
|
|
152
|
+
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>
|
|
153
|
+
</div>
|
|
154
|
+
</div>
|
|
155
|
+
|
|
156
|
+
<div class="flex justify-between">
|
|
157
|
+
<button id="back-btn" type="button"
|
|
158
|
+
class="px-6 py-2.5 bg-gray-100 text-gray-700 rounded-lg text-sm font-medium hover:bg-gray-200 transition-colors">
|
|
159
|
+
Back
|
|
160
|
+
</button>
|
|
161
|
+
<button id="next-btn" data-step="2" type="button"
|
|
162
|
+
class="px-6 py-2.5 bg-blue-600 text-white rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors">
|
|
163
|
+
Preview Files
|
|
164
|
+
</button>
|
|
165
|
+
</div>
|
|
166
|
+
</div>`;
|
|
167
|
+
}
|
|
168
|
+
export function setupPreviewPage(files, roots, patterns) {
|
|
169
|
+
const rootsParam = roots
|
|
170
|
+
.map((r) => `roots=${encodeURIComponent(r)}`)
|
|
171
|
+
.join("&");
|
|
172
|
+
const patternsParam = patterns
|
|
173
|
+
.map((p) => `patterns=${encodeURIComponent(p)}`)
|
|
174
|
+
.join("&");
|
|
175
|
+
const backParams = `${rootsParam}&${patternsParam}`;
|
|
176
|
+
if (files.length === 0) {
|
|
177
|
+
return `
|
|
178
|
+
<div class="max-w-2xl mx-auto">
|
|
179
|
+
${stepIndicator(3)}
|
|
180
|
+
<h1 class="text-2xl font-bold mb-2">No files found</h1>
|
|
181
|
+
<p class="text-gray-500 mb-6">No matching files were found in the selected folders with the selected patterns. Try going back to adjust your selections.</p>
|
|
182
|
+
<a href="/setup/step2?${rootsParam}"
|
|
183
|
+
class="px-6 py-2.5 bg-gray-100 text-gray-700 rounded-lg text-sm font-medium hover:bg-gray-200 transition-colors">
|
|
184
|
+
Back
|
|
185
|
+
</a>
|
|
186
|
+
</div>`;
|
|
187
|
+
}
|
|
188
|
+
// Group by root (scan folder)
|
|
189
|
+
const groups = {};
|
|
190
|
+
const rootOrder = [];
|
|
191
|
+
for (const file of files) {
|
|
192
|
+
const root = file.root || "Other";
|
|
193
|
+
if (!groups[root]) {
|
|
194
|
+
groups[root] = [];
|
|
195
|
+
rootOrder.push(root);
|
|
196
|
+
}
|
|
197
|
+
groups[root].push(file);
|
|
198
|
+
}
|
|
199
|
+
function renderCheckbox(filePath, displayPath) {
|
|
200
|
+
return `
|
|
201
|
+
<label class="flex items-center gap-3 px-3 py-2 rounded-lg hover:bg-gray-50 cursor-pointer">
|
|
202
|
+
<input type="checkbox" name="files" value="${escapeHtml(filePath)}" checked
|
|
203
|
+
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
|
|
204
|
+
<span class="font-mono text-sm">${escapeHtml(displayPath)}</span>
|
|
205
|
+
</label>`;
|
|
206
|
+
}
|
|
207
|
+
let fileListHtml = "";
|
|
208
|
+
for (const root of rootOrder) {
|
|
209
|
+
const rootFiles = groups[root];
|
|
210
|
+
if (!rootFiles || rootFiles.length === 0)
|
|
211
|
+
continue;
|
|
212
|
+
const expanded = expandHome(root);
|
|
213
|
+
// Calculate relative paths and group by first subdirectory
|
|
214
|
+
const subGroups = new Map();
|
|
215
|
+
const subGroupOrder = [];
|
|
216
|
+
for (const file of rootFiles) {
|
|
217
|
+
let relPath = file.path;
|
|
218
|
+
if (file.path.startsWith(expanded + "/")) {
|
|
219
|
+
relPath = file.path.slice(expanded.length + 1);
|
|
220
|
+
}
|
|
221
|
+
const firstSlash = relPath.indexOf("/");
|
|
222
|
+
const groupName = firstSlash === -1 ? "" : relPath.slice(0, firstSlash);
|
|
223
|
+
if (!subGroups.has(groupName)) {
|
|
224
|
+
subGroups.set(groupName, []);
|
|
225
|
+
subGroupOrder.push(groupName);
|
|
226
|
+
}
|
|
227
|
+
subGroups.get(groupName).push({ path: file.path, relPath });
|
|
228
|
+
}
|
|
229
|
+
const hasSubGroups = subGroupOrder.length > 1 || subGroupOrder[0] !== "";
|
|
230
|
+
fileListHtml += `<div class="mb-6">
|
|
231
|
+
<div class="flex items-center justify-between mb-2">
|
|
232
|
+
<h3 class="text-sm font-semibold text-gray-500 uppercase tracking-wide">${escapeHtml(root)}</h3>
|
|
233
|
+
<div class="flex gap-3">
|
|
234
|
+
<button type="button" data-group-select="${escapeHtml(root)}" class="text-xs text-blue-600 hover:underline">Select all</button>
|
|
235
|
+
<button type="button" data-group-deselect="${escapeHtml(root)}" class="text-xs text-blue-600 hover:underline">Deselect all</button>
|
|
236
|
+
</div>
|
|
237
|
+
</div>
|
|
238
|
+
<div class="space-y-1" data-group="${escapeHtml(root)}">`;
|
|
239
|
+
if (!hasSubGroups) {
|
|
240
|
+
// Flat list — no subdirectories
|
|
241
|
+
for (const file of subGroups.get("")) {
|
|
242
|
+
fileListHtml += renderCheckbox(file.path, file.relPath);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
else {
|
|
246
|
+
// Render root-level files first (no subdirectory)
|
|
247
|
+
const rootLevel = subGroups.get("");
|
|
248
|
+
if (rootLevel) {
|
|
249
|
+
for (const file of rootLevel) {
|
|
250
|
+
fileListHtml += renderCheckbox(file.path, file.relPath);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// Render each subdirectory as a collapsible group
|
|
254
|
+
for (const groupName of subGroupOrder) {
|
|
255
|
+
if (groupName === "")
|
|
256
|
+
continue;
|
|
257
|
+
const groupFiles = subGroups.get(groupName);
|
|
258
|
+
fileListHtml += `
|
|
259
|
+
<details class="mt-1">
|
|
260
|
+
<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">
|
|
261
|
+
${escapeHtml(groupName)}/ <span class="text-gray-400">(${groupFiles.length})</span>
|
|
262
|
+
</summary>
|
|
263
|
+
<div class="ml-4">`;
|
|
264
|
+
for (const file of groupFiles) {
|
|
265
|
+
fileListHtml += renderCheckbox(file.path, file.relPath);
|
|
266
|
+
}
|
|
267
|
+
fileListHtml += `
|
|
268
|
+
</div>
|
|
269
|
+
</details>`;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
fileListHtml += `</div></div>`;
|
|
273
|
+
}
|
|
274
|
+
return `
|
|
275
|
+
<div class="max-w-2xl mx-auto">
|
|
276
|
+
${stepIndicator(3)}
|
|
277
|
+
<h1 class="text-2xl font-bold mb-2">Review discovered files</h1>
|
|
278
|
+
<p class="text-gray-500 mb-4">Found <strong>${files.length}</strong> file${files.length === 1 ? "" : "s"}. Uncheck any files you don't want to track.</p>
|
|
279
|
+
|
|
280
|
+
<div class="flex gap-3 mb-4">
|
|
281
|
+
<button id="select-all" type="button" class="text-sm text-blue-600 hover:underline">Select all</button>
|
|
282
|
+
<button id="deselect-all" type="button" class="text-sm text-blue-600 hover:underline">Deselect all</button>
|
|
283
|
+
</div>
|
|
284
|
+
|
|
285
|
+
<div class="bg-white border border-gray-200 rounded-lg p-4 mb-6">
|
|
286
|
+
${fileListHtml}
|
|
287
|
+
</div>
|
|
288
|
+
|
|
289
|
+
<div class="flex justify-between">
|
|
290
|
+
<a href="/setup/step2?${backParams}"
|
|
291
|
+
class="px-6 py-2.5 bg-gray-100 text-gray-700 rounded-lg text-sm font-medium hover:bg-gray-200 transition-colors">
|
|
292
|
+
Back
|
|
293
|
+
</a>
|
|
294
|
+
<button id="confirm-btn" type="button"
|
|
295
|
+
class="px-6 py-2.5 bg-blue-600 text-white rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors">
|
|
296
|
+
Confirm & Start Tracking
|
|
297
|
+
</button>
|
|
298
|
+
</div>
|
|
299
|
+
</div>`;
|
|
300
|
+
}
|
|
301
|
+
export function setupCompletePage(fileCount) {
|
|
302
|
+
return `
|
|
303
|
+
<div class="max-w-2xl mx-auto text-center py-16">
|
|
304
|
+
<div class="w-16 h-16 bg-green-100 rounded-full flex items-center justify-center mx-auto mb-6">
|
|
305
|
+
<svg class="w-8 h-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
306
|
+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"></path>
|
|
307
|
+
</svg>
|
|
308
|
+
</div>
|
|
309
|
+
<h1 class="text-2xl font-bold mb-2">Setup complete</h1>
|
|
310
|
+
<p class="text-gray-500 mb-8">Now tracking <strong>${fileCount}</strong> file${fileCount === 1 ? "" : "s"}. dotmd will snapshot changes whenever you scan.</p>
|
|
311
|
+
<a href="/"
|
|
312
|
+
class="px-6 py-2.5 bg-blue-600 text-white rounded-lg text-sm font-medium hover:bg-blue-700 transition-colors">
|
|
313
|
+
Go to Dashboard
|
|
314
|
+
</a>
|
|
315
|
+
</div>`;
|
|
316
|
+
}
|
|
317
|
+
//# sourceMappingURL=wizard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wizard.js","sourceRoot":"","sources":["../../../src/dashboard/_archive/wizard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.d.ts","sourceRoot":"","sources":["../../src/dashboard/help.ts"],"names":[],"mappings":"AAAA,wBAAgB,QAAQ,IAAI,MAAM,CAuEjC"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
export function helpPage() {
|
|
2
|
+
return `
|
|
3
|
+
<div class="max-w-3xl mx-auto pb-24">
|
|
4
|
+
<h1 class="text-2xl font-bold mb-6">Help</h1>
|
|
5
|
+
|
|
6
|
+
<!-- Commands -->
|
|
7
|
+
<section class="mb-8">
|
|
8
|
+
<h2 class="text-lg font-semibold mb-3">Commands</h2>
|
|
9
|
+
<div class="bg-white border border-gray-200 rounded-lg divide-y divide-gray-100">
|
|
10
|
+
<div class="px-4 py-3">
|
|
11
|
+
<code class="text-sm font-mono text-blue-700">dotmd init</code>
|
|
12
|
+
<p class="text-gray-600 text-sm mt-1">Launch the setup page to pick folders and file patterns to track.</p>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="px-4 py-3">
|
|
15
|
+
<code class="text-sm font-mono text-blue-700">dotmd status</code>
|
|
16
|
+
<p class="text-gray-600 text-sm mt-1">Scan for changes and print a summary in your terminal.</p>
|
|
17
|
+
</div>
|
|
18
|
+
<div class="px-4 py-3">
|
|
19
|
+
<code class="text-sm font-mono text-blue-700">dotmd serve</code>
|
|
20
|
+
<p class="text-gray-600 text-sm mt-1">Start this dashboard at <code class="text-xs">http://localhost:3333</code>.</p>
|
|
21
|
+
</div>
|
|
22
|
+
</div>
|
|
23
|
+
</section>
|
|
24
|
+
|
|
25
|
+
<!-- Scanning -->
|
|
26
|
+
<section class="mb-8 pt-8 border-t border-gray-200">
|
|
27
|
+
<h2 class="text-lg font-semibold mb-3">When does dotmd scan?</h2>
|
|
28
|
+
<ul class="space-y-2 text-sm text-gray-600">
|
|
29
|
+
<li class="flex gap-2"><span class="text-gray-400">•</span>Every time you load a dashboard page (Timeline, Files, or File Detail)</li>
|
|
30
|
+
<li class="flex gap-2"><span class="text-gray-400">•</span>When you run <code class="font-mono text-xs text-blue-700">dotmd status</code> in your terminal</li>
|
|
31
|
+
<li class="flex gap-2"><span class="text-gray-400">•</span>When you click the Refresh button in the nav bar</li>
|
|
32
|
+
</ul>
|
|
33
|
+
<p class="text-sm text-gray-600 mt-3">Each scan compares your tracked files against their last snapshot. If a file changed, dotmd saves the new version and a diff.</p>
|
|
34
|
+
</section>
|
|
35
|
+
|
|
36
|
+
<!-- Dashboard views -->
|
|
37
|
+
<section class="mb-8 pt-8 border-t border-gray-200">
|
|
38
|
+
<h2 class="text-lg font-semibold mb-3">Dashboard views</h2>
|
|
39
|
+
<div class="space-y-4 text-sm text-gray-600">
|
|
40
|
+
<div>
|
|
41
|
+
<h3 class="font-medium text-gray-900">Timeline</h3>
|
|
42
|
+
<p>A chronological feed of every change across all your tracked files. See what changed and when, with collapsible diffs.</p>
|
|
43
|
+
</div>
|
|
44
|
+
<div>
|
|
45
|
+
<h3 class="font-medium text-gray-900">Files</h3>
|
|
46
|
+
<p>Browse all tracked files organized by folder. Yellow dots indicate files that changed since you last viewed them.</p>
|
|
47
|
+
</div>
|
|
48
|
+
<div>
|
|
49
|
+
<h3 class="font-medium text-gray-900">Settings</h3>
|
|
50
|
+
<p>Add or remove folders and file patterns. Changes take effect on the next scan.</p>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
</section>
|
|
54
|
+
|
|
55
|
+
<!-- Data & Privacy -->
|
|
56
|
+
<section class="mb-8 pt-8 border-t border-gray-200">
|
|
57
|
+
<h2 class="text-lg font-semibold mb-3">Data & privacy</h2>
|
|
58
|
+
<p class="text-sm text-gray-600 mb-2">All data stays on your machine. Nothing is sent anywhere.</p>
|
|
59
|
+
<ul class="space-y-2 text-sm text-gray-600">
|
|
60
|
+
<li class="flex gap-2"><span class="text-gray-400">•</span>Snapshots and diffs are stored in a local SQLite database at <code class="font-mono text-xs">~/.dotmd/history.db</code></li>
|
|
61
|
+
<li class="flex gap-2"><span class="text-gray-400">•</span>Configuration is stored at <code class="font-mono text-xs">~/.dotmd/config.yaml</code></li>
|
|
62
|
+
<li class="flex gap-2"><span class="text-gray-400">•</span>To remove everything: <code class="font-mono text-xs">npm uninstall -g @mattli/dotmd && rm -rf ~/.dotmd</code></li>
|
|
63
|
+
</ul>
|
|
64
|
+
</section>
|
|
65
|
+
|
|
66
|
+
<!-- Feedback -->
|
|
67
|
+
<section class="pt-8 border-t border-gray-200">
|
|
68
|
+
<h2 class="text-lg font-semibold mb-3">Feedback</h2>
|
|
69
|
+
<p class="text-sm text-gray-600">Questions, suggestions, or bugs? <a href="https://github.com/mattli/dotmd/issues" class="text-blue-600 hover:underline" target="_blank" rel="noopener">Open an issue on GitHub</a>.</p>
|
|
70
|
+
</section>
|
|
71
|
+
</div>`;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=help.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"help.js","sourceRoot":"","sources":["../../src/dashboard/help.ts"],"names":[],"mappings":"AAAA,MAAM,UAAU,QAAQ;IACtB,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAqEE,CAAC;AACZ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/dashboard/layout.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../src/dashboard/layout.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,aAAa;IAC5B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CA+CtF"}
|
package/dist/dashboard/layout.js
CHANGED
|
@@ -7,6 +7,7 @@ export function layout(title, content, options) {
|
|
|
7
7
|
<a href="/timeline" class="text-gray-600 hover:text-gray-900">Timeline</a>
|
|
8
8
|
<a href="/files" class="text-gray-600 hover:text-gray-900">Files</a>
|
|
9
9
|
<a href="/settings" class="text-gray-600 hover:text-gray-900">Settings</a>
|
|
10
|
+
<a href="/help" class="text-gray-600 hover:text-gray-900">Help</a>
|
|
10
11
|
<button
|
|
11
12
|
onclick="fetch('/api/scan', { method: 'POST' }).then(() => location.reload())"
|
|
12
13
|
class="ml-auto flex items-center gap-1.5 text-gray-400 hover:text-gray-700 transition-colors"
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/dashboard/layout.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,OAAe,EAAE,OAAuB;IAC5E,MAAM,GAAG,GAAG,OAAO,EAAE,OAAO;QAC1B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../src/dashboard/layout.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,MAAM,CAAC,KAAa,EAAE,OAAe,EAAE,OAAuB;IAC5E,MAAM,GAAG,GAAG,OAAO,EAAE,OAAO;QAC1B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC;;;;;;;;;;;;;;;;SAgBG,CAAC;IAER,OAAO;;;;;WAKE,KAAK;;;;;;;IAOZ,OAAO,EAAE,OAAO,IAAI,EAAE;;;;;;;;IAQtB,GAAG;;MAED,OAAO;;;QAGL,CAAC;AACT,CAAC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Hono } from "hono";
|
|
2
2
|
import Database from "better-sqlite3";
|
|
3
3
|
export declare function createApp(db: Database.Database): Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
|
|
4
|
-
export declare function startServer(port?: number): void;
|
|
4
|
+
export declare function startServer(port?: number, onAlreadyRunning?: () => void): void;
|
|
5
5
|
//# sourceMappingURL=server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,QAAQ,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/dashboard/server.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,OAAO,QAAQ,MAAM,gBAAgB,CAAC;AAwBtC,wBAAgB,SAAS,CAAC,EAAE,EAAE,QAAQ,CAAC,QAAQ,8EAmT9C;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,MAAa,EAAE,gBAAgB,CAAC,EAAE,MAAM,IAAI,GAAG,IAAI,CAmBpF"}
|
package/dist/dashboard/server.js
CHANGED
|
@@ -11,10 +11,9 @@ import { scanFiles, discoverFiles } from "../scanner/index.js";
|
|
|
11
11
|
import { CONFIG_PATH, DEFAULT_CONFIG, SUGGESTED_ROOTS, SUGGESTED_PATTERNS, expandHome, } from "../config/defaults.js";
|
|
12
12
|
import { layout } from "./layout.js";
|
|
13
13
|
import { fileListPage, fileDetailPage, timelinePage } from "./views.js";
|
|
14
|
-
import {
|
|
15
|
-
import { WIZARD_CLIENT_SCRIPT } from "./wizard-client.js";
|
|
16
|
-
import { settingsPage } from "./settings.js";
|
|
14
|
+
import { setupPage } from "./settings.js";
|
|
17
15
|
import { SETTINGS_CLIENT_SCRIPT } from "./settings-client.js";
|
|
16
|
+
import { helpPage } from "./help.js";
|
|
18
17
|
export function createApp(db) {
|
|
19
18
|
const app = new Hono();
|
|
20
19
|
// Prevent back-forward cache so unseen indicators stay accurate
|
|
@@ -161,63 +160,42 @@ export function createApp(db) {
|
|
|
161
160
|
}
|
|
162
161
|
const editor = process.env.VISUAL || process.env.EDITOR;
|
|
163
162
|
const [cmd, ...args] = editor ? editor.split(/\s+/) : ["open", "-t"];
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
163
|
+
return new Promise((resolve) => {
|
|
164
|
+
execFile(cmd, [...args, filePath], (err) => {
|
|
165
|
+
if (err) {
|
|
166
|
+
const msg = err.code === "ENOENT"
|
|
167
|
+
? `Editor "${cmd}" not found. Set $EDITOR to an installed editor.`
|
|
168
|
+
: `Failed to open file: ${err.message}`;
|
|
169
|
+
resolve(c.json({ error: msg }, 500));
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
resolve(c.json({ success: true }));
|
|
173
|
+
}
|
|
174
|
+
});
|
|
167
175
|
});
|
|
168
|
-
|
|
176
|
+
});
|
|
177
|
+
// Help page
|
|
178
|
+
app.get("/help", (c) => {
|
|
179
|
+
return c.html(layout("Help", helpPage()));
|
|
169
180
|
});
|
|
170
181
|
// Settings page
|
|
171
182
|
app.get("/settings", (c) => {
|
|
172
183
|
const config = loadConfig();
|
|
173
|
-
const
|
|
184
|
+
const existingSuggestedRoots = SUGGESTED_ROOTS.filter((r) => fs.existsSync(expandHome(r)));
|
|
185
|
+
const html = setupPage(config, existingSuggestedRoots, SUGGESTED_PATTERNS);
|
|
174
186
|
return c.html(layout("Settings", html, { scripts: SETTINGS_CLIENT_SCRIPT }));
|
|
175
187
|
});
|
|
176
|
-
//
|
|
188
|
+
// Setup page (same UI as settings, but for first-time init)
|
|
177
189
|
app.get("/setup", (c) => {
|
|
178
190
|
const existingDirs = SUGGESTED_ROOTS.filter((r) => fs.existsSync(expandHome(r)));
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
const roots = c.req.queries("roots") ?? [];
|
|
184
|
-
const html = setupPatternsPage(roots, SUGGESTED_PATTERNS, DEFAULT_CONFIG.patterns);
|
|
185
|
-
return c.html(layout("Setup — Patterns", html, {
|
|
186
|
-
hideNav: true,
|
|
187
|
-
scripts: WIZARD_CLIENT_SCRIPT,
|
|
188
|
-
}));
|
|
189
|
-
});
|
|
190
|
-
app.get("/setup/step3", (c) => {
|
|
191
|
-
const roots = c.req.queries("roots") ?? [];
|
|
192
|
-
const patterns = c.req.queries("patterns") ?? [];
|
|
193
|
-
const tempConfig = {
|
|
194
|
-
scan_roots: roots,
|
|
195
|
-
patterns,
|
|
191
|
+
// For init, start with existing dirs as roots but no patterns selected
|
|
192
|
+
const initConfig = {
|
|
193
|
+
scan_roots: existingDirs,
|
|
194
|
+
patterns: [],
|
|
196
195
|
exclude: DEFAULT_CONFIG.exclude,
|
|
197
196
|
};
|
|
198
|
-
const
|
|
199
|
-
|
|
200
|
-
expandedRoots.sort((a, b) => b.expanded.length - a.expanded.length);
|
|
201
|
-
const files = filePaths.map((p) => {
|
|
202
|
-
let root = "Other";
|
|
203
|
-
for (const r of expandedRoots) {
|
|
204
|
-
if (p.startsWith(r.expanded + "/") || p === r.expanded) {
|
|
205
|
-
root = r.original;
|
|
206
|
-
break;
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
return { path: p, category: categorizeFile(p), root };
|
|
210
|
-
});
|
|
211
|
-
const html = setupPreviewPage(files, roots, patterns);
|
|
212
|
-
return c.html(layout("Setup — Preview", html, {
|
|
213
|
-
hideNav: true,
|
|
214
|
-
scripts: WIZARD_CLIENT_SCRIPT,
|
|
215
|
-
}));
|
|
216
|
-
});
|
|
217
|
-
app.get("/setup/complete", (c) => {
|
|
218
|
-
const count = parseInt(c.req.query("count") || "0", 10);
|
|
219
|
-
const html = setupCompletePage(count);
|
|
220
|
-
return c.html(layout("Setup Complete", html, { hideNav: true }));
|
|
197
|
+
const html = setupPage(initConfig, existingDirs, SUGGESTED_PATTERNS, { isInit: true });
|
|
198
|
+
return c.html(layout("Setup", html, { hideNav: true, scripts: SETTINGS_CLIENT_SCRIPT }));
|
|
221
199
|
});
|
|
222
200
|
// --- Setup wizard API ---
|
|
223
201
|
app.get("/api/setup/browse", (c) => {
|
|
@@ -293,13 +271,23 @@ export function createApp(db) {
|
|
|
293
271
|
});
|
|
294
272
|
return app;
|
|
295
273
|
}
|
|
296
|
-
export function startServer(port = 3333) {
|
|
274
|
+
export function startServer(port = 3333, onAlreadyRunning) {
|
|
297
275
|
const db = getDb();
|
|
298
276
|
// Run a scan on startup
|
|
299
277
|
const config = loadConfig();
|
|
300
278
|
scanFiles(db, config);
|
|
301
279
|
const app = createApp(db);
|
|
280
|
+
const server = serve({ fetch: app.fetch, port });
|
|
281
|
+
server.on("error", (err) => {
|
|
282
|
+
if (err.code === "EADDRINUSE") {
|
|
283
|
+
console.log(`dotmd is already running at http://localhost:${port}`);
|
|
284
|
+
if (onAlreadyRunning)
|
|
285
|
+
onAlreadyRunning();
|
|
286
|
+
}
|
|
287
|
+
else {
|
|
288
|
+
throw err;
|
|
289
|
+
}
|
|
290
|
+
});
|
|
302
291
|
console.log(`dotmd dashboard running at http://localhost:${port}`);
|
|
303
|
-
serve({ fetch: app.fetch, port });
|
|
304
292
|
}
|
|
305
293
|
//# sourceMappingURL=server.js.map
|