mirror of
https://github.com/olehomelchenko/astrolabe-nvc.git
synced 2025-12-21 21:22:23 +00:00
feat: enhance snippet management with import/export functionality and size display
This commit is contained in:
@@ -89,12 +89,33 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
});
|
||||
});
|
||||
|
||||
// Header links - show placeholder
|
||||
document.querySelectorAll('.header-link').forEach(link => {
|
||||
link.addEventListener('click', function () {
|
||||
// Header links
|
||||
const importLink = document.getElementById('import-link');
|
||||
const exportLink = document.getElementById('export-link');
|
||||
const helpLink = document.getElementById('help-link');
|
||||
const importFileInput = document.getElementById('import-file-input');
|
||||
|
||||
if (importLink && importFileInput) {
|
||||
importLink.addEventListener('click', function () {
|
||||
importFileInput.click();
|
||||
});
|
||||
|
||||
importFileInput.addEventListener('change', function () {
|
||||
importSnippets(this);
|
||||
});
|
||||
}
|
||||
|
||||
if (exportLink) {
|
||||
exportLink.addEventListener('click', function () {
|
||||
exportSnippets();
|
||||
});
|
||||
}
|
||||
|
||||
if (helpLink) {
|
||||
helpLink.addEventListener('click', function () {
|
||||
alert('Coming soon in a future phase!');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// View mode toggle buttons
|
||||
document.getElementById('view-draft').addEventListener('click', () => {
|
||||
|
||||
@@ -239,6 +239,11 @@ function renderSnippetList(searchQuery = null) {
|
||||
dateText = formatSnippetDate(snippet.modified);
|
||||
}
|
||||
|
||||
// Calculate snippet size
|
||||
const snippetSize = new Blob([JSON.stringify(snippet)]).size;
|
||||
const sizeKB = snippetSize / 1024;
|
||||
const sizeHTML = sizeKB >= 1 ? `<span class="snippet-size">${sizeKB.toFixed(0)} KB</span>` : '';
|
||||
|
||||
// Determine status: green if no draft changes, yellow if has draft
|
||||
const hasDraft = JSON.stringify(snippet.spec) !== JSON.stringify(snippet.draftSpec);
|
||||
const statusClass = hasDraft ? 'draft' : 'published';
|
||||
@@ -249,6 +254,7 @@ function renderSnippetList(searchQuery = null) {
|
||||
<div class="snippet-name">${snippet.name}</div>
|
||||
<div class="snippet-date">${dateText}</div>
|
||||
</div>
|
||||
${sizeHTML}
|
||||
<div class="snippet-status ${statusClass}"></div>
|
||||
</li>
|
||||
`;
|
||||
@@ -846,3 +852,125 @@ function updateStorageMonitor() {
|
||||
}
|
||||
}
|
||||
|
||||
// Export all snippets to JSON file
|
||||
function exportSnippets() {
|
||||
const snippets = SnippetStorage.loadSnippets();
|
||||
|
||||
if (snippets.length === 0) {
|
||||
alert('No snippets to export');
|
||||
return;
|
||||
}
|
||||
|
||||
// Create JSON blob
|
||||
const jsonString = JSON.stringify(snippets, null, 2);
|
||||
const blob = new Blob([jsonString], { type: 'application/json' });
|
||||
|
||||
// Create download link
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = `astrolabe-snippets-${new Date().toISOString().slice(0, 10)}.json`;
|
||||
|
||||
// Trigger download
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
// Normalize external snippet format to Astrolabe format
|
||||
function normalizeSnippet(externalSnippet) {
|
||||
// Check if already in Astrolabe format (has 'created' field as ISO string)
|
||||
const isAstrolabeFormat = externalSnippet.created &&
|
||||
typeof externalSnippet.created === 'string' &&
|
||||
externalSnippet.created.includes('T');
|
||||
|
||||
if (isAstrolabeFormat) {
|
||||
// Already in correct format, just ensure all fields exist
|
||||
return {
|
||||
id: externalSnippet.id || generateSnippetId(),
|
||||
name: externalSnippet.name || generateSnippetName(),
|
||||
created: externalSnippet.created,
|
||||
modified: externalSnippet.modified || externalSnippet.created,
|
||||
spec: externalSnippet.spec || {},
|
||||
draftSpec: externalSnippet.draftSpec || externalSnippet.spec || {},
|
||||
comment: externalSnippet.comment || "",
|
||||
tags: externalSnippet.tags || [],
|
||||
datasetRefs: externalSnippet.datasetRefs || [],
|
||||
meta: externalSnippet.meta || {}
|
||||
};
|
||||
}
|
||||
|
||||
// External format - map fields
|
||||
const createdDate = externalSnippet.createdAt ?
|
||||
new Date(externalSnippet.createdAt).toISOString() :
|
||||
new Date().toISOString();
|
||||
|
||||
return {
|
||||
id: generateSnippetId(), // Generate new ID to avoid conflicts
|
||||
name: externalSnippet.name || generateSnippetName(),
|
||||
created: createdDate,
|
||||
modified: createdDate,
|
||||
spec: externalSnippet.content || externalSnippet.spec || {},
|
||||
draftSpec: externalSnippet.draft || externalSnippet.draftSpec || externalSnippet.content || externalSnippet.spec || {},
|
||||
comment: externalSnippet.comment || "",
|
||||
tags: ["imported"], // Add 'imported' tag
|
||||
datasetRefs: [],
|
||||
meta: {}
|
||||
};
|
||||
}
|
||||
|
||||
// Import snippets from JSON file
|
||||
function importSnippets(fileInput) {
|
||||
const file = fileInput.files[0];
|
||||
if (!file) return;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = function(e) {
|
||||
try {
|
||||
const importedData = JSON.parse(e.target.result);
|
||||
|
||||
// Handle both single snippet and array of snippets
|
||||
const snippetsToImport = Array.isArray(importedData) ? importedData : [importedData];
|
||||
|
||||
if (snippetsToImport.length === 0) {
|
||||
alert('No snippets found in file');
|
||||
return;
|
||||
}
|
||||
|
||||
// Normalize and merge with existing snippets
|
||||
const existingSnippets = SnippetStorage.loadSnippets();
|
||||
const existingIds = new Set(existingSnippets.map(s => s.id));
|
||||
|
||||
let importedCount = 0;
|
||||
snippetsToImport.forEach(snippet => {
|
||||
const normalized = normalizeSnippet(snippet);
|
||||
|
||||
// Ensure no ID conflicts
|
||||
while (existingIds.has(normalized.id)) {
|
||||
normalized.id = generateSnippetId();
|
||||
}
|
||||
|
||||
existingSnippets.push(normalized);
|
||||
existingIds.add(normalized.id);
|
||||
importedCount++;
|
||||
});
|
||||
|
||||
// Save all snippets
|
||||
if (SnippetStorage.saveSnippets(existingSnippets)) {
|
||||
alert(`Successfully imported ${importedCount} snippet${importedCount !== 1 ? 's' : ''}`);
|
||||
renderSnippetList();
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Import error:', error);
|
||||
alert('Failed to import snippets. Please check that the file is valid JSON.');
|
||||
}
|
||||
|
||||
// Clear file input
|
||||
fileInput.value = '';
|
||||
};
|
||||
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
|
||||
@@ -458,6 +458,14 @@ body {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.snippet-size {
|
||||
font-size: 10px;
|
||||
color: #808080;
|
||||
margin-left: auto;
|
||||
margin-right: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.editor-placeholder {
|
||||
background: #ffffff;
|
||||
border: 2px inset #c0c0c0;
|
||||
|
||||
Reference in New Issue
Block a user