feat: add storage monitor to track localStorage usage

This commit is contained in:
2025-10-13 13:50:12 +03:00
parent ef81df762b
commit eaf14aafdd
6 changed files with 139 additions and 17 deletions

View File

@@ -12,6 +12,9 @@ document.addEventListener('DOMContentLoaded', function () {
renderSnippetList();
// Update storage monitor
updateStorageMonitor();
// Auto-select first snippet on page load
const firstSnippet = SnippetStorage.listSnippets()[0];
if (firstSnippet) {

View File

@@ -1,5 +1,9 @@
// Snippet management and localStorage functionality
// Storage limits (5MB in bytes)
const STORAGE_LIMIT_BYTES = 5 * 1024 * 1024;
const WARNING_THRESHOLD = 0.9; // 90% = 4.5MB
// Generate unique ID using Date.now() + random numbers
function generateSnippetId() {
return Date.now() + Math.random() * 1000;
@@ -39,10 +43,11 @@ const SnippetStorage = {
saveSnippets(snippets) {
try {
localStorage.setItem(this.STORAGE_KEY, JSON.stringify(snippets));
updateStorageMonitor();
return true;
} catch (error) {
console.error('Failed to save snippets to localStorage:', error);
// TODO: Handle quota exceeded, show user error
alert('Failed to save: Storage quota may be exceeded. Consider deleting old snippets.');
return false;
}
},
@@ -798,3 +803,46 @@ function revertDraft() {
}
}
// Calculate storage usage in bytes
function calculateStorageUsage() {
const snippetsData = localStorage.getItem(SnippetStorage.STORAGE_KEY);
if (!snippetsData) return 0;
// Calculate size in bytes
return new Blob([snippetsData]).size;
}
// Format bytes to human-readable size
function formatBytes(bytes) {
if (bytes < 1024) return `${bytes} B`;
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
}
// Update storage monitor display
function updateStorageMonitor() {
const usedBytes = calculateStorageUsage();
const percentage = (usedBytes / STORAGE_LIMIT_BYTES) * 100;
const storageText = document.getElementById('storage-text');
const storageFill = document.getElementById('storage-fill');
if (storageText) {
storageText.textContent = `${formatBytes(usedBytes)} / 5 MB`;
}
if (storageFill) {
storageFill.style.width = `${Math.min(percentage, 100)}%`;
// Remove all state classes
storageFill.classList.remove('warning', 'critical');
// Add warning/critical classes based on usage
if (percentage >= 95) {
storageFill.classList.add('critical');
} else if (percentage >= 90) {
storageFill.classList.add('warning');
}
}
}

View File

@@ -332,9 +332,11 @@ body {
.panel-content {
flex: 1;
padding: 8px;
overflow: auto;
overflow: hidden;
background: #ffffff;
border: 1px inset #c0c0c0;
display: flex;
flex-direction: column;
}
/* Panel sizing */
@@ -392,6 +394,10 @@ body {
.snippet-list {
list-style: none;
flex: 1;
overflow-y: auto;
overflow-x: hidden;
margin-bottom: 8px;
}
.snippet-item {
@@ -477,13 +483,14 @@ body {
/* Snippet meta section */
.snippet-meta {
margin-top: 12px;
padding: 8px;
padding: 8px 8px 16px 8px;
border-top: 1px solid #808080;
background: #f0f0f0;
border: 1px inset #c0c0c0;
margin-left: -8px;
margin-right: -8px;
margin-bottom: -8px;
margin-bottom: 0;
flex-shrink: 0;
}
.meta-header {
@@ -598,4 +605,52 @@ body {
.ghost-card .snippet-date {
color: #808080;
}
/* Storage monitor */
.storage-monitor {
padding: 8px;
background: #f0f0f0;
border-top: 1px solid #808080;
margin: 0 -8px -8px -8px;
flex-shrink: 0;
}
.storage-info {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 4px;
font-size: 10px;
}
.storage-label {
font-weight: bold;
color: #000000;
}
.storage-text {
color: #606060;
}
.storage-bar {
width: 100%;
height: 12px;
background: #ffffff;
border: 1px inset #c0c0c0;
position: relative;
}
.storage-fill {
height: 100%;
background: #00aa00;
transition: width 0.3s ease, background-color 0.3s ease;
}
.storage-fill.warning {
background: #ff8800;
}
.storage-fill.critical {
background: #ff0000;
}