Skip to content

Commit fac3f1d

Browse files
authored
Merge pull request #60 from b-rodrigues/claude/refactor-simplify-code-YPYsA
refactor: simplify code by removing duplication and dead code
2 parents d114205 + dbed1d0 commit fac3f1d

File tree

3 files changed

+43
-238
lines changed

3 files changed

+43
-238
lines changed

src/diff-preview.js

Lines changed: 1 addition & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
// src/diff-preview.js
22
// Visual diff preview using inline ghost decorations (track-changes style)
33

4-
import { invoke } from '@tauri-apps/api/core';
54
import { calculateCharDiff } from './diff-highlighter.js';
6-
import { getCurrentUserInfo } from './profile-service.js';
7-
import { getActiveDocumentId } from './document-manager.js';
8-
import { mergeText } from './three-way-merge.js';
9-
import { getMarkdown, showDiffPreview, clearEditorHighlight, getMarkdownToPmMapping } from './editor.js';
5+
import { showDiffPreview, clearEditorHighlight, getMarkdownToPmMapping } from './editor.js';
106
import { getConflictState, restoreToPatch } from './timeline.js';
117
import { getConflictGroup } from './conflict-detection.js';
12-
import { stripMarkdown } from './utils.js';
138

149
let previewState = {
1510
active: false,
@@ -204,113 +199,3 @@ export function isPreviewActive() {
204199
return previewState.active;
205200
}
206201

207-
/**
208-
* Get pending patch IDs in the current conflict group
209-
* @param {number|null} excludePatchId - Optional patch ID to exclude from results
210-
* @returns {Promise<Array<number>>} - Array of pending patch IDs
211-
*/
212-
async function getPendingConflictPatchIds(excludePatchId = null) {
213-
if (!previewState.conflictGroup || previewState.conflictGroup.length <= 1) {
214-
return [];
215-
}
216-
217-
const { fetchPatchList } = await import('./timeline.js');
218-
const allPatches = await fetchPatchList();
219-
const docId = getActiveDocumentId();
220-
const { id: currentUserId } = getCurrentUserInfo();
221-
222-
// Build a map of patch ID to patch for quick lookup
223-
const patchMap = new Map(allPatches.map(p => [p.id, p]));
224-
225-
// Filter conflict group to only include pending patches
226-
const pendingIds = [];
227-
228-
for (const patchId of previewState.conflictGroup) {
229-
if (excludePatchId !== null && patchId === excludePatchId) continue;
230-
231-
const patch = patchMap.get(patchId);
232-
if (!patch) continue;
233-
234-
// Patches by current user are implicitly accepted (not pending)
235-
if (patch.author === currentUserId) continue;
236-
237-
// Check if current user has already reviewed this patch
238-
if (patch.uuid && docId) {
239-
const reviews = await invoke("get_document_patch_reviews", {
240-
docId,
241-
patchUuid: patch.uuid
242-
}).catch(() => []);
243-
244-
const hasReviewed = reviews.some(r => r.reviewer_id === currentUserId);
245-
if (hasReviewed) continue;
246-
}
247-
248-
pendingIds.push(patchId);
249-
}
250-
251-
return pendingIds;
252-
}
253-
254-
/**
255-
* Update the conflict tabs in the preview banner
256-
*/
257-
/**
258-
* Update the conflict tabs in the preview banner - Disabled
259-
*/
260-
async function updateConflictTabs() {
261-
// Conflict tabs and merge wizard disabled per user request
262-
return;
263-
}
264-
265-
/**
266-
* Switch preview to a different patch in the conflict group
267-
* @param {number} patchId - The patch ID to switch to
268-
*/
269-
async function switchToConflictPatch(patchId) {
270-
if (patchId === previewState.patchId) return;
271-
272-
const { fetchPatch, fetchPatchList } = await import('./timeline.js');
273-
274-
const patch = await fetchPatch(patchId);
275-
if (!patch) {
276-
alert("Failed to load patch");
277-
return;
278-
}
279-
280-
// Get current editor content as markdown (the "old" state)
281-
const currentContent = getMarkdown();
282-
283-
// Calculate what the merged result would be (3-way merge simulation)
284-
const allPatches = await fetchPatchList();
285-
const savePatchesOnly = allPatches
286-
.filter(p => p.kind === "Save" && p.data?.snapshot)
287-
.sort((a, b) => a.timestamp - b.timestamp);
288-
289-
const baseSnapshot = savePatchesOnly.length > 0
290-
? savePatchesOnly[0].data.snapshot
291-
: '';
292-
293-
const patchContent = patch.data?.snapshot || '';
294-
295-
// Simulate what the merge would produce
296-
const mergedResult = mergeText(baseSnapshot, currentContent, patchContent);
297-
298-
// Update preview state
299-
previewState.patchId = patchId;
300-
previewState.oldText = currentContent;
301-
previewState.newText = mergedResult;
302-
303-
// Update UI
304-
const patchIdEl = document.querySelector('#preview-patch-id');
305-
if (patchIdEl) {
306-
patchIdEl.textContent = patchId;
307-
}
308-
309-
// Update tab states
310-
document.querySelectorAll('.conflict-tab').forEach(tab => {
311-
tab.classList.toggle('active', parseInt(tab.dataset.patchId) === patchId);
312-
});
313-
314-
// Re-render ghost preview
315-
renderGhostPreview();
316-
}

src/document-manager.js

Lines changed: 38 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,42 @@ let activeDocumentId = null;
99
let openDocuments = new Map();
1010
let documentChangeListeners = [];
1111

12+
/**
13+
* Create a Save patch if content has changed from last save
14+
* @param {string} docId - Document ID
15+
* @param {string} content - Current editor content
16+
* @returns {Promise<boolean>} True if patch was created
17+
*/
18+
async function createSavePatchIfChanged(docId, content) {
19+
if (!content) return false;
20+
21+
try {
22+
const patches = await invoke("list_document_patches", { id: docId }).catch(() => []);
23+
const lastSavePatch = patches
24+
.filter(p => p.kind === "Save" && p.data?.snapshot)
25+
.sort((a, b) => b.timestamp - a.timestamp)[0];
26+
27+
if (!lastSavePatch || lastSavePatch.data.snapshot !== content) {
28+
const profile = getCachedProfile();
29+
const patch = {
30+
timestamp: Date.now(),
31+
author: profile?.id || "local",
32+
kind: "Save",
33+
data: {
34+
snapshot: content,
35+
authorName: profile?.name || "Local User",
36+
authorColor: profile?.color || "#3498db"
37+
}
38+
};
39+
await invoke("record_document_patch", { id: docId, patch });
40+
return true;
41+
}
42+
} catch (err) {
43+
console.error("Failed to create save patch:", err);
44+
}
45+
return false;
46+
}
47+
1248
/**
1349
* Create a new empty document
1450
* @returns {Promise<Object>} The document handle
@@ -97,37 +133,7 @@ export async function saveDocument(id = null, path = null) {
97133

98134
// Record a patch with the saved content BEFORE saving the file
99135
// so the patch is included in the bundled history.sqlite
100-
if (editorContent) {
101-
try {
102-
// Check if content has changed from last save
103-
const patches = await invoke("list_document_patches", { id: docId }).catch(() => []);
104-
const lastSavePatch = patches
105-
.filter(p => p.kind === "Save" && p.data?.snapshot)
106-
.sort((a, b) => b.timestamp - a.timestamp)[0];
107-
108-
// Only create a new patch if content has actually changed
109-
if (!lastSavePatch || lastSavePatch.data.snapshot !== editorContent) {
110-
const timestamp = Date.now();
111-
const profile = getCachedProfile();
112-
const author = profile?.id || "local";
113-
const authorName = profile?.name || "Local User";
114-
const authorColor = profile?.color || "#3498db";
115-
const patch = {
116-
timestamp,
117-
author,
118-
kind: "Save",
119-
data: {
120-
snapshot: editorContent,
121-
authorName, // Store author name for display
122-
authorColor // Store author color for multi-author highlighting
123-
}
124-
};
125-
await invoke("record_document_patch", { id: docId, patch });
126-
}
127-
} catch (err) {
128-
console.error("Failed to record save patch:", err);
129-
}
130-
}
136+
await createSavePatchIfChanged(docId, editorContent);
131137

132138
// Save the document (bundles the history.sqlite with the patch we just recorded)
133139
const handle = await invoke("save_document", { id: docId, path });
@@ -176,35 +182,7 @@ export async function closeDocument(id, force = false) {
176182
// Auto-create a Save patch before closing to preserve changes for reconciliation
177183
try {
178184
const { getMarkdown } = await import("./editor.js");
179-
const editorContent = getMarkdown();
180-
181-
if (editorContent) {
182-
// Check if content has changed from last save
183-
const patches = await invoke("list_document_patches", { id }).catch(() => []);
184-
const lastSavePatch = patches
185-
.filter(p => p.kind === "Save" && p.data?.snapshot)
186-
.sort((a, b) => b.timestamp - a.timestamp)[0];
187-
188-
// Create a Save patch if content changed
189-
if (!lastSavePatch || lastSavePatch.data.snapshot !== editorContent) {
190-
const timestamp = Date.now();
191-
const profile = getCachedProfile();
192-
const author = profile?.id || "local";
193-
const authorName = profile?.name || "Local User";
194-
const authorColor = profile?.color || "#3498db";
195-
const patch = {
196-
timestamp,
197-
author,
198-
kind: "Save",
199-
data: {
200-
snapshot: editorContent,
201-
authorName,
202-
authorColor
203-
}
204-
};
205-
await invoke("record_document_patch", { id, patch });
206-
}
207-
}
185+
await createSavePatchIfChanged(id, getMarkdown());
208186
} catch (err) {
209187
console.warn("Could not create auto-save patch on close:", err);
210188
}

src/editor.js

Lines changed: 4 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -632,34 +632,8 @@ export function previewGhostHunkByPosition(text, kind, markdownPos, markdownCont
632632
// We need to find what plain text position corresponds to markdownPos in markdown
633633
const prefixMarkdown = markdownContent.substring(0, markdownPos);
634634

635-
// Strip markdown from the prefix - this gives us the plain text before the insert point
636-
// We need stripMarkdown here - let's inline a simple version
637-
let prefixPlain = prefixMarkdown;
638-
// Remove images: ![alt](url) -> alt
639-
prefixPlain = prefixPlain.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1');
640-
// Remove links: [text](url) -> text
641-
prefixPlain = prefixPlain.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1');
642-
// Remove bold: **text** -> text
643-
prefixPlain = prefixPlain.replace(/\*\*([^*]+)\*\*/g, '$1');
644-
prefixPlain = prefixPlain.replace(/__([^_]+)__/g, '$1');
645-
// Remove italic (careful not to match list items)
646-
prefixPlain = prefixPlain.replace(/(?<![*_])\*([^*\n]+)\*(?![*])/g, '$1');
647-
prefixPlain = prefixPlain.replace(/(?<![_*])_([^_\n]+)_(?![_])/g, '$1');
648-
// Remove strikethrough
649-
prefixPlain = prefixPlain.replace(/~~([^~]+)~~/g, '$1');
650-
// Remove inline code
651-
prefixPlain = prefixPlain.replace(/`([^`]+)`/g, '$1');
652-
// Remove heading markers
653-
prefixPlain = prefixPlain.replace(/^(#{1,6})\s+/gm, '');
654-
// Remove blockquote markers
655-
prefixPlain = prefixPlain.replace(/^>\s*/gm, '');
656-
// Remove horizontal rules
657-
prefixPlain = prefixPlain.replace(/^[-*_]{3,}\s*$/gm, '');
658-
// Remove list markers
659-
prefixPlain = prefixPlain.replace(/^[\s]*[-*+]\s+/gm, '');
660-
prefixPlain = prefixPlain.replace(/^[\s]*\d+\.\s+/gm, '');
661-
// Normalize newlines
662-
prefixPlain = prefixPlain.replace(/\n{2,}/g, '\n');
635+
// Strip markdown from the prefix to get plain text length
636+
const prefixPlain = stripMarkdown(prefixMarkdown);
663637

664638
// The plain text offset is the length of the stripped prefix
665639
const plainOffset = prefixPlain.length;
@@ -674,46 +648,14 @@ export function previewGhostHunkByPosition(text, kind, markdownPos, markdownCont
674648
to = posPm;
675649
} else if (kind === 'delete') {
676650
// Delete: we need the range of text being deleted
677-
// Strip markdown from the delete text to get its plain length
678-
let deleteTextPlain = text;
679-
deleteTextPlain = deleteTextPlain.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1');
680-
deleteTextPlain = deleteTextPlain.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1');
681-
deleteTextPlain = deleteTextPlain.replace(/\*\*([^*]+)\*\*/g, '$1');
682-
deleteTextPlain = deleteTextPlain.replace(/__([^_]+)__/g, '$1');
683-
deleteTextPlain = deleteTextPlain.replace(/(?<![*_])\*([^*\n]+)\*(?![*])/g, '$1');
684-
deleteTextPlain = deleteTextPlain.replace(/(?<![_*])_([^_\n]+)_(?![_])/g, '$1');
685-
deleteTextPlain = deleteTextPlain.replace(/~~([^~]+)~~/g, '$1');
686-
deleteTextPlain = deleteTextPlain.replace(/`([^`]+)`/g, '$1');
687-
deleteTextPlain = deleteTextPlain.replace(/^(#{1,6})\s+/gm, '');
688-
deleteTextPlain = deleteTextPlain.replace(/^>\s*/gm, '');
689-
deleteTextPlain = deleteTextPlain.replace(/^[-*_]{3,}\s*$/gm, '');
690-
deleteTextPlain = deleteTextPlain.replace(/^[\s]*[-*+]\s+/gm, '');
691-
deleteTextPlain = deleteTextPlain.replace(/^[\s]*\d+\.\s+/gm, '');
692-
deleteTextPlain = deleteTextPlain.replace(/\n{2,}/g, '\n');
693-
651+
const deleteTextPlain = stripMarkdown(text);
694652
const fromPm = charToPm(plainOffset);
695653
const toPm = charToPm(plainOffset + deleteTextPlain.length);
696654
from = fromPm;
697655
to = toPm;
698656
} else if (kind === 'replace') {
699657
// Replace: delete the old text and insert new text at that position
700-
const deleteText = deleteTextOrInsertText;
701-
let deleteTextPlain = deleteText || '';
702-
deleteTextPlain = deleteTextPlain.replace(/!\[([^\]]*)\]\([^)]*\)/g, '$1');
703-
deleteTextPlain = deleteTextPlain.replace(/\[([^\]]*)\]\([^)]*\)/g, '$1');
704-
deleteTextPlain = deleteTextPlain.replace(/\*\*([^*]+)\*\*/g, '$1');
705-
deleteTextPlain = deleteTextPlain.replace(/__([^_]+)__/g, '$1');
706-
deleteTextPlain = deleteTextPlain.replace(/(?<![*_])\*([^*\n]+)\*(?![*])/g, '$1');
707-
deleteTextPlain = deleteTextPlain.replace(/(?<![_*])_([^_\n]+)_(?![_])/g, '$1');
708-
deleteTextPlain = deleteTextPlain.replace(/~~([^~]+)~~/g, '$1');
709-
deleteTextPlain = deleteTextPlain.replace(/`([^`]+)`/g, '$1');
710-
deleteTextPlain = deleteTextPlain.replace(/^(#{1,6})\s+/gm, '');
711-
deleteTextPlain = deleteTextPlain.replace(/^>\s*/gm, '');
712-
deleteTextPlain = deleteTextPlain.replace(/^[-*_]{3,}\s*$/gm, '');
713-
deleteTextPlain = deleteTextPlain.replace(/^[\s]*[-*+]\s+/gm, '');
714-
deleteTextPlain = deleteTextPlain.replace(/^[\s]*\d+\.\s+/gm, '');
715-
deleteTextPlain = deleteTextPlain.replace(/\n{2,}/g, '\n');
716-
658+
const deleteTextPlain = stripMarkdown(deleteTextOrInsertText || '');
717659
deleteFrom = charToPm(plainOffset);
718660
deleteTo = charToPm(plainOffset + deleteTextPlain.length);
719661
}

0 commit comments

Comments
 (0)