@nanocollective/nanocoder
Version:
A local-first CLI coding agent that brings the power of agentic coding tools like Claude Code and Gemini CLI to local models or controlled APIs like OpenRouter
89 lines • 3.94 kB
JavaScript
/**
* Detect if a text change represents a deletion that should be atomic
* Returns the modified InputState if atomic deletion occurred, null otherwise
*/
export function handleAtomicDeletion(previousState, newText) {
const previousText = previousState.displayValue;
// Only handle deletions (text getting shorter)
if (newText.length >= previousText.length) {
return null;
}
// Find what was deleted
const deletedChars = previousText.length - newText.length;
// Find where the deletion occurred
let deletionStart = -1;
for (let i = 0; i < Math.min(previousText.length, newText.length); i++) {
if (previousText[i] !== newText[i]) {
deletionStart = i;
break;
}
}
// If no difference found in common part, deletion was at the end
if (deletionStart === -1) {
deletionStart = newText.length;
}
// Check if any placeholder was affected by this deletion
const placeholderRegex = /\[Paste #(\d+): \d+ chars\]/g;
let match;
while ((match = placeholderRegex.exec(previousText)) !== null) {
const placeholderStart = match.index;
const placeholderEnd = placeholderStart + match[0].length;
const placeholderId = match[1];
// Check if deletion overlaps with this placeholder
const deletionEnd = deletionStart + deletedChars;
if ((deletionStart >= placeholderStart && deletionStart < placeholderEnd) ||
(deletionEnd > placeholderStart && deletionEnd <= placeholderEnd) ||
(deletionStart <= placeholderStart && deletionEnd >= placeholderEnd)) {
// Deletion affects this placeholder - remove it atomically
const newDisplayValue = previousText.replace(match[0], '');
const newPlaceholderContent = { ...previousState.placeholderContent };
delete newPlaceholderContent[placeholderId];
return {
displayValue: newDisplayValue,
placeholderContent: newPlaceholderContent,
};
}
}
return null;
}
/**
* Find placeholder at cursor position
* Returns placeholder ID if cursor is within a placeholder, null otherwise
*/
export function findPlaceholderAtPosition(text, position) {
const placeholderRegex = /\[Paste #(\d+): \d+ chars\]/g;
let match;
while ((match = placeholderRegex.exec(text)) !== null) {
const placeholderStart = match.index;
const placeholderEnd = placeholderStart + match[0].length;
if (position >= placeholderStart && position <= placeholderEnd) {
return match[1]; // Return the placeholder ID
}
}
return null;
}
/**
* Check if a deletion would partially affect a placeholder
* Used to prevent partial placeholder deletions
*/
export function wouldPartiallyDeletePlaceholder(text, deletionStart, deletionLength) {
const placeholderRegex = /\[Paste #(\d+): \d+ chars\]/g;
let match;
while ((match = placeholderRegex.exec(text)) !== null) {
const placeholderStart = match.index;
const placeholderEnd = placeholderStart + match[0].length;
const deletionEnd = deletionStart + deletionLength;
// Check for overlap
const overlapsStart = deletionStart >= placeholderStart && deletionStart < placeholderEnd;
const overlapsEnd = deletionEnd > placeholderStart && deletionEnd <= placeholderEnd;
const spansPast = deletionStart < placeholderStart && deletionEnd > placeholderStart;
const spansOver = deletionStart < placeholderEnd && deletionEnd > placeholderEnd;
const hasOverlap = overlapsStart || overlapsEnd || spansPast || spansOver;
const completeOverlap = deletionStart <= placeholderStart && deletionEnd >= placeholderEnd;
if (hasOverlap && !completeOverlap) {
return true;
}
}
return false;
}
//# sourceMappingURL=atomic-deletion.js.map