lean-spec 0.2.5-dev.20251124050427 → 0.2.5-dev.20251124054449
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-YKBICZNR.js → chunk-6FKLWECL.js} +12 -12
- package/dist/chunk-6FKLWECL.js.map +1 -0
- package/dist/cli.js +2 -2
- package/dist/cli.js.map +1 -1
- package/dist/mcp-server.js +1 -1
- package/package.json +1 -1
- package/templates/examples/dark-theme/README.md +55 -0
- package/templates/examples/{email-notifications → dark-theme}/package.json +2 -2
- package/templates/examples/dark-theme/src/public/app.js +92 -0
- package/templates/examples/dark-theme/src/public/index.html +38 -0
- package/templates/examples/dark-theme/src/public/style.css +163 -0
- package/templates/examples/dark-theme/src/server.js +17 -0
- package/dist/chunk-YKBICZNR.js.map +0 -1
- package/templates/examples/email-notifications/README.md +0 -55
- package/templates/examples/email-notifications/src/server.js +0 -57
- package/templates/examples/email-notifications/src/storage.js +0 -40
- package/templates/examples/email-notifications/src/users.js +0 -38
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { analyzeCommand, archiveCommand, backfillCommand, boardCommand, checkCommand, compactCommand, createCommand, depsCommand, examplesCommand, filesCommand, ganttCommand, initCommand, linkCommand, listCommand, mcpCommand, migrateCommand, openCommand, searchCommand, splitCommand, statsCommand, templatesCommand, timelineCommand, tokensCommand, uiCommand, unlinkCommand, updateCommand, validateCommand, viewCommand } from './chunk-
|
|
1
|
+
import { analyzeCommand, archiveCommand, backfillCommand, boardCommand, checkCommand, compactCommand, createCommand, depsCommand, examplesCommand, filesCommand, ganttCommand, initCommand, linkCommand, listCommand, mcpCommand, migrateCommand, openCommand, searchCommand, splitCommand, statsCommand, templatesCommand, timelineCommand, tokensCommand, uiCommand, unlinkCommand, updateCommand, validateCommand, viewCommand } from './chunk-6FKLWECL.js';
|
|
2
2
|
import './chunk-LVD7ZAVZ.js';
|
|
3
3
|
import { Command } from 'commander';
|
|
4
4
|
import { readFileSync } from 'fs';
|
|
@@ -93,7 +93,7 @@ Command Groups:
|
|
|
93
93
|
Examples:
|
|
94
94
|
$ lean-spec init
|
|
95
95
|
$ lean-spec init -y
|
|
96
|
-
$ lean-spec init --example
|
|
96
|
+
$ lean-spec init --example dark-theme
|
|
97
97
|
$ lean-spec init --example dashboard-widgets --name my-demo
|
|
98
98
|
$ lean-spec examples
|
|
99
99
|
$ lean-spec create my-feature --priority high
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/commands/registry.ts","../src/cli.ts"],"names":["program"],"mappings":"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","file":"cli.js","sourcesContent":["import { Command } from 'commander';\nimport {\n analyzeCommand,\n archiveCommand,\n backfillCommand,\n boardCommand,\n checkCommand,\n compactCommand,\n createCommand,\n depsCommand,\n examplesCommand,\n filesCommand,\n ganttCommand,\n initCommand,\n linkCommand,\n listCommand,\n mcpCommand,\n migrateCommand,\n openCommand,\n searchCommand,\n splitCommand,\n statsCommand,\n templatesCommand,\n timelineCommand,\n tokensCommand,\n uiCommand,\n unlinkCommand,\n updateCommand,\n validateCommand,\n viewCommand,\n} from './index.js';\n\n/**\n * Register all commands in alphabetical order\n */\nexport function registerCommands(program: Command): void {\n // Alphabetically sorted command registration\n program.addCommand(analyzeCommand());\n program.addCommand(archiveCommand());\n program.addCommand(backfillCommand());\n program.addCommand(boardCommand());\n program.addCommand(checkCommand());\n program.addCommand(compactCommand());\n program.addCommand(createCommand());\n program.addCommand(depsCommand());\n program.addCommand(examplesCommand());\n program.addCommand(filesCommand());\n program.addCommand(ganttCommand());\n program.addCommand(initCommand());\n program.addCommand(linkCommand());\n program.addCommand(listCommand());\n program.addCommand(mcpCommand());\n program.addCommand(migrateCommand());\n program.addCommand(openCommand());\n program.addCommand(searchCommand());\n program.addCommand(splitCommand());\n program.addCommand(statsCommand());\n program.addCommand(templatesCommand());\n program.addCommand(timelineCommand());\n program.addCommand(tokensCommand());\n program.addCommand(uiCommand());\n program.addCommand(unlinkCommand());\n program.addCommand(updateCommand());\n program.addCommand(validateCommand());\n program.addCommand(viewCommand());\n}\n","import { Command } from 'commander';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { registerCommands } from './commands/registry.js';\n\n// Get version from package.json\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, '../package.json'), 'utf-8')\n);\n\nconst program = new Command();\n\nprogram\n .name('lean-spec')\n .description('Manage LeanSpec documents')\n .version(packageJson.version);\n\n// Add custom help text with grouped commands\nprogram.addHelpText('after', `\nCommand Groups:\n\n Core Workflow:\n archive <spec> Move spec to archived/\n backfill [specs...] Backfill timestamps from git history\n create <name> Create new spec\n examples List example projects for tutorials\n init Initialize LeanSpec in current directory\n link <spec> Add relationships between specs\n migrate <input-path> Migrate specs from other SDD tools\n unlink <spec> Remove relationships between specs\n update <spec> Update spec metadata\n \n Discovery & Search:\n files <spec> List files in a spec\n list List all specs\n open <spec> Open spec in editor\n search <query> Full-text search with metadata filters\n view <spec> View spec content\n \n Project Analytics:\n board Show Kanban-style board view\n deps <spec> Show dependency graph for a spec\n gantt Show timeline with dependencies\n stats Show aggregate statistics\n timeline Show creation/completion over time\n \n Quality & Optimization:\n analyze <spec> Analyze spec complexity and structure\n check Check for sequence conflicts\n tokens [spec] Count tokens for LLM context management\n validate [specs...] Validate specs for quality issues\n \n Advanced Editing:\n compact <spec> Remove specified line ranges from spec\n split <spec> Split spec into multiple files\n \n Configuration:\n templates Manage spec templates\n \n Integration:\n mcp Start MCP server for AI assistants\n ui Start local web UI for spec management\n\nExamples:\n $ lean-spec init\n $ lean-spec init -y\n $ lean-spec init --example
|
|
1
|
+
{"version":3,"sources":["../src/commands/registry.ts","../src/cli.ts"],"names":["program"],"mappings":"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","file":"cli.js","sourcesContent":["import { Command } from 'commander';\nimport {\n analyzeCommand,\n archiveCommand,\n backfillCommand,\n boardCommand,\n checkCommand,\n compactCommand,\n createCommand,\n depsCommand,\n examplesCommand,\n filesCommand,\n ganttCommand,\n initCommand,\n linkCommand,\n listCommand,\n mcpCommand,\n migrateCommand,\n openCommand,\n searchCommand,\n splitCommand,\n statsCommand,\n templatesCommand,\n timelineCommand,\n tokensCommand,\n uiCommand,\n unlinkCommand,\n updateCommand,\n validateCommand,\n viewCommand,\n} from './index.js';\n\n/**\n * Register all commands in alphabetical order\n */\nexport function registerCommands(program: Command): void {\n // Alphabetically sorted command registration\n program.addCommand(analyzeCommand());\n program.addCommand(archiveCommand());\n program.addCommand(backfillCommand());\n program.addCommand(boardCommand());\n program.addCommand(checkCommand());\n program.addCommand(compactCommand());\n program.addCommand(createCommand());\n program.addCommand(depsCommand());\n program.addCommand(examplesCommand());\n program.addCommand(filesCommand());\n program.addCommand(ganttCommand());\n program.addCommand(initCommand());\n program.addCommand(linkCommand());\n program.addCommand(listCommand());\n program.addCommand(mcpCommand());\n program.addCommand(migrateCommand());\n program.addCommand(openCommand());\n program.addCommand(searchCommand());\n program.addCommand(splitCommand());\n program.addCommand(statsCommand());\n program.addCommand(templatesCommand());\n program.addCommand(timelineCommand());\n program.addCommand(tokensCommand());\n program.addCommand(uiCommand());\n program.addCommand(unlinkCommand());\n program.addCommand(updateCommand());\n program.addCommand(validateCommand());\n program.addCommand(viewCommand());\n}\n","import { Command } from 'commander';\nimport { readFileSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { registerCommands } from './commands/registry.js';\n\n// Get version from package.json\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst packageJson = JSON.parse(\n readFileSync(join(__dirname, '../package.json'), 'utf-8')\n);\n\nconst program = new Command();\n\nprogram\n .name('lean-spec')\n .description('Manage LeanSpec documents')\n .version(packageJson.version);\n\n// Add custom help text with grouped commands\nprogram.addHelpText('after', `\nCommand Groups:\n\n Core Workflow:\n archive <spec> Move spec to archived/\n backfill [specs...] Backfill timestamps from git history\n create <name> Create new spec\n examples List example projects for tutorials\n init Initialize LeanSpec in current directory\n link <spec> Add relationships between specs\n migrate <input-path> Migrate specs from other SDD tools\n unlink <spec> Remove relationships between specs\n update <spec> Update spec metadata\n \n Discovery & Search:\n files <spec> List files in a spec\n list List all specs\n open <spec> Open spec in editor\n search <query> Full-text search with metadata filters\n view <spec> View spec content\n \n Project Analytics:\n board Show Kanban-style board view\n deps <spec> Show dependency graph for a spec\n gantt Show timeline with dependencies\n stats Show aggregate statistics\n timeline Show creation/completion over time\n \n Quality & Optimization:\n analyze <spec> Analyze spec complexity and structure\n check Check for sequence conflicts\n tokens [spec] Count tokens for LLM context management\n validate [specs...] Validate specs for quality issues\n \n Advanced Editing:\n compact <spec> Remove specified line ranges from spec\n split <spec> Split spec into multiple files\n \n Configuration:\n templates Manage spec templates\n \n Integration:\n mcp Start MCP server for AI assistants\n ui Start local web UI for spec management\n\nExamples:\n $ lean-spec init\n $ lean-spec init -y\n $ lean-spec init --example dark-theme\n $ lean-spec init --example dashboard-widgets --name my-demo\n $ lean-spec examples\n $ lean-spec create my-feature --priority high\n $ lean-spec list --status in-progress\n $ lean-spec view 042\n $ lean-spec link 085 --depends-on 042,035\n $ lean-spec link 085 --related 082\n $ lean-spec unlink 085 --depends-on 042\n $ lean-spec deps 085\n $ lean-spec backfill --dry-run\n $ lean-spec migrate ./docs/adr\n $ lean-spec migrate ./docs/rfcs --with copilot\n $ lean-spec board --tag backend\n $ lean-spec search \"authentication\"\n $ lean-spec validate\n $ lean-spec tokens 059\n $ lean-spec analyze 045 --json\n $ lean-spec split 045 --output README.md:1-150 --output DESIGN.md:151-end\n $ lean-spec ui\n $ lean-spec ui --port 3001 --no-open\n $ lean-spec ui --specs ./docs/specs --dry-run\n`);\n\n// Register all commands (alphabetically ordered)\nregisterCommands(program);\n\n// Parse and execute\nprogram.parse();\n"]}
|
package/dist/mcp-server.js
CHANGED
package/package.json
CHANGED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Dark Theme Demo
|
|
2
|
+
|
|
3
|
+
> **Tutorial**: [Adding Dark Theme Support](https://leanspec.dev/docs/tutorials/adding-dark-theme)
|
|
4
|
+
|
|
5
|
+
## Scenario
|
|
6
|
+
|
|
7
|
+
You're building a personal task manager web app. The app works great, but users keep requesting a dark mode option for late-night productivity sessions. Currently, the app only has a bright light theme that can strain eyes in low-light environments.
|
|
8
|
+
|
|
9
|
+
## What's Here
|
|
10
|
+
|
|
11
|
+
A minimal single-page task manager with:
|
|
12
|
+
- Task creation and listing interface
|
|
13
|
+
- Simple Express.js server serving static files
|
|
14
|
+
- Clean light theme CSS
|
|
15
|
+
- No dark mode support (yet!)
|
|
16
|
+
|
|
17
|
+
**Files:**
|
|
18
|
+
- `src/server.js` - Express server for static files
|
|
19
|
+
- `src/public/index.html` - Task manager interface
|
|
20
|
+
- `src/public/style.css` - Current light theme styles
|
|
21
|
+
- `src/public/app.js` - Task management logic
|
|
22
|
+
|
|
23
|
+
## Getting Started
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Install dependencies
|
|
27
|
+
npm install
|
|
28
|
+
|
|
29
|
+
# Start the server
|
|
30
|
+
npm start
|
|
31
|
+
|
|
32
|
+
# Open in your browser:
|
|
33
|
+
# http://localhost:3000
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Your Mission
|
|
37
|
+
|
|
38
|
+
Add dark theme support with automatic switching based on system preferences. Follow the tutorial and ask your AI assistant:
|
|
39
|
+
|
|
40
|
+
> "Help me add dark theme support to this app using LeanSpec. It should automatically switch based on the user's system theme preference."
|
|
41
|
+
|
|
42
|
+
The AI will guide you through:
|
|
43
|
+
1. Creating a spec for dark theme support
|
|
44
|
+
2. Designing the CSS for dark mode
|
|
45
|
+
3. Implementing system preference detection
|
|
46
|
+
4. Testing the theme switching
|
|
47
|
+
|
|
48
|
+
## Current Limitations
|
|
49
|
+
|
|
50
|
+
- Only light theme available
|
|
51
|
+
- No manual theme toggle
|
|
52
|
+
- Colors may not be fully accessible
|
|
53
|
+
- No theme persistence
|
|
54
|
+
|
|
55
|
+
These are perfect opportunities to practice spec-driven development!
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
|
-
"name": "
|
|
2
|
+
"name": "dark-theme-demo",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"description": "Simple
|
|
4
|
+
"description": "Simple web app for LeanSpec Dark Theme Tutorial",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"start": "node src/server.js",
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
// Task Manager - Simple task tracking
|
|
2
|
+
let tasks = [];
|
|
3
|
+
let taskIdCounter = 1;
|
|
4
|
+
|
|
5
|
+
const taskInput = document.getElementById('taskInput');
|
|
6
|
+
const addButton = document.getElementById('addButton');
|
|
7
|
+
const taskList = document.getElementById('taskList');
|
|
8
|
+
const taskCount = document.getElementById('taskCount');
|
|
9
|
+
|
|
10
|
+
// Add task on button click
|
|
11
|
+
addButton.addEventListener('click', addTask);
|
|
12
|
+
|
|
13
|
+
// Add task on Enter key
|
|
14
|
+
taskInput.addEventListener('keypress', (e) => {
|
|
15
|
+
if (e.key === 'Enter') {
|
|
16
|
+
addTask();
|
|
17
|
+
}
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
function addTask() {
|
|
21
|
+
const text = taskInput.value.trim();
|
|
22
|
+
|
|
23
|
+
if (!text) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const task = {
|
|
28
|
+
id: taskIdCounter++,
|
|
29
|
+
text: text,
|
|
30
|
+
completed: false
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
tasks.push(task);
|
|
34
|
+
taskInput.value = '';
|
|
35
|
+
|
|
36
|
+
renderTasks();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function toggleTask(id) {
|
|
40
|
+
const task = tasks.find(t => t.id === id);
|
|
41
|
+
if (task) {
|
|
42
|
+
task.completed = !task.completed;
|
|
43
|
+
renderTasks();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function deleteTask(id) {
|
|
48
|
+
tasks = tasks.filter(t => t.id !== id);
|
|
49
|
+
renderTasks();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function renderTasks() {
|
|
53
|
+
// Clear list
|
|
54
|
+
taskList.innerHTML = '';
|
|
55
|
+
|
|
56
|
+
// Show empty state if no tasks
|
|
57
|
+
if (tasks.length === 0) {
|
|
58
|
+
taskList.innerHTML = '<div class="empty-state">No tasks yet. Add one above to get started!</div>';
|
|
59
|
+
taskCount.textContent = '0 tasks';
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Render each task
|
|
64
|
+
tasks.forEach(task => {
|
|
65
|
+
const li = document.createElement('li');
|
|
66
|
+
li.className = `task-item ${task.completed ? 'completed' : ''}`;
|
|
67
|
+
|
|
68
|
+
li.innerHTML = `
|
|
69
|
+
<div class="task-content">
|
|
70
|
+
<input type="checkbox" class="task-checkbox" ${task.completed ? 'checked' : ''}
|
|
71
|
+
onchange="toggleTask(${task.id})">
|
|
72
|
+
<span class="task-text">${escapeHtml(task.text)}</span>
|
|
73
|
+
</div>
|
|
74
|
+
<button class="delete-button" onclick="deleteTask(${task.id})">Delete</button>
|
|
75
|
+
`;
|
|
76
|
+
|
|
77
|
+
taskList.appendChild(li);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// Update count
|
|
81
|
+
const remaining = tasks.filter(t => !t.completed).length;
|
|
82
|
+
taskCount.textContent = `${remaining} task${remaining !== 1 ? 's' : ''} remaining`;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function escapeHtml(text) {
|
|
86
|
+
const div = document.createElement('div');
|
|
87
|
+
div.textContent = text;
|
|
88
|
+
return div.innerHTML;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// Initial render
|
|
92
|
+
renderTasks();
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>Task Manager</title>
|
|
7
|
+
<link rel="stylesheet" href="style.css">
|
|
8
|
+
</head>
|
|
9
|
+
<body>
|
|
10
|
+
<div class="container">
|
|
11
|
+
<header>
|
|
12
|
+
<h1>My Tasks</h1>
|
|
13
|
+
<p class="subtitle">Stay organized, stay productive</p>
|
|
14
|
+
</header>
|
|
15
|
+
|
|
16
|
+
<main>
|
|
17
|
+
<div class="task-input">
|
|
18
|
+
<input type="text" id="taskInput" placeholder="What needs to be done?" />
|
|
19
|
+
<button id="addButton">Add Task</button>
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
<div class="task-list">
|
|
23
|
+
<ul id="taskList"></ul>
|
|
24
|
+
</div>
|
|
25
|
+
|
|
26
|
+
<div class="stats">
|
|
27
|
+
<span id="taskCount">0 tasks</span>
|
|
28
|
+
</div>
|
|
29
|
+
</main>
|
|
30
|
+
|
|
31
|
+
<footer>
|
|
32
|
+
<p>Built with ❤️ • Try adding dark theme support!</p>
|
|
33
|
+
</footer>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<script src="app.js"></script>
|
|
37
|
+
</body>
|
|
38
|
+
</html>
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
* {
|
|
2
|
+
margin: 0;
|
|
3
|
+
padding: 0;
|
|
4
|
+
box-sizing: border-box;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
body {
|
|
8
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
|
|
9
|
+
background-color: #f5f5f5;
|
|
10
|
+
color: #333;
|
|
11
|
+
line-height: 1.6;
|
|
12
|
+
padding: 20px;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.container {
|
|
16
|
+
max-width: 600px;
|
|
17
|
+
margin: 0 auto;
|
|
18
|
+
background: white;
|
|
19
|
+
border-radius: 12px;
|
|
20
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|
21
|
+
padding: 30px;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
header {
|
|
25
|
+
text-align: center;
|
|
26
|
+
margin-bottom: 30px;
|
|
27
|
+
padding-bottom: 20px;
|
|
28
|
+
border-bottom: 2px solid #e0e0e0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
h1 {
|
|
32
|
+
font-size: 2em;
|
|
33
|
+
color: #2c3e50;
|
|
34
|
+
margin-bottom: 8px;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.subtitle {
|
|
38
|
+
color: #7f8c8d;
|
|
39
|
+
font-size: 0.95em;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
.task-input {
|
|
43
|
+
display: flex;
|
|
44
|
+
gap: 10px;
|
|
45
|
+
margin-bottom: 25px;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
#taskInput {
|
|
49
|
+
flex: 1;
|
|
50
|
+
padding: 12px 16px;
|
|
51
|
+
border: 2px solid #e0e0e0;
|
|
52
|
+
border-radius: 8px;
|
|
53
|
+
font-size: 1em;
|
|
54
|
+
transition: border-color 0.2s;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
#taskInput:focus {
|
|
58
|
+
outline: none;
|
|
59
|
+
border-color: #3498db;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
#addButton {
|
|
63
|
+
padding: 12px 24px;
|
|
64
|
+
background: #3498db;
|
|
65
|
+
color: white;
|
|
66
|
+
border: none;
|
|
67
|
+
border-radius: 8px;
|
|
68
|
+
font-size: 1em;
|
|
69
|
+
cursor: pointer;
|
|
70
|
+
transition: background 0.2s;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#addButton:hover {
|
|
74
|
+
background: #2980b9;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.task-list {
|
|
78
|
+
min-height: 200px;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
#taskList {
|
|
82
|
+
list-style: none;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
.task-item {
|
|
86
|
+
padding: 15px;
|
|
87
|
+
margin-bottom: 10px;
|
|
88
|
+
background: #f8f9fa;
|
|
89
|
+
border-radius: 8px;
|
|
90
|
+
display: flex;
|
|
91
|
+
align-items: center;
|
|
92
|
+
justify-content: space-between;
|
|
93
|
+
transition: background 0.2s;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.task-item:hover {
|
|
97
|
+
background: #e9ecef;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.task-item.completed {
|
|
101
|
+
opacity: 0.6;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
.task-item.completed .task-text {
|
|
105
|
+
text-decoration: line-through;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.task-content {
|
|
109
|
+
display: flex;
|
|
110
|
+
align-items: center;
|
|
111
|
+
gap: 12px;
|
|
112
|
+
flex: 1;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
.task-checkbox {
|
|
116
|
+
width: 20px;
|
|
117
|
+
height: 20px;
|
|
118
|
+
cursor: pointer;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.task-text {
|
|
122
|
+
font-size: 1em;
|
|
123
|
+
color: #2c3e50;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
.delete-button {
|
|
127
|
+
padding: 6px 12px;
|
|
128
|
+
background: #e74c3c;
|
|
129
|
+
color: white;
|
|
130
|
+
border: none;
|
|
131
|
+
border-radius: 6px;
|
|
132
|
+
font-size: 0.85em;
|
|
133
|
+
cursor: pointer;
|
|
134
|
+
transition: background 0.2s;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
.delete-button:hover {
|
|
138
|
+
background: #c0392b;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.stats {
|
|
142
|
+
margin-top: 20px;
|
|
143
|
+
padding-top: 20px;
|
|
144
|
+
border-top: 2px solid #e0e0e0;
|
|
145
|
+
text-align: center;
|
|
146
|
+
color: #7f8c8d;
|
|
147
|
+
font-size: 0.9em;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
footer {
|
|
151
|
+
margin-top: 30px;
|
|
152
|
+
padding-top: 20px;
|
|
153
|
+
border-top: 2px solid #e0e0e0;
|
|
154
|
+
text-align: center;
|
|
155
|
+
color: #95a5a6;
|
|
156
|
+
font-size: 0.85em;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
.empty-state {
|
|
160
|
+
text-align: center;
|
|
161
|
+
padding: 40px 20px;
|
|
162
|
+
color: #95a5a6;
|
|
163
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import express from 'express';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { fileURLToPath } from 'url';
|
|
4
|
+
|
|
5
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
6
|
+
const __dirname = path.dirname(__filename);
|
|
7
|
+
|
|
8
|
+
const app = express();
|
|
9
|
+
const PORT = 3000;
|
|
10
|
+
|
|
11
|
+
// Serve static files from public directory
|
|
12
|
+
app.use(express.static(path.join(__dirname, 'public')));
|
|
13
|
+
|
|
14
|
+
app.listen(PORT, () => {
|
|
15
|
+
console.log(`✓ Task Manager running at http://localhost:${PORT}`);
|
|
16
|
+
console.log(`✓ Open your browser and try adding some tasks!`);
|
|
17
|
+
});
|