floq 0.6.0 → 0.8.0
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/changelog.js +26 -9
- package/dist/cli.js +8 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +8 -0
- package/dist/i18n/en.d.ts +20 -0
- package/dist/i18n/en.js +12 -1
- package/dist/i18n/ja.js +12 -1
- package/dist/index.js +0 -0
- package/dist/ui/App.js +11 -4
- package/dist/ui/SplashScreen.js +47 -4
- package/dist/ui/components/GtdDQ.js +177 -14
- package/dist/ui/components/GtdMario.d.ts +7 -0
- package/dist/ui/components/GtdMario.js +839 -0
- package/dist/ui/components/KanbanBoard.js +7 -3
- package/dist/ui/components/KanbanDQ.js +45 -6
- package/dist/ui/components/KanbanMario.d.ts +7 -0
- package/dist/ui/components/KanbanMario.js +431 -0
- package/dist/ui/components/MarioBox.d.ts +19 -0
- package/dist/ui/components/MarioBox.js +83 -0
- package/dist/ui/components/SearchResults.js +4 -1
- package/dist/ui/theme/themes.d.ts +1 -0
- package/dist/ui/theme/themes.js +42 -0
- package/dist/ui/theme/types.d.ts +2 -2
- package/package.json +1 -1
- package/dist/ui/components/DQLayout.d.ts +0 -36
- package/dist/ui/components/DQLayout.js +0 -53
- package/dist/ui/components/DQTaskList.d.ts +0 -53
- package/dist/ui/components/DQTaskList.js +0 -48
- package/dist/ui/components/DQWindow.d.ts +0 -19
- package/dist/ui/components/DQWindow.js +0 -33
- package/dist/ui/components/FullScreenBox.d.ts +0 -8
- package/dist/ui/components/FullScreenBox.js +0 -10
package/dist/changelog.js
CHANGED
|
@@ -1,27 +1,44 @@
|
|
|
1
1
|
import { readFileSync } from 'fs';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
|
+
import { getLocale } from './config.js';
|
|
4
5
|
function findChangelogPath() {
|
|
5
6
|
// Get the directory of the current module
|
|
6
7
|
const currentFile = fileURLToPath(import.meta.url);
|
|
7
8
|
const currentDir = dirname(currentFile);
|
|
8
|
-
|
|
9
|
+
const locale = getLocale();
|
|
10
|
+
const filename = locale === 'ja' ? 'CHANGELOG.ja.md' : 'CHANGELOG.md';
|
|
11
|
+
const fallbackFilename = 'CHANGELOG.md';
|
|
12
|
+
// Try to find localized CHANGELOG relative to the package root
|
|
9
13
|
// From dist/ or src/, go up one level
|
|
10
|
-
const
|
|
11
|
-
join(currentDir, '..'
|
|
12
|
-
join(currentDir, '..', '..'
|
|
13
|
-
|
|
14
|
+
const basePaths = [
|
|
15
|
+
join(currentDir, '..'),
|
|
16
|
+
join(currentDir, '..', '..'),
|
|
17
|
+
process.cwd(),
|
|
14
18
|
];
|
|
15
|
-
|
|
19
|
+
// First, try to find the localized version
|
|
20
|
+
for (const basePath of basePaths) {
|
|
21
|
+
const localizedPath = join(basePath, filename);
|
|
16
22
|
try {
|
|
17
|
-
readFileSync(
|
|
18
|
-
return
|
|
23
|
+
readFileSync(localizedPath, 'utf-8');
|
|
24
|
+
return localizedPath;
|
|
19
25
|
}
|
|
20
26
|
catch {
|
|
21
27
|
// continue to next path
|
|
22
28
|
}
|
|
23
29
|
}
|
|
24
|
-
|
|
30
|
+
// Fall back to default CHANGELOG.md
|
|
31
|
+
for (const basePath of basePaths) {
|
|
32
|
+
const fallbackPath = join(basePath, fallbackFilename);
|
|
33
|
+
try {
|
|
34
|
+
readFileSync(fallbackPath, 'utf-8');
|
|
35
|
+
return fallbackPath;
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// continue to next path
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return join(basePaths[0], fallbackFilename); // Return first path as fallback
|
|
25
42
|
}
|
|
26
43
|
export function parseChangelog() {
|
|
27
44
|
const changelogPath = findChangelogPath();
|
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,14 @@ program
|
|
|
20
20
|
// Default command - launch TUI
|
|
21
21
|
program
|
|
22
22
|
.action(() => {
|
|
23
|
-
|
|
23
|
+
// Enter alternate screen buffer (like btop/vim)
|
|
24
|
+
process.stdout.write('\x1b[?1049h');
|
|
25
|
+
process.stdout.write('\x1b[H'); // Move cursor to top-left
|
|
26
|
+
const { waitUntilExit } = render(React.createElement(App));
|
|
27
|
+
waitUntilExit().then(() => {
|
|
28
|
+
// Leave alternate screen buffer
|
|
29
|
+
process.stdout.write('\x1b[?1049l');
|
|
30
|
+
});
|
|
24
31
|
});
|
|
25
32
|
// Add task
|
|
26
33
|
program
|
package/dist/config.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ export interface Config {
|
|
|
14
14
|
turso?: TursoConfig;
|
|
15
15
|
contexts?: string[];
|
|
16
16
|
splashDuration?: number;
|
|
17
|
+
contextFilter?: string | null;
|
|
17
18
|
}
|
|
18
19
|
export declare function loadConfig(): Config;
|
|
19
20
|
export declare function saveConfig(updates: Partial<Config>): void;
|
|
@@ -33,3 +34,5 @@ export declare function addContext(context: string): boolean;
|
|
|
33
34
|
export declare function removeContext(context: string): boolean;
|
|
34
35
|
export declare function getSplashDuration(): number;
|
|
35
36
|
export declare function setSplashDuration(duration: number): void;
|
|
37
|
+
export declare function getContextFilter(): string | null;
|
|
38
|
+
export declare function setContextFilter(contextFilter: string | null): void;
|
package/dist/config.js
CHANGED
|
@@ -151,3 +151,11 @@ export function setSplashDuration(duration) {
|
|
|
151
151
|
// Allow -1 (wait for key), 0 (disabled), or positive values
|
|
152
152
|
saveConfig({ splashDuration: duration >= 0 ? duration : -1 });
|
|
153
153
|
}
|
|
154
|
+
export function getContextFilter() {
|
|
155
|
+
const config = loadConfig();
|
|
156
|
+
// undefined means not set (default to null = all)
|
|
157
|
+
return config.contextFilter === undefined ? null : config.contextFilter;
|
|
158
|
+
}
|
|
159
|
+
export function setContextFilter(contextFilter) {
|
|
160
|
+
saveConfig({ contextFilter });
|
|
161
|
+
}
|
package/dist/i18n/en.d.ts
CHANGED
|
@@ -85,6 +85,16 @@ export declare const en: {
|
|
|
85
85
|
waitingFor: string;
|
|
86
86
|
refreshed: string;
|
|
87
87
|
footer: string;
|
|
88
|
+
dqFooter: {
|
|
89
|
+
tabs: string;
|
|
90
|
+
tasks: string;
|
|
91
|
+
projectDetail: string;
|
|
92
|
+
taskDetail: string;
|
|
93
|
+
};
|
|
94
|
+
kanbanFooter: {
|
|
95
|
+
category: string;
|
|
96
|
+
tasks: string;
|
|
97
|
+
};
|
|
88
98
|
noTasks: string;
|
|
89
99
|
tabInbox: string;
|
|
90
100
|
tabNext: string;
|
|
@@ -435,6 +445,16 @@ export type TuiTranslations = {
|
|
|
435
445
|
waitingFor: string;
|
|
436
446
|
refreshed: string;
|
|
437
447
|
footer: string;
|
|
448
|
+
dqFooter: {
|
|
449
|
+
tabs: string;
|
|
450
|
+
tasks: string;
|
|
451
|
+
projectDetail: string;
|
|
452
|
+
taskDetail: string;
|
|
453
|
+
};
|
|
454
|
+
kanbanFooter: {
|
|
455
|
+
category: string;
|
|
456
|
+
tasks: string;
|
|
457
|
+
};
|
|
438
458
|
noTasks: string;
|
|
439
459
|
tabInbox: string;
|
|
440
460
|
tabNext: string;
|
package/dist/i18n/en.js
CHANGED
|
@@ -89,7 +89,18 @@ export const en = {
|
|
|
89
89
|
movedToWaiting: 'Moved "{title}" to Waiting (for {person})',
|
|
90
90
|
waitingFor: 'Waiting for: ',
|
|
91
91
|
refreshed: 'Refreshed',
|
|
92
|
-
footer: 'a=add d=done D=delete n=next s=someday w=waiting i=inbox p=project P=link',
|
|
92
|
+
footer: 'a=add d=done D=delete n=next s=someday w=waiting i=inbox p=project P=link c=context',
|
|
93
|
+
// DQ/Mario style footers
|
|
94
|
+
dqFooter: {
|
|
95
|
+
tabs: 'j/k=select l/Enter=tasks 1-6=tab a=add @=filter /=search',
|
|
96
|
+
tasks: 'j/k=select Enter=detail h/Esc=back d=done n=next s=someday w=wait i=inbox c=context p=project P=link D=delete u=undo /=search',
|
|
97
|
+
projectDetail: 'j/k=select a=add d=done Esc/b=back /=search',
|
|
98
|
+
taskDetail: 'j/k=select c/i=add comment P=link D=delete comment Esc/b=back',
|
|
99
|
+
},
|
|
100
|
+
kanbanFooter: {
|
|
101
|
+
category: 'j/k=select l/Enter=tasks a=add @=filter /=search',
|
|
102
|
+
tasks: 'j/k=select h/Esc=back d=done m=move a=add u=undo /=search',
|
|
103
|
+
},
|
|
93
104
|
noTasks: 'No tasks',
|
|
94
105
|
// Tab labels
|
|
95
106
|
tabInbox: 'Inbox',
|
package/dist/i18n/ja.js
CHANGED
|
@@ -89,7 +89,18 @@ export const ja = {
|
|
|
89
89
|
movedToWaiting: '「{title}」を連絡待ち({person})に移動しました',
|
|
90
90
|
waitingFor: '待機相手: ',
|
|
91
91
|
refreshed: '更新しました',
|
|
92
|
-
footer: 'a=追加 d=完了 D=削除 n=次 s=いつか w=待ち i=Inbox p=プロジェクト化 P=紐づけ',
|
|
92
|
+
footer: 'a=追加 d=完了 D=削除 n=次 s=いつか w=待ち i=Inbox p=プロジェクト化 P=紐づけ c=コンテキスト',
|
|
93
|
+
// DQ/Mario style footers
|
|
94
|
+
dqFooter: {
|
|
95
|
+
tabs: 'j/k=選択 l/Enter=タスク 1-6=タブ a=追加 @=フィルター /=検索',
|
|
96
|
+
tasks: 'j/k=選択 Enter=詳細 h/Esc=戻る d=完了 n=次 s=いつか w=待ち i=inbox c=コンテキスト p=プロジェクト化 P=紐づけ D=削除 u=戻す /=検索',
|
|
97
|
+
projectDetail: 'j/k=選択 a=追加 d=完了 Esc/b=戻る /=検索',
|
|
98
|
+
taskDetail: 'j/k=選択 c/i=コメント追加 P=紐づけ D=コメント削除 Esc/b=戻る',
|
|
99
|
+
},
|
|
100
|
+
kanbanFooter: {
|
|
101
|
+
category: 'j/k=選択 l/Enter=タスク a=追加 @=フィルター /=検索',
|
|
102
|
+
tasks: 'j/k=選択 h/Esc=戻る d=完了 m=移動 a=追加 u=戻す /=検索',
|
|
103
|
+
},
|
|
93
104
|
noTasks: 'タスクなし',
|
|
94
105
|
// Tab labels
|
|
95
106
|
tabInbox: 'Inbox',
|
package/dist/index.js
CHANGED
|
File without changes
|
package/dist/ui/App.js
CHANGED
|
@@ -17,10 +17,12 @@ import { LanguageSelector } from './LanguageSelector.js';
|
|
|
17
17
|
import { getDb, schema } from '../db/index.js';
|
|
18
18
|
import { t, fmt } from '../i18n/index.js';
|
|
19
19
|
import { ThemeProvider, useTheme, getTheme } from './theme/index.js';
|
|
20
|
-
import { getThemeName, getViewMode, setThemeName, setViewMode, setLocale, isTursoEnabled, getContexts, addContext, getSplashDuration } from '../config.js';
|
|
20
|
+
import { getThemeName, getViewMode, setThemeName, setViewMode, setLocale, isTursoEnabled, getContexts, addContext, getSplashDuration, getContextFilter, setContextFilter as saveContextFilter } from '../config.js';
|
|
21
21
|
import { KanbanBoard } from './components/KanbanBoard.js';
|
|
22
22
|
import { KanbanDQ } from './components/KanbanDQ.js';
|
|
23
|
+
import { KanbanMario } from './components/KanbanMario.js';
|
|
23
24
|
import { GtdDQ } from './components/GtdDQ.js';
|
|
25
|
+
import { GtdMario } from './components/GtdMario.js';
|
|
24
26
|
import { VERSION } from '../version.js';
|
|
25
27
|
import { HistoryProvider, useHistory, CreateTaskCommand, DeleteTaskCommand, MoveTaskCommand, LinkTaskCommand, ConvertToProjectCommand, CreateCommentCommand, DeleteCommentCommand, SetContextCommand, } from './history/index.js';
|
|
26
28
|
const TABS = ['inbox', 'next', 'waiting', 'someday', 'projects', 'done'];
|
|
@@ -51,6 +53,7 @@ export function App() {
|
|
|
51
53
|
};
|
|
52
54
|
const currentTheme = getTheme(themeName);
|
|
53
55
|
const useDQStyle = currentTheme.uiStyle === 'titled-box';
|
|
56
|
+
const useMarioStyle = currentTheme.uiStyle === 'mario-block';
|
|
54
57
|
// Show splash screen (all themes, configurable duration)
|
|
55
58
|
if (showSplash) {
|
|
56
59
|
return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(SplashScreen, { onComplete: () => setShowSplash(false), duration: splashDuration, viewMode: viewMode }) }));
|
|
@@ -65,7 +68,7 @@ export function App() {
|
|
|
65
68
|
if (settingsMode === 'lang-select') {
|
|
66
69
|
return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(LanguageSelector, { onSelect: handleLocaleSelect, onCancel: handleSettingsCancel }) }));
|
|
67
70
|
}
|
|
68
|
-
return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(HistoryProvider, { children: viewMode === 'kanban' ? (useDQStyle ? (_jsx(KanbanDQ, { onOpenSettings: setSettingsMode })) : (_jsx(KanbanBoard, { onOpenSettings: setSettingsMode }))) : (useDQStyle ? (_jsx(GtdDQ, { onOpenSettings: setSettingsMode })) : (_jsx(AppContent, { onOpenSettings: setSettingsMode }))) }) }));
|
|
71
|
+
return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(HistoryProvider, { children: viewMode === 'kanban' ? (useMarioStyle ? (_jsx(KanbanMario, { onOpenSettings: setSettingsMode })) : useDQStyle ? (_jsx(KanbanDQ, { onOpenSettings: setSettingsMode })) : (_jsx(KanbanBoard, { onOpenSettings: setSettingsMode }))) : (useMarioStyle ? (_jsx(GtdMario, { onOpenSettings: setSettingsMode })) : useDQStyle ? (_jsx(GtdDQ, { onOpenSettings: setSettingsMode })) : (_jsx(AppContent, { onOpenSettings: setSettingsMode }))) }) }));
|
|
69
72
|
}
|
|
70
73
|
function AppContent({ onOpenSettings }) {
|
|
71
74
|
const theme = useTheme();
|
|
@@ -98,8 +101,12 @@ function AppContent({ onOpenSettings }) {
|
|
|
98
101
|
const [searchQuery, setSearchQuery] = useState('');
|
|
99
102
|
const [searchResults, setSearchResults] = useState([]);
|
|
100
103
|
const [searchResultIndex, setSearchResultIndex] = useState(0);
|
|
101
|
-
// Context filter state
|
|
102
|
-
const [contextFilter,
|
|
104
|
+
// Context filter state - load from config for persistence across sessions/terminals
|
|
105
|
+
const [contextFilter, setContextFilterState] = useState(() => getContextFilter());
|
|
106
|
+
const setContextFilter = useCallback((value) => {
|
|
107
|
+
setContextFilterState(value);
|
|
108
|
+
saveContextFilter(value);
|
|
109
|
+
}, []);
|
|
103
110
|
const [contextSelectIndex, setContextSelectIndex] = useState(0);
|
|
104
111
|
const [availableContexts, setAvailableContexts] = useState([]);
|
|
105
112
|
const i18n = t();
|
package/dist/ui/SplashScreen.js
CHANGED
|
@@ -52,6 +52,40 @@ const DQ_BORDER = {
|
|
|
52
52
|
vertical: '│',
|
|
53
53
|
};
|
|
54
54
|
const TAGLINE = 'Flow your tasks, clear your mind';
|
|
55
|
+
// Mario/Nintendo style splash - SFC boot screen inspired
|
|
56
|
+
const MARIO_LOGO = [
|
|
57
|
+
' ★ ★ ★ ★ ★ ★ ★ ★ ★',
|
|
58
|
+
'',
|
|
59
|
+
' ████████ ██ ██████ ██████',
|
|
60
|
+
' ██ ██ ██ ██ ██ ██',
|
|
61
|
+
' ██████ ██ ██ ██ ██ ██',
|
|
62
|
+
' ██ ██ ██ ██ ██ ▄▄ ██',
|
|
63
|
+
' ██ ███████ ██████ ██████',
|
|
64
|
+
'',
|
|
65
|
+
' ★ ★ ★ ★ ★ ★ ★ ★ ★',
|
|
66
|
+
];
|
|
67
|
+
const MARIO_QUOTES_JA = [
|
|
68
|
+
'イッツァミー!タスクマネージャー!',
|
|
69
|
+
'マンマミーア!タスクがいっぱいだ!',
|
|
70
|
+
'レッツァゴー!',
|
|
71
|
+
'ヤッフー!',
|
|
72
|
+
'スーパータスククリア!',
|
|
73
|
+
'コインをゲット!',
|
|
74
|
+
'1UPキノコ!',
|
|
75
|
+
'ファイアフラワー!',
|
|
76
|
+
'スターパワー!',
|
|
77
|
+
];
|
|
78
|
+
const MARIO_QUOTES_EN = [
|
|
79
|
+
"It's-a me! Task Manager!",
|
|
80
|
+
'Mamma mia! So many tasks!',
|
|
81
|
+
"Let's-a go!",
|
|
82
|
+
'Yahoo!',
|
|
83
|
+
'Super Task Clear!',
|
|
84
|
+
'Got a coin!',
|
|
85
|
+
'1-UP Mushroom!',
|
|
86
|
+
'Fire Flower!',
|
|
87
|
+
'Star Power!',
|
|
88
|
+
];
|
|
55
89
|
// Dragon Quest famous quotes for splash screen
|
|
56
90
|
const DQ_QUOTES_JA = [
|
|
57
91
|
'へんじがない。ただのしかばねのようだ。',
|
|
@@ -84,12 +118,17 @@ export function SplashScreen({ onComplete, duration = 1500, viewMode = 'gtd' })
|
|
|
84
118
|
const theme = useTheme();
|
|
85
119
|
const i18n = t();
|
|
86
120
|
const isDqStyle = theme.uiStyle === 'titled-box';
|
|
87
|
-
const
|
|
121
|
+
const isMarioStyle = theme.uiStyle === 'mario-block';
|
|
122
|
+
const isDosStyle = theme.name !== 'modern' && !isMarioStyle;
|
|
88
123
|
const logo = isDosStyle ? LOGO_DOS : LOGO_MODERN;
|
|
89
124
|
const [filled, empty] = theme.style.loadingChars;
|
|
90
125
|
// Pick a random quote (stable across re-renders)
|
|
91
126
|
const [randomQuote] = useState(() => {
|
|
92
127
|
const isJapanese = i18n.splash?.welcome === 'ようこそ!';
|
|
128
|
+
if (isMarioStyle) {
|
|
129
|
+
const quotes = isJapanese ? MARIO_QUOTES_JA : MARIO_QUOTES_EN;
|
|
130
|
+
return quotes[Math.floor(Math.random() * quotes.length)];
|
|
131
|
+
}
|
|
93
132
|
const quotes = isJapanese ? DQ_QUOTES_JA : DQ_QUOTES_EN;
|
|
94
133
|
return quotes[Math.floor(Math.random() * quotes.length)];
|
|
95
134
|
});
|
|
@@ -118,9 +157,9 @@ export function SplashScreen({ onComplete, duration = 1500, viewMode = 'gtd' })
|
|
|
118
157
|
const taglineTimer = setTimeout(() => {
|
|
119
158
|
setShowTagline(true);
|
|
120
159
|
}, 600);
|
|
121
|
-
// Blink effect for DQ style or wait-for-key mode
|
|
160
|
+
// Blink effect for DQ style, Mario style, or wait-for-key mode
|
|
122
161
|
let blinkInterval = null;
|
|
123
|
-
if (isDqStyle || waitForKeyPress) {
|
|
162
|
+
if (isDqStyle || isMarioStyle || waitForKeyPress) {
|
|
124
163
|
blinkInterval = setInterval(() => {
|
|
125
164
|
setBlinkVisible((prev) => !prev);
|
|
126
165
|
}, 500);
|
|
@@ -140,7 +179,11 @@ export function SplashScreen({ onComplete, duration = 1500, viewMode = 'gtd' })
|
|
|
140
179
|
if (blinkInterval)
|
|
141
180
|
clearInterval(blinkInterval);
|
|
142
181
|
};
|
|
143
|
-
}, [onComplete, duration, isDqStyle, waitForKeyPress]);
|
|
182
|
+
}, [onComplete, duration, isDqStyle, isMarioStyle, waitForKeyPress]);
|
|
183
|
+
// Mario / Nintendo style splash (SFC boot screen inspired)
|
|
184
|
+
if (isMarioStyle) {
|
|
185
|
+
return (_jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", padding: 2, children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: theme.colors.secondary, children: "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501" }) }), _jsx(Box, { flexDirection: "column", alignItems: "center", marginBottom: 1, children: MARIO_LOGO.map((line, index) => (_jsx(Text, { color: index === 0 || index === 8 ? theme.colors.secondary : theme.colors.primary, bold: true, children: line }, index))) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: theme.colors.text, bold: true, children: "\uFF5E SUPER TASK MANAGER \uFF5E" }) }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: theme.colors.accent, children: ["\uD83C\uDF44 ", randomQuote] }) }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: theme.colors.secondary, children: "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501" }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textMuted, children: blinkVisible ? '- PRESS START -' : ' ' }) }), _jsxs(Box, { marginTop: 2, flexDirection: "column", alignItems: "center", children: [_jsxs(Text, { color: theme.colors.textMuted, children: ["VER ", VERSION] }), _jsx(Text, { color: theme.colors.textMuted, children: "\u00A9 2026 polidog/PartyHard Inc." })] })] }));
|
|
186
|
+
}
|
|
144
187
|
// Dragon Quest style splash
|
|
145
188
|
if (isDqStyle) {
|
|
146
189
|
const boxWidth = 40;
|
|
@@ -7,13 +7,41 @@ import { v4 as uuidv4 } from 'uuid';
|
|
|
7
7
|
import { getDb, schema } from '../../db/index.js';
|
|
8
8
|
import { t, fmt } from '../../i18n/index.js';
|
|
9
9
|
import { useTheme } from '../theme/index.js';
|
|
10
|
-
import { isTursoEnabled, getContexts, addContext } from '../../config.js';
|
|
11
|
-
import {
|
|
12
|
-
import { useHistory, CreateTaskCommand, DeleteTaskCommand, MoveTaskCommand, LinkTaskCommand, ConvertToProjectCommand, } from '../history/index.js';
|
|
10
|
+
import { isTursoEnabled, getContexts, addContext, getLocale, getContextFilter, setContextFilter as saveContextFilter } from '../../config.js';
|
|
11
|
+
import { useHistory, CreateTaskCommand, DeleteTaskCommand, MoveTaskCommand, LinkTaskCommand, ConvertToProjectCommand, CreateCommentCommand, DeleteCommentCommand, SetContextCommand, } from '../history/index.js';
|
|
13
12
|
import { SearchBar } from './SearchBar.js';
|
|
14
13
|
import { SearchResults } from './SearchResults.js';
|
|
15
14
|
import { HelpModal } from './HelpModal.js';
|
|
16
15
|
const TABS = ['inbox', 'next', 'waiting', 'someday', 'projects', 'done'];
|
|
16
|
+
// Dragon Quest job classes
|
|
17
|
+
const DQ_JOBS_JA = [
|
|
18
|
+
'ゆうしゃ',
|
|
19
|
+
'せんし',
|
|
20
|
+
'まほうつかい',
|
|
21
|
+
'そうりょ',
|
|
22
|
+
'ぶとうか',
|
|
23
|
+
'とうぞく',
|
|
24
|
+
'あそびにん',
|
|
25
|
+
'けんじゃ',
|
|
26
|
+
'バトルマスター',
|
|
27
|
+
'パラディン',
|
|
28
|
+
'レンジャー',
|
|
29
|
+
'スーパースター',
|
|
30
|
+
];
|
|
31
|
+
const DQ_JOBS_EN = [
|
|
32
|
+
'Hero',
|
|
33
|
+
'Warrior',
|
|
34
|
+
'Mage',
|
|
35
|
+
'Priest',
|
|
36
|
+
'Martial Artist',
|
|
37
|
+
'Thief',
|
|
38
|
+
'Gadabout',
|
|
39
|
+
'Sage',
|
|
40
|
+
'Battle Master',
|
|
41
|
+
'Paladin',
|
|
42
|
+
'Ranger',
|
|
43
|
+
'Superstar',
|
|
44
|
+
];
|
|
17
45
|
// Round border characters
|
|
18
46
|
const BORDER = {
|
|
19
47
|
topLeft: '╭',
|
|
@@ -81,6 +109,12 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
81
109
|
const { stdout } = useStdout();
|
|
82
110
|
const history = useHistory();
|
|
83
111
|
const i18n = t();
|
|
112
|
+
// Random job class (stable across re-renders)
|
|
113
|
+
const [jobClass] = useState(() => {
|
|
114
|
+
const isJapanese = getLocale() === 'ja';
|
|
115
|
+
const jobs = isJapanese ? DQ_JOBS_JA : DQ_JOBS_EN;
|
|
116
|
+
return jobs[Math.floor(Math.random() * jobs.length)];
|
|
117
|
+
});
|
|
84
118
|
const [mode, setMode] = useState('normal');
|
|
85
119
|
const [paneFocus, setPaneFocus] = useState('tabs');
|
|
86
120
|
const [currentTabIndex, setCurrentTabIndex] = useState(0);
|
|
@@ -105,7 +139,12 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
105
139
|
const [taskToWaiting, setTaskToWaiting] = useState(null);
|
|
106
140
|
const [taskToDelete, setTaskToDelete] = useState(null);
|
|
107
141
|
const [projectProgress, setProjectProgress] = useState({});
|
|
108
|
-
|
|
142
|
+
// Context filter state - load from config for persistence across sessions/terminals
|
|
143
|
+
const [contextFilter, setContextFilterState] = useState(() => getContextFilter());
|
|
144
|
+
const setContextFilter = useCallback((value) => {
|
|
145
|
+
setContextFilterState(value);
|
|
146
|
+
saveContextFilter(value);
|
|
147
|
+
}, []);
|
|
109
148
|
const [contextSelectIndex, setContextSelectIndex] = useState(0);
|
|
110
149
|
const [availableContexts, setAvailableContexts] = useState([]);
|
|
111
150
|
const [searchQuery, setSearchQuery] = useState('');
|
|
@@ -325,6 +364,46 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
325
364
|
setMessage(fmt(i18n.tui.linkedToProject || 'Linked "{title}" to {project}', { title: task.title, project: project.title }));
|
|
326
365
|
await loadTasks();
|
|
327
366
|
}, [i18n.tui.linkedToProject, loadTasks, history]);
|
|
367
|
+
const addCommentToTask = useCallback(async (task, content) => {
|
|
368
|
+
const commentId = uuidv4();
|
|
369
|
+
const command = new CreateCommentCommand({
|
|
370
|
+
comment: {
|
|
371
|
+
id: commentId,
|
|
372
|
+
taskId: task.id,
|
|
373
|
+
content: content.trim(),
|
|
374
|
+
createdAt: new Date(),
|
|
375
|
+
},
|
|
376
|
+
description: i18n.tui.commentAdded || 'Comment added',
|
|
377
|
+
});
|
|
378
|
+
await history.execute(command);
|
|
379
|
+
setMessage(i18n.tui.commentAdded || 'Comment added');
|
|
380
|
+
await loadTaskComments(task.id);
|
|
381
|
+
}, [history, i18n.tui.commentAdded, loadTaskComments]);
|
|
382
|
+
const deleteComment = useCallback(async (comment) => {
|
|
383
|
+
const command = new DeleteCommentCommand({
|
|
384
|
+
comment,
|
|
385
|
+
description: i18n.tui.commentDeleted || 'Comment deleted',
|
|
386
|
+
});
|
|
387
|
+
await history.execute(command);
|
|
388
|
+
setMessage(i18n.tui.commentDeleted || 'Comment deleted');
|
|
389
|
+
if (selectedTask) {
|
|
390
|
+
await loadTaskComments(selectedTask.id);
|
|
391
|
+
}
|
|
392
|
+
}, [i18n.tui.commentDeleted, loadTaskComments, selectedTask, history]);
|
|
393
|
+
const setTaskContext = useCallback(async (task, context) => {
|
|
394
|
+
const description = context
|
|
395
|
+
? fmt(i18n.tui.context?.contextSet || 'Set context @{context} for "{title}"', { context, title: task.title })
|
|
396
|
+
: fmt(i18n.tui.context?.contextCleared || 'Cleared context for "{title}"', { title: task.title });
|
|
397
|
+
const command = new SetContextCommand({
|
|
398
|
+
taskId: task.id,
|
|
399
|
+
fromContext: task.context,
|
|
400
|
+
toContext: context,
|
|
401
|
+
description,
|
|
402
|
+
});
|
|
403
|
+
await history.execute(command);
|
|
404
|
+
setMessage(description);
|
|
405
|
+
await loadTasks();
|
|
406
|
+
}, [i18n.tui.context, loadTasks, history]);
|
|
328
407
|
const handleInputSubmit = async (value) => {
|
|
329
408
|
// Handle search mode submit
|
|
330
409
|
if (mode === 'search') {
|
|
@@ -340,6 +419,15 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
340
419
|
setSearchResultIndex(0);
|
|
341
420
|
return;
|
|
342
421
|
}
|
|
422
|
+
// Handle add-comment mode
|
|
423
|
+
if (mode === 'add-comment' && selectedTask) {
|
|
424
|
+
if (value.trim()) {
|
|
425
|
+
await addCommentToTask(selectedTask, value);
|
|
426
|
+
}
|
|
427
|
+
setInputValue('');
|
|
428
|
+
setMode('task-detail');
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
343
431
|
if (mode === 'move-to-waiting' && taskToWaiting) {
|
|
344
432
|
if (value.trim()) {
|
|
345
433
|
await moveTaskToWaiting(taskToWaiting, value);
|
|
@@ -394,12 +482,57 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
394
482
|
else if (mode === 'add-context') {
|
|
395
483
|
setMode('set-context');
|
|
396
484
|
}
|
|
485
|
+
else if (mode === 'add-comment') {
|
|
486
|
+
setMode('task-detail');
|
|
487
|
+
}
|
|
397
488
|
else {
|
|
398
489
|
setMode('normal');
|
|
399
490
|
}
|
|
400
491
|
}
|
|
401
492
|
return;
|
|
402
493
|
}
|
|
494
|
+
// Handle task-detail mode
|
|
495
|
+
if (mode === 'task-detail') {
|
|
496
|
+
if (key.escape || input === 'b' || input === 'h' || key.leftArrow) {
|
|
497
|
+
setSelectedTask(null);
|
|
498
|
+
setTaskComments([]);
|
|
499
|
+
setSelectedCommentIndex(0);
|
|
500
|
+
setMode('normal');
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
// Navigate comments
|
|
504
|
+
if (key.upArrow || input === 'k') {
|
|
505
|
+
setSelectedCommentIndex((prev) => (prev > 0 ? prev - 1 : Math.max(0, taskComments.length - 1)));
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
if (key.downArrow || input === 'j') {
|
|
509
|
+
setSelectedCommentIndex((prev) => (prev < taskComments.length - 1 ? prev + 1 : 0));
|
|
510
|
+
return;
|
|
511
|
+
}
|
|
512
|
+
// Add comment
|
|
513
|
+
if (input === 'c' || input === 'i') {
|
|
514
|
+
setMode('add-comment');
|
|
515
|
+
return;
|
|
516
|
+
}
|
|
517
|
+
// Delete comment
|
|
518
|
+
if (input === 'D' && taskComments.length > 0) {
|
|
519
|
+
const comment = taskComments[selectedCommentIndex];
|
|
520
|
+
deleteComment(comment).then(() => {
|
|
521
|
+
if (selectedCommentIndex >= taskComments.length - 1) {
|
|
522
|
+
setSelectedCommentIndex(Math.max(0, selectedCommentIndex - 1));
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
// Link to project (P key)
|
|
528
|
+
if (input === 'P' && tasks.projects.length > 0) {
|
|
529
|
+
setTaskToLink(selectedTask);
|
|
530
|
+
setProjectSelectIndex(0);
|
|
531
|
+
setMode('select-project');
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
403
536
|
// Handle search mode
|
|
404
537
|
if (mode === 'search') {
|
|
405
538
|
if (key.escape) {
|
|
@@ -493,7 +626,13 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
493
626
|
setInputValue('');
|
|
494
627
|
return;
|
|
495
628
|
}
|
|
496
|
-
|
|
629
|
+
const task = currentTasks[selectedTaskIndex];
|
|
630
|
+
if (selected === 'clear') {
|
|
631
|
+
setTaskContext(task, null);
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
setTaskContext(task, selected);
|
|
635
|
+
}
|
|
497
636
|
setContextSelectIndex(0);
|
|
498
637
|
setMode('normal');
|
|
499
638
|
return;
|
|
@@ -503,9 +642,10 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
503
642
|
// Handle select-project mode
|
|
504
643
|
if (mode === 'select-project') {
|
|
505
644
|
if (key.escape) {
|
|
645
|
+
const wasFromTaskDetail = selectedTask !== null;
|
|
506
646
|
setTaskToLink(null);
|
|
507
647
|
setProjectSelectIndex(0);
|
|
508
|
-
setMode('normal');
|
|
648
|
+
setMode(wasFromTaskDetail ? 'task-detail' : 'normal');
|
|
509
649
|
return;
|
|
510
650
|
}
|
|
511
651
|
if (key.upArrow || input === 'k') {
|
|
@@ -518,10 +658,11 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
518
658
|
}
|
|
519
659
|
if (key.return && taskToLink && tasks.projects.length > 0) {
|
|
520
660
|
const project = tasks.projects[projectSelectIndex];
|
|
661
|
+
const wasFromTaskDetail = selectedTask !== null;
|
|
521
662
|
linkTaskToProject(taskToLink, project);
|
|
522
663
|
setTaskToLink(null);
|
|
523
664
|
setProjectSelectIndex(0);
|
|
524
|
-
setMode('normal');
|
|
665
|
+
setMode(wasFromTaskDetail ? 'task-detail' : 'normal');
|
|
525
666
|
return;
|
|
526
667
|
}
|
|
527
668
|
return;
|
|
@@ -641,6 +782,14 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
641
782
|
setSelectedTaskIndex((prev) => (prev < currentTasks.length - 1 ? prev + 1 : 0));
|
|
642
783
|
return;
|
|
643
784
|
}
|
|
785
|
+
// Enter to view task details (on non-projects tabs)
|
|
786
|
+
if (key.return && currentTab !== 'projects' && currentTasks.length > 0) {
|
|
787
|
+
const task = currentTasks[selectedTaskIndex];
|
|
788
|
+
setSelectedTask(task);
|
|
789
|
+
loadTaskComments(task.id);
|
|
790
|
+
setMode('task-detail');
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
644
793
|
// Task actions
|
|
645
794
|
if (currentTasks.length > 0) {
|
|
646
795
|
const task = currentTasks[selectedTaskIndex];
|
|
@@ -708,6 +857,12 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
708
857
|
setMode('confirm-delete');
|
|
709
858
|
return;
|
|
710
859
|
}
|
|
860
|
+
// Set context
|
|
861
|
+
if (input === 'c' && currentTab !== 'projects') {
|
|
862
|
+
setContextSelectIndex(0);
|
|
863
|
+
setMode('set-context');
|
|
864
|
+
return;
|
|
865
|
+
}
|
|
711
866
|
}
|
|
712
867
|
// Undo
|
|
713
868
|
if (input === 'u') {
|
|
@@ -741,10 +896,16 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
741
896
|
if (mode === 'help') {
|
|
742
897
|
return (_jsx(Box, { flexDirection: "column", padding: 1, children: _jsx(HelpModal, { onClose: () => setMode('normal') }) }));
|
|
743
898
|
}
|
|
744
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: theme.colors.
|
|
899
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsxs(Box, { marginBottom: 1, justifyContent: "space-between", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: jobClass }), _jsx(Text, { color: theme.colors.text, children: " Lv." }), _jsx(Text, { bold: true, color: theme.colors.secondary, children: Math.floor(tasks.done.length / 5) + 1 }), _jsx(Text, { color: theme.colors.text, children: " HP " }), _jsx(Text, { bold: true, color: theme.colors.statusNext, children: tasks.inbox.length + tasks.next.length }), _jsxs(Text, { color: theme.colors.textMuted, children: ["/", tasks.inbox.length + tasks.next.length + tasks.waiting.length + tasks.someday.length] }), _jsx(Text, { color: theme.colors.text, children: " MP " }), _jsx(Text, { bold: true, color: theme.colors.statusWaiting, children: tasks.projects.length }), _jsx(Text, { color: tursoEnabled ? theme.colors.accent : theme.colors.textMuted, children: tursoEnabled ? ' [TURSO]' : '' }), contextFilter !== null && (_jsxs(Text, { color: theme.colors.accent, children: [' ', "@", contextFilter === '' ? 'none' : contextFilter] }))] }), _jsx(Text, { color: theme.colors.textMuted, children: "?=help q=quit" })] }), mode === 'context-filter' ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: "Filter by context" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: ['all', 'none', ...availableContexts].map((ctx, index) => {
|
|
745
900
|
const label = ctx === 'all' ? 'All' : ctx === 'none' ? 'No context' : `@${ctx}`;
|
|
746
901
|
return (_jsxs(Text, { color: index === contextSelectIndex ? theme.colors.textSelected : theme.colors.text, bold: index === contextSelectIndex, children: [index === contextSelectIndex ? '▶ ' : ' ', label] }, ctx));
|
|
747
|
-
}) })] })) : mode === '
|
|
902
|
+
}) })] })) : mode === 'set-context' && currentTasks.length > 0 ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.context?.setContext || 'Set context for', ": ", currentTasks[selectedTaskIndex]?.title] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: ['clear', ...availableContexts, 'new'].map((ctx, index) => {
|
|
903
|
+
const label = ctx === 'clear' ? (i18n.tui.context?.none || 'Clear context') : ctx === 'new' ? (i18n.tui.context?.addNew || 'New context...') : `@${ctx}`;
|
|
904
|
+
return (_jsxs(Text, { color: index === contextSelectIndex ? theme.colors.textSelected : theme.colors.text, bold: index === contextSelectIndex, children: [index === contextSelectIndex ? '▶ ' : ' ', label] }, ctx));
|
|
905
|
+
}) })] })) : mode === 'select-project' && taskToLink ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.selectProject || 'Select project', ": ", taskToLink.title] }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: tasks.projects.map((project, index) => (_jsxs(Text, { color: index === projectSelectIndex ? theme.colors.textSelected : theme.colors.text, bold: index === projectSelectIndex, children: [index === projectSelectIndex ? '▶ ' : ' ', project.title] }, project.id))) })] })) : mode === 'confirm-delete' && taskToDelete ? (_jsx(Box, { flexDirection: "column", children: _jsx(Text, { color: theme.colors.accent, bold: true, children: fmt(i18n.tui.deleteConfirm || 'Delete "{title}"? (y/n)', { title: taskToDelete.title }) }) })) : (mode === 'task-detail' || mode === 'add-comment') && selectedTask ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(TitledBoxInline, { title: i18n.tui.taskDetailTitle || 'Task Details', width: terminalWidth - 4, minHeight: 4, isActive: true, children: [_jsx(Text, { color: theme.colors.text, bold: true, children: selectedTask.title }), selectedTask.description && (_jsx(Text, { color: theme.colors.textMuted, children: selectedTask.description })), _jsxs(Box, { children: [_jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.taskDetailStatus || 'Status', ": "] }), _jsxs(Text, { color: theme.colors.accent, children: [i18n.status[selectedTask.status], selectedTask.waitingFor && ` (${selectedTask.waitingFor})`] })] }), _jsxs(Box, { children: [_jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.context?.label || 'Context', ": "] }), _jsx(Text, { color: theme.colors.accent, children: selectedTask.context ? `@${selectedTask.context}` : (i18n.tui.context?.none || 'No context') })] })] }), _jsx(Box, { marginTop: 1, children: _jsx(TitledBoxInline, { title: `${i18n.tui.comments || 'Comments'} (${taskComments.length})`, width: terminalWidth - 4, minHeight: 5, isActive: mode === 'task-detail', children: taskComments.length === 0 ? (_jsx(Text, { color: theme.colors.textMuted, italic: true, children: i18n.tui.noComments || 'No comments yet' })) : (taskComments.map((comment, index) => {
|
|
906
|
+
const isSelected = index === selectedCommentIndex && mode === 'task-detail';
|
|
907
|
+
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { color: theme.colors.textMuted, children: [isSelected ? '▶ ' : ' ', "[", comment.createdAt.toLocaleString(), "]"] }), _jsxs(Text, { color: isSelected ? theme.colors.textSelected : theme.colors.text, bold: isSelected, children: [' ', comment.content] })] }, comment.id));
|
|
908
|
+
})) }) })] })) : (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { marginRight: 2, children: _jsx(TitledBoxInline, { title: mode === 'project-detail' && selectedProject ? selectedProject.title : 'GTD', width: leftPaneWidth, minHeight: 8, isActive: paneFocus === 'tabs' && mode !== 'project-detail', children: mode === 'project-detail' ? (_jsx(Text, { color: theme.colors.textMuted, children: "\u2190 Esc/b: back" })) : (TABS.map((tab, index) => {
|
|
748
909
|
const isSelected = index === currentTabIndex;
|
|
749
910
|
const count = tasks[tab].length;
|
|
750
911
|
return (_jsxs(Text, { color: isSelected && paneFocus === 'tabs' ? theme.colors.textSelected : theme.colors.text, bold: isSelected, children: [isSelected ? '▶ ' : ' ', index + 1, ":", getTabLabel(tab), " (", count, ")"] }, tab));
|
|
@@ -765,9 +926,11 @@ export function GtdDQ({ onOpenSettings }) {
|
|
|
765
926
|
return (_jsxs(Text, { color: isSelected ? theme.colors.textSelected : theme.colors.text, bold: isSelected, children: [prefix, displayTitle, task.waitingFor && _jsxs(Text, { color: theme.colors.muted, children: [" (", task.waitingFor, ")"] }), task.context && _jsxs(Text, { color: theme.colors.muted, children: [" @", task.context] }), parentProject && _jsxs(Text, { color: theme.colors.muted, children: [" [", parentProject.title, "]"] }), progress && _jsxs(Text, { color: theme.colors.muted, children: [" [", progress.completed, "/", progress.total, "]"] })] }, task.id));
|
|
766
927
|
})) }) })] })), (mode === 'add' || mode === 'add-to-project') && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: mode === 'add-to-project' && selectedProject
|
|
767
928
|
? `${i18n.tui.newTask}[${selectedProject.title}] `
|
|
768
|
-
: i18n.tui.newTask }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: i18n.tui.placeholder })] })), mode === 'move-to-waiting' && taskToWaiting && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: i18n.tui.waitingFor }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: "" })] })), mode === 'add-context' && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: "New context: " }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: "Enter context name..." })] })), mode === 'search' && (_jsx(SearchBar, { value: searchQuery, onChange: handleSearchChange, onSubmit: handleInputSubmit })), mode === 'search' && searchQuery && (_jsx(SearchResults, { results: searchResults, selectedIndex: searchResultIndex, query: searchQuery })), message && mode === 'normal' && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textHighlight, children: message }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textMuted, children: mode === '
|
|
769
|
-
?
|
|
770
|
-
:
|
|
771
|
-
?
|
|
772
|
-
:
|
|
929
|
+
: i18n.tui.newTask }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: i18n.tui.placeholder })] })), mode === 'move-to-waiting' && taskToWaiting && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: i18n.tui.waitingFor }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: "" })] })), mode === 'add-context' && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: "New context: " }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: "Enter context name..." })] })), mode === 'add-comment' && selectedTask && (_jsxs(Box, { marginTop: 1, children: [_jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.addComment || 'Add comment', ": "] }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: "Enter comment..." })] })), mode === 'search' && (_jsx(SearchBar, { value: searchQuery, onChange: handleSearchChange, onSubmit: handleInputSubmit })), mode === 'search' && searchQuery && (_jsx(SearchResults, { results: searchResults, selectedIndex: searchResultIndex, query: searchQuery })), message && mode === 'normal' && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textHighlight, children: message }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textMuted, children: mode === 'task-detail'
|
|
930
|
+
? i18n.tui.dqFooter.taskDetail
|
|
931
|
+
: mode === 'project-detail'
|
|
932
|
+
? i18n.tui.dqFooter.projectDetail
|
|
933
|
+
: paneFocus === 'tabs'
|
|
934
|
+
? i18n.tui.dqFooter.tabs
|
|
935
|
+
: i18n.tui.dqFooter.tasks }) })] }));
|
|
773
936
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
type SettingsMode = 'none' | 'theme-select' | 'mode-select' | 'lang-select';
|
|
3
|
+
interface GtdMarioProps {
|
|
4
|
+
onOpenSettings?: (mode: SettingsMode) => void;
|
|
5
|
+
}
|
|
6
|
+
export declare function GtdMario({ onOpenSettings }: GtdMarioProps): React.ReactElement;
|
|
7
|
+
export {};
|