commit c4477bee103e1bf2594934f99c3cb4e2fe0537bc
Author: root <root@hub.scroll.pub> Date: 2024-12-30 13:36:40 +0000 Subject: Initial commit diff --git a/body.html b/body.html new file mode 100644 index 0000000..0c302f3 --- /dev/null +++ b/body.html @@ -0,0 +1,67 @@ +<header> + <nav> + <div class="logo">WorkLog</div> + <div class="theme-toggle"> + <button id="themeToggle" aria-label="Toggle dark mode">🌓</button> + </div> + </nav> +</header> + +<main> + <section class="log-entry"> + <h2>Quick Log</h2> + <div class="editor-container"> + <div class="toolbar"> + <button data-command="bold" aria-label="Bold">B</button> + <button data-command="italic" aria-label="Italic">I</button> + <button data-command="insertUnorderedList" aria-label="Bullet list">•</button> + </div> + <div id="editor" contenteditable="true" aria-label="Log entry editor"></div> + </div> + <div class="entry-meta"> + <input type="text" id="tags" placeholder="Add tags..." aria-label="Add tags"> + <select id="category" aria-label="Select category"> + <option value="work">Work</option> + <option value="study">Study</option> + <option value="project">Project</option> + </select> + </div> + <button id="saveLog" class="primary-button">Save Log</button> + </section> + + <section class="logs-view"> + <h2>Recent Logs</h2> + <div class="filters"> + <input type="text" id="search" placeholder="Search logs..." aria-label="Search logs"> + <input type="date" id="dateFilter" aria-label="Filter by date"> + </div> + <div id="logsList" class="logs-list"></div> + </section> + + <section class="statistics"> + <h2>Statistics</h2> + <div class="stats-grid"> + <div class="stat-card"> + <span class="stat-number" id="totalLogs">0</span> + <span class="stat-label">Total Logs</span> + </div> + <div class="stat-card"> + <span class="stat-number" id="weeklyLogs">0</span> + <span class="stat-label">This Week</span> + </div> + <div class="stat-card"> + <span class="stat-number" id="streak">0</span> + <span class="stat-label">Day Streak</span> + </div> + </div> + </section> +</main> + +<footer> + <p>WorkLog - Smart Browser-Based Work Journal</p> + <nav> + <a href="/privacy">Privacy</a> + <a href="/terms">Terms</a> + <a href="/help">Help</a> + </nav> +</footer> \ No newline at end of file diff --git a/index.scroll b/index.scroll new file mode 100644 index 0000000..0afedbc --- /dev/null +++ b/index.scroll @@ -0,0 +1,8 @@ +buildHtml +baseUrl https://worklog.scroll.pub +metaTags +editButton /edit.html +title WorkLog - Smart Browser-Based Work Journal +style.css +body.html +script.js \ No newline at end of file diff --git a/prompt.json b/prompt.json new file mode 100644 index 0000000..ffeaf30 --- /dev/null +++ b/prompt.json @@ -0,0 +1,20 @@ +{ + "id": "msg_016yApr9vuTGpBhL3YrQhpMR", + "type": "message", + "role": "assistant", + "model": "claude-3-5-sonnet-20241022", + "content": [ + { + "type": "text", + "text": "I'll create a website for this Chrome logging/journaling extension.\n\nSuggested domain: worklog.scroll.pub (simple, memorable, represents the core logging functionality)\n\n---domain---\nworklog.scroll.pub\n---index.scroll---\nbuildHtml\nbaseUrl https://worklog.scroll.pub\nmetaTags\neditButton /edit.html\ntitle WorkLog - Smart Browser-Based Work Journal\nstyle.css\nbody.html\nscript.js\n---body.html---\n<header>\n <nav>\n <div class=\"logo\">WorkLog</div>\n <div class=\"theme-toggle\">\n <button id=\"themeToggle\" aria-label=\"Toggle dark mode\">🌓</button>\n </div>\n </nav>\n</header>\n\n<main>\n <section class=\"log-entry\">\n <h2>Quick Log</h2>\n <div class=\"editor-container\">\n <div class=\"toolbar\">\n <button data-command=\"bold\" aria-label=\"Bold\">B</button>\n <button data-command=\"italic\" aria-label=\"Italic\">I</button>\n <button data-command=\"insertUnorderedList\" aria-label=\"Bullet list\">•</button>\n </div>\n <div id=\"editor\" contenteditable=\"true\" aria-label=\"Log entry editor\"></div>\n </div>\n <div class=\"entry-meta\">\n <input type=\"text\" id=\"tags\" placeholder=\"Add tags...\" aria-label=\"Add tags\">\n <select id=\"category\" aria-label=\"Select category\">\n <option value=\"work\">Work</option>\n <option value=\"study\">Study</option>\n <option value=\"project\">Project</option>\n </select>\n </div>\n <button id=\"saveLog\" class=\"primary-button\">Save Log</button>\n </section>\n\n <section class=\"logs-view\">\n <h2>Recent Logs</h2>\n <div class=\"filters\">\n <input type=\"text\" id=\"search\" placeholder=\"Search logs...\" aria-label=\"Search logs\">\n <input type=\"date\" id=\"dateFilter\" aria-label=\"Filter by date\">\n </div>\n <div id=\"logsList\" class=\"logs-list\"></div>\n </section>\n\n <section class=\"statistics\">\n <h2>Statistics</h2>\n <div class=\"stats-grid\">\n <div class=\"stat-card\">\n <span class=\"stat-number\" id=\"totalLogs\">0</span>\n <span class=\"stat-label\">Total Logs</span>\n </div>\n <div class=\"stat-card\">\n <span class=\"stat-number\" id=\"weeklyLogs\">0</span>\n <span class=\"stat-label\">This Week</span>\n </div>\n <div class=\"stat-card\">\n <span class=\"stat-number\" id=\"streak\">0</span>\n <span class=\"stat-label\">Day Streak</span>\n </div>\n </div>\n </section>\n</main>\n\n<footer>\n <p>WorkLog - Smart Browser-Based Work Journal</p>\n <nav>\n <a href=\"/privacy\">Privacy</a>\n <a href=\"/terms\">Terms</a>\n <a href=\"/help\">Help</a>\n </nav>\n</footer>\n---style.css---\n:root {\n --primary: #2563eb;\n --primary-dark: #1d4ed8;\n --bg: #ffffff;\n --text: #1f2937;\n --border: #e5e7eb;\n --card-bg: #f9fafb;\n --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);\n --radius: 8px;\n}\n\n[data-theme=\"dark\"] {\n --primary: #3b82f6;\n --primary-dark: #2563eb;\n --bg: #111827;\n --text: #f9fafb;\n --border: #374151;\n --card-bg: #1f2937;\n --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3);\n}\n\n* {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n}\n\nbody {\n font-family: system-ui, -apple-system, sans-serif;\n background: var(--bg);\n color: var(--text);\n line-height: 1.5;\n transition: background-color 0.3s, color 0.3s;\n}\n\nheader {\n padding: 1rem;\n border-bottom: 1px solid var(--border);\n background: var(--card-bg);\n}\n\nnav {\n max-width: 1200px;\n margin: 0 auto;\n display: flex;\n justify-content: space-between;\n align-items: center;\n}\n\n.logo {\n font-size: 1.5rem;\n font-weight: bold;\n background: linear-gradient(45deg, var(--primary), var(--primary-dark));\n -webkit-background-clip: text;\n color: transparent;\n}\n\nmain {\n max-width: 1200px;\n margin: 2rem auto;\n padding: 0 1rem;\n}\n\nsection {\n margin-bottom: 2rem;\n}\n\n.editor-container {\n border: 1px solid var(--border);\n border-radius: var(--radius);\n overflow: hidden;\n}\n\n.toolbar {\n padding: 0.5rem;\n border-bottom: 1px solid var(--border);\n background: var(--card-bg);\n}\n\n.toolbar button {\n padding: 0.25rem 0.5rem;\n margin-right: 0.5rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--bg);\n color: var(--text);\n cursor: pointer;\n}\n\n#editor {\n min-height: 200px;\n padding: 1rem;\n outline: none;\n}\n\n.entry-meta {\n display: flex;\n gap: 1rem;\n margin: 1rem 0;\n}\n\ninput, select {\n padding: 0.5rem;\n border: 1px solid var(--border);\n border-radius: var(--radius);\n background: var(--bg);\n color: var(--text);\n}\n\n.primary-button {\n padding: 0.75rem 1.5rem;\n background: var(--primary);\n color: white;\n border: none;\n border-radius: var(--radius);\n cursor: pointer;\n transition: background-color 0.3s;\n}\n\n.primary-button:hover {\n background: var(--primary-dark);\n}\n\n.stats-grid {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));\n gap: 1rem;\n}\n\n.stat-card {\n padding: 1.5rem;\n background: var(--card-bg);\n border-radius: var(--radius);\n box-shadow: var(--shadow);\n text-align: center;\n}\n\n.stat-number {\n font-size: 2rem;\n font-weight: bold;\n color: var(--primary);\n}\n\n.stat-label {\n display: block;\n margin-top: 0.5rem;\n font-size: 0.875rem;\n}\n\nfooter {\n padding: 2rem 1rem;\n background: var(--card-bg);\n text-align: center;\n}\n\nfooter nav {\n margin-top: 1rem;\n justify-content: center;\n gap: 2rem;\n}\n\nfooter a {\n color: var(--text);\n text-decoration: none;\n}\n\n@media (max-width: 768px) {\n .entry-meta {\n flex-direction: column;\n }\n \n .stats-grid {\n grid-template-columns: 1fr;\n }\n}\n---script.js---\ndocument.addEventListener('DOMContentLoaded', () => {\n // Theme Toggle\n const themeToggle = document.getElementById('themeToggle');\n const prefersDark = window.matchMedia('(prefers-color-scheme: dark)');\n \n function setTheme(isDark) {\n document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light');\n localStorage.setItem('theme', isDark ? 'dark' : 'light');\n }\n\n themeToggle.addEventListener('click', () => {\n const isDark = document.documentElement.getAttribute('data-theme') === 'dark';\n setTheme(!isDark);\n });\n\n // Rich Text Editor\n const toolbar = document.querySelector('.toolbar');\n toolbar.addEventListener('click', (e) => {\n if (e.target.matches('button[data-command]')) {\n const command = e.target.getAttribute('data-command');\n document.execCommand(command, false, null);\n }\n });\n\n // Save Log\n const saveLog = document.getElementById('saveLog');\n saveLog.addEventListener('click', () => {\n const content = document.getElementById('editor').innerHTML;\n const tags = document.getElementById('tags').value;\n const category = document.getElementById('category').value;\n const timestamp = new Date().toISOString();\n\n const log = {\n content,\n tags: tags.split(',').map(tag => tag.trim()),\n category,\n timestamp\n };\n\n // Store in localStorage for demo\n const logs = JSON.parse(localStorage.getItem('logs') || '[]');\n logs.unshift(log);\n localStorage.setItem('logs', JSON.stringify(logs));\n\n updateLogsList();\n updateStats();\n clearEditor();\n });\n\n function clearEditor() {\n document.getElementById('editor').innerHTML = '';\n document.getElementById('tags').value = '';\n document.getElementById('category').selectedIndex = 0;\n }\n\n function updateLogsList() {\n const logsList = document.getElementById('logsList');\n const logs = JSON.parse(localStorage.getItem('logs') || '[]');\n \n logsList.innerHTML = logs.map(log => `\n <div class=\"log-item\">\n <div class=\"log-content\">${log.content}</div>\n <div class=\"log-meta\">\n <span>${new Date(log.timestamp).toLocaleDateString()}</span>\n <span>${log.category}</span>\n <span>${log.tags.join(', ')}</span>\n </div>\n </div>\n `).join('');\n }\n\n function updateStats() {\n const logs = JSON.parse(localStorage.getItem('logs') || '[]');\n \n document.getElementById('totalLogs').textContent = logs.length;\n \n const weekAgo = new Date();\n weekAgo.setDate(weekAgo.getDate() - 7);\n const weeklyLogs = logs.filter(log => \n new Date(log.timestamp) > weekAgo\n ).length;\n document.getElementById('weeklyLogs').textContent = weeklyLogs;\n \n // Calculate streak\n let streak = 0;\n let currentDate = new Date();\n while (true) {\n const hasLog = logs.some(log => \n new Date(log.timestamp).toDateString() === currentDate.toDateString()\n );\n if (!hasLog) break;\n streak++;\n currentDate.setDate(currentDate.getDate() - 1);\n }\n document.getElementById('streak').textContent = streak;\n }\n\n // Initial load\n updateLogsList();\n updateStats();\n});\n---end---" + } + ], + "stop_reason": "end_turn", + "stop_sequence": null, + "usage": { + "input_tokens": 1918, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 2978 + } +} \ No newline at end of file diff --git a/readme.scroll b/readme.scroll new file mode 100644 index 0000000..a68b491 --- /dev/null +++ b/readme.scroll @@ -0,0 +1,2 @@ +# worklog3.scroll.pub +Website generated by Claude from prompt: Google Chrome 插件需求文档 目标用户 职场人士 需要记录日常工作内容、任务进展和总结。 学生/研究人员 需要记录学习笔记、研究进展和灵感。 自由职业者 需要跟踪项目进展、任务完成情况和时间管理。 核心需求 便捷记录:用户可以快速记录工作内容,无需离开浏览器。 分类整理:支持对日志进行分类、标签化管理,便于后续查找和总结。 数据持久化:日志数据需要安全存储,支持本地存储或云端同步。 总结与回顾:提供总结功能,帮助用户回顾工作进展和成果。 提醒与习惯养成:支持定时提醒用户记录日志,帮助形成习惯。 功能需求 日志记录功能 功能点 快速输入 实现简单输入框,支持快速记录工作内容。 自动保存内容到本地。 富文本支持 使用富文本编辑器,支持加粗、斜体、列表等基本格式化功能。 日期与时间 自动记录日志创建时间,允许用户手动调整。 附件支持 上传图片或文件作为附件,文件大小限制 5MB。 技术实现 <textarea id="log-input" placeholder="输入日志内容..."></textarea> <button id="save-log">保存</button> document.getElementById("save-log").addEventListener("click", () => { const log = document.getElementById("log-input").value; const timestamp = new Date().toISOString(); chrome.storage.local.set({ [timestamp]: log }); }); 分类与标签功能 功能点 分类管理 用户可创建、编辑和删除分类。 提供默认分类(如“未分类”)。 标签管理 支持为每条日志添加多个标签。 技术实现 分类存储: { "categories": ["日常工作", "学习笔记", "项目进展"] } 标签输入库:Tagify 搜索与过滤功能 功能点 关键词搜索 支持通过关键词搜索日志内容。 日期过滤 按日期范围过滤日志。 分类/标签过滤 按分类或标签筛选��志。 技术实现 const logs = await chrome.storage.local.get(null); const filteredLogs = Object.values(logs).filter(log => log.category === selectedCategory); 数据导出功能 功能点 支持导出格式:文本、Markdown 和 Excel。 批量导出:选择多条日志导出。 导出范围:支持按日期范围、分类或标签导出。 技术实现 const exportData = JSON.stringify(logs, null, 2); // 结构化数据 const blob = new Blob([exportData], { type: "application/json" }); const url = URL.createObjectURL(blob); const link = document.createElement("a"); link.href = url; link.download = "logs.json"; link.click(); 提醒功能 功能点 定时提醒 提供每日、每周和每月提醒功能。 提醒内容 支持自定义提醒文本。 技术实现 chrome.alarms.create("dailyReminder", { when: Date.now() + 1000 * 60 * 60 * 24 }); chrome.alarms.onAlarm.addListener((alarm) => { if (alarm.name === "dailyReminder") { chrome.notifications.create("reminder", { title: "日志提醒", message: "请记录今天的工作日志!", type: "basic", iconUrl: "icon.png", }); } }); 数据同步功能 功能点 本地存储 使用 chrome.storage.local 存储日志数据。 云端同步 支持与 Google Drive 或 OneDrive 同步。 技术实现 function uploadToGoogleDrive(file) { fetch("https://www.googleapis.com/upload/drive/v3/files?uploadType=media", { method: "POST", headers: { Authorization: `Bearer ${accessToken}`, }, body: file, }); } 统计与总结功能 功能点 日志统计 统计总日志数量、每日平均日志数量等。 分类统计 按分类统计日志数量。 总结报告 生成每周/每月总结报告,支持导出 PDF。 技术实现 const summaryHtml = ` <h1>本周总结</h1> <p>总日志数量:${totalLogs}</p> <p>最常用分类:${topCategory}</p> `; const blob = new Blob([summaryHtml], { type: "application/pdf" }); const url = URL.createObjectURL(blob); 用户界面设计 功能点 简洁易用 使用直观的界面布局,便于用户快速上手。 主题切换 提供浅色和深色主题切换功能。 技术实现 :root { --background-color: white; } [data-theme="dark"] { --background-color: black; } 技术实现建议 前端技术 使用 HTML/CSS/JavaScript 构建插件界面。 引入 React 或 Vue.js 提高开发效率。 数据存储 使用 chrome.storage.local 存储本地数据。 云同步时,通过 Google Drive 或 OneDrive API 上传文件。 插件开发 定义 manifest.json 文件以描述插件。 使用 background.js 实现后台逻辑。 如需与网页内容交互,使用 content.js。 安全性 加密存储敏感数据。 仅请求必要的权限,保护用户隐私。 \ No newline at end of file diff --git a/script.js b/script.js new file mode 100644 index 0000000..1311818 --- /dev/null +++ b/script.js @@ -0,0 +1,101 @@ +document.addEventListener('DOMContentLoaded', () => { + // Theme Toggle + const themeToggle = document.getElementById('themeToggle'); + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)'); + + function setTheme(isDark) { + document.documentElement.setAttribute('data-theme', isDark ? 'dark' : 'light'); + localStorage.setItem('theme', isDark ? 'dark' : 'light'); + } + + themeToggle.addEventListener('click', () => { + const isDark = document.documentElement.getAttribute('data-theme') === 'dark'; + setTheme(!isDark); + }); + + // Rich Text Editor + const toolbar = document.querySelector('.toolbar'); + toolbar.addEventListener('click', (e) => { + if (e.target.matches('button[data-command]')) { + const command = e.target.getAttribute('data-command'); + document.execCommand(command, false, null); + } + }); + + // Save Log + const saveLog = document.getElementById('saveLog'); + saveLog.addEventListener('click', () => { + const content = document.getElementById('editor').innerHTML; + const tags = document.getElementById('tags').value; + const category = document.getElementById('category').value; + const timestamp = new Date().toISOString(); + + const log = { + content, + tags: tags.split(',').map(tag => tag.trim()), + category, + timestamp + }; + + // Store in localStorage for demo + const logs = JSON.parse(localStorage.getItem('logs') || '[]'); + logs.unshift(log); + localStorage.setItem('logs', JSON.stringify(logs)); + + updateLogsList(); + updateStats(); + clearEditor(); + }); + + function clearEditor() { + document.getElementById('editor').innerHTML = ''; + document.getElementById('tags').value = ''; + document.getElementById('category').selectedIndex = 0; + } + + function updateLogsList() { + const logsList = document.getElementById('logsList'); + const logs = JSON.parse(localStorage.getItem('logs') || '[]'); + + logsList.innerHTML = logs.map(log => ` + <div class="log-item"> + <div class="log-content">${log.content}</div> + <div class="log-meta"> + <span>${new Date(log.timestamp).toLocaleDateString()}</span> + <span>${log.category}</span> + <span>${log.tags.join(', ')}</span> + </div> + </div> + `).join(''); + } + + function updateStats() { + const logs = JSON.parse(localStorage.getItem('logs') || '[]'); + + document.getElementById('totalLogs').textContent = logs.length; + + const weekAgo = new Date(); + weekAgo.setDate(weekAgo.getDate() - 7); + const weeklyLogs = logs.filter(log => + new Date(log.timestamp) > weekAgo + ).length; + document.getElementById('weeklyLogs').textContent = weeklyLogs; + + // Calculate streak + let streak = 0; + let currentDate = new Date(); + while (true) { + const hasLog = logs.some(log => + new Date(log.timestamp).toDateString() === currentDate.toDateString() + ); + if (!hasLog) break; + streak++; + currentDate.setDate(currentDate.getDate() - 1); + } + document.getElementById('streak').textContent = streak; + } + + // Initial load + updateLogsList(); + updateStats(); +}); \ No newline at end of file diff --git a/style.css b/style.css new file mode 100644 index 0000000..75c8e81 --- /dev/null +++ b/style.css @@ -0,0 +1,175 @@ +:root { + --primary: #2563eb; + --primary-dark: #1d4ed8; + --bg: #ffffff; + --text: #1f2937; + --border: #e5e7eb; + --card-bg: #f9fafb; + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); + --radius: 8px; +} + +[data-theme="dark"] { + --primary: #3b82f6; + --primary-dark: #2563eb; + --bg: #111827; + --text: #f9fafb; + --border: #374151; + --card-bg: #1f2937; + --shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.3); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: system-ui, -apple-system, sans-serif; + background: var(--bg); + color: var(--text); + line-height: 1.5; + transition: background-color 0.3s, color 0.3s; +} + +header { + padding: 1rem; + border-bottom: 1px solid var(--border); + background: var(--card-bg); +} + +nav { + max-width: 1200px; + margin: 0 auto; + display: flex; + justify-content: space-between; + align-items: center; +} + +.logo { + font-size: 1.5rem; + font-weight: bold; + background: linear-gradient(45deg, var(--primary), var(--primary-dark)); + -webkit-background-clip: text; + color: transparent; +} + +main { + max-width: 1200px; + margin: 2rem auto; + padding: 0 1rem; +} + +section { + margin-bottom: 2rem; +} + +.editor-container { + border: 1px solid var(--border); + border-radius: var(--radius); + overflow: hidden; +} + +.toolbar { + padding: 0.5rem; + border-bottom: 1px solid var(--border); + background: var(--card-bg); +} + +.toolbar button { + padding: 0.25rem 0.5rem; + margin-right: 0.5rem; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg); + color: var(--text); + cursor: pointer; +} + +#editor { + min-height: 200px; + padding: 1rem; + outline: none; +} + +.entry-meta { + display: flex; + gap: 1rem; + margin: 1rem 0; +} + +input, select { + padding: 0.5rem; + border: 1px solid var(--border); + border-radius: var(--radius); + background: var(--bg); + color: var(--text); +} + +.primary-button { + padding: 0.75rem 1.5rem; + background: var(--primary); + color: white; + border: none; + border-radius: var(--radius); + cursor: pointer; + transition: background-color 0.3s; +} + +.primary-button:hover { + background: var(--primary-dark); +} + +.stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; +} + +.stat-card { + padding: 1.5rem; + background: var(--card-bg); + border-radius: var(--radius); + box-shadow: var(--shadow); + text-align: center; +} + +.stat-number { + font-size: 2rem; + font-weight: bold; + color: var(--primary); +} + +.stat-label { + display: block; + margin-top: 0.5rem; + font-size: 0.875rem; +} + +footer { + padding: 2rem 1rem; + background: var(--card-bg); + text-align: center; +} + +footer nav { + margin-top: 1rem; + justify-content: center; + gap: 2rem; +} + +footer a { + color: var(--text); + text-decoration: none; +} + +@media (max-width: 768px) { + .entry-meta { + flex-direction: column; + } + + .stats-grid { + grid-template-columns: 1fr; + } +} \ No newline at end of file