mirror of
https://github.com/cjo4m06/mcp-shrimp-task-manager.git
synced 2025-07-26 07:52:25 +08:00
新增網頁圖形介面功能,透過環境變數 ENABLE_GUI
控制
This commit is contained in:
parent
e31b87327e
commit
bf5f3678ba
@ -19,6 +19,12 @@ ENABLE_THOUGHT_CHAIN=true
|
||||
# (e.g., to 'my_templates'), and set TEMPLATES_USE to the new directory name (e.g., 'my_templates').
|
||||
TEMPLATES_USE=en
|
||||
|
||||
# Enable GUI interface
|
||||
# If set to true, a file named 'WebGUI.md' will be created in the DATA_DIR.
|
||||
# This file will contain a local web address you can open in your browser to access the GUI.
|
||||
# Default is false.
|
||||
ENABLE_GUI=false
|
||||
|
||||
# ============================
|
||||
# Prompt Customization
|
||||
# ============================
|
||||
|
25
README.md
25
README.md
@ -7,6 +7,7 @@
|
||||
- [🧠 Task Memory Function](#task-memory-function)
|
||||
- [🤔 Thought Chain Process](#thought-chain)
|
||||
- [📋 Project Rules Initialization](#project-rules)
|
||||
- [🌐 Web GUI](#web-gui)
|
||||
- [📚 Documentation Resources](#documentation)
|
||||
- [🔧 Installation and Usage](#installation)
|
||||
- [🔌 Using with MCP-Compatible Clients](#clients)
|
||||
@ -39,6 +40,7 @@ Shrimp Task Manager guides Agents through structured workflows for systematic pr
|
||||
- **Task Memory Function**: Automatically backup task history, providing long-term memory and reference capabilities
|
||||
- **Thought Chain Process**: Step-by-step reasoning to analyze complex problems systematically
|
||||
- **Project Rules Initialization**: Define project standards and rules to maintain consistency across large projects
|
||||
- **<a id="web-gui"></a>Web GUI**: Provides an optional web-based graphical user interface for task management. Enable by setting `ENABLE_GUI=true` in your `.env` file. When enabled, a `WebGUI.md` file containing the access address will be created in your `DATA_DIR`.
|
||||
|
||||
## 🧭 <a id="usage-guide"></a>Usage Guide
|
||||
|
||||
@ -178,7 +180,10 @@ Shrimp Task Manager offers two configuration methods: global configuration and p
|
||||
"command": "node",
|
||||
"args": ["/mcp-shrimp-task-manager/dist/index.js"],
|
||||
"env": {
|
||||
"DATA_DIR": "/path/to/project/data" // 必須使用絕對路徑
|
||||
"DATA_DIR": "/path/to/project/data", // 必須使用絕對路徑
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -193,7 +198,10 @@ or
|
||||
"command": "npx",
|
||||
"args": ["-y", "mcp-shrimp-task-manager"],
|
||||
"env": {
|
||||
"DATA_DIR": "/mcp-shrimp-task-manager/data"
|
||||
"DATA_DIR": "/mcp-shrimp-task-manager/data",
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -216,7 +224,10 @@ You can also set up dedicated configurations for each project to use independent
|
||||
"command": "node",
|
||||
"args": ["/path/to/mcp-shrimp-task-manager/dist/index.js"],
|
||||
"env": {
|
||||
"DATA_DIR": "/path/to/project/data" // Must use absolute path
|
||||
"DATA_DIR": "/path/to/project/data", // Must use absolute path
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -231,7 +242,10 @@ or
|
||||
"command": "npx",
|
||||
"args": ["-y", "mcp-shrimp-task-manager"],
|
||||
"env": {
|
||||
"DATA_DIR": "/path/to/project/data" // 必須使用絕對路徑
|
||||
"DATA_DIR": "/path/to/project/data", // 必須使用絕對路徑
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -264,7 +278,8 @@ Shrimp Task Manager supports customizing prompt behavior through environment var
|
||||
"MCP_PROMPT_PLAN_TASK": "Custom planning guidance...",
|
||||
"MCP_PROMPT_EXECUTE_TASK_APPEND": "Additional execution instructions...",
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en"
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
- [🧠 任務記憶功能](#任務記憶功能)
|
||||
- [🤔 思維鏈過程](#思維鏈過程)
|
||||
- [📋 專案規範初始化](#專案規範初始化)
|
||||
- [🌐 網頁圖形介面](#網頁圖形介面)
|
||||
- [📚 文件資源](#文件資源)
|
||||
- [🔧 安裝與使用](#安裝與使用)
|
||||
- [🔌 在支援 MCP 的客戶端中使用](#客戶端中使用)
|
||||
@ -35,6 +36,7 @@
|
||||
- **任務記憶功能**:自動備份任務歷史記錄,提供長期記憶和參考能力
|
||||
- **思維鏈過程**:通過步驟化的推理系統性地分析複雜問題
|
||||
- **專案規範初始化**:定義專案標準和規則,維持大型專案的一致性
|
||||
- **<a id="網頁圖形介面"></a>網頁圖形介面**:提供選用的網頁圖形化使用者介面來管理任務。透過在您的 `.env` 檔案中設定 `ENABLE_GUI=true` 來啟用。啟用後,將會在您的 `DATA_DIR` 中建立一個包含存取網址的 `WebGUI.md` 檔案。
|
||||
|
||||
## 🧭 <a id="使用指南"></a>使用指南
|
||||
|
||||
@ -174,7 +176,10 @@ npm run build
|
||||
"command": "node",
|
||||
"args": ["/mcp-shrimp-task-manager/dist/index.js"],
|
||||
"env": {
|
||||
"DATA_DIR": "/mcp-shrimp-task-manager/data"
|
||||
"DATA_DIR": "/mcp-shrimp-task-manager/data",
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -188,7 +193,10 @@ or
|
||||
"command": "npx",
|
||||
"args": ["-y", "mcp-shrimp-task-manager"],
|
||||
"env": {
|
||||
"DATA_DIR": "/mcp-shrimp-task-manager/data"
|
||||
"DATA_DIR": "/mcp-shrimp-task-manager/data",
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -211,7 +219,10 @@ or
|
||||
"command": "node",
|
||||
"args": ["/path/to/mcp-shrimp-task-manager/dist/index.js"],
|
||||
"env": {
|
||||
"DATA_DIR": "/path/to/project/data" // 必須使用絕對路徑
|
||||
"DATA_DIR": "/path/to/project/data", // 必須使用絕對路徑
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -225,7 +236,10 @@ or
|
||||
"command": "npx",
|
||||
"args": ["-y", "mcp-shrimp-task-manager"],
|
||||
"env": {
|
||||
"DATA_DIR": "/path/to/project/data" // 必須使用絕對路徑
|
||||
"DATA_DIR": "/path/to/project/data", // 必須使用絕對路徑
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -258,7 +272,8 @@ or
|
||||
"MCP_PROMPT_PLAN_TASK": "自定義規劃指導...",
|
||||
"MCP_PROMPT_EXECUTE_TASK_APPEND": "附加執行說明...",
|
||||
"ENABLE_THOUGHT_CHAIN": "true",
|
||||
"TEMPLATES_USE": "en"
|
||||
"TEMPLATES_USE": "en",
|
||||
"ENABLE_GUI": "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
197
package-lock.json
generated
197
package-lock.json
generated
@ -1,16 +1,18 @@
|
||||
{
|
||||
"name": "mcp-shrimp-task-manager",
|
||||
"version": "1.0.10",
|
||||
"version": "1.0.11",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "mcp-shrimp-task-manager",
|
||||
"version": "1.0.10",
|
||||
"version": "1.0.11",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.0.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"express": "^5.1.0",
|
||||
"get-port": "^7.1.0",
|
||||
"uuid": "^9.0.1",
|
||||
"zod": "^3.22.4",
|
||||
"zod-to-json-schema": "^3.24.5"
|
||||
@ -19,6 +21,7 @@
|
||||
"mcp-shrimp-task-manager": "dist/index.js"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/node": "^20.8.2",
|
||||
"@types/uuid": "^9.0.6",
|
||||
"copyfiles": "^2.4.1",
|
||||
@ -107,6 +110,60 @@
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/body-parser": {
|
||||
"version": "1.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/connect": {
|
||||
"version": "3.4.38",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz",
|
||||
"integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/express-serve-static-core": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
|
||||
"integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/range-parser": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/http-errors": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
|
||||
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "20.17.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz",
|
||||
@ -116,6 +173,39 @@
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/qs": {
|
||||
"version": "6.9.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
|
||||
"integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/range-parser": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
||||
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/send": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
||||
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/serve-static": {
|
||||
"version": "1.15.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
|
||||
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/node": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/uuid": {
|
||||
"version": "9.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
|
||||
@ -677,6 +767,17 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/get-port": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz",
|
||||
"integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw==",
|
||||
"engines": {
|
||||
"node": ">=16"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
@ -1664,6 +1765,60 @@
|
||||
"integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/body-parser": {
|
||||
"version": "1.19.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz",
|
||||
"integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/connect": {
|
||||
"version": "3.4.38",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz",
|
||||
"integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/express": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.1.tgz",
|
||||
"integrity": "sha512-UZUw8vjpWFXuDnjFTh7/5c2TWDlQqeXHi6hcN7F2XSVT5P+WmUnnbFS3KA6Jnc6IsEqI2qCVu2bK0R0J4A8ZQQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "^5.0.0",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "5.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz",
|
||||
"integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/qs": "*",
|
||||
"@types/range-parser": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"@types/http-errors": {
|
||||
"version": "2.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz",
|
||||
"integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz",
|
||||
"integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "20.17.30",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.30.tgz",
|
||||
@ -1673,6 +1828,39 @@
|
||||
"undici-types": "~6.19.2"
|
||||
}
|
||||
},
|
||||
"@types/qs": {
|
||||
"version": "6.9.18",
|
||||
"resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.18.tgz",
|
||||
"integrity": "sha512-kK7dgTYDyGqS+e2Q4aK9X3D7q234CIZ1Bv0q/7Z5IwRDoADNU81xXJK/YVyLbLTZCoIwUoDoffFeF+p/eIklAA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz",
|
||||
"integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/send": {
|
||||
"version": "0.17.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz",
|
||||
"integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/mime": "^1",
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/serve-static": {
|
||||
"version": "1.15.7",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz",
|
||||
"integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/http-errors": "*",
|
||||
"@types/node": "*",
|
||||
"@types/send": "*"
|
||||
}
|
||||
},
|
||||
"@types/uuid": {
|
||||
"version": "9.0.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-9.0.8.tgz",
|
||||
@ -2074,6 +2262,11 @@
|
||||
"math-intrinsics": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"get-port": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/get-port/-/get-port-7.1.0.tgz",
|
||||
"integrity": "sha512-QB9NKEeDg3xxVwCCwJQ9+xycaz6pBB6iQ76wiWMl1927n0Kir6alPiP+yuiICLLU4jpMe08dXfpebuQppFA2zw=="
|
||||
},
|
||||
"get-proto": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
|
||||
|
@ -16,7 +16,7 @@
|
||||
"mcp-shrimp-task-manager": "./dist/index.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc && copyfiles -u 1 './src/**/*.md' dist && node scripts/add-shebang.js",
|
||||
"build": "tsc && copyfiles -u 1 './src/**/*.md' dist && copyfiles -u 1 './src/public/**/*' dist && node scripts/add-shebang.js",
|
||||
"dev": "ts-node src/index.ts",
|
||||
"start": "node dist/index.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
@ -32,11 +32,14 @@
|
||||
"dependencies": {
|
||||
"@modelcontextprotocol/sdk": "^1.0.0",
|
||||
"dotenv": "^16.5.0",
|
||||
"express": "^5.1.0",
|
||||
"get-port": "^7.1.0",
|
||||
"uuid": "^9.0.1",
|
||||
"zod": "^3.22.4",
|
||||
"zod-to-json-schema": "^3.24.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^5.0.1",
|
||||
"@types/node": "^20.8.2",
|
||||
"@types/uuid": "^9.0.6",
|
||||
"copyfiles": "^2.4.1",
|
||||
|
136
src/index.ts
136
src/index.ts
@ -8,6 +8,12 @@ import {
|
||||
CallToolRequestSchema,
|
||||
ListToolsRequestSchema,
|
||||
} from "@modelcontextprotocol/sdk/types.js";
|
||||
import express, { Request, Response, NextFunction } from "express";
|
||||
import getPort from "get-port";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
import fsPromises from "fs/promises";
|
||||
import { fileURLToPath } from "url";
|
||||
|
||||
// 導入工具函數
|
||||
import {
|
||||
@ -54,6 +60,136 @@ import {
|
||||
async function main() {
|
||||
try {
|
||||
console.log("Starting Shrimp Task Manager service...");
|
||||
const ENABLE_GUI = process.env.ENABLE_GUI === "true";
|
||||
|
||||
if (ENABLE_GUI) {
|
||||
// 創建 Express 應用
|
||||
const app = express();
|
||||
|
||||
// 儲存 SSE 客戶端的列表
|
||||
let sseClients: Response[] = [];
|
||||
|
||||
// 發送 SSE 事件的輔助函數
|
||||
function sendSseUpdate() {
|
||||
console.log("Tasks changed, sending update to clients...");
|
||||
sseClients.forEach((client) => {
|
||||
// 檢查客戶端是否仍然連接
|
||||
if (!client.writableEnded) {
|
||||
client.write(
|
||||
`event: update\ndata: ${JSON.stringify({
|
||||
timestamp: Date.now(),
|
||||
})}\n\n`
|
||||
);
|
||||
}
|
||||
});
|
||||
// 清理已斷開的客戶端 (可選,但建議)
|
||||
sseClients = sseClients.filter((client) => !client.writableEnded);
|
||||
}
|
||||
|
||||
// 設置靜態文件目錄
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const publicPath = path.join(__dirname, "public");
|
||||
const DATA_DIR = process.env.DATA_DIR || path.join(__dirname, "data");
|
||||
const TASKS_FILE_PATH = path.join(DATA_DIR, "tasks.json"); // 提取檔案路徑
|
||||
|
||||
app.use(express.static(publicPath));
|
||||
|
||||
// 設置 API 路由
|
||||
app.get("/api/tasks", async (req: Request, res: Response) => {
|
||||
try {
|
||||
// 使用 fsPromises 保持異步讀取
|
||||
const tasksData = await fsPromises.readFile(TASKS_FILE_PATH, "utf-8");
|
||||
res.json(JSON.parse(tasksData));
|
||||
} catch (error) {
|
||||
console.error("Error reading tasks.json:", error);
|
||||
// 確保檔案不存在時返回空任務列表
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT") {
|
||||
res.json({ tasks: [] });
|
||||
} else {
|
||||
res.status(500).json({ error: "Failed to read tasks data" });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// 新增:SSE 端點
|
||||
app.get("/api/tasks/stream", (req: Request, res: Response) => {
|
||||
res.writeHead(200, {
|
||||
"Content-Type": "text/event-stream",
|
||||
"Cache-Control": "no-cache",
|
||||
Connection: "keep-alive",
|
||||
// 可選: CORS 頭,如果前端和後端不在同一個 origin
|
||||
// "Access-Control-Allow-Origin": "*",
|
||||
});
|
||||
|
||||
// 發送一個初始事件或保持連接
|
||||
res.write("data: connected\n\n");
|
||||
|
||||
// 將客戶端添加到列表
|
||||
sseClients.push(res);
|
||||
|
||||
// 當客戶端斷開連接時,將其從列表中移除
|
||||
req.on("close", () => {
|
||||
sseClients = sseClients.filter((client) => client !== res);
|
||||
});
|
||||
});
|
||||
|
||||
// 獲取可用埠
|
||||
const port = await getPort();
|
||||
|
||||
// 啟動 HTTP 伺服器
|
||||
const httpServer = app.listen(port, () => {
|
||||
// 在伺服器啟動後開始監聽檔案變化
|
||||
try {
|
||||
// 檢查檔案是否存在,如果不存在則不監聽 (避免 watch 報錯)
|
||||
if (fs.existsSync(TASKS_FILE_PATH)) {
|
||||
fs.watch(TASKS_FILE_PATH, (eventType, filename) => {
|
||||
if (
|
||||
filename &&
|
||||
(eventType === "change" || eventType === "rename")
|
||||
) {
|
||||
// 稍微延遲發送,以防短時間內多次觸發 (例如編輯器保存)
|
||||
// debounce sendSseUpdate if needed
|
||||
sendSseUpdate();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.warn(
|
||||
`${TASKS_FILE_PATH} does not exist. File watching not started. It will start if the file is created later by the application.`
|
||||
);
|
||||
// 可以考慮在這裡也設置一個 watcher 監聽目錄創建或檔案創建
|
||||
}
|
||||
} catch (watchError) {
|
||||
console.error(
|
||||
`Error setting up file watch on ${TASKS_FILE_PATH}:`,
|
||||
watchError
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// 將 URL 寫入 ebGUI.md
|
||||
try {
|
||||
const websiteUrl = `[Task Manager UI](http://localhost:${port})`;
|
||||
const websiteFilePath = path.join(DATA_DIR, "WebGUI.md");
|
||||
await fsPromises.writeFile(websiteFilePath, websiteUrl, "utf-8");
|
||||
} catch (error) {
|
||||
console.error("Error writing website URL to file:", error);
|
||||
}
|
||||
|
||||
// 設置進程終止事件處理 (確保移除 watcher)
|
||||
const shutdownHandler = async () => {
|
||||
// 關閉所有 SSE 連接
|
||||
sseClients.forEach((client) => client.end());
|
||||
sseClients = [];
|
||||
|
||||
// 關閉 HTTP 伺服器
|
||||
await new Promise<void>((resolve) => httpServer.close(() => resolve()));
|
||||
process.exit(0);
|
||||
};
|
||||
|
||||
process.on("SIGINT", shutdownHandler);
|
||||
process.on("SIGTERM", shutdownHandler);
|
||||
}
|
||||
|
||||
// 創建MCP服務器
|
||||
const server = new Server(
|
||||
|
139
src/public/index.html
Normal file
139
src/public/index.html
Normal file
@ -0,0 +1,139 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title data-i18n-key="app_title">Shrimp Task Manager</title>
|
||||
<link rel="stylesheet" href="style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1 data-i18n-key="app_title">Shrimp Task Manager</h1>
|
||||
<div class="header-controls">
|
||||
<div class="status-bar">
|
||||
<div class="status-indicator"></div>
|
||||
<span data-i18n-key="status_indicator_online">ONLINE</span>
|
||||
</div>
|
||||
<select id="lang-switcher" aria-label="Select Language">
|
||||
<option value="en">English</option>
|
||||
<option value="zh-TW">繁體中文</option>
|
||||
</select>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div class="progress-indicator" id="progress-indicator">
|
||||
<div class="progress-bar-container">
|
||||
<div
|
||||
class="progress-segment progress-completed"
|
||||
id="progress-completed"
|
||||
></div>
|
||||
<div
|
||||
class="progress-segment progress-in-progress"
|
||||
id="progress-in-progress"
|
||||
></div>
|
||||
<div
|
||||
class="progress-segment progress-pending"
|
||||
id="progress-pending"
|
||||
></div>
|
||||
</div>
|
||||
<div class="progress-labels" id="progress-labels">
|
||||
<!-- Labels will be generated by JS -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main>
|
||||
<div class="dependency-view">
|
||||
<div class="panel-header">
|
||||
<h2 data-i18n-key="dependency_view_title">Dependency View</h2>
|
||||
</div>
|
||||
<div id="dependency-graph" class="dependency-graph">
|
||||
<p class="placeholder"></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="bottom-panels">
|
||||
<div class="task-panel">
|
||||
<div class="panel-header">
|
||||
<h2 data-i18n-key="task_list_title">Task List</h2>
|
||||
<div class="filters">
|
||||
<input
|
||||
type="text"
|
||||
id="search-input"
|
||||
placeholder="Search tasks..."
|
||||
data-i18n-key="search_placeholder"
|
||||
/>
|
||||
<select id="sort-options">
|
||||
<option
|
||||
value="date-desc"
|
||||
data-i18n-key="sort_option_date_desc"
|
||||
>
|
||||
Creation Time (New-Old)
|
||||
</option>
|
||||
<option value="date-asc" data-i18n-key="sort_option_date_asc">
|
||||
Creation Time (Old-New)
|
||||
</option>
|
||||
<option value="name-asc" data-i18n-key="sort_option_name_asc">
|
||||
Name (A-Z)
|
||||
</option>
|
||||
<option
|
||||
value="name-desc"
|
||||
data-i18n-key="sort_option_name_desc"
|
||||
>
|
||||
Name (Z-A)
|
||||
</option>
|
||||
<option value="status" data-i18n-key="sort_option_status">
|
||||
Status
|
||||
</option>
|
||||
</select>
|
||||
<select id="status-filter">
|
||||
<option value="all" data-i18n-key="status_filter_all">
|
||||
All Statuses
|
||||
</option>
|
||||
<option value="pending" data-i18n-key="status_filter_pending">
|
||||
Pending
|
||||
</option>
|
||||
<option
|
||||
value="in_progress"
|
||||
data-i18n-key="status_filter_in_progress"
|
||||
>
|
||||
In Progress
|
||||
</option>
|
||||
<option
|
||||
value="completed"
|
||||
data-i18n-key="status_filter_completed"
|
||||
>
|
||||
Completed
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-list" id="task-list">
|
||||
<div class="loading"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="task-details">
|
||||
<div class="panel-header">
|
||||
<h2 data-i18n-key="task_details_title">Task Details</h2>
|
||||
</div>
|
||||
<div id="task-details-content">
|
||||
<p class="placeholder"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer>
|
||||
<p>
|
||||
© 2023 Shrimp Task Manager - Current time:
|
||||
<span id="current-time"></span>
|
||||
</p>
|
||||
</footer>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
<script src="https://d3js.org/d3.v7.min.js"></script>
|
||||
</body>
|
||||
</html>
|
55
src/public/locales/en.json
Normal file
55
src/public/locales/en.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"app_title": "Shrimp Task Manager",
|
||||
"status_indicator_online": "ONLINE",
|
||||
"dependency_view_title": "Dependency View",
|
||||
"dependency_graph_placeholder": "Dependency relationship for all tasks",
|
||||
"task_list_title": "Task List",
|
||||
"search_placeholder": "Search tasks...",
|
||||
"sort_option_date_desc": "Creation Time (New-Old)",
|
||||
"sort_option_date_asc": "Creation Time (Old-New)",
|
||||
"sort_option_name_asc": "Name (A-Z)",
|
||||
"sort_option_name_desc": "Name (Z-A)",
|
||||
"sort_option_status": "Status",
|
||||
"status_filter_all": "All Statuses",
|
||||
"status_filter_pending": "Pending",
|
||||
"status_filter_in_progress": "In Progress",
|
||||
"status_filter_completed": "Completed",
|
||||
"task_list_loading": "Loading...",
|
||||
"task_list_empty": "No matching tasks",
|
||||
"task_details_title": "Task Details",
|
||||
"task_details_placeholder": "Select a task to view details",
|
||||
"footer_copyright": "© 2023 Shrimp Task Manager - Current time: ",
|
||||
|
||||
"status_pending": "Pending",
|
||||
"status_in_progress": "In Progress",
|
||||
"status_completed": "Completed",
|
||||
|
||||
"task_detail_status_label": "Status:",
|
||||
"task_detail_summary_title": "Completion Summary",
|
||||
"task_detail_description_title": "Task Description",
|
||||
"task_detail_implementation_guide_title": "Implementation Guide",
|
||||
"task_detail_verification_criteria_title": "Verification Criteria",
|
||||
"task_detail_dependencies_title": "Dependencies (Prerequisites)",
|
||||
"task_detail_related_files_title": "Related Files",
|
||||
"task_detail_notes_title": "Notes",
|
||||
"task_detail_no_summary": "No summary provided.",
|
||||
"task_detail_no_description": "No description",
|
||||
"task_detail_no_implementation_guide": "No implementation guide",
|
||||
"task_detail_no_verification_criteria": "No verification criteria",
|
||||
"task_detail_no_dependencies": "No dependencies",
|
||||
"task_detail_no_related_files": "No related files",
|
||||
"task_detail_no_notes": "No notes",
|
||||
"task_detail_unknown_dependency": "Unknown Task",
|
||||
|
||||
"progress_completed": "Completed",
|
||||
"progress_in_progress": "In Progress",
|
||||
"progress_pending": "Pending",
|
||||
"progress_total": "Total",
|
||||
|
||||
"global_analysis_title": "Goal",
|
||||
|
||||
"error_loading_tasks": "Failed to load tasks: {message}",
|
||||
"error_updating_tasks": "Failed to update tasks: {message}",
|
||||
"error_loading_graph": "Failed to load dependency graph",
|
||||
"error_task_not_found": "Task not found"
|
||||
}
|
55
src/public/locales/zh-TW.json
Normal file
55
src/public/locales/zh-TW.json
Normal file
@ -0,0 +1,55 @@
|
||||
{
|
||||
"app_title": "Shrimp Task Manager",
|
||||
"status_indicator_online": "在線",
|
||||
"dependency_view_title": "依賴關係",
|
||||
"dependency_graph_placeholder": "所有任務的依賴關係",
|
||||
"task_list_title": "任務列表",
|
||||
"search_placeholder": "搜索任務...",
|
||||
"sort_option_date_desc": "創建時間 (新-舊)",
|
||||
"sort_option_date_asc": "創建時間 (舊-新)",
|
||||
"sort_option_name_asc": "名稱 (A-Z)",
|
||||
"sort_option_name_desc": "名稱 (Z-A)",
|
||||
"sort_option_status": "狀態",
|
||||
"status_filter_all": "所有狀態",
|
||||
"status_filter_pending": "等待中",
|
||||
"status_filter_in_progress": "進行中",
|
||||
"status_filter_completed": "已完成",
|
||||
"task_list_loading": "載入中...",
|
||||
"task_list_empty": "沒有符合條件的任務",
|
||||
"task_details_title": "任務詳情",
|
||||
"task_details_placeholder": "選擇一個任務查看詳情",
|
||||
"footer_copyright": "© 2023 Shrimp Task Manager - 當前時間: ",
|
||||
|
||||
"status_pending": "等待中",
|
||||
"status_in_progress": "進行中",
|
||||
"status_completed": "已完成",
|
||||
|
||||
"task_detail_status_label": "狀態:",
|
||||
"task_detail_summary_title": "完成摘要",
|
||||
"task_detail_description_title": "任務描述",
|
||||
"task_detail_implementation_guide_title": "實現指南",
|
||||
"task_detail_verification_criteria_title": "驗證標準",
|
||||
"task_detail_dependencies_title": "依賴項 (前置任務)",
|
||||
"task_detail_related_files_title": "相關文件",
|
||||
"task_detail_notes_title": "備註",
|
||||
"task_detail_no_summary": "沒有提供摘要。",
|
||||
"task_detail_no_description": "無描述",
|
||||
"task_detail_no_implementation_guide": "無實現指南",
|
||||
"task_detail_no_verification_criteria": "無驗證標準",
|
||||
"task_detail_no_dependencies": "無依賴項",
|
||||
"task_detail_no_related_files": "無相關文件",
|
||||
"task_detail_no_notes": "無備註",
|
||||
"task_detail_unknown_dependency": "未知任務",
|
||||
|
||||
"progress_completed": "已完成",
|
||||
"progress_in_progress": "進行中",
|
||||
"progress_pending": "待處理",
|
||||
"progress_total": "總計",
|
||||
|
||||
"global_analysis_title": "目標",
|
||||
|
||||
"error_loading_tasks": "載入任務失敗: {message}",
|
||||
"error_updating_tasks": "更新任務失敗: {message}",
|
||||
"error_loading_graph": "載入依賴圖失敗",
|
||||
"error_task_not_found": "任務未找到"
|
||||
}
|
1186
src/public/script.js
Normal file
1186
src/public/script.js
Normal file
File diff suppressed because it is too large
Load Diff
461
src/public/style.css
Normal file
461
src/public/style.css
Normal file
@ -0,0 +1,461 @@
|
||||
:root {
|
||||
--primary-color: #3498db;
|
||||
--secondary-color: #2ecc71;
|
||||
--background-color: #1a1a2e;
|
||||
--panel-color: #16213e;
|
||||
--text-color: #f0f0f0;
|
||||
--accent-color: #4cd137;
|
||||
--danger-color: #e74c3c;
|
||||
--border-radius: 8px;
|
||||
--box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif;
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
max-width: 1400px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 20px 0;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.8rem;
|
||||
font-weight: 600;
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.status-bar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
background-color: var(--accent-color);
|
||||
box-shadow: 0 0 8px var(--accent-color);
|
||||
animation: pulse 2s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 1;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
main {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
grid-template-areas:
|
||||
"dependency"
|
||||
"bottom";
|
||||
gap: 20px;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.task-panel,
|
||||
.task-details {
|
||||
background-color: var(--panel-color);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.dependency-view {
|
||||
grid-area: dependency;
|
||||
background-color: var(--panel-color);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow);
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.bottom-panels {
|
||||
grid-area: bottom;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.panel-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 15px 20px;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size: 1.2rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
select {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
color: var(--text-color);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.task-list,
|
||||
.dependency-graph,
|
||||
#task-details-content {
|
||||
padding: 15px;
|
||||
flex-grow: 1;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.dependency-graph {
|
||||
min-height: 300px;
|
||||
}
|
||||
|
||||
.task-item {
|
||||
padding: 15px;
|
||||
margin-bottom: 15px;
|
||||
background-color: rgba(0, 0, 0, 0.2);
|
||||
border-radius: var(--border-radius);
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.task-item::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
width: 4px;
|
||||
background-color: transparent;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.task-item.status-pending::before {
|
||||
background-color: #f1c40f;
|
||||
}
|
||||
|
||||
.task-item.status-in-progress::before {
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.task-item.status-completed::before {
|
||||
background-color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.task-item:hover {
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.task-item.selected {
|
||||
background-color: rgba(52, 152, 219, 0.15);
|
||||
box-shadow: 0 0 0 2px var(--primary-color);
|
||||
}
|
||||
|
||||
.task-item h3 {
|
||||
font-size: 1.1rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.task-meta {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 0.85rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.task-status {
|
||||
padding: 2px 8px;
|
||||
border-radius: 20px;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.status-pending {
|
||||
background-color: rgba(241, 196, 15, 0.2);
|
||||
color: #f1c40f;
|
||||
}
|
||||
|
||||
.status-in-progress {
|
||||
background-color: rgba(52, 152, 219, 0.2);
|
||||
color: #3498db;
|
||||
}
|
||||
|
||||
.status-completed {
|
||||
background-color: rgba(46, 204, 113, 0.2);
|
||||
color: #2ecc71;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
text-align: center;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
padding: 50px 0;
|
||||
}
|
||||
|
||||
.loading {
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
animation: fadeInOut 1.5s infinite;
|
||||
}
|
||||
|
||||
@keyframes fadeInOut {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.5;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.task-details-header {
|
||||
margin-bottom: 20px;
|
||||
padding-bottom: 15px;
|
||||
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
.task-details-header h3 {
|
||||
font-size: 1.3rem;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.task-details-section {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.task-details-section h4 {
|
||||
font-size: 1rem;
|
||||
color: var(--primary-color);
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.dependencies,
|
||||
.related-files {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.dependency-tag,
|
||||
.file-tag {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
padding: 15px;
|
||||
border-radius: var(--border-radius);
|
||||
overflow-x: auto;
|
||||
margin: 10px 0;
|
||||
font-family: "Consolas", "Monaco", monospace;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
footer {
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
font-size: 0.8rem;
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
padding: 10px 0;
|
||||
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
/* 進度指示器樣式 */
|
||||
.progress-indicator {
|
||||
margin-bottom: 20px;
|
||||
padding: 15px;
|
||||
background-color: var(--panel-color);
|
||||
border-radius: var(--border-radius);
|
||||
box-shadow: var(--box-shadow);
|
||||
}
|
||||
|
||||
.progress-bar-container {
|
||||
display: flex;
|
||||
height: 10px;
|
||||
border-radius: 5px;
|
||||
overflow: hidden;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.progress-segment {
|
||||
height: 100%;
|
||||
transition: width 0.5s ease-in-out;
|
||||
}
|
||||
|
||||
.progress-completed {
|
||||
background-color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.progress-in-progress {
|
||||
background-color: var(--primary-color);
|
||||
}
|
||||
|
||||
.progress-pending {
|
||||
background-color: #f1c40f; /* 與status-pending一致 */
|
||||
}
|
||||
|
||||
.progress-labels {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-top: 10px;
|
||||
font-size: 0.8rem;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.progress-labels span {
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
.label-completed {
|
||||
color: var(--secondary-color);
|
||||
}
|
||||
|
||||
.label-in-progress {
|
||||
color: var(--primary-color);
|
||||
}
|
||||
|
||||
.label-pending {
|
||||
color: #f1c40f;
|
||||
}
|
||||
|
||||
/* 依賴關係圖樣式 */
|
||||
.dependency-graph svg {
|
||||
display: block; /* 避免底部多餘空間 */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.nodes g.node-item circle {
|
||||
transition: r 0.2s, stroke 0.2s;
|
||||
/* 根據類型設置基礎顏色 */
|
||||
}
|
||||
.nodes g.type-current circle {
|
||||
fill: var(--primary-color); /* 當前選中任務 */
|
||||
}
|
||||
.nodes g.type-dependency circle {
|
||||
fill: var(--danger-color); /* 前置依賴任務 */
|
||||
}
|
||||
.nodes g.type-dependent circle {
|
||||
fill: var(--secondary-color); /* 後置依賴任務 */
|
||||
}
|
||||
.nodes g.type-unknown circle {
|
||||
fill: #7f8c8d; /* 未知任務 */
|
||||
}
|
||||
|
||||
/* 根據狀態調整節點透明度或邊框 */
|
||||
.nodes g.status-completed circle {
|
||||
opacity: 0.6;
|
||||
}
|
||||
.nodes g.status-in-progress circle {
|
||||
/* 可以添加特殊效果,如描邊動畫 */
|
||||
stroke: var(--accent-color);
|
||||
stroke-width: 2px;
|
||||
}
|
||||
|
||||
.nodes g.node-item:hover circle {
|
||||
r: 14; /* 懸停時放大 */
|
||||
stroke: #fff;
|
||||
stroke-width: 2.5px;
|
||||
}
|
||||
|
||||
.nodes g.node-item text {
|
||||
fill: var(--text-color);
|
||||
font-size: 10px;
|
||||
pointer-events: none; /* 避免文本干擾點擊 */
|
||||
}
|
||||
|
||||
/* 新增:高亮節點樣式 */
|
||||
g.node-item.highlighted circle {
|
||||
stroke: var(--accent-color) !important; /* 使用重要標誌確保覆蓋 */
|
||||
stroke-width: 3px !important;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
main {
|
||||
grid-template-rows: auto auto; /* Stack dependency and bottom panels */
|
||||
grid-template-areas:
|
||||
"dependency"
|
||||
"bottom";
|
||||
}
|
||||
|
||||
.bottom-panels {
|
||||
grid-template-columns: 1fr; /* Stack task list and details */
|
||||
grid-template-rows: auto auto; /* Or let them take natural height */
|
||||
}
|
||||
|
||||
.task-panel,
|
||||
.dependency-view,
|
||||
.task-details {
|
||||
/* grid-column: 1 / -1; No longer needed */
|
||||
min-height: 300px; /* Ensure panels have some height */
|
||||
}
|
||||
}
|
||||
|
||||
/* 過濾器區域樣式 */
|
||||
.filters {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.filters input[type="text"],
|
||||
.filters select {
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
color: var(--text-color);
|
||||
border: 1px solid rgba(255, 255, 255, 0.1);
|
||||
padding: 5px 10px;
|
||||
border-radius: 4px;
|
||||
outline: none;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
|
||||
.filters input[type="text"] {
|
||||
flex-grow: 1; /* 讓搜索框佔據更多空間 */
|
||||
min-width: 150px;
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user