Update et upgrade by codex
This commit is contained in:
parent
e3debb3f71
commit
4f4252686a
@ -485,20 +485,24 @@ button.secondary:hover {
|
|||||||
border-color: var(--secondary-color);
|
border-color: var(--secondary-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.danger {
|
button.danger,
|
||||||
|
.btn.danger {
|
||||||
background: var(--accent-color);
|
background: var(--accent-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.danger:hover {
|
button.danger:hover,
|
||||||
|
.btn.danger:hover {
|
||||||
background: #c0392b;
|
background: #c0392b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
button.success {
|
button.success,
|
||||||
|
.btn.success {
|
||||||
background: var(--success-color);
|
background: var(--success-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
button.success:hover {
|
button.success:hover,
|
||||||
|
.btn.success:hover {
|
||||||
background: #229954;
|
background: #229954;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1176,6 +1180,36 @@ select:focus-visible {
|
|||||||
box-shadow: var(--shadow-hover);
|
box-shadow: var(--shadow-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.journal-item.active {
|
||||||
|
border-color: var(--primary-color);
|
||||||
|
box-shadow: 0 0 0 2px rgba(41, 128, 185, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.journal-item-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: stretch;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.journal-item-actions .journal-item {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.journal-item-actions .journal-delete {
|
||||||
|
flex-shrink: 0;
|
||||||
|
padding: 0.6rem 0.9rem;
|
||||||
|
text-transform: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.journal-list.empty {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1.5rem;
|
||||||
|
align-items: center;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-light);
|
||||||
|
}
|
||||||
|
|
||||||
.journal-preview {
|
.journal-preview {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
306
assets/js/app.js
306
assets/js/app.js
@ -8,6 +8,9 @@ class ConceptionAssistant {
|
|||||||
this.tocTimer = null;
|
this.tocTimer = null;
|
||||||
this.isPreviewMode = false;
|
this.isPreviewMode = false;
|
||||||
this.originalContent = '';
|
this.originalContent = '';
|
||||||
|
this.localBackupKey = 'design-journal-autosave';
|
||||||
|
this.localBackupTimer = null;
|
||||||
|
this.localStorageSupported = this.detectLocalStorageSupport();
|
||||||
this.init();
|
this.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -15,6 +18,7 @@ class ConceptionAssistant {
|
|||||||
this.setupEditor();
|
this.setupEditor();
|
||||||
this.setupEventListeners();
|
this.setupEventListeners();
|
||||||
await this.loadJournalList();
|
await this.loadJournalList();
|
||||||
|
this.checkForLocalBackup();
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
this.updateStatistics();
|
this.updateStatistics();
|
||||||
}
|
}
|
||||||
@ -26,6 +30,7 @@ class ConceptionAssistant {
|
|||||||
this.editor.addEventListener('input', () => {
|
this.editor.addEventListener('input', () => {
|
||||||
this.debounceTOC();
|
this.debounceTOC();
|
||||||
this.updateStatistics();
|
this.updateStatistics();
|
||||||
|
this.scheduleLocalBackup();
|
||||||
});
|
});
|
||||||
|
|
||||||
// Keyboard shortcut handling
|
// Keyboard shortcut handling
|
||||||
@ -171,6 +176,134 @@ class ConceptionAssistant {
|
|||||||
this.redoStack = [];
|
this.redoStack = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
detectLocalStorageSupport() {
|
||||||
|
if (typeof window === 'undefined') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const testKey = '__design_journal_test__';
|
||||||
|
window.localStorage.setItem(testKey, '1');
|
||||||
|
window.localStorage.removeItem(testKey);
|
||||||
|
return true;
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Local storage unavailable, disabling local backups.', error);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scheduleLocalBackup() {
|
||||||
|
if (!this.localStorageSupported) return;
|
||||||
|
|
||||||
|
if (this.localBackupTimer) {
|
||||||
|
clearTimeout(this.localBackupTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.localBackupTimer = setTimeout(() => {
|
||||||
|
this.saveLocalBackup();
|
||||||
|
}, 1500);
|
||||||
|
}
|
||||||
|
|
||||||
|
saveLocalBackup(contentOverride = null) {
|
||||||
|
if (!this.localStorageSupported || typeof window === 'undefined') return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const content = typeof contentOverride === 'string'
|
||||||
|
? contentOverride
|
||||||
|
: (this.editor ? this.editor.innerText : '');
|
||||||
|
|
||||||
|
const normalizedContent = content || '';
|
||||||
|
|
||||||
|
if (!normalizedContent.trim()) {
|
||||||
|
window.localStorage.removeItem(this.localBackupKey);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const backupPayload = {
|
||||||
|
content: normalizedContent,
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
journalId: this.currentJournalId
|
||||||
|
};
|
||||||
|
|
||||||
|
window.localStorage.setItem(this.localBackupKey, JSON.stringify(backupPayload));
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Unable to persist local backup.', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
discardLocalBackup() {
|
||||||
|
if (!this.localStorageSupported || typeof window === 'undefined') return;
|
||||||
|
try {
|
||||||
|
window.localStorage.removeItem(this.localBackupKey);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Unable to discard local backup.', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkForLocalBackup() {
|
||||||
|
if (!this.localStorageSupported || typeof window === 'undefined') return;
|
||||||
|
|
||||||
|
let rawBackup;
|
||||||
|
try {
|
||||||
|
rawBackup = window.localStorage.getItem(this.localBackupKey);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Unable to access local backup.', error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!rawBackup) return;
|
||||||
|
|
||||||
|
let backup;
|
||||||
|
try {
|
||||||
|
backup = JSON.parse(rawBackup);
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('Invalid local backup payload, clearing it.', error);
|
||||||
|
this.discardLocalBackup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!backup || typeof backup.content !== 'string' || !backup.content.trim()) {
|
||||||
|
this.discardLocalBackup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currentContent = this.editor ? this.editor.innerText : '';
|
||||||
|
if (currentContent.trim() === backup.content.trim()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const savedAt = backup.timestamp ? new Date(backup.timestamp) : null;
|
||||||
|
const formattedDate = savedAt && !Number.isNaN(savedAt.getTime())
|
||||||
|
? savedAt.toLocaleString()
|
||||||
|
: 'a previous session';
|
||||||
|
|
||||||
|
const shouldRestore = window.confirm(
|
||||||
|
`A local draft saved on ${formattedDate} was found. Do you want to restore it?`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!shouldRestore) {
|
||||||
|
this.discardLocalBackup();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.editor.innerText = backup.content;
|
||||||
|
this.currentJournalId = backup.journalId ?? this.currentJournalId;
|
||||||
|
this.undoStack = [backup.content];
|
||||||
|
this.redoStack = [];
|
||||||
|
this.generateTOC();
|
||||||
|
this.updateStatistics();
|
||||||
|
this.saveState(true);
|
||||||
|
this.saveLocalBackup(backup.content);
|
||||||
|
|
||||||
|
const statusEl = document.getElementById('save-status');
|
||||||
|
if (statusEl) {
|
||||||
|
statusEl.textContent = 'Local draft restored';
|
||||||
|
setTimeout(() => statusEl.textContent = '', 4000);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showNotification('Local draft restored', 'success');
|
||||||
|
}
|
||||||
|
|
||||||
undo() {
|
undo() {
|
||||||
if (this.undoStack.length > 1) {
|
if (this.undoStack.length > 1) {
|
||||||
const currentContent = this.undoStack.pop();
|
const currentContent = this.undoStack.pop();
|
||||||
@ -183,6 +316,7 @@ class ConceptionAssistant {
|
|||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
|
|
||||||
this.showNotification('Undo completed', 'success');
|
this.showNotification('Undo completed', 'success');
|
||||||
|
this.saveLocalBackup(previousContent);
|
||||||
} else {
|
} else {
|
||||||
this.showNotification('Nothing to undo', 'warning');
|
this.showNotification('Nothing to undo', 'warning');
|
||||||
}
|
}
|
||||||
@ -199,6 +333,7 @@ class ConceptionAssistant {
|
|||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
|
|
||||||
this.showNotification('Redo completed', 'success');
|
this.showNotification('Redo completed', 'success');
|
||||||
|
this.saveLocalBackup(nextContent);
|
||||||
} else {
|
} else {
|
||||||
this.showNotification('Nothing to redo', 'warning');
|
this.showNotification('Nothing to redo', 'warning');
|
||||||
}
|
}
|
||||||
@ -241,6 +376,8 @@ class ConceptionAssistant {
|
|||||||
this.currentJournalId = result.data.id;
|
this.currentJournalId = result.data.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.saveLocalBackup(content);
|
||||||
|
|
||||||
statusEl.textContent = 'Saved';
|
statusEl.textContent = 'Saved';
|
||||||
this.showNotification('Journal saved successfully', 'success');
|
this.showNotification('Journal saved successfully', 'success');
|
||||||
setTimeout(() => statusEl.textContent = '', 3000);
|
setTimeout(() => statusEl.textContent = '', 3000);
|
||||||
@ -282,6 +419,8 @@ class ConceptionAssistant {
|
|||||||
this.currentJournalId = id;
|
this.currentJournalId = id;
|
||||||
this.editor.innerText = journal.markdownContent;
|
this.editor.innerText = journal.markdownContent;
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
|
this.updateStatistics();
|
||||||
|
this.saveLocalBackup(journal.markdownContent);
|
||||||
|
|
||||||
// Reset history for the new journal
|
// Reset history for the new journal
|
||||||
this.undoStack = [journal.markdownContent];
|
this.undoStack = [journal.markdownContent];
|
||||||
@ -304,46 +443,12 @@ class ConceptionAssistant {
|
|||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
const journalList = result.data.map(journal => {
|
this.renderJournalSelector(result.data);
|
||||||
// Extract first lines for preview
|
|
||||||
const lines = journal.markdownContent.split('\n');
|
|
||||||
const firstLines = lines.slice(0, 3).join('\n');
|
|
||||||
const preview = firstLines.length > 150 ? firstLines.substring(0, 150) + '...' : firstLines;
|
|
||||||
|
|
||||||
return `
|
|
||||||
<div class="journal-item-container">
|
|
||||||
<button class="journal-item btn secondary" data-id="${journal.id}">
|
|
||||||
<div class="journal-preview">
|
|
||||||
<strong>Journal ${journal.id}</strong>
|
|
||||||
<div>${preview}</div>
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
const modalBody = document.getElementById('journal-modal-body');
|
|
||||||
modalBody.innerHTML = `
|
|
||||||
<div class="journal-list">
|
|
||||||
<button class="btn success mb-2" onclick="app.createNewJournal(); app.closeModal();" style="width: 100%;">
|
|
||||||
+ New Journal
|
|
||||||
</button>
|
|
||||||
${journalList}
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
|
|
||||||
// Show modal with animation
|
// Show modal with animation
|
||||||
const modal = document.getElementById('journal-modal');
|
const modal = document.getElementById('journal-modal');
|
||||||
modal.style.display = 'flex';
|
modal.style.display = 'flex';
|
||||||
setTimeout(() => modal.classList.add('show'), 10);
|
setTimeout(() => modal.classList.add('show'), 10);
|
||||||
|
|
||||||
// Add event listeners
|
|
||||||
document.querySelectorAll('.journal-item').forEach(btn => {
|
|
||||||
btn.addEventListener('click', () => {
|
|
||||||
this.loadJournal(btn.dataset.id);
|
|
||||||
this.closeModal();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error:', error);
|
console.error('Error:', error);
|
||||||
@ -359,11 +464,133 @@ class ConceptionAssistant {
|
|||||||
}, 300);
|
}, 300);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
renderJournalSelector(journals = []) {
|
||||||
|
const modalBody = document.getElementById('journal-modal-body');
|
||||||
|
if (!modalBody) return;
|
||||||
|
|
||||||
|
if (!Array.isArray(journals) || journals.length === 0) {
|
||||||
|
modalBody.innerHTML = `
|
||||||
|
<div class="journal-list empty">
|
||||||
|
<p>No journals saved yet.</p>
|
||||||
|
<button class="btn success" onclick="app.createNewJournal(); app.closeModal();" style="width: 100%;">
|
||||||
|
+ New Journal
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const journalList = journals.map(journal => {
|
||||||
|
const lines = journal.markdownContent.split('\n');
|
||||||
|
const firstLines = lines.slice(0, 3).join('\n');
|
||||||
|
const preview = firstLines.length > 150 ? firstLines.substring(0, 150) + '...' : firstLines;
|
||||||
|
|
||||||
|
const isCurrent = String(this.currentJournalId) === String(journal.id);
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="journal-item-container">
|
||||||
|
<div class="journal-item-actions">
|
||||||
|
<button class="journal-item btn secondary${isCurrent ? ' active' : ''}" data-id="${journal.id}">
|
||||||
|
<div class="journal-preview">
|
||||||
|
<strong>Journal ${journal.id}${isCurrent ? ' (current)' : ''}</strong>
|
||||||
|
<div>${preview}</div>
|
||||||
|
</div>
|
||||||
|
</button>
|
||||||
|
<button class="btn danger journal-delete" data-id="${journal.id}" title="Delete journal ${journal.id}">
|
||||||
|
Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
modalBody.innerHTML = `
|
||||||
|
<div class="journal-list">
|
||||||
|
<button class="btn success mb-2" onclick="app.createNewJournal(); app.closeModal();" style="width: 100%;">
|
||||||
|
+ New Journal
|
||||||
|
</button>
|
||||||
|
${journalList}
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
// Add load listeners
|
||||||
|
modalBody.querySelectorAll('.journal-item').forEach(btn => {
|
||||||
|
btn.addEventListener('click', () => {
|
||||||
|
this.loadJournal(btn.dataset.id);
|
||||||
|
this.closeModal();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Add delete listeners
|
||||||
|
modalBody.querySelectorAll('.journal-delete').forEach(btn => {
|
||||||
|
btn.addEventListener('click', (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
|
this.deleteJournal(btn.dataset.id, btn);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteJournal(id, buttonEl) {
|
||||||
|
if (!id) return;
|
||||||
|
|
||||||
|
const confirmation = window.confirm(`Are you sure you want to delete journal ${id}? This action cannot be undone.`);
|
||||||
|
if (!confirmation) return;
|
||||||
|
|
||||||
|
if (buttonEl) {
|
||||||
|
buttonEl.disabled = true;
|
||||||
|
buttonEl.textContent = 'Deleting...';
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`/api/journals/${id}`, {
|
||||||
|
method: 'DELETE'
|
||||||
|
});
|
||||||
|
|
||||||
|
const result = await response.json();
|
||||||
|
if (!result.success) {
|
||||||
|
throw new Error(result.error || 'Deletion failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.showNotification(`Journal ${id} deleted`, 'success');
|
||||||
|
|
||||||
|
// Refresh list of journals
|
||||||
|
const refreshResponse = await fetch('/api/journals');
|
||||||
|
const refreshResult = await refreshResponse.json();
|
||||||
|
|
||||||
|
if (refreshResult.success) {
|
||||||
|
const remaining = refreshResult.data;
|
||||||
|
|
||||||
|
// If the current journal was deleted, load another available journal or clear editor
|
||||||
|
if (String(this.currentJournalId) === String(id)) {
|
||||||
|
if (remaining.length > 0) {
|
||||||
|
const lastJournal = remaining[remaining.length - 1];
|
||||||
|
await this.loadJournal(lastJournal.id);
|
||||||
|
} else {
|
||||||
|
this.createNewJournal();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.renderJournalSelector(remaining);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error deleting journal:', error);
|
||||||
|
this.showNotification(`Error deleting journal: ${error.message}`, 'error');
|
||||||
|
} finally {
|
||||||
|
if (buttonEl) {
|
||||||
|
buttonEl.disabled = false;
|
||||||
|
buttonEl.textContent = 'Delete';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
createNewJournal() {
|
createNewJournal() {
|
||||||
this.currentJournalId = null;
|
this.currentJournalId = null;
|
||||||
this.editor.innerText = '';
|
this.editor.innerText = '';
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
|
this.updateStatistics();
|
||||||
this.clearFeedback();
|
this.clearFeedback();
|
||||||
|
this.saveLocalBackup('');
|
||||||
|
|
||||||
// Reset history for new journal
|
// Reset history for new journal
|
||||||
this.undoStack = [''];
|
this.undoStack = [''];
|
||||||
@ -391,6 +618,7 @@ class ConceptionAssistant {
|
|||||||
|
|
||||||
// Update statistics
|
// Update statistics
|
||||||
this.updateStatistics();
|
this.updateStatistics();
|
||||||
|
this.saveLocalBackup('');
|
||||||
|
|
||||||
// Show notification
|
// Show notification
|
||||||
this.showNotification('New blank document created', 'success');
|
this.showNotification('New blank document created', 'success');
|
||||||
@ -750,6 +978,8 @@ class ConceptionAssistant {
|
|||||||
const importedContent = e.target.result;
|
const importedContent = e.target.result;
|
||||||
this.editor.innerText = importedContent;
|
this.editor.innerText = importedContent;
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
|
this.updateStatistics();
|
||||||
|
this.saveLocalBackup(importedContent);
|
||||||
this.currentJournalId = null; // New journal
|
this.currentJournalId = null; // New journal
|
||||||
|
|
||||||
// Reset history for imported file
|
// Reset history for imported file
|
||||||
@ -1162,6 +1392,8 @@ class ConceptionAssistant {
|
|||||||
if (data.markdown && data.markdown !== this.editor.innerText) {
|
if (data.markdown && data.markdown !== this.editor.innerText) {
|
||||||
this.editor.innerText = data.markdown;
|
this.editor.innerText = data.markdown;
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
|
this.updateStatistics();
|
||||||
|
this.saveLocalBackup(data.markdown);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scroll to bottom of feedback to see new iteration
|
// Scroll to bottom of feedback to see new iteration
|
||||||
@ -1179,6 +1411,8 @@ class ConceptionAssistant {
|
|||||||
// Ensure final content is in editor
|
// Ensure final content is in editor
|
||||||
this.editor.innerText = data.finalMarkdown;
|
this.editor.innerText = data.finalMarkdown;
|
||||||
this.generateTOC();
|
this.generateTOC();
|
||||||
|
this.updateStatistics();
|
||||||
|
this.saveLocalBackup(data.finalMarkdown);
|
||||||
// Save final state
|
// Save final state
|
||||||
this.saveState(true);
|
this.saveState(true);
|
||||||
}
|
}
|
||||||
@ -1649,6 +1883,8 @@ function initializeTemplateForm() {
|
|||||||
// Load template into editor
|
// Load template into editor
|
||||||
app.editor.innerText = result.data.content;
|
app.editor.innerText = result.data.content;
|
||||||
app.generateTOC();
|
app.generateTOC();
|
||||||
|
app.updateStatistics();
|
||||||
|
app.saveLocalBackup(result.data.content);
|
||||||
app.currentJournalId = null; // New journal
|
app.currentJournalId = null; // New journal
|
||||||
|
|
||||||
// Reset history for new template
|
// Reset history for new template
|
||||||
|
|||||||
@ -53,23 +53,30 @@ function createMd(markdownContent = "# Title\nContent...") {
|
|||||||
const mdPath = path.join(dataDir, `${uuid}.md`);
|
const mdPath = path.join(dataDir, `${uuid}.md`);
|
||||||
const mapPath = path.join(dataDir, 'uuid_map.json');
|
const mapPath = path.join(dataDir, 'uuid_map.json');
|
||||||
|
|
||||||
|
// Guarantee storage directory exists (defensive in case server init didn't run)
|
||||||
|
if (!fs.existsSync(dataDir)) {
|
||||||
|
fs.mkdirSync(dataDir, { recursive: true });
|
||||||
|
logger.info('STORAGE', 'Created data directory on demand', { path: dataDir });
|
||||||
|
}
|
||||||
|
|
||||||
fs.writeFileSync(mdPath, markdownContent, { encoding: 'utf8', flag: 'w' });
|
fs.writeFileSync(mdPath, markdownContent, { encoding: 'utf8', flag: 'w' });
|
||||||
|
|
||||||
let map = {};
|
let map = {};
|
||||||
if (fs.existsSync(mapPath)) {
|
if (fs.existsSync(mapPath)) {
|
||||||
map = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
|
map = JSON.parse(fs.readFileSync(mapPath, 'utf8'));
|
||||||
}
|
}
|
||||||
let id = Object.keys(map).at(-1);
|
|
||||||
if (id == undefined){
|
const existingIds = Object.keys(map)
|
||||||
map[0] = uuid;
|
.map((key) => Number.parseInt(key, 10))
|
||||||
} else {
|
.filter((value) => Number.isInteger(value) && value >= 0);
|
||||||
id = +id+1
|
|
||||||
|
const nextIdNumber = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 0;
|
||||||
|
const id = String(nextIdNumber);
|
||||||
map[id] = uuid;
|
map[id] = uuid;
|
||||||
}
|
|
||||||
|
|
||||||
fs.writeFileSync(mapPath, JSON.stringify(map, null, 2), 'utf8');
|
fs.writeFileSync(mapPath, JSON.stringify(map, null, 2), 'utf8');
|
||||||
|
|
||||||
return { id, uuid, path: mdPath, markdownContent};
|
return { id, uuid, path: mdPath, markdownContent };
|
||||||
}
|
}
|
||||||
|
|
||||||
function readMd(id = undefined) {
|
function readMd(id = undefined) {
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user