prerun(toHex): save attempt at citation modal based on new spec

On branch development Changes to be committed:
modified:   src/modals/CitationModal.ts
modified:   src/services/citationService.ts
This commit is contained in:
mpstaton 2025-07-07 14:25:30 +03:00
parent 0cb22b7450
commit 90f7d18b12
2 changed files with 109 additions and 87 deletions

View file

@ -175,55 +175,55 @@ export class CitationModal extends Modal {
private async convertCitationToHex(match: CitationMatch) {
try {
console.log('Starting citation conversion for:', match);
const content = this.editor.getValue();
const lines = content.split('\n');
let updated = false;
// Generate hex ID for this citation
const hexId = citationService.generateHexId(parseInt(match.number));
// Use the citation service to handle the conversion
const result = citationService.convertCitations(
content,
match.type === 'footnote' ? `[^${match.number}]` : `[${match.number}]`
);
// Replace all instances of this citation
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (!line) continue;
if (result.changed) {
console.log('Changes detected, updating file...');
// Handle footnote references [^1]
if (match.type === 'footnote') {
const footnoteRef = new RegExp(`\\[\\^${match.number}\\]`, 'g');
if (footnoteRef.test(line)) {
lines[i] = line.replace(footnoteRef, `[^${hexId}]`);
updated = true;
}
// Also check for footnote definitions
const footnoteDef = new RegExp(`^\\[\\^${match.number}\\]:`);
if (footnoteDef.test(line.trim())) {
lines[i] = line.replace(`[^${match.number}]:`, `[^${hexId}]:`);
updated = true;
}
}
// Handle regular citations [1]
else {
const citationRegex = new RegExp(`\\[${match.number}\\]`, 'g');
if (citationRegex.test(line)) {
// Skip if it's part of a markdown link
if (!/\]\([^)]*$/.test(line.substring(0, line.indexOf(`[${match.number}]`)))) {
lines[i] = line.replace(citationRegex, `[${hexId}]`);
updated = true;
}
}
// Get the current file
const activeFile = this.app.workspace.getActiveFile();
if (!activeFile) {
const msg = 'No active file found';
console.error(msg);
new Notice(msg);
return;
}
}
if (updated) {
// Update the editor content
this.editor.setValue(lines.join('\n'));
new Notice(`Converted citation [${match.number}] to hex format`);
console.log('New content prepared, saving to file...');
try {
await this.app.vault.modify(activeFile, result.updatedContent);
console.log('File saved successfully');
// Update the editor content to reflect the changes
this.editor.setValue(result.updatedContent);
new Notice(`✅ Successfully converted citation [${match.number}]`);
} catch (error) {
const saveError = error as Error;
const errorMsg = `Failed to save file: ${saveError.message || 'Unknown error'}`;
console.error('Save error:', saveError);
new Notice(`${errorMsg}`);
}
} else {
console.log('No changes were made to the file');
new Notice(`No instances of [${match.number}] found to convert`);
}
} catch (error) {
console.error('Error converting citation to hex:', error);
new Notice('Error converting citation to hex. Check console for details.');
const conversionError = error as Error;
const errorMsg = `Error converting citation: ${conversionError.message || 'Unknown error'}`;
console.error('Conversion error:', conversionError);
new Notice(`${errorMsg}`);
} finally {
console.log('Citation conversion completed');
}
}

View file

@ -21,64 +21,86 @@ export class CitationService {
}
/**
* Convert all citations to random hex format
* Convert citations to random hex format
* @param content - The markdown content to process
* @param targetCitation - Optional specific citation to convert (e.g., '[1]' or '[^1]')
* @returns Object with updated content and statistics
*/
public convertCitations(content: string): CitationConversionResult {
public convertCitations(content: string, targetCitation?: string): CitationConversionResult {
if (!content) {
console.error('Content is empty or undefined');
return { updatedContent: content || '', changed: false, stats: { citationsConverted: 0 } };
}
let updatedContent = content;
let citationsConverted = 0;
const citationMap = new Map<string, string>(); // Maps original ID to hex ID
// First pass: collect all citations and generate hex IDs
const collectCitations = (match: string, _prefix: string, id: string) => {
// Only process if it's a numeric ID and we haven't seen it before
if (/^\d+$/.test(id) && !citationMap.has(id)) {
citationMap.set(id, this.generateHexId());
}
return match; // Don't modify yet
};
// If we have a target citation, we'll handle it specifically
if (targetCitation) {
console.warn('Targeted citation conversion not yet implemented for URL-based citations');
return { updatedContent, changed: false, stats: { citationsConverted: 0 } };
}
// Collect numeric citations [^1]
updatedContent = updatedContent.replace(/\[\^(\d+)\]/g, collectCitations);
// Collect plain numeric citations [1] (only if not part of a link)
updatedContent = updatedContent.replace(/\[(\d+)\]/g, (match, id, offset) => {
// Only collect if it's a single digit and not part of a link
if (/^\d+$/.test(id) && !/\]\([^)]*$/.test(updatedContent.substring(0, offset))) {
return collectCitations(match, '', id);
}
return match;
});
// Second pass: replace all collected citations with their hex equivalents
citationMap.forEach((hexId, originalId) => {
try {
// Replace [^1] style citations
updatedContent = updatedContent.replace(
new RegExp(`\\[\\^${originalId}\\]`, 'g'),
`[^${hexId}]`
);
try {
// Map to store URL to hex ID mappings
const urlToHexMap = new Map<string, string>();
const hexToUrlMap = new Map<string, string>();
// First pass: Find all [number](url) patterns and replace with [^hex]
updatedContent = updatedContent.replace(/\[(\d+)\]\(([^)]+)\)/g, (match, number, url) => {
let hexId: string;
// Replace [1] style citations (only if not part of a link)
updatedContent = updatedContent.replace(
new RegExp(`(^|[^\\])\\[${originalId}\\]`, 'g'),
`$1[^${hexId}]`
);
// If we've seen this URL before, use the existing hex ID
if (urlToHexMap.has(url)) {
hexId = urlToHexMap.get(url)!;
} else {
// Generate a new hex ID for this URL
hexId = this.generateHexId();
urlToHexMap.set(url, hexId);
hexToUrlMap.set(hexId, url);
}
citationsConverted++;
} catch (error) {
console.error(`Error processing citation ${originalId}:`, error);
return ` [^${hexId}]`; // Add space before citation for better formatting
});
// If we found any citations, add the footnotes section
if (citationsConverted > 0) {
// Check if footnotes section already exists
const hasFootnotesSection = /\n#+\s*Footnotes\s*\n/.test(updatedContent);
// If no footnotes section exists, add one
if (!hasFootnotesSection) {
updatedContent += '\n\n# Footnotes\n';
} else {
updatedContent += '\n';
}
// Add all footnote definitions
hexToUrlMap.forEach((url, hexId) => {
// Check if this footnote is already defined
const footnoteRegex = new RegExp(`\\[\\^${hexId}\\]:.*`, 'g');
if (!footnoteRegex.test(updatedContent)) {
updatedContent += `\n[^${hexId}]: ${url}`;
}
});
}
});
return {
updatedContent,
changed: citationsConverted > 0,
stats: {
citationsConverted
}
};
return {
updatedContent,
changed: citationsConverted > 0,
stats: {
citationsConverted
}
};
} catch (error) {
console.error('Error processing citations:', error);
return {
updatedContent: content, // Return original content on error
changed: false,
stats: { citationsConverted: 0 }
};
}
}
}