floq 0.0.1
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/README.ja.md +133 -0
- package/README.md +133 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +124 -0
- package/dist/commands/add.d.ts +6 -0
- package/dist/commands/add.js +36 -0
- package/dist/commands/config.d.ts +6 -0
- package/dist/commands/config.js +73 -0
- package/dist/commands/done.d.ts +1 -0
- package/dist/commands/done.js +37 -0
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +96 -0
- package/dist/commands/move.d.ts +1 -0
- package/dist/commands/move.js +50 -0
- package/dist/commands/project.d.ts +8 -0
- package/dist/commands/project.js +160 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +64 -0
- package/dist/db/index.d.ts +6 -0
- package/dist/db/index.js +72 -0
- package/dist/db/schema.d.ts +192 -0
- package/dist/db/schema.js +13 -0
- package/dist/i18n/en.d.ts +160 -0
- package/dist/i18n/en.js +108 -0
- package/dist/i18n/index.d.ts +7 -0
- package/dist/i18n/index.js +15 -0
- package/dist/i18n/ja.d.ts +2 -0
- package/dist/i18n/ja.js +108 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3 -0
- package/dist/paths.d.ts +6 -0
- package/dist/paths.js +22 -0
- package/dist/ui/App.d.ts +2 -0
- package/dist/ui/App.js +472 -0
- package/dist/ui/SplashScreen.d.ts +7 -0
- package/dist/ui/SplashScreen.js +61 -0
- package/dist/ui/TaskList.d.ts +10 -0
- package/dist/ui/TaskList.js +9 -0
- package/dist/ui/ThemeSelector.d.ts +7 -0
- package/dist/ui/ThemeSelector.js +17 -0
- package/dist/ui/components/FullScreenBox.d.ts +8 -0
- package/dist/ui/components/FullScreenBox.js +10 -0
- package/dist/ui/components/FunctionKeyBar.d.ts +10 -0
- package/dist/ui/components/FunctionKeyBar.js +19 -0
- package/dist/ui/components/HelpModal.d.ts +6 -0
- package/dist/ui/components/HelpModal.js +11 -0
- package/dist/ui/components/StatusBadge.d.ts +7 -0
- package/dist/ui/components/StatusBadge.js +16 -0
- package/dist/ui/components/TaskItem.d.ts +9 -0
- package/dist/ui/components/TaskItem.js +10 -0
- package/dist/ui/theme/index.d.ts +10 -0
- package/dist/ui/theme/index.js +11 -0
- package/dist/ui/theme/themes.d.ts +9 -0
- package/dist/ui/theme/themes.js +163 -0
- package/dist/ui/theme/types.d.ts +43 -0
- package/dist/ui/theme/types.js +1 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +7 -0
- package/package.json +57 -0
package/README.ja.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Floq
|
|
2
|
+
|
|
3
|
+
[English](./README.md)
|
|
4
|
+
|
|
5
|
+
MS-DOSスタイルのテーマを備えたターミナルベースのGTD(Getting Things Done)タスクマネージャー。
|
|
6
|
+
|
|
7
|
+
## 特徴
|
|
8
|
+
|
|
9
|
+
- **TUIインターフェース**: Ink(CLI用React)で構築されたインタラクティブなターミナルUI
|
|
10
|
+
- **GTDワークフロー**: Inbox、Next Actions、Waiting For、Someday/Maybe
|
|
11
|
+
- **プロジェクト**: タスクをプロジェクトに整理
|
|
12
|
+
- **テーマ**: MS-DOSノスタルジックスタイルを含む複数テーマ
|
|
13
|
+
- **多言語対応**: 英語・日本語サポート
|
|
14
|
+
- **Vimスタイルナビゲーション**: hjklまたは矢印キーで操作
|
|
15
|
+
|
|
16
|
+
## インストール
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
npm run build
|
|
21
|
+
npm link
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## 使い方
|
|
25
|
+
|
|
26
|
+
### TUIモード
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
floq
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### キーボードショートカット
|
|
33
|
+
|
|
34
|
+
| キー | アクション |
|
|
35
|
+
|------|-----------|
|
|
36
|
+
| `1-5` | タブ切り替え |
|
|
37
|
+
| `h/l` または `←/→` | 前/次のタブ |
|
|
38
|
+
| `j/k` または `↑/↓` | タスク選択 |
|
|
39
|
+
| `a` | タスク追加 |
|
|
40
|
+
| `d` | 完了にする |
|
|
41
|
+
| `n` | Next Actionsに移動 |
|
|
42
|
+
| `s` | Someday/Maybeに移動 |
|
|
43
|
+
| `i` | Inboxに移動 |
|
|
44
|
+
| `p` | プロジェクトに変換 |
|
|
45
|
+
| `P` | プロジェクトに紐付け |
|
|
46
|
+
| `Enter` | プロジェクトを開く(Projectsタブ) |
|
|
47
|
+
| `Esc/b` | プロジェクトから戻る |
|
|
48
|
+
| `r` | 更新 |
|
|
49
|
+
| `?` | ヘルプ |
|
|
50
|
+
| `q` | 終了 |
|
|
51
|
+
|
|
52
|
+
### CLIコマンド
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# タスク追加
|
|
56
|
+
floq add "タスクのタイトル"
|
|
57
|
+
floq add "タスクのタイトル" -p "プロジェクト名"
|
|
58
|
+
|
|
59
|
+
# タスク一覧
|
|
60
|
+
floq list # 未完了タスク全て
|
|
61
|
+
floq list inbox # Inboxのみ
|
|
62
|
+
floq list next # Next actions
|
|
63
|
+
floq list waiting # Waiting for
|
|
64
|
+
floq list someday # Someday/maybe
|
|
65
|
+
floq list projects # プロジェクト
|
|
66
|
+
|
|
67
|
+
# タスク移動
|
|
68
|
+
floq move <id> next
|
|
69
|
+
floq move <id> waiting "担当者名"
|
|
70
|
+
floq move <id> someday
|
|
71
|
+
|
|
72
|
+
# タスク完了
|
|
73
|
+
floq done <id>
|
|
74
|
+
|
|
75
|
+
# プロジェクト
|
|
76
|
+
floq project add "プロジェクト名"
|
|
77
|
+
floq project list
|
|
78
|
+
floq project show <id>
|
|
79
|
+
floq project complete <id>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## 設定
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# 設定表示
|
|
86
|
+
floq config show
|
|
87
|
+
|
|
88
|
+
# 言語設定
|
|
89
|
+
floq config lang en # 英語
|
|
90
|
+
floq config lang ja # 日本語
|
|
91
|
+
|
|
92
|
+
# テーマ設定
|
|
93
|
+
floq config theme modern # デフォルト
|
|
94
|
+
floq config theme norton-commander # MS-DOSスタイル
|
|
95
|
+
floq config theme dos-prompt # 緑テキスト
|
|
96
|
+
floq config theme turbo-pascal # IDEスタイル
|
|
97
|
+
|
|
98
|
+
# データベースパス設定
|
|
99
|
+
floq config db /path/to/custom.db
|
|
100
|
+
floq config db # デフォルトに戻す
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## テーマ
|
|
104
|
+
|
|
105
|
+
### modern(デフォルト)
|
|
106
|
+
シンプルでクリーンなスタイル。シングルボーダー。
|
|
107
|
+
|
|
108
|
+
### norton-commander
|
|
109
|
+
- ダブルラインボーダー(╔═╗║╚═╝)
|
|
110
|
+
- 大文字ヘッダー
|
|
111
|
+
- 画面下部にファンクションキーバー
|
|
112
|
+
- シアン/黄色のカラースキーム
|
|
113
|
+
|
|
114
|
+
### dos-prompt
|
|
115
|
+
- シングルラインボーダー
|
|
116
|
+
- 緑テキスト(CRTモニター風)
|
|
117
|
+
- シンプルな `>` 選択インジケータ
|
|
118
|
+
|
|
119
|
+
### turbo-pascal
|
|
120
|
+
- ダブルラインボーダー
|
|
121
|
+
- 黄色のアクセント
|
|
122
|
+
- IDE風の外観
|
|
123
|
+
|
|
124
|
+
> **注意**: 背景色はターミナルの設定に依存します。完全なDOS体験のためには、ターミナルの背景色を青(#0000AA)に設定してください。
|
|
125
|
+
|
|
126
|
+
## データ保存場所
|
|
127
|
+
|
|
128
|
+
- 設定: `~/.config/gtd-cli/config.json`
|
|
129
|
+
- データベース: `~/.local/share/gtd-cli/gtd.db`
|
|
130
|
+
|
|
131
|
+
## ライセンス
|
|
132
|
+
|
|
133
|
+
MIT
|
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# Floq
|
|
2
|
+
|
|
3
|
+
[日本語](./README.ja.md)
|
|
4
|
+
|
|
5
|
+
A terminal-based GTD (Getting Things Done) task manager with MS-DOS style themes.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **TUI Interface**: Interactive terminal UI built with Ink (React for CLI)
|
|
10
|
+
- **GTD Workflow**: Inbox, Next Actions, Waiting For, Someday/Maybe
|
|
11
|
+
- **Projects**: Organize tasks into projects
|
|
12
|
+
- **Themes**: Multiple themes including MS-DOS nostalgic styles
|
|
13
|
+
- **i18n**: English and Japanese support
|
|
14
|
+
- **Vim-style Navigation**: Use hjkl or arrow keys
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
npm install
|
|
20
|
+
npm run build
|
|
21
|
+
npm link
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Usage
|
|
25
|
+
|
|
26
|
+
### TUI Mode
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
floq
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Keyboard Shortcuts
|
|
33
|
+
|
|
34
|
+
| Key | Action |
|
|
35
|
+
|-----|--------|
|
|
36
|
+
| `1-5` | Switch tabs |
|
|
37
|
+
| `h/l` or `←/→` | Previous/Next tab |
|
|
38
|
+
| `j/k` or `↑/↓` | Navigate tasks |
|
|
39
|
+
| `a` | Add task |
|
|
40
|
+
| `d` | Mark as done |
|
|
41
|
+
| `n` | Move to Next Actions |
|
|
42
|
+
| `s` | Move to Someday/Maybe |
|
|
43
|
+
| `i` | Move to Inbox |
|
|
44
|
+
| `p` | Convert to project |
|
|
45
|
+
| `P` | Link to project |
|
|
46
|
+
| `Enter` | Open project (on Projects tab) |
|
|
47
|
+
| `Esc/b` | Back from project |
|
|
48
|
+
| `r` | Refresh |
|
|
49
|
+
| `?` | Help |
|
|
50
|
+
| `q` | Quit |
|
|
51
|
+
|
|
52
|
+
### CLI Commands
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
# Add task
|
|
56
|
+
floq add "Task title"
|
|
57
|
+
floq add "Task title" -p "Project name"
|
|
58
|
+
|
|
59
|
+
# List tasks
|
|
60
|
+
floq list # All non-done tasks
|
|
61
|
+
floq list inbox # Inbox only
|
|
62
|
+
floq list next # Next actions
|
|
63
|
+
floq list waiting # Waiting for
|
|
64
|
+
floq list someday # Someday/maybe
|
|
65
|
+
floq list projects # Projects
|
|
66
|
+
|
|
67
|
+
# Move task
|
|
68
|
+
floq move <id> next
|
|
69
|
+
floq move <id> waiting "Person name"
|
|
70
|
+
floq move <id> someday
|
|
71
|
+
|
|
72
|
+
# Complete task
|
|
73
|
+
floq done <id>
|
|
74
|
+
|
|
75
|
+
# Projects
|
|
76
|
+
floq project add "Project name"
|
|
77
|
+
floq project list
|
|
78
|
+
floq project show <id>
|
|
79
|
+
floq project complete <id>
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
## Configuration
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# Show configuration
|
|
86
|
+
floq config show
|
|
87
|
+
|
|
88
|
+
# Set language
|
|
89
|
+
floq config lang en # English
|
|
90
|
+
floq config lang ja # Japanese
|
|
91
|
+
|
|
92
|
+
# Set theme
|
|
93
|
+
floq config theme modern # Default
|
|
94
|
+
floq config theme norton-commander # MS-DOS style
|
|
95
|
+
floq config theme dos-prompt # Green on black
|
|
96
|
+
floq config theme turbo-pascal # IDE style
|
|
97
|
+
|
|
98
|
+
# Set database path
|
|
99
|
+
floq config db /path/to/custom.db
|
|
100
|
+
floq config db # Reset to default
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Themes
|
|
104
|
+
|
|
105
|
+
### modern (default)
|
|
106
|
+
Clean, minimal style with single borders.
|
|
107
|
+
|
|
108
|
+
### norton-commander
|
|
109
|
+
- Double-line borders (╔═╗║╚═╝)
|
|
110
|
+
- Uppercase headers
|
|
111
|
+
- Function key bar at bottom
|
|
112
|
+
- Cyan/yellow color scheme
|
|
113
|
+
|
|
114
|
+
### dos-prompt
|
|
115
|
+
- Single-line borders
|
|
116
|
+
- Green text (CRT monitor style)
|
|
117
|
+
- Simple `>` selection indicator
|
|
118
|
+
|
|
119
|
+
### turbo-pascal
|
|
120
|
+
- Double-line borders
|
|
121
|
+
- Yellow accents
|
|
122
|
+
- IDE-style appearance
|
|
123
|
+
|
|
124
|
+
> **Note**: Background colors depend on your terminal settings. For the full DOS experience, configure your terminal's background color to blue (#0000AA).
|
|
125
|
+
|
|
126
|
+
## Data Storage
|
|
127
|
+
|
|
128
|
+
- Config: `~/.config/gtd-cli/config.json`
|
|
129
|
+
- Database: `~/.local/share/gtd-cli/gtd.db`
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
MIT
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { render } from 'ink';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { App } from './ui/App.js';
|
|
5
|
+
import { addTask } from './commands/add.js';
|
|
6
|
+
import { listTasks, listProjects } from './commands/list.js';
|
|
7
|
+
import { moveTask } from './commands/move.js';
|
|
8
|
+
import { markDone } from './commands/done.js';
|
|
9
|
+
import { addProject, listProjectsCommand, showProject, completeProject, } from './commands/project.js';
|
|
10
|
+
import { showConfig, setLanguage, setDbPath, resetDbPath, setTheme, selectTheme } from './commands/config.js';
|
|
11
|
+
import { VERSION } from './version.js';
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name('floq')
|
|
15
|
+
.description('Floq - Flow your tasks, clear your mind')
|
|
16
|
+
.version(VERSION);
|
|
17
|
+
// Default command - launch TUI
|
|
18
|
+
program
|
|
19
|
+
.action(() => {
|
|
20
|
+
render(React.createElement(App));
|
|
21
|
+
});
|
|
22
|
+
// Add task
|
|
23
|
+
program
|
|
24
|
+
.command('add <title>')
|
|
25
|
+
.description('Add a new task to Inbox')
|
|
26
|
+
.option('-p, --project <name>', 'Add to a specific project')
|
|
27
|
+
.option('-d, --description <text>', 'Add a description')
|
|
28
|
+
.action(async (title, options) => {
|
|
29
|
+
await addTask(title, options);
|
|
30
|
+
});
|
|
31
|
+
// List tasks
|
|
32
|
+
program
|
|
33
|
+
.command('list [status]')
|
|
34
|
+
.description('List tasks (inbox, next, waiting, someday, done, projects, all)')
|
|
35
|
+
.action(async (status) => {
|
|
36
|
+
if (status === 'projects') {
|
|
37
|
+
await listProjects();
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
await listTasks(status);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
// Move task
|
|
44
|
+
program
|
|
45
|
+
.command('move <id> <status> [waitingFor]')
|
|
46
|
+
.description('Move a task to another list (inbox, next, waiting, someday)')
|
|
47
|
+
.action(async (id, status, waitingFor) => {
|
|
48
|
+
await moveTask(id, status, waitingFor);
|
|
49
|
+
});
|
|
50
|
+
// Mark task as done
|
|
51
|
+
program
|
|
52
|
+
.command('done <id>')
|
|
53
|
+
.description('Mark a task as done')
|
|
54
|
+
.action(async (id) => {
|
|
55
|
+
await markDone(id);
|
|
56
|
+
});
|
|
57
|
+
// Project commands
|
|
58
|
+
const projectCmd = program
|
|
59
|
+
.command('project')
|
|
60
|
+
.description('Project management commands');
|
|
61
|
+
projectCmd
|
|
62
|
+
.command('add <name>')
|
|
63
|
+
.description('Create a new project')
|
|
64
|
+
.option('-d, --description <text>', 'Add a description')
|
|
65
|
+
.action(async (name, options) => {
|
|
66
|
+
await addProject(name, options);
|
|
67
|
+
});
|
|
68
|
+
projectCmd
|
|
69
|
+
.command('list')
|
|
70
|
+
.description('List all projects')
|
|
71
|
+
.action(async () => {
|
|
72
|
+
await listProjectsCommand();
|
|
73
|
+
});
|
|
74
|
+
projectCmd
|
|
75
|
+
.command('show <id>')
|
|
76
|
+
.description('Show project details and tasks')
|
|
77
|
+
.action(async (id) => {
|
|
78
|
+
await showProject(id);
|
|
79
|
+
});
|
|
80
|
+
projectCmd
|
|
81
|
+
.command('complete <id>')
|
|
82
|
+
.description('Mark a project as completed')
|
|
83
|
+
.action(async (id) => {
|
|
84
|
+
await completeProject(id);
|
|
85
|
+
});
|
|
86
|
+
// Config commands
|
|
87
|
+
const configCmd = program
|
|
88
|
+
.command('config')
|
|
89
|
+
.description('Configuration commands');
|
|
90
|
+
configCmd
|
|
91
|
+
.command('show')
|
|
92
|
+
.description('Show current configuration')
|
|
93
|
+
.action(async () => {
|
|
94
|
+
await showConfig();
|
|
95
|
+
});
|
|
96
|
+
configCmd
|
|
97
|
+
.command('lang <locale>')
|
|
98
|
+
.description('Set language (en, ja)')
|
|
99
|
+
.action(async (locale) => {
|
|
100
|
+
await setLanguage(locale);
|
|
101
|
+
});
|
|
102
|
+
configCmd
|
|
103
|
+
.command('db [path]')
|
|
104
|
+
.description('Set database path (omit path to reset to default)')
|
|
105
|
+
.action(async (path) => {
|
|
106
|
+
if (path) {
|
|
107
|
+
await setDbPath(path);
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
await resetDbPath();
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
configCmd
|
|
114
|
+
.command('theme [name]')
|
|
115
|
+
.description('Set UI theme (interactive if no name provided)')
|
|
116
|
+
.action(async (name) => {
|
|
117
|
+
if (name) {
|
|
118
|
+
await setTheme(name);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
await selectTheme();
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
export { program };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { eq, and } from 'drizzle-orm';
|
|
3
|
+
import { getDb, schema } from '../db/index.js';
|
|
4
|
+
import { t, fmt } from '../i18n/index.js';
|
|
5
|
+
export async function addTask(title, options) {
|
|
6
|
+
const db = getDb();
|
|
7
|
+
const now = new Date();
|
|
8
|
+
const i18n = t();
|
|
9
|
+
let parentId;
|
|
10
|
+
if (options.project) {
|
|
11
|
+
const project = db
|
|
12
|
+
.select()
|
|
13
|
+
.from(schema.tasks)
|
|
14
|
+
.where(and(eq(schema.tasks.isProject, true), eq(schema.tasks.title, options.project)))
|
|
15
|
+
.get();
|
|
16
|
+
if (!project) {
|
|
17
|
+
console.error(fmt(i18n.commands.add.projectNotFound, { project: options.project }));
|
|
18
|
+
process.exit(1);
|
|
19
|
+
}
|
|
20
|
+
parentId = project.id;
|
|
21
|
+
}
|
|
22
|
+
const task = {
|
|
23
|
+
id: uuidv4(),
|
|
24
|
+
title,
|
|
25
|
+
description: options.description,
|
|
26
|
+
status: 'inbox',
|
|
27
|
+
parentId,
|
|
28
|
+
createdAt: now,
|
|
29
|
+
updatedAt: now,
|
|
30
|
+
};
|
|
31
|
+
db.insert(schema.tasks).values(task).run();
|
|
32
|
+
console.log(fmt(i18n.commands.add.success, { title }));
|
|
33
|
+
if (parentId) {
|
|
34
|
+
console.log(fmt(i18n.commands.add.withProject, { project: options.project }));
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare function showConfig(): Promise<void>;
|
|
2
|
+
export declare function setLanguage(locale: string): Promise<void>;
|
|
3
|
+
export declare function setDbPath(dbPath: string): Promise<void>;
|
|
4
|
+
export declare function resetDbPath(): Promise<void>;
|
|
5
|
+
export declare function setTheme(theme: string): Promise<void>;
|
|
6
|
+
export declare function selectTheme(): Promise<void>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { render } from 'ink';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { loadConfig, saveConfig, getDbPath } from '../config.js';
|
|
4
|
+
import { CONFIG_FILE } from '../paths.js';
|
|
5
|
+
import { ThemeSelector } from '../ui/ThemeSelector.js';
|
|
6
|
+
const VALID_LOCALES = ['en', 'ja'];
|
|
7
|
+
const VALID_THEMES = ['modern', 'norton-commander', 'dos-prompt', 'turbo-pascal'];
|
|
8
|
+
export async function showConfig() {
|
|
9
|
+
const config = loadConfig();
|
|
10
|
+
console.log('GTD CLI Configuration');
|
|
11
|
+
console.log('─'.repeat(40));
|
|
12
|
+
console.log(`Config file: ${CONFIG_FILE}`);
|
|
13
|
+
console.log(`Language: ${config.locale}`);
|
|
14
|
+
console.log(`Database: ${getDbPath()}`);
|
|
15
|
+
console.log(`Theme: ${config.theme || 'modern'}`);
|
|
16
|
+
if (config.db_path) {
|
|
17
|
+
console.log(` (custom: ${config.db_path})`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export async function setLanguage(locale) {
|
|
21
|
+
if (!VALID_LOCALES.includes(locale)) {
|
|
22
|
+
console.error(`Invalid locale: ${locale}`);
|
|
23
|
+
console.error(`Valid locales: ${VALID_LOCALES.join(', ')}`);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
saveConfig({ locale: locale });
|
|
27
|
+
const messages = {
|
|
28
|
+
en: 'Language set to English',
|
|
29
|
+
ja: '言語を日本語に設定しました',
|
|
30
|
+
};
|
|
31
|
+
console.log(messages[locale]);
|
|
32
|
+
}
|
|
33
|
+
export async function setDbPath(dbPath) {
|
|
34
|
+
saveConfig({ db_path: dbPath });
|
|
35
|
+
console.log(`Database path set to: ${getDbPath()}`);
|
|
36
|
+
}
|
|
37
|
+
export async function resetDbPath() {
|
|
38
|
+
saveConfig({ db_path: undefined });
|
|
39
|
+
console.log(`Database path reset to default: ${getDbPath()}`);
|
|
40
|
+
}
|
|
41
|
+
export async function setTheme(theme) {
|
|
42
|
+
if (!VALID_THEMES.includes(theme)) {
|
|
43
|
+
console.error(`Invalid theme: ${theme}`);
|
|
44
|
+
console.error(`Valid themes: ${VALID_THEMES.join(', ')}`);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
saveConfig({ theme: theme });
|
|
48
|
+
const messages = {
|
|
49
|
+
'modern': 'Theme set to Modern',
|
|
50
|
+
'norton-commander': 'Theme set to Norton Commander (MS-DOS style)',
|
|
51
|
+
'dos-prompt': 'Theme set to DOS Prompt (green on black)',
|
|
52
|
+
'turbo-pascal': 'Theme set to Turbo Pascal IDE',
|
|
53
|
+
};
|
|
54
|
+
console.log(messages[theme]);
|
|
55
|
+
}
|
|
56
|
+
export async function selectTheme() {
|
|
57
|
+
return new Promise((resolve) => {
|
|
58
|
+
const { unmount } = render(React.createElement(ThemeSelector, {
|
|
59
|
+
onSelect: (theme) => {
|
|
60
|
+
unmount();
|
|
61
|
+
saveConfig({ theme });
|
|
62
|
+
const messages = {
|
|
63
|
+
'modern': 'Theme set to Modern',
|
|
64
|
+
'norton-commander': 'Theme set to Norton Commander (MS-DOS style)',
|
|
65
|
+
'dos-prompt': 'Theme set to DOS Prompt (green on black)',
|
|
66
|
+
'turbo-pascal': 'Theme set to Turbo Pascal IDE',
|
|
67
|
+
};
|
|
68
|
+
console.log(messages[theme]);
|
|
69
|
+
resolve();
|
|
70
|
+
},
|
|
71
|
+
}));
|
|
72
|
+
});
|
|
73
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function markDone(taskId: string): Promise<void>;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { eq, like } from 'drizzle-orm';
|
|
2
|
+
import { getDb, schema } from '../db/index.js';
|
|
3
|
+
import { t, fmt } from '../i18n/index.js';
|
|
4
|
+
export async function markDone(taskId) {
|
|
5
|
+
const db = getDb();
|
|
6
|
+
const i18n = t();
|
|
7
|
+
// Find task by ID prefix
|
|
8
|
+
const tasks = db
|
|
9
|
+
.select()
|
|
10
|
+
.from(schema.tasks)
|
|
11
|
+
.where(like(schema.tasks.id, `${taskId}%`))
|
|
12
|
+
.all();
|
|
13
|
+
if (tasks.length === 0) {
|
|
14
|
+
console.error(fmt(i18n.commands.done.notFound, { id: taskId }));
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
if (tasks.length > 1) {
|
|
18
|
+
console.error(fmt(i18n.commands.done.multipleMatch, { id: taskId }));
|
|
19
|
+
for (const task of tasks) {
|
|
20
|
+
console.error(` [${task.id.slice(0, 8)}] ${task.title}`);
|
|
21
|
+
}
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const task = tasks[0];
|
|
25
|
+
if (task.status === 'done') {
|
|
26
|
+
console.log(fmt(i18n.commands.done.alreadyDone, { title: task.title }));
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
db.update(schema.tasks)
|
|
30
|
+
.set({
|
|
31
|
+
status: 'done',
|
|
32
|
+
updatedAt: new Date(),
|
|
33
|
+
})
|
|
34
|
+
.where(eq(schema.tasks.id, task.id))
|
|
35
|
+
.run();
|
|
36
|
+
console.log(fmt(i18n.commands.done.success, { title: task.title }));
|
|
37
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { eq } from 'drizzle-orm';
|
|
2
|
+
import { getDb, schema } from '../db/index.js';
|
|
3
|
+
import { t, fmt } from '../i18n/index.js';
|
|
4
|
+
export async function listTasks(status) {
|
|
5
|
+
const db = getDb();
|
|
6
|
+
const i18n = t();
|
|
7
|
+
if (status && status !== 'all') {
|
|
8
|
+
const validStatuses = ['inbox', 'next', 'waiting', 'someday', 'done'];
|
|
9
|
+
if (!validStatuses.includes(status)) {
|
|
10
|
+
console.error(fmt(i18n.commands.list.invalidStatus, { status }));
|
|
11
|
+
console.error(fmt(i18n.commands.list.validStatuses, { statuses: validStatuses.join(', ') }));
|
|
12
|
+
process.exit(1);
|
|
13
|
+
}
|
|
14
|
+
const tasks = db
|
|
15
|
+
.select()
|
|
16
|
+
.from(schema.tasks)
|
|
17
|
+
.where(eq(schema.tasks.status, status))
|
|
18
|
+
.all();
|
|
19
|
+
console.log(`\n${i18n.status[status]} (${tasks.length})`);
|
|
20
|
+
console.log('─'.repeat(40));
|
|
21
|
+
if (tasks.length === 0) {
|
|
22
|
+
console.log(` ${i18n.commands.list.noTasks}`);
|
|
23
|
+
}
|
|
24
|
+
else {
|
|
25
|
+
for (const task of tasks) {
|
|
26
|
+
const shortId = task.id.slice(0, 8);
|
|
27
|
+
let line = ` [${shortId}] ${task.title}`;
|
|
28
|
+
if (task.waitingFor) {
|
|
29
|
+
line += ` (${i18n.status.waiting.toLowerCase()}: ${task.waitingFor})`;
|
|
30
|
+
}
|
|
31
|
+
if (task.dueDate) {
|
|
32
|
+
line += ` (due: ${task.dueDate.toLocaleDateString()})`;
|
|
33
|
+
}
|
|
34
|
+
console.log(line);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
console.log();
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
// Show all lists
|
|
41
|
+
const statuses = ['inbox', 'next', 'waiting', 'someday'];
|
|
42
|
+
for (const s of statuses) {
|
|
43
|
+
const tasks = db
|
|
44
|
+
.select()
|
|
45
|
+
.from(schema.tasks)
|
|
46
|
+
.where(eq(schema.tasks.status, s))
|
|
47
|
+
.all();
|
|
48
|
+
console.log(`\n${i18n.status[s]} (${tasks.length})`);
|
|
49
|
+
console.log('─'.repeat(40));
|
|
50
|
+
if (tasks.length === 0) {
|
|
51
|
+
console.log(` ${i18n.commands.list.noTasks}`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
for (const task of tasks) {
|
|
55
|
+
const shortId = task.id.slice(0, 8);
|
|
56
|
+
let line = ` [${shortId}] ${task.title}`;
|
|
57
|
+
if (task.waitingFor) {
|
|
58
|
+
line += ` (${i18n.status.waiting.toLowerCase()}: ${task.waitingFor})`;
|
|
59
|
+
}
|
|
60
|
+
console.log(line);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
console.log();
|
|
65
|
+
}
|
|
66
|
+
export async function listProjects() {
|
|
67
|
+
const db = getDb();
|
|
68
|
+
const i18n = t();
|
|
69
|
+
const projects = db
|
|
70
|
+
.select()
|
|
71
|
+
.from(schema.tasks)
|
|
72
|
+
.where(eq(schema.tasks.isProject, true))
|
|
73
|
+
.all()
|
|
74
|
+
.filter(p => p.status !== 'done');
|
|
75
|
+
console.log(`\n${i18n.projectStatus.active} (${projects.length})`);
|
|
76
|
+
console.log('─'.repeat(40));
|
|
77
|
+
if (projects.length === 0) {
|
|
78
|
+
console.log(` ${i18n.commands.list.noProjects}`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
for (const project of projects) {
|
|
82
|
+
const childTasks = db
|
|
83
|
+
.select()
|
|
84
|
+
.from(schema.tasks)
|
|
85
|
+
.where(eq(schema.tasks.parentId, project.id))
|
|
86
|
+
.all();
|
|
87
|
+
const activeTasks = childTasks.filter(t => t.status !== 'done').length;
|
|
88
|
+
const shortId = project.id.slice(0, 8);
|
|
89
|
+
console.log(` [${shortId}] ${project.title} (${fmt(i18n.commands.list.tasks, { count: activeTasks })})`);
|
|
90
|
+
if (project.description) {
|
|
91
|
+
console.log(` ${project.description}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
console.log();
|
|
96
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function moveTask(taskId: string, targetStatus: string, waitingFor?: string): Promise<void>;
|