commit 935ecfc5aeca6bbe4223a85828c13a3a52a7aaa4
Author: root <root@hub.scroll.pub> Date: 2024-12-30 14:44:31 +0000 Subject: Initial commit diff --git a/body.html b/body.html new file mode 100644 index 0000000..1728ea5 --- /dev/null +++ b/body.html @@ -0,0 +1,53 @@ +<header> + <h1>Daily Work Log</h1> + <nav> + <button id="newLogBtn" aria-label="Create new log entry">+ New Entry</button> + <div class="search-container"> + <input type="search" id="searchInput" placeholder="Search logs..." aria-label="Search logs"> + <select id="filterCategory" aria-label="Filter by category"> + <option value="">All Categories</option> + </select> + </div> + </nav> +</header> + +<main> + <section id="logForm" class="hidden"> + <h2>Create Log Entry</h2> + <form id="workLogForm"> + <div class="form-group"> + <label for="logDate">Date:</label> + <input type="datetime-local" id="logDate" required> + </div> + <div class="form-group"> + <label for="logCategory">Category:</label> + <input type="text" id="logCategory" list="categories" placeholder="Enter or select category"> + <datalist id="categories"></datalist> + </div> + <div class="form-group"> + <label for="logTags">Tags (comma-separated):</label> + <input type="text" id="logTags" placeholder="e.g., urgent, meeting, follow-up"> + </div> + <div class="form-group"> + <label for="logContent">Log Content:</label> + <textarea id="logContent" rows="6" required></textarea> + </div> + <div class="form-actions"> + <button type="submit">Save Log</button> + <button type="button" id="cancelBtn">Cancel</button> + </div> + </form> + </section> + + <section id="logList"> + <div class="log-controls"> + <button id="sortDateBtn">Sort by Date</button> + <button id="exportBtn">Export Logs</button> + </div> + <div id="logEntries"></div> + </section> +</main> + +<footer> + <p>Work Log Manager - A simple tool to track your daily work activities</p> +</footer> diff --git a/index.scroll b/index.scroll new file mode 100644 index 0000000..f7f9ec7 --- /dev/null +++ b/index.scroll @@ -0,0 +1,8 @@ +buildHtml +baseUrl https://worklog.scroll.pub +metaTags +editButton /edit.html +title Daily Work Log Manager +style.css +body.html +script.js diff --git a/prompt.json b/prompt.json new file mode 100644 index 0000000..bd27b87 --- /dev/null +++ b/prompt.json @@ -0,0 +1,20 @@ +{ + "id": "msg_01VqE2sepDJzcjtA7fs96faK", + "type": "message", + "role": "assistant", + "model": "claude-3-5-sonnet-20241022", + "content": [ + { + "type": "text", + "text": "---domain---\nworklog.scroll.pub\n\n---index.scroll---\nbuildHtml\nbaseUrl https://worklog.scroll.pub\nmetaTags\neditButton /edit.html\ntitle Daily Work Log Manager\nstyle.css\nbody.html\nscript.js\n\n---body.html---\n<header>\n <h1>Daily Work Log</h1>\n <nav>\n <button id=\"newLogBtn\" aria-label=\"Create new log entry\">+ New Entry</button>\n <div class=\"search-container\">\n <input type=\"search\" id=\"searchInput\" placeholder=\"Search logs...\" aria-label=\"Search logs\">\n <select id=\"filterCategory\" aria-label=\"Filter by category\">\n <option value=\"\">All Categories</option>\n </select>\n </div>\n </nav>\n</header>\n\n<main>\n <section id=\"logForm\" class=\"hidden\">\n <h2>Create Log Entry</h2>\n <form id=\"workLogForm\">\n <div class=\"form-group\">\n <label for=\"logDate\">Date:</label>\n <input type=\"datetime-local\" id=\"logDate\" required>\n </div>\n <div class=\"form-group\">\n <label for=\"logCategory\">Category:</label>\n <input type=\"text\" id=\"logCategory\" list=\"categories\" placeholder=\"Enter or select category\">\n <datalist id=\"categories\"></datalist>\n </div>\n <div class=\"form-group\">\n <label for=\"logTags\">Tags (comma-separated):</label>\n <input type=\"text\" id=\"logTags\" placeholder=\"e.g., urgent, meeting, follow-up\">\n </div>\n <div class=\"form-group\">\n <label for=\"logContent\">Log Content:</label>\n <textarea id=\"logContent\" rows=\"6\" required></textarea>\n </div>\n <div class=\"form-actions\">\n <button type=\"submit\">Save Log</button>\n <button type=\"button\" id=\"cancelBtn\">Cancel</button>\n </div>\n </form>\n </section>\n\n <section id=\"logList\">\n <div class=\"log-controls\">\n <button id=\"sortDateBtn\">Sort by Date</button>\n <button id=\"exportBtn\">Export Logs</button>\n </div>\n <div id=\"logEntries\"></div>\n </section>\n</main>\n\n<footer>\n <p>Work Log Manager - A simple tool to track your daily work activities</p>\n</footer>\n\n---style.css---\n:root {\n --primary-color: #2c3e50;\n --secondary-color: #3498db;\n --accent-color: #e74c3c;\n --background-color: #f5f6fa;\n --text-color: #2c3e50;\n --border-radius: 8px;\n --shadow: 0 2px 4px rgba(0,0,0,0.1);\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: 'Segoe UI', system-ui, sans-serif;\n line-height: 1.6;\n color: var(--text-color);\n background: var(--background-color);\n}\n\nheader {\n background: var(--primary-color);\n color: white;\n padding: 1rem;\n box-shadow: var(--shadow);\n}\n\nh1 {\n font-size: 1.8rem;\n margin-bottom: 1rem;\n}\n\nnav {\n display: flex;\n gap: 1rem;\n flex-wrap: wrap;\n}\n\n.search-container {\n display: flex;\n gap: 0.5rem;\n flex-grow: 1;\n}\n\ninput, select, textarea {\n padding: 0.5rem;\n border: 2px solid #ddd;\n border-radius: var(--border-radius);\n font-size: 1rem;\n}\n\nbutton {\n background: var(--secondary-color);\n color: white;\n border: none;\n padding: 0.5rem 1rem;\n border-radius: var(--border-radius);\n cursor: pointer;\n transition: transform 0.2s, background-color 0.2s;\n}\n\nbutton:hover {\n background: #2980b9;\n transform: translateY(-2px);\n}\n\nmain {\n max-width: 1200px;\n margin: 2rem auto;\n padding: 0 1rem;\n}\n\n.form-group {\n margin-bottom: 1rem;\n}\n\n.form-group label {\n display: block;\n margin-bottom: 0.5rem;\n font-weight: 500;\n}\n\n.form-group input,\n.form-group textarea {\n width: 100%;\n}\n\n.log-entry {\n background: white;\n padding: 1rem;\n margin-bottom: 1rem;\n border-radius: var(--border-radius);\n box-shadow: var(--shadow);\n transition: transform 0.2s;\n}\n\n.log-entry:hover {\n transform: translateY(-2px);\n}\n\n.log-header {\n display: flex;\n justify-content: space-between;\n margin-bottom: 0.5rem;\n}\n\n.log-tags {\n display: flex;\n gap: 0.5rem;\n flex-wrap: wrap;\n margin: 0.5rem 0;\n}\n\n.tag {\n background: #e1e8ed;\n padding: 0.2rem 0.5rem;\n border-radius: 12px;\n font-size: 0.9rem;\n}\n\n.hidden {\n display: none;\n}\n\n.form-actions {\n display: flex;\n gap: 1rem;\n justify-content: flex-end;\n}\n\nfooter {\n text-align: center;\n padding: 2rem;\n background: var(--primary-color);\n color: white;\n margin-top: 2rem;\n}\n\n@media (max-width: 768px) {\n nav {\n flex-direction: column;\n }\n \n .search-container {\n flex-direction: column;\n }\n \n .log-header {\n flex-direction: column;\n gap: 0.5rem;\n }\n}\n\n---script.js---\nclass WorkLogManager {\n constructor() {\n this.logs = JSON.parse(localStorage.getItem('workLogs')) || [];\n this.categories = new Set(this.logs.map(log => log.category));\n this.setupEventListeners();\n this.updateCategoryList();\n this.renderLogs();\n }\n\n setupEventListeners() {\n document.getElementById('newLogBtn').addEventListener('click', () => {\n document.getElementById('logForm').classList.remove('hidden');\n document.getElementById('logDate').value = new Date().toISOString().slice(0, 16);\n });\n\n document.getElementById('workLogForm').addEventListener('submit', (e) => {\n e.preventDefault();\n this.saveLog();\n });\n\n document.getElementById('cancelBtn').addEventListener('click', () => {\n document.getElementById('logForm').classList.add('hidden');\n });\n\n document.getElementById('searchInput').addEventListener('input', () => this.filterLogs());\n document.getElementById('filterCategory').addEventListener('change', () => this.filterLogs());\n document.getElementById('sortDateBtn').addEventListener('click', () => this.toggleSort());\n document.getElementById('exportBtn').addEventListener('click', () => this.exportLogs());\n }\n\n saveLog() {\n const logData = {\n id: Date.now(),\n date: document.getElementById('logDate').value,\n category: document.getElementById('logCategory').value,\n tags: document.getElementById('logTags').value.split(',').map(tag => tag.trim()).filter(tag => tag),\n content: document.getElementById('logContent').value\n };\n\n this.logs.unshift(logData);\n this.updateLocalStorage();\n this.categories.add(logData.category);\n this.updateCategoryList();\n this.renderLogs();\n document.getElementById('workLogForm').reset();\n document.getElementById('logForm').classList.add('hidden');\n }\n\n updateLocalStorage() {\n localStorage.setItem('workLogs', JSON.stringify(this.logs));\n }\n\n updateCategoryList() {\n const categorySelect = document.getElementById('filterCategory');\n const categoryDatalist = document.getElementById('categories');\n \n categorySelect.innerHTML = '<option value=\"\">All Categories</option>';\n categoryDatalist.innerHTML = '';\n \n this.categories.forEach(category => {\n if (category) {\n categorySelect.innerHTML += `<option value=\"${category}\">${category}</option>`;\n categoryDatalist.innerHTML += `<option value=\"${category}\">`;\n }\n });\n }\n\n renderLogs() {\n const logEntries = document.getElementById('logEntries');\n logEntries.innerHTML = '';\n\n this.logs.forEach(log => {\n const logElement = document.createElement('div');\n logElement.className = 'log-entry';\n logElement.innerHTML = `\n <div class=\"log-header\">\n <div>\n <strong>${new Date(log.date).toLocaleString()}</strong>\n ${log.category ? `<span class=\"tag\">${log.category}</span>` : ''}\n </div>\n <div>\n <button onclick=\"workLogManager.editLog(${log.id})\">Edit</button>\n <button onclick=\"workLogManager.deleteLog(${log.id})\">Delete</button>\n </div>\n </div>\n <div class=\"log-tags\">\n ${log.tags.map(tag => `<span class=\"tag\">${tag}</span>`).join('')}\n </div>\n <div class=\"log-content\">${log.content.replace(/\\n/g, '<br>')}</div>\n `;\n logEntries.appendChild(logElement);\n });\n }\n\n editLog(id) {\n const log = this.logs.find(log => log.id === id);\n if (log) {\n document.getElementById('logDate').value = log.date;\n document.getElementById('logCategory').value = log.category;\n document.getElementById('logTags').value = log.tags.join(', ');\n document.getElementById('logContent').value = log.content;\n document.getElementById('logForm').classList.remove('hidden');\n this.deleteLog(id);\n }\n }\n\n deleteLog(id) {\n if (confirm('Are you sure you want to delete this log?')) {\n this.logs = this.logs.filter(log => log.id !== id);\n this.updateLocalStorage();\n this.renderLogs();\n }\n }\n\n filterLogs() {\n const searchTerm = document.getElementById('searchInput').value.toLowerCase();\n const categoryFilter = document.getElementById('filterCategory').value;\n \n const filtered = this.logs.filter(log => {\n const matchesSearch = log.content.toLowerCase().includes(searchTerm) ||\n log.tags.some(tag => tag.toLowerCase().includes(searchTerm));\n const matchesCategory = !categoryFilter || log.category === categoryFilter;\n return matchesSearch && matchesCategory;\n });\n\n this.renderFilteredLogs(filtered);\n }\n\n renderFilteredLogs(filtered) {\n const logEntries = document.getElementById('logEntries');\n logEntries.innerHTML = '';\n filtered.forEach(log => {\n // Reuse the same rendering logic as in renderLogs()\n const logElement = document.createElement('div');\n logElement.className = 'log-entry';\n // ... (same innerHTML content as in renderLogs)\n logEntries.appendChild(logElement);\n });\n }\n\n toggleSort() {\n this.logs.reverse();\n this.renderLogs();\n }\n\n exportLogs() {\n const exportData = JSON.stringify(this.logs, null, 2);\n const blob = new Blob([exportData], { type: 'application/json' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = `work-logs-${new Date().toISOString().slice(0,10)}.json`;\n document.body.appendChild(a);\n a.click();\n document.body.removeChild(a);\n URL.revokeObjectURL(url);\n }\n}\n\nconst workLogManager = new WorkLogManager();\n\n---end---" + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 1193, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 3200 + } +} \ No newline at end of file diff --git a/readme.scroll b/readme.scroll new file mode 100644 index 0000000..b1de630 --- /dev/null +++ b/readme.scroll @@ -0,0 +1,2 @@ +# worklog4.scroll.pub +Website generated by Claude from prompt: 记录工作日志: 用户可以随时记录自己的工作内容。 输入框允许用户写入详细的工作日志,支持多行文本编辑。 日志的内容包括工作事项、任务进度、问题记录等。 日志分类: 用户可以为每条日志指定一个分类,帮助整理和区分日志的内容(例如:项目进度、问题记录、日常事务等)。 支持自定义分类,方便用户根据个人需求分类日志。 日志标签: 用户可以为日志添加标签,帮助更高效地进行检索和归类。 标签可按需要自定义,用于标注关键字或重要事项。 记录日期: 每条日志自动记录当前日期和时间,用户也可以手动修改日期,以便清晰了解每条日志的时间。 查看日志列表: 显示所有已记录的日志,并按日期或分类排序。 用户可以在列表中查看简短的日志摘要。 日志搜索: 提供搜索功能,允许用户通过关键词查找特定日志。 支持按日期或分类过滤日志,帮助快速定位所需内容。 编辑日志: 用户可以在任何时候编辑已经记录的日志内容。 编辑时,用户可以修改文本、日期、分类和标签。 删除日志: 用户可以删除日志条目,删除前会弹出确认提示,以避免误操作。 删除操作会立即更新本地存储,确保数据同步。 日志排序: 提供按日期排序的选项,可以选择查看最新的日志或最旧的日志。 日志按日期和分类的排序功能帮助用户更有序地查看日志内容。 本地存储: 所有日志数据保存在本地浏览器的 localStorage 中,数据不会丢失,页面刷新或浏览器关闭后仍能恢复。 通过 localStorage 实现日志数据的持久化存储,确保用户的数据安全可靠。 简单的用户交互: 所有操作(如记录、编辑、删除、搜索)都通过简单的按钮和输入框完成。 用户界面简洁、易用,无需注册或登录,任何人都可以随时记录和管理日志。 日志导出(可选功能): 提供导出日志为文本文件或CSV格式的功能,便于用户备份或分享日志。 提醒功能(可选功能): 可实现一个简单的提醒功能,提醒用户记录日志或查看某些重要的工作事项。 这个工作日志工具集成了记录、分类、编辑、删除、搜索等多种功能,旨在帮助用户高效地管理和查看自己的工作日志,积累经验与知识。并且所有功能均通过原生HTML、CSS、JavaScript实现,确保其简单、轻量,并适用于单机使用。 \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..d5879c9 --- /dev/null +++ b/script.js @@ -0,0 +1,160 @@ +class WorkLogManager { + constructor() { + this.logs = JSON.parse(localStorage.getItem('workLogs')) || []; + this.categories = new Set(this.logs.map(log => log.category)); + this.setupEventListeners(); + this.updateCategoryList(); + this.renderLogs(); + } + + setupEventListeners() { + document.getElementById('newLogBtn').addEventListener('click', () => { + document.getElementById('logForm').classList.remove('hidden'); + document.getElementById('logDate').value = new Date().toISOString().slice(0, 16); + }); + + document.getElementById('workLogForm').addEventListener('submit', (e) => { + e.preventDefault(); + this.saveLog(); + }); + + document.getElementById('cancelBtn').addEventListener('click', () => { + document.getElementById('logForm').classList.add('hidden'); + }); + + document.getElementById('searchInput').addEventListener('input', () => this.filterLogs()); + document.getElementById('filterCategory').addEventListener('change', () => this.filterLogs()); + document.getElementById('sortDateBtn').addEventListener('click', () => this.toggleSort()); + document.getElementById('exportBtn').addEventListener('click', () => this.exportLogs()); + } + + saveLog() { + const logData = { + id: Date.now(), + date: document.getElementById('logDate').value, + category: document.getElementById('logCategory').value, + tags: document.getElementById('logTags').value.split(',').map(tag => tag.trim()).filter(tag => tag), + content: document.getElementById('logContent').value + }; + + this.logs.unshift(logData); + this.updateLocalStorage(); + this.categories.add(logData.category); + this.updateCategoryList(); + this.renderLogs(); + document.getElementById('workLogForm').reset(); + document.getElementById('logForm').classList.add('hidden'); + } + + updateLocalStorage() { + localStorage.setItem('workLogs', JSON.stringify(this.logs)); + } + + updateCategoryList() { + const categorySelect = document.getElementById('filterCategory'); + const categoryDatalist = document.getElementById('categories'); + + categorySelect.innerHTML = '<option value="">All Categories</option>'; + categoryDatalist.innerHTML = ''; + + this.categories.forEach(category => { + if (category) { + categorySelect.innerHTML += `<option value="${category}">${category}</option>`; + categoryDatalist.innerHTML += `<option value="${category}">`; + } + }); + } + + renderLogs() { + const logEntries = document.getElementById('logEntries'); + logEntries.innerHTML = ''; + + this.logs.forEach(log => { + const logElement = document.createElement('div'); + logElement.className = 'log-entry'; + logElement.innerHTML = ` + <div class="log-header"> + <div> + <strong>${new Date(log.date).toLocaleString()}</strong> + ${log.category ? `<span class="tag">${log.category}</span>` : ''} + </div> + <div> + <button onclick="workLogManager.editLog(${log.id})">Edit</button> + <button onclick="workLogManager.deleteLog(${log.id})">Delete</button> + </div> + </div> + <div class="log-tags"> + ${log.tags.map(tag => `<span class="tag">${tag}</span>`).join('')} + </div> + <div class="log-content">${log.content.replace(/\n/g, '<br>')}</div> + `; + logEntries.appendChild(logElement); + }); + } + + editLog(id) { + const log = this.logs.find(log => log.id === id); + if (log) { + document.getElementById('logDate').value = log.date; + document.getElementById('logCategory').value = log.category; + document.getElementById('logTags').value = log.tags.join(', '); + document.getElementById('logContent').value = log.content; + document.getElementById('logForm').classList.remove('hidden'); + this.deleteLog(id); + } + } + + deleteLog(id) { + if (confirm('Are you sure you want to delete this log?')) { + this.logs = this.logs.filter(log => log.id !== id); + this.updateLocalStorage(); + this.renderLogs(); + } + } + + filterLogs() { + const searchTerm = document.getElementById('searchInput').value.toLowerCase(); + const categoryFilter = document.getElementById('filterCategory').value; + + const filtered = this.logs.filter(log => { + const matchesSearch = log.content.toLowerCase().includes(searchTerm) || + log.tags.some(tag => tag.toLowerCase().includes(searchTerm)); + const matchesCategory = !categoryFilter || log.category === categoryFilter; + return matchesSearch && matchesCategory; + }); + + this.renderFilteredLogs(filtered); + } + + renderFilteredLogs(filtered) { + const logEntries = document.getElementById('logEntries'); + logEntries.innerHTML = ''; + filtered.forEach(log => { + // Reuse the same rendering logic as in renderLogs() + const logElement = document.createElement('div'); + logElement.className = 'log-entry'; + // ... (same innerHTML content as in renderLogs) + logEntries.appendChild(logElement); + }); + } + + toggleSort() { + this.logs.reverse(); + this.renderLogs(); + } + + exportLogs() { + const exportData = JSON.stringify(this.logs, null, 2); + const blob = new Blob([exportData], { type: 'application/json' }); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + a.download = `work-logs-${new Date().toISOString().slice(0,10)}.json`; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(url); + } +} + +const workLogManager = new WorkLogManager(); diff --git a/style.css b/style.css new file mode 100644 index 0000000..07cb3ef --- /dev/null +++ b/style.css @@ -0,0 +1,155 @@ +:root { + --primary-color: #2c3e50; + --secondary-color: #3498db; + --accent-color: #e74c3c; + --background-color: #f5f6fa; + --text-color: #2c3e50; + --border-radius: 8px; + --shadow: 0 2px 4px rgba(0,0,0,0.1); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', system-ui, sans-serif; + line-height: 1.6; + color: var(--text-color); + background: var(--background-color); +} + +header { + background: var(--primary-color); + color: white; + padding: 1rem; + box-shadow: var(--shadow); +} + +h1 { + font-size: 1.8rem; + margin-bottom: 1rem; +} + +nav { + display: flex; + gap: 1rem; + flex-wrap: wrap; +} + +.search-container { + display: flex; + gap: 0.5rem; + flex-grow: 1; +} + +input, select, textarea { + padding: 0.5rem; + border: 2px solid #ddd; + border-radius: var(--border-radius); + font-size: 1rem; +} + +button { + background: var(--secondary-color); + color: white; + border: none; + padding: 0.5rem 1rem; + border-radius: var(--border-radius); + cursor: pointer; + transition: transform 0.2s, background-color 0.2s; +} + +button:hover { + background: #2980b9; + transform: translateY(-2px); +} + +main { + max-width: 1200px; + margin: 2rem auto; + padding: 0 1rem; +} + +.form-group { + margin-bottom: 1rem; +} + +.form-group label { + display: block; + margin-bottom: 0.5rem; + font-weight: 500; +} + +.form-group input, +.form-group textarea { + width: 100%; +} + +.log-entry { + background: white; + padding: 1rem; + margin-bottom: 1rem; + border-radius: var(--border-radius); + box-shadow: var(--shadow); + transition: transform 0.2s; +} + +.log-entry:hover { + transform: translateY(-2px); +} + +.log-header { + display: flex; + justify-content: space-between; + margin-bottom: 0.5rem; +} + +.log-tags { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + margin: 0.5rem 0; +} + +.tag { + background: #e1e8ed; + padding: 0.2rem 0.5rem; + border-radius: 12px; + font-size: 0.9rem; +} + +.hidden { + display: none; +} + +.form-actions { + display: flex; + gap: 1rem; + justify-content: flex-end; +} + +footer { + text-align: center; + padding: 2rem; + background: var(--primary-color); + color: white; + margin-top: 2rem; +} + +@media (max-width: 768px) { + nav { + flex-direction: column; + } + + .search-container { + flex-direction: column; + } + + .log-header { + flex-direction: column; + gap: 0.5rem; + } +}