diff --git a/chartsmith-app/app/hooks/useSession.ts b/chartsmith-app/app/hooks/useSession.ts index 59082020..28d634ac 100644 --- a/chartsmith-app/app/hooks/useSession.ts +++ b/chartsmith-app/app/hooks/useSession.ts @@ -83,8 +83,27 @@ export const useSession = (redirectIfNotLoggedIn: boolean = false) => { validate(token); }, [router, redirectIfNotLoggedIn]); + const refreshSession = useCallback(async () => { + const token = document.cookie + .split("; ") + .find((cookie) => cookie.startsWith("session=")) + ?.split("=")[1]; + + if (!token) { + return; + } + + try { + const sess = await validateSession(token); + setSession(sess); + } catch (error) { + logger.error("Session refresh failed:", error); + } + }, []); + return { isLoading, session, + refreshSession, }; }; diff --git a/chartsmith-app/components/CodeEditor.tsx b/chartsmith-app/components/CodeEditor.tsx index 8754af9f..2d92f60e 100644 --- a/chartsmith-app/components/CodeEditor.tsx +++ b/chartsmith-app/components/CodeEditor.tsx @@ -144,6 +144,30 @@ export const CodeEditor = React.memo(function CodeEditor({ updateCurrentDiffIndex(allFilesWithContentPending); }, [selectedFile, allFilesWithContentPending, updateCurrentDiffIndex]); + // Auto-accept patches when YOLO mode is enabled + useEffect(() => { + const shouldAutoAccept = session.user?.settings?.automaticallyAcceptPatches === true; + const hasPatches = allFilesWithContentPending.length > 0; + const planIsApplied = mostRecentPlan?.status === "applied"; + + if (shouldAutoAccept && hasPatches && planIsApplied && workspace) { + console.log("YOLO mode enabled: automatically accepting all patches"); + // Use setTimeout to avoid state update conflicts + setTimeout(async () => { + try { + await acceptAllPatchesAction(session, workspace.id, workspace.currentRevisionNumber); + // Reload workspace after auto-accept + const freshWorkspace = await getWorkspaceAction(session, workspace.id); + if (freshWorkspace) { + setWorkspace(freshWorkspace); + } + } catch (error) { + console.error("Auto-accept failed:", error); + } + }, 100); + } + }, [allFilesWithContentPending, mostRecentPlan?.status, session.user?.settings?.automaticallyAcceptPatches, session, workspace, setWorkspace]); + // Handle outside clicks for dropdowns useEffect(() => { const handleClickOutside = (event: MouseEvent) => { @@ -626,7 +650,18 @@ export const CodeEditor = React.memo(function CodeEditor({
{mostRecentPlan?.status === "applied" && ( <> -
+ {session.user?.settings?.automaticallyAcceptPatches ? ( +
+ + Auto-accepting changes... +
+ ) : ( + <> +
+ + )} )} diff --git a/chartsmith-app/components/SettingsModal.tsx b/chartsmith-app/components/SettingsModal.tsx index f5d35140..690a0a7e 100644 --- a/chartsmith-app/components/SettingsModal.tsx +++ b/chartsmith-app/components/SettingsModal.tsx @@ -1,7 +1,7 @@ "use client" import React, { useState, useEffect } from 'react'; -import { X, Trash2, Key, Check, Loader2 } from 'lucide-react'; +import { X, Trash2, Key, Check, Loader2, Lock } from 'lucide-react'; import { useTheme, Theme } from '@/contexts/ThemeContext'; import { useAuth } from '@/contexts/AuthContext'; import { updateUserSettingAction } from '@/lib/auth/actions/update-user-setting'; @@ -22,14 +22,21 @@ interface SettingsSection { } export function SettingsModal({ isOpen, onClose, session }: SettingsModalProps) { + const { refreshSession } = useSession(); const { theme, setTheme } = useTheme(); const [activeSection, setActiveSection] = useState<'general' | 'replicated' | 'appearance' | 'editor' | 'changes'>('general'); - const [autoAcceptChanges, setAutoAcceptChanges] = useState(session.user?.settings?.automaticallyAcceptPatches || false); - const [validateBeforeAccept, setValidateBeforeAccept] = useState(session.user?.settings?.evalBeforeAccept || false); + const [autoAcceptChanges, setAutoAcceptChanges] = useState(session.user?.settings?.automaticallyAcceptPatches ?? false); + const [validateBeforeAccept, setValidateBeforeAccept] = useState(session.user?.settings?.evalBeforeAccept ?? false); const [isDeleting, setIsDeleting] = useState(false); const [apiToken, setApiToken] = useState(''); const [savingAutoAccept, setSavingAutoAccept] = useState(false); const [savingValidate, setSavingValidate] = useState(false); + const [showMinimap, setShowMinimap] = useState(session.user?.settings?.showMinimap ?? false); + const [tabSize, setTabSize] = useState(session.user?.settings?.tabSize ?? '2 spaces'); + const [localTheme, setLocalTheme] = useState(session.user?.settings?.theme ?? 'auto'); + const [savingMinimap, setSavingMinimap] = useState(false); + const [savingTabSize, setSavingTabSize] = useState(false); + const [isChangingTheme, setIsChangingTheme] = useState(false); const [publicEnv, setPublicEnv] = useState>({}); useEffect(() => { @@ -49,11 +56,32 @@ export function SettingsModal({ isOpen, onClose, session }: SettingsModalProps) useEffect(() => { if (session.user?.settings) { - setAutoAcceptChanges(session.user.settings.automaticallyAcceptPatches); - setValidateBeforeAccept(session.user.settings.evalBeforeAccept); + setAutoAcceptChanges(session.user.settings.automaticallyAcceptPatches ?? false); + setValidateBeforeAccept(session.user.settings.evalBeforeAccept ?? false); + setShowMinimap(session.user.settings.showMinimap ?? false); + setTabSize(session.user.settings.tabSize ?? '2 spaces'); + + // Only sync theme from database if there's a significant difference and we're not changing themes + // Remove automatic sync to prevent hydration conflicts - let user manually change theme + const dbTheme = session.user.settings.theme ?? 'auto'; + setLocalTheme(dbTheme); } }, [session.user?.settings]); + // Sync localTheme only when modal opens (isOpen changes from false to true) + useEffect(() => { + if (isOpen && session.user?.settings) { + const dbTheme = session.user.settings.theme ?? 'auto'; + console.log('Modal opened, syncing localTheme to:', dbTheme); + setLocalTheme(dbTheme); + } + }, [isOpen, session.user?.settings?.theme]); + + // Debug localTheme changes + useEffect(() => { + console.log('localTheme state changed to:', localTheme); + }, [localTheme]); + if (!isOpen) return null; const handleDeleteChats = () => { @@ -83,10 +111,16 @@ export function SettingsModal({ isOpen, onClose, session }: SettingsModalProps) const handleAutoAcceptChange = async (checked: boolean) => { if (!session.user) return; + console.log('handleAutoAcceptChange called', { checked, currentValue: session.user.settings.automaticallyAcceptPatches }); setSavingAutoAccept(true); try { setAutoAcceptChanges(checked); - await updateUserSettingAction(session, 'automatically_accept_patches', checked.toString()); + console.log('About to save automatically_accept_patches to database'); + const result = await updateUserSettingAction(session, 'automatically_accept_patches', checked.toString()); + console.log('Database save result:', result); + // Update session locally to reflect the change immediately + session.user.settings.automaticallyAcceptPatches = checked; + console.log('Updated local session state:', session.user.settings.automaticallyAcceptPatches); } finally { setSavingAutoAccept(false); } @@ -94,15 +128,102 @@ export function SettingsModal({ isOpen, onClose, session }: SettingsModalProps) const handleValidateBeforeAcceptChange = async (checked: boolean) => { if (!session.user) return; + console.log('handleValidateBeforeAcceptChange called', { checked, currentValue: session.user.settings.evalBeforeAccept }); setSavingValidate(true); try { setValidateBeforeAccept(checked); - await updateUserSettingAction(session, 'eval_before_accept', checked.toString()); + console.log('About to save eval_before_accept to database'); + const result = await updateUserSettingAction(session, 'eval_before_accept', checked.toString()); + console.log('Database save result:', result); + // Update session locally to reflect the change immediately + session.user.settings.evalBeforeAccept = checked; + console.log('Updated local session state:', session.user.settings.evalBeforeAccept); } finally { setSavingValidate(false); } }; + + const handleThemeChange = async (newTheme: string) => { + if (!session.user) return; + console.log('handleThemeChange called', { + newTheme, + currentValue: session.user.settings.theme, + currentLocalTheme: localTheme, + currentContextTheme: theme + }); + try { + // Set flag to prevent automatic theme sync from interfering + setIsChangingTheme(true); + + // Update local theme state immediately for UI responsiveness + console.log('Setting localTheme to:', newTheme); + setLocalTheme(newTheme); + + // Update theme context (this will also set the cookie) + console.log('Setting theme context to:', newTheme); + setTheme(newTheme as Theme); + + console.log('About to save theme to database'); + const result = await updateUserSettingAction(session, 'theme', newTheme); + console.log('Database save result:', result); + + // Update session locally with the result from database + if (result.theme) { + session.user.settings.theme = result.theme; + console.log('Updated local session state:', session.user.settings.theme); + } + + console.log('After all updates:', { + localTheme, + contextTheme: theme, + sessionTheme: session.user.settings.theme + }); + } catch (error) { + console.error('Failed to save theme:', error); + // Revert both local theme and context theme on error + setLocalTheme(session.user.settings.theme); + setTheme(session.user.settings.theme as Theme); + } finally { + // Clear the flag after a short delay to allow state updates to settle + setTimeout(() => setIsChangingTheme(false), 500); + } + }; + + const handleTabSizeChange = async (newTabSize: string) => { + if (!session.user) return; + console.log('handleTabSizeChange called', { newTabSize, currentValue: session.user.settings.tabSize }); + setSavingTabSize(true); + try { + setTabSize(newTabSize); + console.log('About to save tab_size to database'); + const result = await updateUserSettingAction(session, 'tab_size', newTabSize); + console.log('Database save result:', result); + // Update session locally to reflect the change immediately + session.user.settings.tabSize = newTabSize; + console.log('Updated local session state:', session.user.settings.tabSize); + } finally { + setSavingTabSize(false); + } + }; + + const handleMinimapChange = async (checked: boolean) => { + if (!session.user) return; + console.log('handleMinimapChange called', { checked, currentValue: session.user.settings.showMinimap }); + setSavingMinimap(true); + try { + setShowMinimap(checked); + console.log('About to save show_minimap to database'); + const result = await updateUserSettingAction(session, 'show_minimap', checked.toString()); + console.log('Database save result:', result); + // Update session locally to reflect the change immediately + session.user.settings.showMinimap = checked; + console.log('Updated local session state:', session.user.settings.showMinimap); + } finally { + setSavingMinimap(false); + } + }; + const sections: SettingsSection[] = [ { id: 'general', @@ -199,9 +320,16 @@ export function SettingsModal({ isOpen, onClose, session }: SettingsModalProps)
{ + console.log('Select onChange triggered:', { + selectedValue: e.target.value, + currentLocalTheme: localTheme + }); + handleThemeChange(e.target.value); + }} className={`w-full px-3 py-2 rounded-lg transition-colors ${ theme === 'dark' ? 'bg-dark border-dark-border text-gray-300' @@ -229,26 +357,42 @@ export function SettingsModal({ isOpen, onClose, session }: SettingsModalProps) - + {savingTabSize ? ( +
+ + Saving... +
+ ) : ( + + )}
- + {savingMinimap ? ( + + ) : ( + handleMinimapChange(e.target.checked)} + className={`rounded border transition-colors ${ + theme === 'dark' + ? 'border-dark-border bg-dark text-primary' + : 'border-gray-300 bg-white text-primary' + } focus:ring-primary`} + /> + )}
), - } + }, ]; return ( diff --git a/chartsmith-app/contexts/ThemeContext.tsx b/chartsmith-app/contexts/ThemeContext.tsx index d5942316..226898d0 100644 --- a/chartsmith-app/contexts/ThemeContext.tsx +++ b/chartsmith-app/contexts/ThemeContext.tsx @@ -14,23 +14,36 @@ const ThemeContext = createContext(undefined); export function ThemeProvider({ children, - defaultTheme = 'dark' + defaultTheme = 'auto' }: { children: React.ReactNode; defaultTheme?: Theme; }) { const [theme, setThemeState] = useState(defaultTheme); + const [isHydrated, setIsHydrated] = useState(false); - const [systemTheme, setSystemTheme] = useState<"light" | "dark">( - typeof window !== 'undefined' - ? window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' - : 'dark' - ); + const [systemTheme, setSystemTheme] = useState<"light" | "dark">('dark'); + // Hydration effect - read theme from cookie only after client hydration useEffect(() => { if (typeof window === 'undefined') return; + setIsHydrated(true); + + // Read theme from cookie after hydration to prevent SSR mismatch + const cookieValue = document.cookie + .split('; ') + .find(row => row.startsWith('theme=')) + ?.split('=')[1]; + + if (cookieValue && ['light', 'dark', 'auto'].includes(cookieValue)) { + setThemeState(cookieValue as Theme); + } + + // Set initial system theme const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + setSystemTheme(mediaQuery.matches ? 'dark' : 'light'); + const handleChange = (e: MediaQueryListEvent) => { setSystemTheme(e.matches ? 'dark' : 'light'); }; @@ -41,11 +54,16 @@ export function ThemeProvider({ const setTheme = (newTheme: Theme) => { // Set cookie with theme preference - document.cookie = `theme=${newTheme}; path=/; SameSite=Lax`; + if (typeof window !== 'undefined') { + document.cookie = `theme=${newTheme}; path=/; SameSite=Lax`; + } setThemeState(newTheme); }; useEffect(() => { + // Only apply theme to DOM after hydration to prevent mismatch + if (!isHydrated || typeof window === 'undefined') return; + const activeTheme = theme === 'auto' ? systemTheme : theme; document.documentElement.classList.remove('light', 'dark'); document.documentElement.classList.add(activeTheme); @@ -62,7 +80,7 @@ export function ThemeProvider({ document.documentElement.style.setProperty('--border', '#e2e8f0'); document.documentElement.style.setProperty('--text', '#0f172a'); } - }, [theme, systemTheme]); + }, [theme, systemTheme, isHydrated]); const resolvedTheme = theme === 'auto' ? systemTheme : theme; diff --git a/chartsmith-app/lib/auth/session.ts b/chartsmith-app/lib/auth/session.ts index 9a68e32d..c26a3d9b 100644 --- a/chartsmith-app/lib/auth/session.ts +++ b/chartsmith-app/lib/auth/session.ts @@ -92,7 +92,6 @@ export async function sessionToken(session: Session): Promise { name: session.user.name, email: session.user.email, picture: session.user.imageUrl, - userSettings: session.user.settings, isWaitlisted: session.user.isWaitlisted, isAdmin: session.user.isAdmin || false }; @@ -193,6 +192,9 @@ export async function findSession(token: string): Promise { settings: { automaticallyAcceptPatches: false, evalBeforeAccept: false, + theme: 'auto', + tabSize: '2 spaces', + showMinimap: false, }, isAdmin: false }, @@ -237,6 +239,9 @@ export async function findSession(token: string): Promise { settings: { automaticallyAcceptPatches: false, evalBeforeAccept: false, + theme: 'auto', + tabSize: '2 spaces', + showMinimap: false, }, isAdmin: false }; diff --git a/chartsmith-app/lib/auth/user.ts b/chartsmith-app/lib/auth/user.ts index 4ba74361..855c5a7d 100644 --- a/chartsmith-app/lib/auth/user.ts +++ b/chartsmith-app/lib/auth/user.ts @@ -9,6 +9,9 @@ import { logger } from "../utils/logger"; const defaultUserSettings: UserSetting = { automaticallyAcceptPatches: false, evalBeforeAccept: false, + theme: 'auto', + tabSize: '2 spaces', + showMinimap: false, }; /** @@ -205,6 +208,7 @@ export async function findUser(email: string): Promise { export async function updateUserSetting(id: string, key: string, value: string): Promise { try { + logger.info("Updating user setting in database", { userId: id, key, value }); const db = getDB(await getParam("DB_URI")); await db.query( ` @@ -215,14 +219,17 @@ export async function updateUserSetting(id: string, key: string, value: string): [id, key, value], ); - return await getUserSettings(id); + logger.info("Successfully updated user setting, fetching fresh settings"); + const updatedSettings = await getUserSettings(id); + logger.info("Returning updated settings", { updatedSettings }); + return updatedSettings; } catch (err) { logger.error("Failed to update user setting", { err }); throw err; } } -async function getUserSettings(id: string): Promise { +export async function getUserSettings(id: string): Promise { try { const db = getDB(await getParam("DB_URI")); const result = await db.query( @@ -239,17 +246,34 @@ async function getUserSettings(id: string): Promise { ); const userSettings: UserSetting = { ...defaultUserSettings }; + logger.info("Loading user settings from database", { + userId: id, + rowCount: result.rows.length, + defaultSettings: defaultUserSettings + }); for (const row of result.rows) { + logger.info("Processing setting row", { key: row.key, value: row.value }); switch (row.key) { case "automatically_accept_patches": userSettings.automaticallyAcceptPatches = row.value === "true"; break; case "eval_before_accept": userSettings.evalBeforeAccept = row.value === "true"; + break; + case "theme": + userSettings.theme = row.value; + break; + case "tab_size": + userSettings.tabSize = row.value; + break; + case "show_minimap": + userSettings.showMinimap = row.value === "true"; + break; } } + logger.info("Final user settings loaded", { userId: id, finalSettings: userSettings }); return userSettings; } catch (err) { logger.error("Failed to get user settings", { err }); @@ -257,6 +281,7 @@ async function getUserSettings(id: string): Promise { } } + export async function getUser(id: string): Promise { try { const db = getDB(await getParam("DB_URI")); @@ -368,6 +393,9 @@ export async function listWaitlistUsers(): Promise { settings: { automaticallyAcceptPatches: false, evalBeforeAccept: false, + theme: 'auto', + tabSize: '2 spaces', + showMinimap: false, }, isAdmin: false, }); diff --git a/chartsmith-app/lib/types/user.ts b/chartsmith-app/lib/types/user.ts index 1f177530..324cad80 100644 --- a/chartsmith-app/lib/types/user.ts +++ b/chartsmith-app/lib/types/user.ts @@ -29,4 +29,7 @@ export interface User { export interface UserSetting { automaticallyAcceptPatches: boolean; evalBeforeAccept: boolean; + theme: string; + tabSize: string; + showMinimap: boolean; } diff --git a/chartsmith-app/lib/workspace/workspace.ts b/chartsmith-app/lib/workspace/workspace.ts index 01eacf62..1449cb95 100644 --- a/chartsmith-app/lib/workspace/workspace.ts +++ b/chartsmith-app/lib/workspace/workspace.ts @@ -239,7 +239,10 @@ export async function createChatMessage(userId: string, workspaceId: string, par additionalFiles: params.additionalFiles, }); } else if (params.knownIntent === ChatMessageIntent.NON_PLAN) { - await client.query(`SELECT pg_notify('new_nonplan_chat_message', $1)`, [chatMessageId]); + await enqueueWork("conversational", { + chatMessageId, + workspaceId, + }); } else if (params.knownIntent === ChatMessageIntent.RENDER) { await renderWorkspace(workspaceId, chatMessageId); } else if (params.knownIntent === ChatMessageIntent.CONVERT_K8S_TO_HELM) { diff --git a/chartsmith-app/tests/upload-chart.spec.ts b/chartsmith-app/tests/upload-chart.spec.ts index 2b14e143..71024c49 100644 --- a/chartsmith-app/tests/upload-chart.spec.ts +++ b/chartsmith-app/tests/upload-chart.spec.ts @@ -98,7 +98,7 @@ test('upload helm chart', async ({ page }) => { await page.screenshot({ path: './test-results/upload-proceed-button-verification.png' }); // Test should fail if button is not visible in viewport - expect(isInViewport).toBe(true, 'Proceed button is not visible in the viewport without scrolling'); + expect(isInViewport).toBe(true); // click on the Proceed button await proceedButton.click(); diff --git a/pkg/llm/execute-action.go b/pkg/llm/execute-action.go index 5e6e805f..084a6560 100644 --- a/pkg/llm/execute-action.go +++ b/pkg/llm/execute-action.go @@ -239,23 +239,23 @@ func PerformStringReplacement(content, oldStr, newStr string) (string, bool, err // Add logging to track performance startTime := time.Now() defer func() { - logger.Debug("String replacement operation completed", + logger.Debug("String replacement operation completed", zap.Duration("time_taken", time.Since(startTime))) }() - + // Log content sizes for diagnostics - logger.Debug("Starting string replacement", - zap.Int("content_size", len(content)), + logger.Debug("Starting string replacement", + zap.Int("content_size", len(content)), zap.Int("old_string_size", len(oldStr)), zap.Int("new_string_size", len(newStr))) - + // First try exact match if strings.Contains(content, oldStr) { logger.Debug("Found exact match, performing replacement") updatedContent := strings.ReplaceAll(content, oldStr, newStr) return updatedContent, true, nil } - + logger.Debug("No exact match found, attempting fuzzy matching") // Create a context with timeout for fuzzy matching @@ -272,14 +272,14 @@ func PerformStringReplacement(content, oldStr, newStr string) (string, bool, err go func() { logger.Debug("Starting fuzzy match search") fuzzyStartTime := time.Now() - + start, end := findBestMatchRegion(content, oldStr, minFuzzyMatchLen) - - logger.Debug("Fuzzy match search completed", + + logger.Debug("Fuzzy match search completed", zap.Duration("time_taken", time.Since(fuzzyStartTime)), zap.Int("start_pos", start), zap.Int("end_pos", end)) - + if start == -1 || end == -1 { resultCh <- struct { start, end int @@ -301,15 +301,15 @@ func PerformStringReplacement(content, oldStr, newStr string) (string, bool, err return content, false, result.err } // Replace the matched region with newStr - logger.Debug("Found fuzzy match, performing replacement", - zap.Int("match_start", result.start), + logger.Debug("Found fuzzy match, performing replacement", + zap.Int("match_start", result.start), zap.Int("match_end", result.end), zap.Int("match_length", result.end - result.start)) - + updatedContent := content[:result.start] + newStr + content[result.end:] return updatedContent, false, nil case <-ctx.Done(): - logger.Warn("Fuzzy matching timed out", + logger.Warn("Fuzzy matching timed out", zap.Duration("timeout", fuzzyMatchTimeout), zap.Duration("time_elapsed", time.Since(startTime))) return content, false, fmt.Errorf("fuzzy matching timed out after %v", fuzzyMatchTimeout) @@ -319,8 +319,8 @@ func PerformStringReplacement(content, oldStr, newStr string) (string, bool, err func findBestMatchRegion(content, oldStr string, minMatchLen int) (int, int) { // Early return if strings are too small if len(oldStr) < minMatchLen { - logger.Debug("String too small for fuzzy matching", - zap.Int("length", len(oldStr)), + logger.Debug("String too small for fuzzy matching", + zap.Int("length", len(oldStr)), zap.Int("min_length", minMatchLen)) return -1, -1 } @@ -328,7 +328,7 @@ func findBestMatchRegion(content, oldStr string, minMatchLen int) (int, int) { bestStart := -1 bestEnd := -1 bestLen := 0 - + // Set a max number of chunks to process to prevent excessive computation maxChunks := 100 chunksProcessed := 0 @@ -344,32 +344,32 @@ func findBestMatchRegion(content, oldStr string, minMatchLen int) (int, int) { // Get the current chunk chunk := oldStr[i:chunkEnd] - + // Skip empty or tiny chunks if len(chunk) < 10 { continue } - + chunksProcessed++ - + // Find all occurrences of this chunk in the content start := 0 maxOccurrences := 100 // Limit number of occurrences to check occurrencesChecked := 0 - - logger.Debug("Processing chunk", - zap.Int("chunk_index", i), + + logger.Debug("Processing chunk", + zap.Int("chunk_index", i), zap.Int("chunk_size", len(chunk)), zap.Int("chunks_processed", chunksProcessed)) - + for occurrencesChecked < maxOccurrences { idx := strings.Index(content[start:], chunk) if idx == -1 { break } - + occurrencesChecked++ - + // Adjust index to be relative to the start of content idx += start @@ -377,7 +377,7 @@ func findBestMatchRegion(content, oldStr string, minMatchLen int) (int, int) { matchStart := idx matchEnd := idx + len(chunk) matchLen := len(chunk) - + // Store the original i value, we'll need it for backward extension originalI := i @@ -408,8 +408,8 @@ func findBestMatchRegion(content, oldStr string, minMatchLen int) (int, int) { bestStart = matchStart bestEnd = matchEnd bestLen = matchLen - - logger.Debug("Found better match", + + logger.Debug("Found better match", zap.Int("match_length", matchLen), zap.Int("match_start", matchStart), zap.Int("match_end", matchEnd)) @@ -421,13 +421,13 @@ func findBestMatchRegion(content, oldStr string, minMatchLen int) (int, int) { } if bestLen >= minMatchLen { - logger.Debug("Found best match", + logger.Debug("Found best match", zap.Int("best_length", bestLen), zap.Int("best_start", bestStart), zap.Int("best_end", bestEnd)) return bestStart, bestEnd } - + logger.Debug("No match found with minimum length", zap.Int("best_length", bestLen), zap.Int("required_min_length", minMatchLen))