feat: implement sidebar
- add sidebar - update assets to use new task-master logo - change to task master instead of taskr
This commit is contained in:
3
apps/extension/assets/icon-dark.svg
Normal file
3
apps/extension/assets/icon-dark.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 224 291" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M101.635 286.568L71.4839 256.414C65.6092 250.539 65.6092 241.03 71.4839 235.155L142.52 164.11C144.474 162.156 147.643 162.156 149.61 164.11L176.216 190.719C178.17 192.673 181.339 192.673 183.305 190.719L189.719 184.305C191.673 182.35 191.673 179.181 189.719 177.214L163.113 150.605C161.159 148.651 161.159 145.481 163.113 143.514L191.26 115.365C193.214 113.41 193.214 110.241 191.26 108.274L182.316 99.3291C180.362 97.3748 177.193 97.3748 175.226 99.3291L55.8638 218.706C49.989 224.581 40.4816 224.581 34.6068 218.706L4.4061 188.501C-1.4687 182.626 -1.4687 173.117 4.4061 167.242L23.8342 147.811C25.7883 145.857 25.7883 142.688 23.8342 140.721L4.78187 121.666C-1.09293 115.791 -1.09293 106.282 4.78187 100.406L34.7195 70.4527C40.5943 64.5772 50.1017 64.5772 55.9765 70.4527L75.555 90.0335C77.5091 91.9879 80.6782 91.9879 82.6448 90.0335L124.144 48.5292C126.098 46.5749 126.098 43.4054 124.144 41.4385L115.463 32.7568C113.509 30.8025 110.34 30.8025 108.374 32.7568L99.8683 41.2632C97.9143 43.2175 94.7451 43.2175 92.7785 41.2632L82.1438 30.6271C80.1897 28.6728 80.1897 25.5033 82.1438 23.5364L101.271 4.40662C107.146 -1.46887 116.653 -1.46887 122.528 4.40662L152.478 34.3604C158.353 40.2359 158.353 49.7444 152.478 55.6199L82.6323 125.474C80.6782 127.429 77.5091 127.429 75.5425 125.474L48.8741 98.8029C46.9201 96.8486 43.7509 96.8486 41.7843 98.8029L33.1036 107.485C31.1496 109.439 31.1496 112.608 33.1036 114.575L59.2458 140.721C61.1999 142.675 61.1999 145.844 59.2458 147.811L32.7404 174.32C30.7863 176.274 30.7863 179.444 32.7404 181.411L41.6841 190.355C43.6382 192.31 46.8073 192.31 48.7739 190.355L168.136 70.9789C174.011 65.1034 183.518 65.1034 189.393 70.9789L219.594 101.183C225.469 107.059 225.469 116.567 219.594 122.443L198.537 143.502C196.583 145.456 196.583 148.626 198.537 150.592L218.053 170.111C223.928 175.986 223.928 185.495 218.053 191.37L190.37 219.056C184.495 224.932 174.988 224.932 169.113 219.056L149.597 199.538C147.643 197.584 144.474 197.584 142.508 199.538L99.8057 242.245C97.8516 244.2 97.8516 247.369 99.8057 249.336L108.699 258.231C110.653 260.185 113.823 260.185 115.789 258.231L122.954 251.065C124.908 249.11 128.077 249.11 130.044 251.065L140.679 261.701C142.633 263.655 142.633 266.825 140.679 268.791L122.879 286.593C117.004 292.469 107.497 292.469 101.622 286.593L101.635 286.568Z" fill="#CCCCCC"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
3
apps/extension/assets/icon-light.svg
Normal file
3
apps/extension/assets/icon-light.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 224 291" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M101.635 286.568L71.4839 256.414C65.6092 250.539 65.6092 241.03 71.4839 235.155L142.52 164.11C144.474 162.156 147.643 162.156 149.61 164.11L176.216 190.719C178.17 192.673 181.339 192.673 183.305 190.719L189.719 184.305C191.673 182.35 191.673 179.181 189.719 177.214L163.113 150.605C161.159 148.651 161.159 145.481 163.113 143.514L191.26 115.365C193.214 113.41 193.214 110.241 191.26 108.274L182.316 99.3291C180.362 97.3748 177.193 97.3748 175.226 99.3291L55.8638 218.706C49.989 224.581 40.4816 224.581 34.6068 218.706L4.4061 188.501C-1.4687 182.626 -1.4687 173.117 4.4061 167.242L23.8342 147.811C25.7883 145.857 25.7883 142.688 23.8342 140.721L4.78187 121.666C-1.09293 115.791 -1.09293 106.282 4.78187 100.406L34.7195 70.4527C40.5943 64.5772 50.1017 64.5772 55.9765 70.4527L75.555 90.0335C77.5091 91.9879 80.6782 91.9879 82.6448 90.0335L124.144 48.5292C126.098 46.5749 126.098 43.4054 124.144 41.4385L115.463 32.7568C113.509 30.8025 110.34 30.8025 108.374 32.7568L99.8683 41.2632C97.9143 43.2175 94.7451 43.2175 92.7785 41.2632L82.1438 30.6271C80.1897 28.6728 80.1897 25.5033 82.1438 23.5364L101.271 4.40662C107.146 -1.46887 116.653 -1.46887 122.528 4.40662L152.478 34.3604C158.353 40.2359 158.353 49.7444 152.478 55.6199L82.6323 125.474C80.6782 127.429 77.5091 127.429 75.5425 125.474L48.8741 98.8029C46.9201 96.8486 43.7509 96.8486 41.7843 98.8029L33.1036 107.485C31.1496 109.439 31.1496 112.608 33.1036 114.575L59.2458 140.721C61.1999 142.675 61.1999 145.844 59.2458 147.811L32.7404 174.32C30.7863 176.274 30.7863 179.444 32.7404 181.411L41.6841 190.355C43.6382 192.31 46.8073 192.31 48.7739 190.355L168.136 70.9789C174.011 65.1034 183.518 65.1034 189.393 70.9789L219.594 101.183C225.469 107.059 225.469 116.567 219.594 122.443L198.537 143.502C196.583 145.456 196.583 148.626 198.537 150.592L218.053 170.111C223.928 175.986 223.928 185.495 218.053 191.37L190.37 219.056C184.495 224.932 174.988 224.932 169.113 219.056L149.597 199.538C147.643 197.584 144.474 197.584 142.508 199.538L99.8057 242.245C97.8516 244.2 97.8516 247.369 99.8057 249.336L108.699 258.231C110.653 260.185 113.823 260.185 115.789 258.231L122.954 251.065C124.908 249.11 128.077 249.11 130.044 251.065L140.679 261.701C142.633 263.655 142.633 266.825 140.679 268.791L122.879 286.593C117.004 292.469 107.497 292.469 101.622 286.593L101.635 286.568Z" fill="#424242"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
3
apps/extension/assets/sidebar-icon.svg
Normal file
3
apps/extension/assets/sidebar-icon.svg
Normal file
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 224 291" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M101.635 286.568L71.4839 256.414C65.6092 250.539 65.6092 241.03 71.4839 235.155L142.52 164.11C144.474 162.156 147.643 162.156 149.61 164.11L176.216 190.719C178.17 192.673 181.339 192.673 183.305 190.719L189.719 184.305C191.673 182.35 191.673 179.181 189.719 177.214L163.113 150.605C161.159 148.651 161.159 145.481 163.113 143.514L191.26 115.365C193.214 113.41 193.214 110.241 191.26 108.274L182.316 99.3291C180.362 97.3748 177.193 97.3748 175.226 99.3291L55.8638 218.706C49.989 224.581 40.4816 224.581 34.6068 218.706L4.4061 188.501C-1.4687 182.626 -1.4687 173.117 4.4061 167.242L23.8342 147.811C25.7883 145.857 25.7883 142.688 23.8342 140.721L4.78187 121.666C-1.09293 115.791 -1.09293 106.282 4.78187 100.406L34.7195 70.4527C40.5943 64.5772 50.1017 64.5772 55.9765 70.4527L75.555 90.0335C77.5091 91.9879 80.6782 91.9879 82.6448 90.0335L124.144 48.5292C126.098 46.5749 126.098 43.4054 124.144 41.4385L115.463 32.7568C113.509 30.8025 110.34 30.8025 108.374 32.7568L99.8683 41.2632C97.9143 43.2175 94.7451 43.2175 92.7785 41.2632L82.1438 30.6271C80.1897 28.6728 80.1897 25.5033 82.1438 23.5364L101.271 4.40662C107.146 -1.46887 116.653 -1.46887 122.528 4.40662L152.478 34.3604C158.353 40.2359 158.353 49.7444 152.478 55.6199L82.6323 125.474C80.6782 127.429 77.5091 127.429 75.5425 125.474L48.8741 98.8029C46.9201 96.8486 43.7509 96.8486 41.7843 98.8029L33.1036 107.485C31.1496 109.439 31.1496 112.608 33.1036 114.575L59.2458 140.721C61.1999 142.675 61.1999 145.844 59.2458 147.811L32.7404 174.32C30.7863 176.274 30.7863 179.444 32.7404 181.411L41.6841 190.355C43.6382 192.31 46.8073 192.31 48.7739 190.355L168.136 70.9789C174.011 65.1034 183.518 65.1034 189.393 70.9789L219.594 101.183C225.469 107.059 225.469 116.567 219.594 122.443L198.537 143.502C196.583 145.456 196.583 148.626 198.537 150.592L218.053 170.111C223.928 175.986 223.928 185.495 218.053 191.37L190.37 219.056C184.495 224.932 174.988 224.932 169.113 219.056L149.597 199.538C147.643 197.584 144.474 197.584 142.508 199.538L99.8057 242.245C97.8516 244.2 97.8516 247.369 99.8057 249.336L108.699 258.231C110.653 260.185 113.823 260.185 115.789 258.231L122.954 251.065C124.908 249.11 128.077 249.11 130.044 251.065L140.679 261.701C142.633 263.655 142.633 266.825 140.679 268.791L122.879 286.593C117.004 292.469 107.497 292.469 101.622 286.593L101.635 286.568Z" fill="currentColor"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.4 KiB |
@@ -109,7 +109,7 @@ The automation ensures these fields stay in sync between `package.json` and `pac
|
||||
```json
|
||||
{
|
||||
"version": "1.0.2", // ✅ AUTO-SYNCED
|
||||
"publisher": "DavidMaliglowka", // ⚠️ MUST MATCH MANUALLY
|
||||
"publisher": "Hamster", // ⚠️ MUST MATCH MANUALLY
|
||||
"displayName": "taskr: Task Master Kanban", // ⚠️ MUST MATCH MANUALLY
|
||||
"description": "...", // ⚠️ MUST MATCH MANUALLY
|
||||
"engines": { "vscode": "^1.93.0" }, // ⚠️ MUST MATCH MANUALLY
|
||||
|
||||
@@ -102,7 +102,7 @@ When updating extension metadata, ensure these fields match between `package.jso
|
||||
```json
|
||||
{
|
||||
"version": "1.0.1", // ⚠️ MUST MATCH
|
||||
"publisher": "DavidMaliglowka", // ⚠️ MUST MATCH
|
||||
"publisher": "Hamster", // ⚠️ MUST MATCH
|
||||
"displayName": "taskr: Task Master Kanban", // ⚠️ MUST MATCH
|
||||
"description": "A visual Kanban board...", // ⚠️ MUST MATCH
|
||||
}
|
||||
|
||||
@@ -118,12 +118,52 @@ async function main() {
|
||||
plugins: [esbuildProblemMatcherPlugin, aliasPlugin]
|
||||
});
|
||||
|
||||
// Build configuration for the React sidebar
|
||||
const sidebarCtx = await esbuild.context({
|
||||
entryPoints: ['src/webview/sidebar.tsx'],
|
||||
bundle: true,
|
||||
format: 'iife',
|
||||
globalName: 'SidebarApp',
|
||||
minify: production,
|
||||
sourcemap: !production ? 'inline' : false,
|
||||
sourcesContent: !production,
|
||||
platform: 'browser',
|
||||
outdir: 'dist',
|
||||
logLevel: 'silent',
|
||||
target: ['es2020'],
|
||||
jsx: 'automatic',
|
||||
jsxImportSource: 'react',
|
||||
external: ['*.css'],
|
||||
alias: {
|
||||
react: path.resolve(__dirname, '../../node_modules/react'),
|
||||
'react-dom': path.resolve(__dirname, '../../node_modules/react-dom')
|
||||
},
|
||||
define: {
|
||||
'process.env.NODE_ENV': production ? '"production"' : '"development"',
|
||||
global: 'globalThis'
|
||||
},
|
||||
...(production && {
|
||||
drop: ['debugger'],
|
||||
pure: ['console.log', 'console.debug', 'console.trace']
|
||||
}),
|
||||
plugins: [esbuildProblemMatcherPlugin, aliasPlugin]
|
||||
});
|
||||
|
||||
if (watch) {
|
||||
await Promise.all([extensionCtx.watch(), webviewCtx.watch()]);
|
||||
await Promise.all([
|
||||
extensionCtx.watch(),
|
||||
webviewCtx.watch(),
|
||||
sidebarCtx.watch()
|
||||
]);
|
||||
} else {
|
||||
await Promise.all([extensionCtx.rebuild(), webviewCtx.rebuild()]);
|
||||
await Promise.all([
|
||||
extensionCtx.rebuild(),
|
||||
webviewCtx.rebuild(),
|
||||
sidebarCtx.rebuild()
|
||||
]);
|
||||
await extensionCtx.dispose();
|
||||
await webviewCtx.dispose();
|
||||
await sidebarCtx.dispose();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "taskr",
|
||||
"name": "extension",
|
||||
"private": true,
|
||||
"displayName": "Task Master Kanban",
|
||||
"displayName": "Task Master",
|
||||
"description": "A visual Kanban board interface for Task Master projects in VS Code",
|
||||
"version": "1.1.0",
|
||||
"publisher": "DavidMaliglowka",
|
||||
"publisher": "Hamster",
|
||||
"icon": "assets/icon.png",
|
||||
"engines": {
|
||||
"vscode": "^1.93.0"
|
||||
@@ -12,24 +12,52 @@
|
||||
"categories": ["AI", "Visualization", "Education", "Other"],
|
||||
"main": "./dist/extension.js",
|
||||
"contributes": {
|
||||
"viewsContainers": {
|
||||
"activitybar": [
|
||||
{
|
||||
"id": "taskmaster",
|
||||
"title": "Task Master",
|
||||
"icon": "assets/sidebar-icon.svg"
|
||||
}
|
||||
]
|
||||
},
|
||||
"views": {
|
||||
"taskmaster": [
|
||||
{
|
||||
"id": "taskmaster.welcome",
|
||||
"name": "Task Master",
|
||||
"type": "webview"
|
||||
}
|
||||
]
|
||||
},
|
||||
"commands": [
|
||||
{
|
||||
"command": "taskr.showKanbanBoard",
|
||||
"title": "Task Master Kanban: Show Board"
|
||||
"command": "tm.showKanbanBoard",
|
||||
"title": "Task Master: Show Board",
|
||||
"icon": "$(checklist)"
|
||||
},
|
||||
{
|
||||
"command": "taskr.checkConnection",
|
||||
"title": "Task Master Kanban: Check Connection"
|
||||
"command": "tm.checkConnection",
|
||||
"title": "Task Master: Check Connection"
|
||||
},
|
||||
{
|
||||
"command": "taskr.reconnect",
|
||||
"title": "Task Master Kanban: Reconnect"
|
||||
"command": "tm.reconnect",
|
||||
"title": "Task Master: Reconnect"
|
||||
},
|
||||
{
|
||||
"command": "taskr.openSettings",
|
||||
"title": "Task Master Kanban: Open Settings"
|
||||
"command": "tm.openSettings",
|
||||
"title": "Task Master: Open Settings"
|
||||
}
|
||||
],
|
||||
"menus": {
|
||||
"view/title": [
|
||||
{
|
||||
"command": "tm.showKanbanBoard",
|
||||
"when": "view == taskmaster.welcome",
|
||||
"group": "navigation"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Task Master Kanban",
|
||||
"properties": {
|
||||
@@ -89,6 +117,13 @@
|
||||
"maximum": 60000,
|
||||
"description": "Health check interval in milliseconds"
|
||||
},
|
||||
"taskmaster.mcp.requestTimeoutMs": {
|
||||
"type": "number",
|
||||
"default": 300000,
|
||||
"minimum": 30000,
|
||||
"maximum": 600000,
|
||||
"description": "MCP request timeout in milliseconds (default: 5 minutes)"
|
||||
},
|
||||
"taskmaster.ui.autoRefresh": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
@@ -192,14 +227,18 @@
|
||||
"vscode:prepublish": "npm run build",
|
||||
"build": "npm run build:js && npm run build:css",
|
||||
"build:js": "node ./esbuild.js --production",
|
||||
"build:css": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/index.css --minify",
|
||||
"build:css": "npm run build:css:main && npm run build:css:sidebar",
|
||||
"build:css:main": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/index.css --minify",
|
||||
"build:css:sidebar": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/sidebar.css --minify",
|
||||
"package": "npm exec node ./package.mjs",
|
||||
"package:direct": "node ./package.mjs",
|
||||
"debug:env": "node ./debug-env.mjs",
|
||||
"compile": "node ./esbuild.js",
|
||||
"watch": "npm run watch:js & npm run watch:css",
|
||||
"watch:js": "node ./esbuild.js --watch",
|
||||
"watch:css": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/index.css --watch",
|
||||
"watch:css": "npm run watch:css:main & npm run watch:css:sidebar",
|
||||
"watch:css:main": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/index.css --watch",
|
||||
"watch:css:sidebar": "npx @tailwindcss/cli -i ./src/webview/index.css -o ./dist/sidebar.css --watch",
|
||||
"test": "vscode-test",
|
||||
"check-types": "tsc --noEmit"
|
||||
},
|
||||
|
||||
@@ -30,7 +30,13 @@ try {
|
||||
fs.ensureDirSync(targetDistDir);
|
||||
|
||||
// Only copy the files we need (exclude .map files)
|
||||
const filesToCopy = ['extension.js', 'index.js', 'index.css'];
|
||||
const filesToCopy = [
|
||||
'extension.js',
|
||||
'index.js',
|
||||
'index.css',
|
||||
'sidebar.js',
|
||||
'sidebar.css'
|
||||
];
|
||||
for (const file of filesToCopy) {
|
||||
const srcFile = path.resolve(distDir, file);
|
||||
const destFile = path.resolve(targetDistDir, file);
|
||||
@@ -127,7 +133,7 @@ try {
|
||||
// Use the synced version for output
|
||||
const finalVersion = devPackage.version;
|
||||
console.log(
|
||||
`\nYour extension will be packaged to: vsix-build/taskr-kanban-${finalVersion}.vsix`
|
||||
`\nYour extension will be packaged to: vsix-build/task-master-${finalVersion}.vsix`
|
||||
);
|
||||
} catch (error) {
|
||||
console.error('\n❌ Packaging failed!');
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"name": "taskr-kanban",
|
||||
"displayName": "taskr: Task Master Kanban",
|
||||
"name": "task-master",
|
||||
"displayName": "Task Master",
|
||||
"description": "A visual Kanban board interface for Task Master projects in VS Code",
|
||||
"version": "1.0.0",
|
||||
"publisher": "DavidMaliglowka",
|
||||
"publisher": "Hamster",
|
||||
"icon": "assets/icon.png",
|
||||
"engines": {
|
||||
"vscode": "^1.93.0"
|
||||
@@ -44,29 +44,29 @@
|
||||
],
|
||||
"repository": "https://github.com/eyaltoledano/claude-task-master",
|
||||
"activationEvents": [
|
||||
"onCommand:taskr.showKanbanBoard",
|
||||
"onCommand:taskr.checkConnection",
|
||||
"onCommand:taskr.reconnect",
|
||||
"onCommand:taskr.openSettings"
|
||||
"onCommand:tm.showKanbanBoard",
|
||||
"onCommand:tm.checkConnection",
|
||||
"onCommand:tm.reconnect",
|
||||
"onCommand:tm.openSettings"
|
||||
],
|
||||
"main": "./dist/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "taskr.showKanbanBoard",
|
||||
"title": "Task Master Kanban: Show Board"
|
||||
"command": "tm.showKanbanBoard",
|
||||
"title": "Task Master: Show Board"
|
||||
},
|
||||
{
|
||||
"command": "taskr.checkConnection",
|
||||
"title": "Task Master Kanban: Check Connection"
|
||||
"command": "tm.checkConnection",
|
||||
"title": "Task Master: Check Connection"
|
||||
},
|
||||
{
|
||||
"command": "taskr.reconnect",
|
||||
"title": "Task Master Kanban: Reconnect"
|
||||
"command": "tm.reconnect",
|
||||
"title": "Task Master: Reconnect"
|
||||
},
|
||||
{
|
||||
"command": "taskr.openSettings",
|
||||
"title": "Task Master Kanban: Open Settings"
|
||||
"command": "tm.openSettings",
|
||||
"title": "Task Master: Open Settings"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
@@ -128,6 +128,13 @@
|
||||
"maximum": 60000,
|
||||
"description": "Health check interval in milliseconds"
|
||||
},
|
||||
"taskmaster.mcp.requestTimeoutMs": {
|
||||
"type": "number",
|
||||
"default": 300000,
|
||||
"minimum": 30000,
|
||||
"maximum": 600000,
|
||||
"description": "MCP request timeout in milliseconds (default: 5 minutes)"
|
||||
},
|
||||
"taskmaster.ui.autoRefresh": {
|
||||
"type": "boolean",
|
||||
"default": true,
|
||||
|
||||
23
apps/extension/src/components/TaskMasterLogo.tsx
Normal file
23
apps/extension/src/components/TaskMasterLogo.tsx
Normal file
@@ -0,0 +1,23 @@
|
||||
import React from 'react';
|
||||
|
||||
interface TaskMasterLogoProps {
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export const TaskMasterLogo: React.FC<TaskMasterLogoProps> = ({
|
||||
className = ''
|
||||
}) => {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
viewBox="0 0 224 291"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M101.635 286.568L71.4839 256.414C65.6092 250.539 65.6092 241.03 71.4839 235.155L142.52 164.11C144.474 162.156 147.643 162.156 149.61 164.11L176.216 190.719C178.17 192.673 181.339 192.673 183.305 190.719L189.719 184.305C191.673 182.35 191.673 179.181 189.719 177.214L163.113 150.605C161.159 148.651 161.159 145.481 163.113 143.514L191.26 115.365C193.214 113.41 193.214 110.241 191.26 108.274L182.316 99.3291C180.362 97.3748 177.193 97.3748 175.226 99.3291L55.8638 218.706C49.989 224.581 40.4816 224.581 34.6068 218.706L4.4061 188.501C-1.4687 182.626 -1.4687 173.117 4.4061 167.242L23.8342 147.811C25.7883 145.857 25.7883 142.688 23.8342 140.721L4.78187 121.666C-1.09293 115.791 -1.09293 106.282 4.78187 100.406L34.7195 70.4527C40.5943 64.5772 50.1017 64.5772 55.9765 70.4527L75.555 90.0335C77.5091 91.9879 80.6782 91.9879 82.6448 90.0335L124.144 48.5292C126.098 46.5749 126.098 43.4054 124.144 41.4385L115.463 32.7568C113.509 30.8025 110.34 30.8025 108.374 32.7568L99.8683 41.2632C97.9143 43.2175 94.7451 43.2175 92.7785 41.2632L82.1438 30.6271C80.1897 28.6728 80.1897 25.5033 82.1438 23.5364L101.271 4.40662C107.146 -1.46887 116.653 -1.46887 122.528 4.40662L152.478 34.3604C158.353 40.2359 158.353 49.7444 152.478 55.6199L82.6323 125.474C80.6782 127.429 77.5091 127.429 75.5425 125.474L48.8741 98.8029C46.9201 96.8486 43.7509 96.8486 41.7843 98.8029L33.1036 107.485C31.1496 109.439 31.1496 112.608 33.1036 114.575L59.2458 140.721C61.1999 142.675 61.1999 145.844 59.2458 147.811L32.7404 174.32C30.7863 176.274 30.7863 179.444 32.7404 181.411L41.6841 190.355C43.6382 192.31 46.8073 192.31 48.7739 190.355L168.136 70.9789C174.011 65.1034 183.518 65.1034 189.393 70.9789L219.594 101.183C225.469 107.059 225.469 116.567 219.594 122.443L198.537 143.502C196.583 145.456 196.583 148.626 198.537 150.592L218.053 170.111C223.928 175.986 223.928 185.495 218.053 191.37L190.37 219.056C184.495 224.932 174.988 224.932 169.113 219.056L149.597 199.538C147.643 197.584 144.474 197.584 142.508 199.538L99.8057 242.245C97.8516 244.2 97.8516 247.369 99.8057 249.336L108.699 258.231C110.653 260.185 113.823 260.185 115.789 258.231L122.954 251.065C124.908 249.11 128.077 249.11 130.044 251.065L140.679 261.701C142.633 263.655 142.633 266.825 140.679 268.791L122.879 286.593C117.004 292.469 107.497 292.469 101.622 286.593L101.635 286.568Z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
||||
@@ -16,6 +16,7 @@ import {
|
||||
createMCPConfigFromSettings
|
||||
} from './utils/mcpClient';
|
||||
import { TaskMasterApi } from './utils/task-master-api';
|
||||
import { SidebarWebviewManager } from './services/sidebar-webview-manager';
|
||||
|
||||
let logger: ExtensionLogger;
|
||||
let mcpClient: MCPClientManager;
|
||||
@@ -25,6 +26,7 @@ let pollingService: PollingService;
|
||||
let webviewManager: WebviewManager;
|
||||
let events: EventEmitter;
|
||||
let configService: ConfigService;
|
||||
let sidebarManager: SidebarWebviewManager;
|
||||
|
||||
export async function activate(context: vscode.ExtensionContext) {
|
||||
try {
|
||||
@@ -57,12 +59,16 @@ export async function activate(context: vscode.ExtensionContext) {
|
||||
webviewManager = new WebviewManager(context, repository, events, logger);
|
||||
webviewManager.setConfigService(configService);
|
||||
|
||||
// Sidebar webview manager
|
||||
sidebarManager = new SidebarWebviewManager(context.extensionUri);
|
||||
|
||||
// Initialize connection
|
||||
await initializeConnection();
|
||||
|
||||
// Set MCP client and API after connection
|
||||
webviewManager.setMCPClient(mcpClient);
|
||||
webviewManager.setApi(api);
|
||||
sidebarManager.setApi(api);
|
||||
|
||||
// Register commands
|
||||
registerCommands(context);
|
||||
@@ -121,6 +127,9 @@ async function initializeConnection() {
|
||||
status: 'Connected'
|
||||
});
|
||||
}
|
||||
if (sidebarManager) {
|
||||
sidebarManager.updateConnectionStatus();
|
||||
}
|
||||
} else {
|
||||
throw new Error(testResult.error || 'Connection test failed');
|
||||
}
|
||||
@@ -134,6 +143,9 @@ async function initializeConnection() {
|
||||
status: 'Disconnected'
|
||||
});
|
||||
}
|
||||
if (sidebarManager) {
|
||||
sidebarManager.updateConnectionStatus();
|
||||
}
|
||||
|
||||
handleConnectionError(error);
|
||||
}
|
||||
@@ -166,27 +178,36 @@ function handleConnectionError(error: any) {
|
||||
function registerCommands(context: vscode.ExtensionContext) {
|
||||
// Main command
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('taskr.showKanbanBoard', async () => {
|
||||
vscode.commands.registerCommand('tm.showKanbanBoard', async () => {
|
||||
await webviewManager.createOrShowPanel();
|
||||
})
|
||||
);
|
||||
|
||||
// Utility commands
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('taskr.refreshTasks', async () => {
|
||||
vscode.commands.registerCommand('tm.refreshTasks', async () => {
|
||||
await repository.refresh();
|
||||
vscode.window.showInformationMessage('Tasks refreshed!');
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('taskr.openSettings', () => {
|
||||
vscode.commands.registerCommand('tm.openSettings', () => {
|
||||
vscode.commands.executeCommand(
|
||||
'workbench.action.openSettings',
|
||||
'@ext:taskr taskmaster'
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
// Register sidebar view provider
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.window.registerWebviewViewProvider(
|
||||
'taskmaster.welcome',
|
||||
sidebarManager
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {
|
||||
|
||||
@@ -112,9 +112,9 @@ export class ErrorHandler {
|
||||
)
|
||||
.then((action) => {
|
||||
if (action === 'Retry') {
|
||||
vscode.commands.executeCommand('taskr.reconnect');
|
||||
vscode.commands.executeCommand('tm.reconnect');
|
||||
} else if (action === 'Settings') {
|
||||
vscode.commands.executeCommand('taskr.openSettings');
|
||||
vscode.commands.executeCommand('tm.openSettings');
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
90
apps/extension/src/services/sidebar-webview-manager.ts
Normal file
90
apps/extension/src/services/sidebar-webview-manager.ts
Normal file
@@ -0,0 +1,90 @@
|
||||
import * as vscode from 'vscode';
|
||||
import type { TaskMasterApi } from '../utils/task-master-api';
|
||||
|
||||
export class SidebarWebviewManager implements vscode.WebviewViewProvider {
|
||||
private webviewView?: vscode.WebviewView;
|
||||
private api?: TaskMasterApi;
|
||||
|
||||
constructor(private readonly extensionUri: vscode.Uri) {}
|
||||
|
||||
setApi(api: TaskMasterApi): void {
|
||||
this.api = api;
|
||||
// Update connection status if webview exists
|
||||
if (this.webviewView) {
|
||||
this.updateConnectionStatus();
|
||||
}
|
||||
}
|
||||
|
||||
resolveWebviewView(
|
||||
webviewView: vscode.WebviewView,
|
||||
context: vscode.WebviewViewResolveContext,
|
||||
token: vscode.CancellationToken
|
||||
): void {
|
||||
this.webviewView = webviewView;
|
||||
|
||||
webviewView.webview.options = {
|
||||
enableScripts: true,
|
||||
localResourceRoots: [
|
||||
vscode.Uri.joinPath(this.extensionUri, 'dist'),
|
||||
vscode.Uri.joinPath(this.extensionUri, 'assets')
|
||||
]
|
||||
};
|
||||
|
||||
webviewView.webview.html = this.getHtmlContent(webviewView.webview);
|
||||
|
||||
// Handle messages from the webview
|
||||
webviewView.webview.onDidReceiveMessage((message) => {
|
||||
if (message.command === 'openBoard') {
|
||||
vscode.commands.executeCommand('tm.showKanbanBoard');
|
||||
}
|
||||
});
|
||||
|
||||
// Update connection status on load
|
||||
this.updateConnectionStatus();
|
||||
}
|
||||
|
||||
updateConnectionStatus(): void {
|
||||
if (!this.webviewView || !this.api) return;
|
||||
|
||||
const status = this.api.getConnectionStatus();
|
||||
this.webviewView.webview.postMessage({
|
||||
type: 'connectionStatus',
|
||||
data: status
|
||||
});
|
||||
}
|
||||
|
||||
private getHtmlContent(webview: vscode.Webview): string {
|
||||
const scriptUri = webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(this.extensionUri, 'dist', 'sidebar.js')
|
||||
);
|
||||
const styleUri = webview.asWebviewUri(
|
||||
vscode.Uri.joinPath(this.extensionUri, 'dist', 'sidebar.css')
|
||||
);
|
||||
const nonce = this.getNonce();
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; img-src ${webview.cspSource} https:; script-src 'nonce-${nonce}'; style-src ${webview.cspSource} 'unsafe-inline';">
|
||||
<link href="${styleUri}" rel="stylesheet">
|
||||
<title>Task Master</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script nonce="${nonce}" src="${scriptUri}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
private getNonce(): string {
|
||||
let text = '';
|
||||
const possible =
|
||||
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
for (let i = 0; i < 32; i++) {
|
||||
text += possible.charAt(Math.floor(Math.random() * possible.length));
|
||||
}
|
||||
return text;
|
||||
}
|
||||
}
|
||||
@@ -22,14 +22,25 @@ export class TaskRepository extends EventEmitter {
|
||||
super();
|
||||
}
|
||||
|
||||
async getAll(): Promise<Task[]> {
|
||||
// Return from cache if valid
|
||||
if (this.cache && Date.now() - this.cacheTimestamp < this.CACHE_DURATION) {
|
||||
return this.cache;
|
||||
async getAll(options?: {
|
||||
tag?: string;
|
||||
withSubtasks?: boolean;
|
||||
}): Promise<Task[]> {
|
||||
// If a tag is specified, always fetch fresh data
|
||||
const shouldUseCache =
|
||||
!options?.tag &&
|
||||
this.cache &&
|
||||
Date.now() - this.cacheTimestamp < this.CACHE_DURATION;
|
||||
|
||||
if (shouldUseCache) {
|
||||
return this.cache || [];
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.api.getTasks({ withSubtasks: true });
|
||||
const result = await this.api.getTasks({
|
||||
withSubtasks: options?.withSubtasks ?? true,
|
||||
tag: options?.tag
|
||||
});
|
||||
|
||||
if (result.success && result.data) {
|
||||
this.cache = result.data;
|
||||
|
||||
@@ -58,6 +58,20 @@ export class WebviewManager {
|
||||
}
|
||||
);
|
||||
|
||||
// Set the icon for the webview tab
|
||||
panel.iconPath = {
|
||||
light: vscode.Uri.joinPath(
|
||||
this.context.extensionUri,
|
||||
'assets',
|
||||
'icon-light.svg'
|
||||
),
|
||||
dark: vscode.Uri.joinPath(
|
||||
this.context.extensionUri,
|
||||
'assets',
|
||||
'icon-dark.svg'
|
||||
)
|
||||
};
|
||||
|
||||
this.panels.add(panel);
|
||||
panel.webview.html = this.getWebviewContent(panel.webview);
|
||||
|
||||
@@ -122,7 +136,11 @@ export class WebviewManager {
|
||||
return;
|
||||
|
||||
case 'getTasks':
|
||||
response = await this.repository.getAll();
|
||||
// Pass options to getAll including tag if specified
|
||||
response = await this.repository.getAll({
|
||||
tag: data?.tag,
|
||||
withSubtasks: data?.withSubtasks ?? true
|
||||
});
|
||||
break;
|
||||
|
||||
case 'updateTaskStatus':
|
||||
@@ -279,9 +297,9 @@ export class WebviewManager {
|
||||
name: data.tagName,
|
||||
projectRoot: vscode.workspace.workspaceFolders?.[0]?.uri.fsPath
|
||||
});
|
||||
// Clear cache and refresh tasks for the new tag
|
||||
// Clear cache and fetch tasks for the new tag
|
||||
await this.repository.refresh();
|
||||
const tasks = await this.repository.getAll();
|
||||
const tasks = await this.repository.getAll({ tag: data.tagName });
|
||||
this.broadcast('tasksUpdated', { tasks, source: 'tag-switch' });
|
||||
response = { success: true };
|
||||
} catch (error) {
|
||||
@@ -293,6 +311,13 @@ export class WebviewManager {
|
||||
}
|
||||
break;
|
||||
|
||||
case 'openExternal':
|
||||
// Open external URL
|
||||
if (message.url) {
|
||||
vscode.env.openExternal(vscode.Uri.parse(message.url));
|
||||
}
|
||||
return;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown message type: ${type}`);
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface MCPConfig {
|
||||
args: string[];
|
||||
cwd?: string;
|
||||
env?: Record<string, string>;
|
||||
timeout?: number;
|
||||
}
|
||||
|
||||
export interface MCPServerStatus {
|
||||
@@ -210,9 +211,6 @@ export class MCPClientManager {
|
||||
};
|
||||
|
||||
logger.log('MCP client connected successfully');
|
||||
vscode.window.showInformationMessage(
|
||||
'Task Master connected successfully'
|
||||
);
|
||||
} catch (error) {
|
||||
logger.error('Failed to connect to MCP server:', error);
|
||||
this.status = {
|
||||
@@ -275,10 +273,21 @@ export class MCPClientManager {
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await this.client.callTool({
|
||||
name: toolName,
|
||||
arguments: arguments_
|
||||
});
|
||||
// Use the configured timeout or default to 5 minutes
|
||||
const timeout = this.config.timeout || 300000; // 5 minutes default
|
||||
|
||||
logger.log(`Calling MCP tool "${toolName}" with timeout: ${timeout}ms`);
|
||||
|
||||
const result = await this.client.callTool(
|
||||
{
|
||||
name: toolName,
|
||||
arguments: arguments_
|
||||
},
|
||||
undefined,
|
||||
{
|
||||
timeout: timeout
|
||||
}
|
||||
);
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
@@ -297,6 +306,7 @@ export class MCPClientManager {
|
||||
return false;
|
||||
}
|
||||
|
||||
// listTools is a simple metadata request, no need for extended timeout
|
||||
const result = await this.client.listTools();
|
||||
logger.log(
|
||||
'Available MCP tools:',
|
||||
@@ -347,6 +357,7 @@ export function createMCPConfigFromSettings(): MCPConfig {
|
||||
vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || process.cwd();
|
||||
const cwd = config.get<string>('mcp.cwd', defaultCwd);
|
||||
const env = config.get<Record<string, string>>('mcp.env');
|
||||
const timeout = config.get<number>('mcp.requestTimeoutMs', 300000);
|
||||
|
||||
logger.log('✅ Using workspace directory:', defaultCwd);
|
||||
|
||||
@@ -377,6 +388,7 @@ export function createMCPConfigFromSettings(): MCPConfig {
|
||||
command,
|
||||
args,
|
||||
cwd: cwd || defaultCwd,
|
||||
env
|
||||
env,
|
||||
timeout
|
||||
};
|
||||
}
|
||||
|
||||
138
apps/extension/src/webview/components/EmptyState.tsx
Normal file
138
apps/extension/src/webview/components/EmptyState.tsx
Normal file
@@ -0,0 +1,138 @@
|
||||
import React from 'react';
|
||||
import { ExternalLink, Terminal, MessageSquare, Plus } from 'lucide-react';
|
||||
import { TaskMasterLogo } from '../../components/TaskMasterLogo';
|
||||
|
||||
interface EmptyStateProps {
|
||||
currentTag: string;
|
||||
}
|
||||
|
||||
export const EmptyState: React.FC<EmptyStateProps> = ({ currentTag }) => {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full overflow-auto">
|
||||
<div className="max-w-2xl mx-auto text-center p-8">
|
||||
{/* Empty state illustration */}
|
||||
<div className="mb-8 max-w-96 mx-auto">
|
||||
<TaskMasterLogo className="w-32 h-32 mx-auto text-vscode-foreground/20" />
|
||||
</div>
|
||||
|
||||
<h2 className="text-2xl font-semibold mb-2 text-vscode-foreground">
|
||||
No tasks in "{currentTag}" tag
|
||||
</h2>
|
||||
<p className="text-vscode-foreground/70 mb-8">
|
||||
Get started by adding tasks to this tag using the commands below
|
||||
</p>
|
||||
|
||||
{/* Command suggestions */}
|
||||
<div className="space-y-4 text-left">
|
||||
<div className="bg-vscode-editor-background/50 border border-vscode-panel-border rounded-lg p-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<Terminal className="w-4 h-4 text-vscode-terminal-ansiGreen" />
|
||||
<h3 className="font-medium">CLI Commands</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<div className="bg-vscode-editor-background rounded p-2 font-mono text-sm">
|
||||
<span className="text-vscode-terminal-ansiYellow">
|
||||
task-master
|
||||
</span>{' '}
|
||||
<span className="text-vscode-terminal-ansiCyan">parse-prd</span>{' '}
|
||||
<span className="text-vscode-foreground/70">
|
||||
<path-to-prd>
|
||||
</span>{' '}
|
||||
<span className="text-vscode-terminal-ansiMagenta">
|
||||
--append
|
||||
</span>
|
||||
<div className="text-xs text-vscode-foreground/50 mt-1">
|
||||
Parse a PRD and append tasks to current tag
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-vscode-editor-background rounded p-2 font-mono text-sm">
|
||||
<span className="text-vscode-terminal-ansiYellow">
|
||||
task-master
|
||||
</span>{' '}
|
||||
<span className="text-vscode-terminal-ansiCyan">add-task</span>{' '}
|
||||
<span className="text-vscode-terminal-ansiMagenta">
|
||||
--prompt
|
||||
</span>{' '}
|
||||
<span className="text-vscode-foreground/70">
|
||||
"Your task description"
|
||||
</span>
|
||||
<div className="text-xs text-vscode-foreground/50 mt-1">
|
||||
Add a single task with AI assistance
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-vscode-editor-background rounded p-2 font-mono text-sm">
|
||||
<span className="text-vscode-terminal-ansiYellow">
|
||||
task-master
|
||||
</span>{' '}
|
||||
<span className="text-vscode-terminal-ansiCyan">add-task</span>{' '}
|
||||
<span className="text-vscode-terminal-ansiMagenta">--help</span>
|
||||
<div className="text-xs text-vscode-foreground/50 mt-1">
|
||||
View all options for adding tasks
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="bg-vscode-editor-background/50 border border-vscode-panel-border rounded-lg p-4">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<MessageSquare className="w-4 h-4 text-vscode-textLink-foreground" />
|
||||
<h3 className="font-medium">MCP Examples</h3>
|
||||
</div>
|
||||
<div className="space-y-2 text-sm">
|
||||
<div className="flex items-start gap-2">
|
||||
<Plus className="w-4 h-4 mt-0.5 text-vscode-foreground/50" />
|
||||
<div>
|
||||
<div className="text-vscode-foreground">
|
||||
"Add a task to tag {currentTag}: Implement user
|
||||
authentication"
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<Plus className="w-4 h-4 mt-0.5 text-vscode-foreground/50" />
|
||||
<div>
|
||||
<div className="text-vscode-foreground">
|
||||
"Parse this PRD and add tasks to {currentTag}: [paste PRD
|
||||
content]"
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-start gap-2">
|
||||
<Plus className="w-4 h-4 mt-0.5 text-vscode-foreground/50" />
|
||||
<div>
|
||||
<div className="text-vscode-foreground">
|
||||
"Create 5 tasks for building a REST API in tag {currentTag}"
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Documentation link */}
|
||||
<div className="flex justify-center pt-4">
|
||||
<a
|
||||
href="https://docs.task-master.dev"
|
||||
className="inline-flex items-center gap-2 text-vscode-textLink-foreground hover:text-vscode-textLink-activeForeground transition-colors"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
// Use VS Code API to open external link
|
||||
if (window.acquireVsCodeApi) {
|
||||
const vscode = window.acquireVsCodeApi();
|
||||
vscode.postMessage({
|
||||
type: 'openExternal',
|
||||
url: 'https://docs.task-master.dev'
|
||||
});
|
||||
}
|
||||
}}
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
<span className="text-sm font-medium">
|
||||
View Task Master Documentation
|
||||
</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
58
apps/extension/src/webview/components/SidebarView.tsx
Normal file
58
apps/extension/src/webview/components/SidebarView.tsx
Normal file
@@ -0,0 +1,58 @@
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { TaskMasterLogo } from '../../components/TaskMasterLogo';
|
||||
|
||||
interface SidebarViewProps {
|
||||
initialConnectionStatus?: boolean;
|
||||
}
|
||||
|
||||
export const SidebarView: React.FC<SidebarViewProps> = ({
|
||||
initialConnectionStatus = false
|
||||
}) => {
|
||||
const [isConnected, setIsConnected] = useState(initialConnectionStatus);
|
||||
const vscode = window.acquireVsCodeApi ? window.acquireVsCodeApi() : null;
|
||||
|
||||
useEffect(() => {
|
||||
const handleMessage = (event: MessageEvent) => {
|
||||
const message = event.data;
|
||||
if (message.type === 'connectionStatus') {
|
||||
setIsConnected(message.data.isConnected);
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('message', handleMessage);
|
||||
return () => window.removeEventListener('message', handleMessage);
|
||||
}, []);
|
||||
|
||||
const handleOpenBoard = () => {
|
||||
vscode?.postMessage({ command: 'openBoard' });
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="h-full flex items-center justify-center p-6">
|
||||
<div className="text-center">
|
||||
<TaskMasterLogo className="w-20 h-20 mx-auto mb-5 opacity-80 text-vscode-foreground" />
|
||||
|
||||
<h2 className="text-xl font-semibold mb-6 text-vscode-foreground">
|
||||
Task Master
|
||||
</h2>
|
||||
|
||||
<button
|
||||
onClick={handleOpenBoard}
|
||||
className="w-full px-4 py-2 bg-vscode-button-background text-vscode-button-foreground rounded hover:bg-vscode-button-hoverBackground transition-colors text-sm font-medium"
|
||||
>
|
||||
Open Kanban Board
|
||||
</button>
|
||||
|
||||
<div
|
||||
className={`mt-5 p-2.5 bg-vscode-editor-background rounded text-xs ${
|
||||
isConnected
|
||||
? 'border-l-[3px] border-vscode-testing-iconPassed'
|
||||
: 'border-l-[3px] border-vscode-testing-iconFailed'
|
||||
}`}
|
||||
>
|
||||
Status: {isConnected ? 'Connected' : 'Disconnected'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -14,6 +14,7 @@ import { TaskCard } from './TaskCard';
|
||||
import { TaskEditModal } from './TaskEditModal';
|
||||
import { PollingStatus } from './PollingStatus';
|
||||
import { TagDropdown } from './TagDropdown';
|
||||
import { EmptyState } from './EmptyState';
|
||||
import { useVSCodeContext } from '../contexts/VSCodeContext';
|
||||
import { kanbanStatuses, HEADER_HEIGHT } from '../constants';
|
||||
import type { TaskMasterTask, TaskUpdates } from '../types';
|
||||
@@ -177,8 +178,11 @@ export const TaskMasterKanban: React.FC = () => {
|
||||
async (tagName: string) => {
|
||||
console.log('Switching to tag:', tagName);
|
||||
await sendMessage({ type: 'switchTag', data: { tagName } });
|
||||
// After switching tags, fetch the new tasks
|
||||
const tasksData = await sendMessage({ type: 'getTasks' });
|
||||
// After switching tags, fetch the new tasks for that specific tag
|
||||
const tasksData = await sendMessage({
|
||||
type: 'getTasks',
|
||||
data: { tag: tagName }
|
||||
});
|
||||
console.log('Received new tasks for tag', tagName, ':', {
|
||||
tasksData,
|
||||
isArray: Array.isArray(tasksData),
|
||||
@@ -276,72 +280,76 @@ export const TaskMasterKanban: React.FC = () => {
|
||||
className="flex-1 px-4 py-4 overflow-hidden"
|
||||
style={{ height: `${kanbanHeight}px` }}
|
||||
>
|
||||
<KanbanProvider
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
className="kanban-container w-full h-full overflow-x-auto overflow-y-hidden"
|
||||
dragOverlay={
|
||||
activeTask ? <TaskCard task={activeTask} dragging /> : null
|
||||
}
|
||||
>
|
||||
<div className="flex gap-4 h-full min-w-fit">
|
||||
{kanbanStatuses.map((status) => {
|
||||
const statusTasks = tasksByStatus[status.id] || [];
|
||||
const hasScrollbar = statusTasks.length > 4;
|
||||
{tasks.length === 0 ? (
|
||||
<EmptyState currentTag={currentTag} />
|
||||
) : (
|
||||
<KanbanProvider
|
||||
onDragStart={handleDragStart}
|
||||
onDragEnd={handleDragEnd}
|
||||
className="kanban-container w-full h-full overflow-x-auto overflow-y-hidden"
|
||||
dragOverlay={
|
||||
activeTask ? <TaskCard task={activeTask} dragging /> : null
|
||||
}
|
||||
>
|
||||
<div className="flex gap-4 h-full min-w-fit">
|
||||
{kanbanStatuses.map((status) => {
|
||||
const statusTasks = tasksByStatus[status.id] || [];
|
||||
const hasScrollbar = statusTasks.length > 4;
|
||||
|
||||
return (
|
||||
<KanbanBoard
|
||||
key={status.id}
|
||||
id={status.id}
|
||||
title={status.name}
|
||||
className={`
|
||||
w-80 flex flex-col
|
||||
border border-vscode-border/30
|
||||
rounded-lg
|
||||
bg-vscode-sidebar-background/50
|
||||
`}
|
||||
>
|
||||
<KanbanHeader
|
||||
name={`${status.name} (${statusTasks.length})`}
|
||||
color={status.color}
|
||||
className="px-3 py-3 text-sm font-medium flex-shrink-0 border-b border-vscode-border/30"
|
||||
/>
|
||||
<div
|
||||
return (
|
||||
<KanbanBoard
|
||||
key={status.id}
|
||||
id={status.id}
|
||||
title={status.name}
|
||||
className={`
|
||||
flex flex-col gap-2
|
||||
overflow-y-auto overflow-x-hidden
|
||||
p-2
|
||||
scrollbar-thin scrollbar-track-transparent
|
||||
${hasScrollbar ? 'pr-1' : ''}
|
||||
w-80 flex flex-col
|
||||
border border-vscode-border/30
|
||||
rounded-lg
|
||||
bg-vscode-sidebar-background/50
|
||||
`}
|
||||
style={{
|
||||
maxHeight: `${kanbanHeight - 80}px`
|
||||
}}
|
||||
>
|
||||
<KanbanCards column={status.id}>
|
||||
{statusTasks.map((task) => (
|
||||
<TaskCard
|
||||
key={task.id}
|
||||
task={task}
|
||||
onViewDetails={(taskId) => {
|
||||
console.log(
|
||||
'🔍 Navigating to task details:',
|
||||
taskId
|
||||
);
|
||||
dispatch({
|
||||
type: 'NAVIGATE_TO_TASK',
|
||||
payload: taskId
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</KanbanCards>
|
||||
</div>
|
||||
</KanbanBoard>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</KanbanProvider>
|
||||
<KanbanHeader
|
||||
name={`${status.name} (${statusTasks.length})`}
|
||||
color={status.color}
|
||||
className="px-3 py-3 text-sm font-medium flex-shrink-0 border-b border-vscode-border/30"
|
||||
/>
|
||||
<div
|
||||
className={`
|
||||
flex flex-col gap-2
|
||||
overflow-y-auto overflow-x-hidden
|
||||
p-2
|
||||
scrollbar-thin scrollbar-track-transparent
|
||||
${hasScrollbar ? 'pr-1' : ''}
|
||||
`}
|
||||
style={{
|
||||
maxHeight: `${kanbanHeight - 80}px`
|
||||
}}
|
||||
>
|
||||
<KanbanCards column={status.id}>
|
||||
{statusTasks.map((task) => (
|
||||
<TaskCard
|
||||
key={task.id}
|
||||
task={task}
|
||||
onViewDetails={(taskId) => {
|
||||
console.log(
|
||||
'🔍 Navigating to task details:',
|
||||
taskId
|
||||
);
|
||||
dispatch({
|
||||
type: 'NAVIGATE_TO_TASK',
|
||||
payload: taskId
|
||||
});
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</KanbanCards>
|
||||
</div>
|
||||
</KanbanBoard>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</KanbanProvider>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
14
apps/extension/src/webview/sidebar.tsx
Normal file
14
apps/extension/src/webview/sidebar.tsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import React from 'react';
|
||||
import ReactDOM from 'react-dom/client';
|
||||
import { SidebarView } from './components/SidebarView';
|
||||
import './index.css';
|
||||
|
||||
// Mount the React app
|
||||
const root = ReactDOM.createRoot(
|
||||
document.getElementById('root') as HTMLElement
|
||||
);
|
||||
root.render(
|
||||
<React.StrictMode>
|
||||
<SidebarView />
|
||||
</React.StrictMode>
|
||||
);
|
||||
Reference in New Issue
Block a user