mirror of
https://github.com/sune-org/store.git
synced 2026-01-13 16:17:58 +00:00
28 lines
14 KiB
JSON
28 lines
14 KiB
JSON
[
|
|
{
|
|
"id": "d79ejic",
|
|
"name": "Storage Manager (LF)",
|
|
"pinned": false,
|
|
"avatar": "",
|
|
"url": "gh://sune-org/store@main/storagelf.sune",
|
|
"updatedAt": 1757099450910,
|
|
"settings": {
|
|
"model": "openai/gpt-5",
|
|
"temperature": 1,
|
|
"top_p": 0.96,
|
|
"top_k": 0,
|
|
"frequency_penalty": 0,
|
|
"presence_penalty": 0,
|
|
"repetition_penalty": 1,
|
|
"min_p": 0,
|
|
"top_a": 0,
|
|
"max_tokens": 0,
|
|
"verbosity": "",
|
|
"reasoning_effort": "default",
|
|
"system_prompt": "",
|
|
"html": "<div id=\"storage-manager-sune\" class=\"p-2 sm:p-4 bg-white text-xs font-mono\">\n <!-- Header -->\n <div class=\"flex items-center justify-between pb-2 mb-2 border-b\">\n <div class=\"flex items-center gap-2\">\n <i data-lucide=\"database\" class=\"w-5 h-5\"></i>\n <h3 class=\"font-sans font-bold text-base\">Storage Manager</h3>\n </div>\n <div class=\"flex items-center gap-2\">\n <span class=\"text-gray-400 font-sans text-xs\">v1.0.1</span>\n <button data-action=\"refresh\" class=\"p-2 rounded-lg hover:bg-gray-100 active:scale-95 transition-transform\" title=\"Refresh\">\n <i data-lucide=\"refresh-cw\" class=\"w-4 h-4\"></i>\n </button>\n </div>\n </div>\n\n <!-- Main Content -->\n <div id=\"storage-lists\" class=\"space-y-2\">\n <p class=\"px-2 py-4 text-center text-gray-400 font-sans\"><i data-lucide=\"loader\" class=\"w-4 h-4 inline-block animate-spin\"></i> Loading...</p>\n </div>\n\n <!-- Danger Zone -->\n <div class=\"mt-4 pt-4 border-t border-dashed border-red-300\">\n <h4 class=\"text-sm font-bold text-red-700 font-sans mb-2\">Danger Zone</h4>\n <div class=\"grid grid-cols-2 sm:grid-cols-3 gap-2\">\n <button data-action=\"nuke-localstorage\" class=\"w-full text-center px-2 py-2 text-xs font-sans font-semibold text-white bg-red-600 rounded-lg hover:bg-red-700 active:scale-95 transition-transform\">Clear localStorage</button>\n <button data-action=\"nuke-sessionstorage\" class=\"w-full text-center px-2 py-2 text-xs font-sans font-semibold text-white bg-red-600 rounded-lg hover:bg-red-700 active:scale-95 transition-transform\">Clear sessionStorage</button>\n <button data-action=\"nuke-all-idb\" class=\"w-full col-span-2 sm:col-span-1 text-center px-2 py-2 text-xs font-sans font-semibold text-white bg-red-800 rounded-lg hover:bg-red-900 active:scale-95 transition-transform\">DELETE ALL DATABASES</button>\n </div>\n </div>\n\n <!-- Value Viewer Modal -->\n <div id=\"storage-value-viewer\" class=\"fixed inset-0 z-[99] bg-black/50 backdrop-blur-sm hidden p-4 sm:p-8\" data-action=\"close-viewer\">\n <div class=\"w-full h-full max-w-2xl mx-auto bg-white rounded-xl shadow-2xl flex flex-col overflow-hidden\">\n <div class=\"flex-shrink-0 flex items-center justify-between p-3 border-b\">\n <h4 class=\"font-sans font-bold\">Value Inspector</h4>\n <button data-action=\"close-viewer\" class=\"p-2 rounded-full hover:bg-gray-100\"><i data-lucide=\"x\" class=\"w-5 h-5\"></i></button>\n </div>\n <pre class=\"flex-1 p-4 overflow-auto bg-gray-50 text-xs\"></pre>\n </div>\n </div>\n</div>\n\n<script>\n(()=>{\n if(window.StorageManagerSune) return window.StorageManagerSune.init();\n\n const SUNE_ID = 'storage-manager-sune';\n const G = {\n root: document.getElementById(SUNE_ID),\n lists: document.getElementById('storage-lists'),\n viewer: document.getElementById('storage-value-viewer'),\n viewerPre: document.querySelector('#storage-value-viewer pre'),\n };\n\n const esc=s=>String(s).replace(/[&<>\"'`]/g,c=>({\n \"&\":\"&\",\"<\":\"<\",\">\":\">\",\"\\\"\":\""\",\"'\":\"'\",\"`\":\"`\"\n }[c]));\n\n const fmtVal = v => {\n if(v==null) return '<i class=\"text-gray-400\">null</i>';\n let p; try { p = JSON.parse(v); } catch (e) { return esc(String(v).substring(0, 100)) + (String(v).length > 100 ? '...' : ''); }\n if(typeof p === 'object' && p !== null) return '<span class=\"text-blue-600\">{...} Object</span>';\n return esc(String(v).substring(0, 100)) + (String(v).length > 100 ? '...' : '');\n };\n \n const fmtSize=b=>{const u=['B','KB','MB'],i=b==0?0:Math.floor(Math.log(b)/Math.log(1024));return`${(b/Math.pow(1024,i)).toFixed(1)}${u[i]}`};\n\n const createItemRow = (key, value, { storage, db, store }) => `\n <div class=\"flex items-center justify-between pl-8 pr-2 py-1 hover:bg-gray-100 rounded\">\n <div class=\"truncate pr-2\">\n <strong class=\"text-purple-700\">${esc(key)}:</strong>\n <span class=\"text-gray-600\">${fmtVal(value)}</span>\n </div>\n <div class=\"flex items-center flex-shrink-0\">\n <button data-action=\"view-item\" data-storage=\"${storage}\" data-key=\"${esc(key)}\" ${db?`data-db=\"${esc(db)}\"`:''} ${store?`data-store=\"${esc(store)}\"`:''} class=\"p-1.5 rounded hover:bg-blue-100\" title=\"View\"><i data-lucide=\"eye\" class=\"w-3 h-3 text-blue-600 pointer-events-none\"></i></button>\n <button data-action=\"delete-item\" data-storage=\"${storage}\" data-key=\"${esc(key)}\" ${db?`data-db=\"${esc(db)}\"`:''} ${store?`data-store=\"${esc(store)}\"`:''} class=\"p-1.5 rounded hover:bg-red-100\" title=\"Delete\"><i data-lucide=\"trash-2\" class=\"w-3 h-3 text-red-600 pointer-events-none\"></i></button>\n </div>\n </div>`;\n\n const createTree = ({ title, icon, count, content, onClear, clearData }) => `\n <details class=\"group bg-gray-50 rounded-lg border\">\n <summary class=\"flex items-center justify-between p-2 cursor-pointer select-none\">\n <div class=\"flex items-center gap-2 font-sans font-medium\">\n <i data-lucide=\"chevron-right\" class=\"w-4 h-4 transition-transform group-open:rotate-90\"></i>\n <i data-lucide=\"${icon}\" class=\"w-4 h-4 text-gray-600\"></i>\n <span>${esc(title)}</span>\n <span class=\"text-gray-400 text-xs\">(${count})</span>\n </div>\n ${onClear ? `<button data-action=\"${onClear}\" ${Object.entries(clearData).map(([k,v])=>`data-${k}=\"${esc(v)}\"`).join(' ')} class=\"p-1.5 rounded hover:bg-red-100\" title=\"Clear ${title}\"><i data-lucide=\"x-circle\" class=\"w-3.5 h-3.5 text-red-500 pointer-events-none\"></i></button>` : ''}\n </summary>\n <div class=\"border-t py-1\">${content}</div>\n </details>`;\n \n const renderWebStorage = async (storageType) => {\n const storage = storageType === 'local' ? localStorage : sessionStorage;\n let content = '', count = storage.length;\n if(count === 0) return createTree({ title: `${storageType}Storage`, icon: 'archive', count: 0, content: '<p class=\"pl-8 py-1 text-gray-400 font-sans\">Empty</p>' });\n \n for (let i = 0; i < count; i++) {\n const key = storage.key(i);\n const value = storage.getItem(key);\n content += createItemRow(key, value, { storage: storageType });\n }\n return createTree({ title: `${storageType}Storage`, icon: 'archive', count, content });\n };\n\n const renderIndexedDB = async () => {\n if (!('indexedDB' in window) || !indexedDB.databases) {\n return createTree({ title: 'IndexedDB', icon: 'server-off', count: 'N/A', content: '<p class=\"pl-8 py-1 text-red-500 font-sans\">Not available in this context.</p>' });\n }\n const dbs = await indexedDB.databases();\n if(dbs.length === 0) return createTree({ title: 'IndexedDB', icon: 'server', count: 0, content: '<p class=\"pl-8 py-1 text-gray-400 font-sans\">No databases found.</p>' });\n \n const dbPromises = dbs.map(dbInfo => new Promise(async (resolve) => {\n const openDb = (name, version) => new Promise((res, rej) => {\n const req = indexedDB.open(name, version);\n req.onsuccess = e => res(e.target.result);\n req.onerror = e => rej(e.target.error);\n });\n\n try {\n const db = await openDb(dbInfo.name, dbInfo.version);\n const storeNames = Array.from(db.objectStoreNames);\n const storePromises = storeNames.map(storeName => new Promise(async (resStore) => {\n const tx = db.transaction(storeName, 'readonly');\n const store = tx.objectStore(storeName);\n const req = store.getAll();\n const keysReq = store.getAllKeys();\n \n req.onsuccess = () => {\n const values = req.result;\n keysReq.onsuccess = () => {\n const keys = keysReq.result;\n let itemsContent = '';\n if (keys.length === 0) {\n itemsContent = '<p class=\"pl-8 py-1 text-gray-400 font-sans\">Empty</p>';\n } else {\n for(let i=0; i<keys.length; i++) {\n itemsContent += createItemRow(keys[i], JSON.stringify(values[i]), { storage: 'idb', db: db.name, store: storeName });\n }\n }\n resStore(createTree({ title: storeName, icon: 'table', count: keys.length, content: itemsContent, onClear: 'clear-store', clearData: { db: db.name, store: storeName } }));\n };\n };\n req.onerror = () => resStore('');\n keysReq.onerror = () => resStore('');\n }));\n const storesContent = (await Promise.all(storePromises)).join('');\n db.close();\n resolve(createTree({ title: dbInfo.name, icon: 'database', count: storeNames.length, content: storesContent, onClear: 'delete-db', clearData: { db: db.name } }));\n } catch (e) {\n resolve(createTree({ title: dbInfo.name, icon: 'server-crash', count: 'Error', content: `<p class=\"pl-8 py-1 text-red-500 font-sans\">Could not open: ${esc(e.message)}</p>` }));\n }\n }));\n const dbsContent = (await Promise.all(dbPromises)).join('');\n return createTree({ title: 'IndexedDB', icon: 'server', count: dbs.length, content: dbsContent });\n };\n\n const refresh = async () => {\n G.lists.innerHTML = `<p class=\"px-2 py-4 text-center text-gray-400 font-sans\"><i data-lucide=\"loader\" class=\"w-4 h-4 inline-block animate-spin\"></i> Refreshing...</p>`;\n if (window.lucide) window.lucide.createIcons();\n \n const [local, session, idb] = await Promise.all([renderWebStorage('local'), renderWebStorage('session'), renderIndexedDB()]);\n G.lists.innerHTML = [idb, local, session].join('');\n if (window.lucide) window.lucide.createIcons();\n };\n \n const handleAction = async (e) => {\n const t = e.target.closest('[data-action]');\n if(!t) return;\n const { action, storage, key, db, store } = t.dataset;\n e.stopPropagation();\n\n switch(action) {\n case 'refresh': await refresh(); break;\n case 'close-viewer': G.viewer.classList.add('hidden'); break;\n case 'view-item': {\n let value;\n if (storage === 'idb') {\n value = await new Promise(res => {\n const req = indexedDB.open(db);\n req.onsuccess = e => {\n const idb = e.target.result;\n const itemReq = idb.transaction(store,'readonly').objectStore(store).get(key);\n itemReq.onsuccess = e => res(JSON.stringify(e.target.result, null, 2));\n itemReq.onerror = () => res('Error reading value');\n };\n req.onerror = () => res('Error opening DB');\n });\n } else {\n const s = storage === 'local' ? localStorage : sessionStorage;\n const raw = s.getItem(key);\n try { value = JSON.stringify(JSON.parse(raw), null, 2); } catch { value = raw; }\n }\n G.viewerPre.textContent = value;\n G.viewer.classList.remove('hidden');\n if(window.lucide) lucide.createIcons({nodes:[G.viewer]});\n break;\n }\n case 'delete-item': {\n if (!confirm(`Delete key \"${key}\"?`)) return;\n if (storage === 'idb') {\n await new Promise(res => {\n const req = indexedDB.open(db);\n req.onsuccess = e => {\n const idb = e.target.result;\n const delReq = idb.transaction(store, 'readwrite').objectStore(store).delete(key);\n delReq.onsuccess = () => { idb.close(); res(); };\n };\n });\n } else { (storage === 'local' ? localStorage : sessionStorage).removeItem(key); }\n await refresh();\n break;\n }\n case 'clear-store': {\n if (!confirm(`Clear ALL items in store \"${store}\"?`)) return;\n await new Promise(res => {\n const req = indexedDB.open(db);\n req.onsuccess = e => {\n const idb = e.target.result;\n const clrReq = idb.transaction(store, 'readwrite').objectStore(store).clear();\n clrReq.onsuccess = () => { idb.close(); res(); };\n };\n });\n await refresh();\n break;\n }\n case 'delete-db': {\n if (!confirm(`DELETE database \"${db}\"? THIS CANNOT BE UNDONE.`)) return;\n await new Promise(res => {\n const req = indexedDB.deleteDatabase(db);\n req.onsuccess = res;\n req.onerror = res;\n req.onblocked = ()=> alert(`Could not delete \"${db}\" as it's blocked. Please close other tabs using it and refresh.`);\n });\n await refresh();\n break;\n }\n case 'nuke-localstorage':\n if (confirm('Clear ALL of localStorage?')) { localStorage.clear(); await refresh(); }\n break;\n case 'nuke-sessionstorage':\n if (confirm('Clear ALL of sessionStorage?')) { sessionStorage.clear(); await refresh(); }\n break;\n case 'nuke-all-idb': {\n if (!confirm('DELETE ALL INDEXEDDB DATABASES? THIS IS EXTREMELY DESTRUCTIVE.')) return;\n const dbs = await indexedDB.databases();\n for (const dbInfo of dbs) {\n await new Promise(res => {\n const req = indexedDB.deleteDatabase(dbInfo.name);\n req.onsuccess = req.onerror = res;\n });\n }\n await refresh();\n break;\n }\n }\n };\n\n const init = () => {\n G.root.removeEventListener('click', handleAction);\n G.root.addEventListener('click', handleAction);\n refresh();\n };\n\n window.StorageManagerSune = { init };\n init();\n\n})();\n</script>\n",
|
|
"extension_html": "<sune src='https://raw.githubusercontent.com/sune-org/store/refs/heads/main/sync.sune' private />"
|
|
},
|
|
"storage": {}
|
|
}
|
|
] |