commit 2ea1678e642c47cb8208a5fcd1d9247dca009f7d Author: abearxiong Date: Wed Jul 16 02:09:42 2025 +0800 update diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b512c09 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..e69de29 diff --git a/index.html b/index.html new file mode 100644 index 0000000..f0dd474 --- /dev/null +++ b/index.html @@ -0,0 +1,48 @@ + + + + + + + Storage Editor + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/js/main.js b/js/main.js new file mode 100644 index 0000000..b13fd9c --- /dev/null +++ b/js/main.js @@ -0,0 +1,618 @@ +import { LitElement, html, css } from 'lit'; +import { StorageStore } from 'storage-store'; + +class StorageEditor extends LitElement { + static styles = css` + :host { + display: block; + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif; + width: 100%; + margin: 0 auto; + padding: 20px; + background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); + min-height: 100vh; + box-sizing: border-box; + } + .container { + padding: 20px; + } + + .header { + text-align: center; + margin-bottom: 40px; + padding: 30px; + background: white; + border-radius: 20px; + box-shadow: 0 8px 32px rgba(0,0,0,0.1); + backdrop-filter: blur(10px); + } + + .header h1 { + color: #2d3748; + margin: 0 0 12px 0; + font-size: 2.5rem; + font-weight: 700; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + } + + .header p { + color: #718096; + margin: 0; + font-size: 1.1rem; + font-weight: 400; + } + + .storage-selector { + display: flex; + justify-content: center; + gap: 24px; + margin-bottom: 40px; + } + + .storage-btn { + padding: 16px 32px; + border: none; + border-radius: 12px; + cursor: pointer; + font-size: 1.1rem; + font-weight: 600; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + background: white; + color: #4a5568; + box-shadow: 0 4px 16px rgba(0,0,0,0.08); + position: relative; + overflow: hidden; + } + + .storage-btn::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + opacity: 0; + transition: opacity 0.3s ease; + } + + .storage-btn:hover { + transform: translateY(-4px); + box-shadow: 0 8px 24px rgba(0,0,0,0.15); + } + + .storage-btn:hover::before { + opacity: 0.1; + } + + .storage-btn.active { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + box-shadow: 0 6px 20px rgba(102, 126, 234, 0.4); + } + + .storage-btn.active::before { + opacity: 0; + } + + .add-section { + background: white; + border-radius: 20px; + padding: 30px; + margin-bottom: 30px; + box-shadow: 0 8px 32px rgba(0,0,0,0.1); + backdrop-filter: blur(10px); + } + + .add-section h3 { + margin: 0 0 20px 0; + color: #2d3748; + font-size: 1.4rem; + font-weight: 600; + } + + .add-form { + display: flex; + flex-direction: column; + gap: 24px; + } + + .input-group { + display: flex; + flex-direction: column; + gap: 12px; + } + + .input-group label { + font-weight: 600; + color: #4a5568; + font-size: 0.9rem; + letter-spacing: 0.5px; + text-transform: uppercase; + } + + .input-group input, + .input-group textarea { + padding: 14px 16px; + border: 2px solid #e2e8f0; + border-radius: 12px; + font-size: 1rem; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + background: #f8fafc; + color: #2d3748; + font-family: inherit; + resize: vertical; + } + + .input-group textarea { + min-height: 80px; + max-height: 200px; + line-height: 1.5; + } + + .input-group input:focus, + .input-group textarea:focus { + outline: none; + border-color: #667eea; + background: white; + box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); + } + + .input-group input::placeholder, + .input-group textarea::placeholder { + color: #a0aec0; + } + + .add-btn { + padding: 16px 32px; + background: linear-gradient(135deg, #48bb78 0%, #38a169 100%); + color: white; + border: none; + border-radius: 12px; + cursor: pointer; + font-size: 1.1rem; + font-weight: 600; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 4px 16px rgba(72, 187, 120, 0.3); + align-self: flex-end; + margin-top: 8px; + } + + .add-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(72, 187, 120, 0.4); + } + + .add-btn:active { + transform: translateY(0); + } + + .storage-list { + background: white; + border-radius: 20px; + overflow: hidden; + box-shadow: 0 8px 32px rgba(0,0,0,0.1); + backdrop-filter: blur(10px); + } + + .list-header { + background: linear-gradient(135deg, #f8fafc 0%, #edf2f7 100%); + padding: 20px 30px; + border-bottom: 1px solid #e2e8f0; + } + + .list-header h3 { + margin: 0; + color: #2d3748; + font-size: 1.3rem; + font-weight: 600; + } + + .empty-state { + text-align: center; + padding: 60px 20px; + color: #a0aec0; + font-size: 1.1rem; + font-weight: 500; + } + + .storage-item { + padding: 20px 30px; + border-bottom: 1px solid #f1f5f9; + display: flex; + align-items: center; + gap: 20px; + transition: background 0.3s ease; + } + + .storage-item:hover { + background: #f8fafc; + } + + .storage-item:last-child { + border-bottom: none; + } + + .item-key { + font-weight: 700; + color: #2d3748; + min-width: 180px; + word-break: break-all; + background: linear-gradient(135deg, #edf2f7 0%, #e2e8f0 100%); + padding: 8px 12px; + border-radius: 8px; + font-size: 0.9rem; + } + + .item-value { + flex: 1; + display: flex; + gap: 12px; + align-items: center; + } + + .value-input { + flex: 1; + padding: 12px 16px; + border: 2px solid #e2e8f0; + border-radius: 10px; + font-size: 0.95rem; + background: #f8fafc; + color: #2d3748; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + min-height: 60px; + resize: vertical; + font-family: inherit; + line-height: 1.5; + } + + .value-input:focus { + outline: none; + border-color: #667eea; + background: white; + box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1); + } + + .item-actions { + display: flex; + gap: 10px; + } + + .action-btn { + padding: 8px 16px; + border: none; + border-radius: 8px; + cursor: pointer; + font-size: 0.85rem; + font-weight: 600; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + } + + .save-btn { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); + color: white; + box-shadow: 0 2px 8px rgba(102, 126, 234, 0.3); + } + + .save-btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(102, 126, 234, 0.4); + } + + .delete-btn { + background: linear-gradient(135deg, #fc8181 0%, #f56565 100%); + color: white; + box-shadow: 0 2px 8px rgba(252, 129, 129, 0.3); + } + + .delete-btn:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(252, 129, 129, 0.4); + } + + .clear-all-btn { + background: linear-gradient(135deg, #fc8181 0%, #f56565 100%); + color: white; + padding: 14px 28px; + border: none; + border-radius: 12px; + cursor: pointer; + font-size: 1rem; + font-weight: 600; + margin: 20px 30px; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 4px 16px rgba(252, 129, 129, 0.3); + } + + .clear-all-btn:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(252, 129, 129, 0.4); + } + + .stats { + display: flex; + justify-content: space-between; + padding: 20px 30px; + background: linear-gradient(135deg, #f8fafc 0%, #edf2f7 100%); + border-top: 1px solid #e2e8f0; + font-size: 0.95rem; + color: #4a5568; + font-weight: 500; + } + + @media (max-width: 768px) { + :host { + padding: 16px; + } + + .header { + padding: 20px; + margin-bottom: 30px; + } + + .header h1 { + font-size: 2rem; + } + + .storage-selector { + flex-direction: column; + gap: 16px; + margin-bottom: 30px; + } + + .storage-btn { + width: 100%; + padding: 16px; + } + + .add-section { + padding: 24px; + } + + .add-form { + flex-direction: column; + gap: 20px; + } + + .storage-item { + flex-direction: column; + align-items: flex-start; + gap: 16px; + padding: 20px; + } + + .item-key { + min-width: auto; + width: 100%; + } + + .item-value { + width: 100%; + } + + .item-actions { + width: 100%; + justify-content: flex-end; + } + + .list-header { + padding: 16px 20px; + } + + .stats { + flex-direction: column; + gap: 8px; + text-align: center; + padding: 16px 20px; + } + + .clear-all-btn { + margin: 16px 20px; + width: calc(100% - 40px); + } + } + + @media (max-width: 480px) { + .header h1 { + font-size: 1.8rem; + } + + .add-section { + padding: 20px; + } + + .storage-item { + padding: 16px; + } + + .item-actions { + flex-direction: column; + gap: 8px; + } + + .action-btn { + width: 100%; + padding: 10px; + } + } + `; + + static properties = { + currentStorageType: { type: String }, + storageItems: { type: Array }, + newKey: { type: String }, + newValue: { type: String } + }; + + constructor() { + super(); + this.currentStorageType = 'localStorage'; + this.storageItems = []; + this.newKey = ''; + this.newValue = ''; + this.localStorageStore = new StorageStore(localStorage); + this.sessionStorageStore = new StorageStore(sessionStorage); + } + + connectedCallback() { + super.connectedCallback(); + this.loadStorageItems(); + } + + get currentStore() { + return this.currentStorageType === 'localStorage' + ? this.localStorageStore + : this.sessionStorageStore; + } + + loadStorageItems() { + this.storageItems = this.currentStore.getAllItemsAsArray(); + this.requestUpdate(); + } + + switchStorageType(type) { + this.currentStorageType = type; + this.loadStorageItems(); + } + + addItem() { + if (this.newKey && this.newValue) { + this.currentStore.setItem(this.newKey, this.newValue); + this.newKey = ''; + this.newValue = ''; + this.loadStorageItems(); + } + } + + updateItem(key, value) { + this.currentStore.setItem(key, value); + this.loadStorageItems(); + } + + deleteItem(key) { + this.currentStore.removeItem(key); + this.loadStorageItems(); + } + + clearAll() { + if (confirm(`确定要清空所有 ${this.currentStorageType} 数据吗?`)) { + this.currentStore.clear(); + this.loadStorageItems(); + } + } + + render() { + return html` +
+
+

Storage Editor

+

管理您的本地存储和会话存储数据

+
+ +
+ + +
+ +
+

添加新项目

+
+
+ + this.newKey = e.target.value} + @keydown=${(e) => e.key === 'Enter' && this.addItem()} + /> +
+
+ + +
+ +
+
+ +
+
+

${this.currentStorageType === 'localStorage' ? 'Local Storage' : 'Session Storage'} 项目

+
+ + ${this.storageItems.length === 0 ? html` +
+ 暂无数据 +
+ ` : html` + ${this.storageItems.map(item => html` +
+
${item.key}
+
+ +
+ + +
+
+
+ `)} + `} + + ${this.storageItems.length > 0 ? html` +
+ 总计: ${this.storageItems.length} 项 + 存储类型: ${this.currentStorageType} +
+ + ` : ''} +
+
+ `; + } +} + +customElements.define('storage-editor', StorageEditor); + +export const render = () => { + const root = document.getElementById('root'); + if (root) { + root.innerHTML = ''; + } +}; diff --git a/lib/storage-store.js b/lib/storage-store.js new file mode 100644 index 0000000..21eb8df --- /dev/null +++ b/lib/storage-store.js @@ -0,0 +1,51 @@ +export class StorageStore { + storage = null; + constructor(storage) { + const defaultStorage = typeof window !== 'undefined' ? window.localStorage : null; + this.storage = storage || defaultStorage; + } + getItem(key) { + return this.storage.getItem(key); + } + setItem(key, value) { + this.storage.setItem(key, value); + } + removeItem(key) { + this.storage.removeItem(key); + } + clear() { + this.storage.clear(); + } + key(index) { + return this.storage.key(index); + } + get length() { + return this.storage.length; + } + getAllKeys() { + const keys = []; + for (let i = 0; i < this.length; i++) { + keys.push(this.key(i)); + } + return keys; + } + getAllItems() { + const items = {}; + for (let i = 0; i < this.length; i++) { + const key = this.key(i); + items[key] = this.getItem(key); + } + return items; + } + getAllItemsAsArray() { + const items = []; + for (let i = 0; i < this.length; i++) { + const key = this.key(i); + items.push({ key, value: this.getItem(key) }); + } + return items; + } + hasItem(key) { + return this.storage.getItem(key) !== null; + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..fff52fc --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "@kevisual/storage-editor", + "version": "0.0.1", + "description": "", + "main": "index.js", + "basename": "/root/storage-editor", + "scripts": { + "pub": "ev deploy . -k storage-editor -v 0.0.1 -u" + }, + "files": [ + "lib", + "js" + ], + "keywords": [], + "author": "abearxiong (https://www.xiongxiao.me)", + "license": "MIT", + "packageManager": "pnpm@10.11.1", + "type": "module" +} diff --git a/prompts/01-storage-editor.md b/prompts/01-storage-editor.md new file mode 100644 index 0000000..ce83edc --- /dev/null +++ b/prompts/01-storage-editor.md @@ -0,0 +1,7 @@ +# 前端代码生成 + +我需要有一个网页能够对storage的内容进行修改,可以是localStorage,也可以是sessionStorage。 + +使用StorageManager获取列表,编辑value可以保存,可以删除对应的key的值。 + +美化样式。使用lit进行修改 \ No newline at end of file diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..475410d --- /dev/null +++ b/readme.md @@ -0,0 +1,3 @@ +# Storage Editor + +localStorage 和 sessionStorage \ No newline at end of file