floq 0.2.0 → 0.2.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/dist/i18n/en.d.ts +41 -0
- package/dist/i18n/en.js +22 -1
- package/dist/i18n/ja.js +22 -1
- package/dist/ui/App.js +44 -16
- package/dist/ui/components/HelpModal.js +41 -4
- package/dist/ui/components/KanbanBoard.js +4 -14
- package/package.json +1 -1
package/dist/i18n/en.d.ts
CHANGED
|
@@ -107,6 +107,7 @@ export declare const en: {
|
|
|
107
107
|
title: string;
|
|
108
108
|
whatsNewTab: string;
|
|
109
109
|
keybindingsTab: string;
|
|
110
|
+
infoTab: string;
|
|
110
111
|
tabHint: string;
|
|
111
112
|
navigation: string;
|
|
112
113
|
tabSwitch: string;
|
|
@@ -168,6 +169,10 @@ export declare const en: {
|
|
|
168
169
|
commentDeleted: string;
|
|
169
170
|
taskDetailTitle: string;
|
|
170
171
|
taskDetailFooter: string;
|
|
172
|
+
taskDetailStatus: string;
|
|
173
|
+
deleteConfirm: string;
|
|
174
|
+
deleted: string;
|
|
175
|
+
deleteCancelled: string;
|
|
171
176
|
comments: string;
|
|
172
177
|
projectTasks: string;
|
|
173
178
|
search: {
|
|
@@ -178,6 +183,21 @@ export declare const en: {
|
|
|
178
183
|
resultsTitle: string;
|
|
179
184
|
searchTasks: string;
|
|
180
185
|
};
|
|
186
|
+
info: {
|
|
187
|
+
settings: string;
|
|
188
|
+
database: string;
|
|
189
|
+
paths: string;
|
|
190
|
+
theme: string;
|
|
191
|
+
language: string;
|
|
192
|
+
viewMode: string;
|
|
193
|
+
dbType: string;
|
|
194
|
+
dbPath: string;
|
|
195
|
+
tursoUrl: string;
|
|
196
|
+
configFile: string;
|
|
197
|
+
dataDir: string;
|
|
198
|
+
local: string;
|
|
199
|
+
turso: string;
|
|
200
|
+
};
|
|
181
201
|
};
|
|
182
202
|
setup: {
|
|
183
203
|
welcome: {
|
|
@@ -232,6 +252,7 @@ export type HelpTranslations = {
|
|
|
232
252
|
title: string;
|
|
233
253
|
whatsNewTab: string;
|
|
234
254
|
keybindingsTab: string;
|
|
255
|
+
infoTab: string;
|
|
235
256
|
tabHint: string;
|
|
236
257
|
navigation: string;
|
|
237
258
|
tabSwitch: string;
|
|
@@ -312,6 +333,21 @@ export type SearchTranslations = {
|
|
|
312
333
|
resultsTitle: string;
|
|
313
334
|
searchTasks: string;
|
|
314
335
|
};
|
|
336
|
+
export type InfoTranslations = {
|
|
337
|
+
settings: string;
|
|
338
|
+
database: string;
|
|
339
|
+
paths: string;
|
|
340
|
+
theme: string;
|
|
341
|
+
language: string;
|
|
342
|
+
viewMode: string;
|
|
343
|
+
dbType: string;
|
|
344
|
+
dbPath: string;
|
|
345
|
+
tursoUrl: string;
|
|
346
|
+
configFile: string;
|
|
347
|
+
dataDir: string;
|
|
348
|
+
local: string;
|
|
349
|
+
turso: string;
|
|
350
|
+
};
|
|
315
351
|
export type TuiTranslations = {
|
|
316
352
|
title: string;
|
|
317
353
|
helpHint: string;
|
|
@@ -342,6 +378,7 @@ export type TuiTranslations = {
|
|
|
342
378
|
whatsNew: WhatsNewTranslations;
|
|
343
379
|
kanbanHelp: KanbanHelpTranslations;
|
|
344
380
|
search: SearchTranslations;
|
|
381
|
+
info: InfoTranslations;
|
|
345
382
|
addComment: string;
|
|
346
383
|
noComments: string;
|
|
347
384
|
commentHint: string;
|
|
@@ -350,6 +387,10 @@ export type TuiTranslations = {
|
|
|
350
387
|
commentDeleted: string;
|
|
351
388
|
taskDetailTitle: string;
|
|
352
389
|
taskDetailFooter: string;
|
|
390
|
+
taskDetailStatus: string;
|
|
391
|
+
deleteConfirm: string;
|
|
392
|
+
deleted: string;
|
|
393
|
+
deleteCancelled: string;
|
|
353
394
|
comments: string;
|
|
354
395
|
projectTasks: string;
|
|
355
396
|
};
|
package/dist/i18n/en.js
CHANGED
|
@@ -81,7 +81,7 @@ export const en = {
|
|
|
81
81
|
movedToWaiting: 'Moved "{title}" to Waiting (for {person})',
|
|
82
82
|
waitingFor: 'Waiting for: ',
|
|
83
83
|
refreshed: 'Refreshed',
|
|
84
|
-
footer: 'a=add d=done n=next s=someday w=waiting i=inbox p=project P=link',
|
|
84
|
+
footer: 'a=add d=done D=delete n=next s=someday w=waiting i=inbox p=project P=link',
|
|
85
85
|
noTasks: 'No tasks',
|
|
86
86
|
// Tab labels
|
|
87
87
|
tabInbox: 'Inbox',
|
|
@@ -116,6 +116,7 @@ export const en = {
|
|
|
116
116
|
title: 'Keyboard Shortcuts',
|
|
117
117
|
whatsNewTab: "What's New",
|
|
118
118
|
keybindingsTab: 'Keybindings',
|
|
119
|
+
infoTab: 'Info',
|
|
119
120
|
tabHint: 'Tab: switch view',
|
|
120
121
|
navigation: 'Navigation',
|
|
121
122
|
tabSwitch: 'Switch tab (5=Projects, 6=Done)',
|
|
@@ -179,6 +180,10 @@ export const en = {
|
|
|
179
180
|
commentDeleted: 'Comment deleted',
|
|
180
181
|
taskDetailTitle: 'Task Details',
|
|
181
182
|
taskDetailFooter: 'j/k=select i=comment d=delete P=link b/Esc=back',
|
|
183
|
+
taskDetailStatus: 'Status',
|
|
184
|
+
deleteConfirm: 'Delete "{title}"? (y/n)',
|
|
185
|
+
deleted: 'Deleted: "{title}"',
|
|
186
|
+
deleteCancelled: 'Delete cancelled',
|
|
182
187
|
comments: 'Comments',
|
|
183
188
|
projectTasks: 'Tasks',
|
|
184
189
|
// Search
|
|
@@ -190,6 +195,22 @@ export const en = {
|
|
|
190
195
|
resultsTitle: 'Search Results',
|
|
191
196
|
searchTasks: 'Search tasks',
|
|
192
197
|
},
|
|
198
|
+
// Info tab
|
|
199
|
+
info: {
|
|
200
|
+
settings: 'Settings',
|
|
201
|
+
database: 'Database',
|
|
202
|
+
paths: 'Paths',
|
|
203
|
+
theme: 'Theme',
|
|
204
|
+
language: 'Language',
|
|
205
|
+
viewMode: 'View Mode',
|
|
206
|
+
dbType: 'Type',
|
|
207
|
+
dbPath: 'Path',
|
|
208
|
+
tursoUrl: 'Turso URL',
|
|
209
|
+
configFile: 'Config File',
|
|
210
|
+
dataDir: 'Data Directory',
|
|
211
|
+
local: 'local',
|
|
212
|
+
turso: 'turso',
|
|
213
|
+
},
|
|
193
214
|
},
|
|
194
215
|
// Setup wizard
|
|
195
216
|
setup: {
|
package/dist/i18n/ja.js
CHANGED
|
@@ -81,7 +81,7 @@ export const ja = {
|
|
|
81
81
|
movedToWaiting: '「{title}」を連絡待ち({person})に移動しました',
|
|
82
82
|
waitingFor: '待機相手: ',
|
|
83
83
|
refreshed: '更新しました',
|
|
84
|
-
footer: 'a=追加 d=完了 n=次 s=いつか w=待ち i=Inbox p=プロジェクト化 P=紐づけ',
|
|
84
|
+
footer: 'a=追加 d=完了 D=削除 n=次 s=いつか w=待ち i=Inbox p=プロジェクト化 P=紐づけ',
|
|
85
85
|
noTasks: 'タスクなし',
|
|
86
86
|
// Tab labels
|
|
87
87
|
tabInbox: 'Inbox',
|
|
@@ -116,6 +116,7 @@ export const ja = {
|
|
|
116
116
|
title: 'キーボードショートカット',
|
|
117
117
|
whatsNewTab: '更新履歴',
|
|
118
118
|
keybindingsTab: 'キー操作',
|
|
119
|
+
infoTab: '情報',
|
|
119
120
|
tabHint: 'Tab: 表示切替',
|
|
120
121
|
navigation: 'ナビゲーション',
|
|
121
122
|
tabSwitch: 'タブ切替 (5=プロジェクト, 6=完了)',
|
|
@@ -179,6 +180,10 @@ export const ja = {
|
|
|
179
180
|
commentDeleted: 'コメントを削除しました',
|
|
180
181
|
taskDetailTitle: 'タスク詳細',
|
|
181
182
|
taskDetailFooter: 'j/k=選択 i=コメント d=削除 P=紐づけ b/Esc=戻る',
|
|
183
|
+
taskDetailStatus: 'ステータス',
|
|
184
|
+
deleteConfirm: '「{title}」を削除しますか? (y/n)',
|
|
185
|
+
deleted: '削除しました: 「{title}」',
|
|
186
|
+
deleteCancelled: '削除をキャンセルしました',
|
|
182
187
|
comments: 'コメント',
|
|
183
188
|
projectTasks: 'タスク一覧',
|
|
184
189
|
// Search
|
|
@@ -190,6 +195,22 @@ export const ja = {
|
|
|
190
195
|
resultsTitle: '検索結果',
|
|
191
196
|
searchTasks: 'タスク検索',
|
|
192
197
|
},
|
|
198
|
+
// Info tab
|
|
199
|
+
info: {
|
|
200
|
+
settings: '設定',
|
|
201
|
+
database: 'データベース',
|
|
202
|
+
paths: 'パス',
|
|
203
|
+
theme: 'テーマ',
|
|
204
|
+
language: '言語',
|
|
205
|
+
viewMode: '表示モード',
|
|
206
|
+
dbType: '種類',
|
|
207
|
+
dbPath: 'パス',
|
|
208
|
+
tursoUrl: 'Turso URL',
|
|
209
|
+
configFile: '設定ファイル',
|
|
210
|
+
dataDir: 'データディレクトリ',
|
|
211
|
+
local: 'ローカル',
|
|
212
|
+
turso: 'turso',
|
|
213
|
+
},
|
|
193
214
|
},
|
|
194
215
|
// Setup wizard
|
|
195
216
|
setup: {
|
package/dist/ui/App.js
CHANGED
|
@@ -13,7 +13,7 @@ import { SplashScreen } from './SplashScreen.js';
|
|
|
13
13
|
import { getDb, schema } from '../db/index.js';
|
|
14
14
|
import { t, fmt } from '../i18n/index.js';
|
|
15
15
|
import { ThemeProvider, useTheme } from './theme/index.js';
|
|
16
|
-
import { getThemeName, getViewMode, isTursoEnabled
|
|
16
|
+
import { getThemeName, getViewMode, isTursoEnabled } from '../config.js';
|
|
17
17
|
import { KanbanBoard } from './components/KanbanBoard.js';
|
|
18
18
|
import { VERSION } from '../version.js';
|
|
19
19
|
const TABS = ['inbox', 'next', 'waiting', 'someday', 'projects', 'done'];
|
|
@@ -46,6 +46,7 @@ function AppContent() {
|
|
|
46
46
|
const [taskComments, setTaskComments] = useState([]);
|
|
47
47
|
const [selectedCommentIndex, setSelectedCommentIndex] = useState(0);
|
|
48
48
|
const [taskToWaiting, setTaskToWaiting] = useState(null);
|
|
49
|
+
const [taskToDelete, setTaskToDelete] = useState(null);
|
|
49
50
|
const [projectProgress, setProjectProgress] = useState({});
|
|
50
51
|
// Search state
|
|
51
52
|
const [searchQuery, setSearchQuery] = useState('');
|
|
@@ -267,6 +268,15 @@ function AppContent() {
|
|
|
267
268
|
setMessage(fmt(i18n.tui.madeProject || 'Made project: {title}', { title: task.title }));
|
|
268
269
|
await loadTasks();
|
|
269
270
|
}, [i18n.tui.madeProject, loadTasks]);
|
|
271
|
+
const deleteTask = useCallback(async (task) => {
|
|
272
|
+
const db = getDb();
|
|
273
|
+
// Delete comments first
|
|
274
|
+
await db.delete(schema.comments).where(eq(schema.comments.taskId, task.id));
|
|
275
|
+
// Delete the task
|
|
276
|
+
await db.delete(schema.tasks).where(eq(schema.tasks.id, task.id));
|
|
277
|
+
setMessage(fmt(i18n.tui.deleted || 'Deleted: "{title}"', { title: task.title }));
|
|
278
|
+
await loadTasks();
|
|
279
|
+
}, [i18n.tui.deleted, loadTasks]);
|
|
270
280
|
const getTabLabel = (tab) => {
|
|
271
281
|
switch (tab) {
|
|
272
282
|
case 'inbox':
|
|
@@ -314,6 +324,27 @@ function AppContent() {
|
|
|
314
324
|
// Let TextInput handle other keys
|
|
315
325
|
return;
|
|
316
326
|
}
|
|
327
|
+
// Handle confirm-delete mode
|
|
328
|
+
if (mode === 'confirm-delete' && taskToDelete) {
|
|
329
|
+
if (input === 'y' || input === 'Y') {
|
|
330
|
+
deleteTask(taskToDelete).then(() => {
|
|
331
|
+
if (selectedTaskIndex >= currentTasks.length - 1) {
|
|
332
|
+
setSelectedTaskIndex(Math.max(0, selectedTaskIndex - 1));
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
setTaskToDelete(null);
|
|
336
|
+
setMode('normal');
|
|
337
|
+
return;
|
|
338
|
+
}
|
|
339
|
+
if (input === 'n' || input === 'N' || key.escape) {
|
|
340
|
+
setMessage(i18n.tui.deleteCancelled || 'Delete cancelled');
|
|
341
|
+
setTaskToDelete(null);
|
|
342
|
+
setMode('normal');
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
// Ignore other keys in confirm mode
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
317
348
|
// Handle add mode
|
|
318
349
|
if (mode === 'add' || mode === 'add-to-project' || mode === 'add-comment' || mode === 'move-to-waiting') {
|
|
319
350
|
if (key.escape) {
|
|
@@ -626,6 +657,13 @@ function AppContent() {
|
|
|
626
657
|
});
|
|
627
658
|
return;
|
|
628
659
|
}
|
|
660
|
+
// Delete task (D key - with confirmation)
|
|
661
|
+
if (input === 'D' && currentTasks.length > 0 && currentTab !== 'projects') {
|
|
662
|
+
const task = currentTasks[selectedTaskIndex];
|
|
663
|
+
setTaskToDelete(task);
|
|
664
|
+
setMode('confirm-delete');
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
629
667
|
// Move to next actions
|
|
630
668
|
if (input === 'n' && currentTasks.length > 0 && currentTab !== 'next' && currentTab !== 'projects' && currentTab !== 'done') {
|
|
631
669
|
const task = currentTasks[selectedTaskIndex];
|
|
@@ -685,24 +723,14 @@ function AppContent() {
|
|
|
685
723
|
};
|
|
686
724
|
// Turso 接続情報を取得
|
|
687
725
|
const tursoEnabled = isTursoEnabled();
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
try {
|
|
692
|
-
return new URL(config.url).host;
|
|
693
|
-
}
|
|
694
|
-
catch {
|
|
695
|
-
return config.url;
|
|
696
|
-
}
|
|
697
|
-
}
|
|
698
|
-
return '';
|
|
699
|
-
})() : '';
|
|
700
|
-
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.primary, children: formatTitle(i18n.tui.title) }), _jsx(Text, { color: theme.colors.textMuted, children: theme.name === 'modern' ? ` v${VERSION}` : ` VER ${VERSION}` }), tursoEnabled && (_jsxs(Text, { color: theme.colors.accent, children: [theme.name === 'modern' ? ' ☁️ ' : ' [SYNC] ', tursoHost] })), !tursoEnabled && (_jsx(Text, { color: theme.colors.textMuted, children: theme.name === 'modern' ? ' 💾 local' : ' [LOCAL]' }))] }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.helpHint })] }), _jsx(Box, { marginBottom: 1, children: TABS.map((tab, index) => {
|
|
726
|
+
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.primary, children: formatTitle(i18n.tui.title) }), _jsx(Text, { color: theme.colors.textMuted, children: theme.name === 'modern' ? ` v${VERSION}` : ` VER ${VERSION}` }), _jsx(Text, { color: tursoEnabled ? theme.colors.accent : theme.colors.textMuted, children: theme.name === 'modern'
|
|
727
|
+
? (tursoEnabled ? ' ☁️ turso' : ' 💾 local')
|
|
728
|
+
: (tursoEnabled ? ' [DB]turso' : ' [DB]local') })] }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.helpHint })] }), _jsx(Box, { marginBottom: 1, children: TABS.map((tab, index) => {
|
|
701
729
|
const isActive = index === currentListIndex && mode !== 'project-detail';
|
|
702
730
|
const count = tasks[tab].length;
|
|
703
731
|
const label = `${index + 1}:${getTabLabel(tab)}(${count})`;
|
|
704
732
|
return (_jsx(Box, { marginRight: 1, children: _jsx(Text, { color: isActive ? theme.colors.textSelected : theme.colors.textMuted, bold: isActive, inverse: isActive && theme.style.tabActiveInverse, children: formatTabLabel(` ${label} `, isActive) }) }, tab));
|
|
705
|
-
}) }), mode === 'project-detail' && selectedProject && (_jsxs(_Fragment, { children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: theme.colors.accent, bold: true, children: [theme.name === 'modern' ? '📁 ' : '>> ', selectedProject.title] }), _jsxs(Text, { color: theme.colors.textMuted, children: [" (Esc/b: ", i18n.tui.back || 'back', ")"] })] }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.projectTasks || 'Tasks', " (", projectTasks.length, ")"] }) })] })), (mode === 'task-detail' || mode === 'add-comment') && selectedTask && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: theme.colors.accent, bold: true, children: [theme.name === 'modern' ? '📋 ' : '>> ', i18n.tui.taskDetailTitle || 'Task Details'] }), _jsxs(Text, { color: theme.colors.textMuted, children: [" (Esc/b: ", i18n.tui.back || 'back', ", ", i18n.tui.commentHint || 'i: add comment', ")"] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, marginBottom: 1, children: [_jsx(Text, { color: theme.colors.text, bold: true, children: selectedTask.title }), selectedTask.description && (_jsx(Text, { color: theme.colors.textMuted, children: selectedTask.description })), _jsxs(Text, { color: theme.colors.
|
|
733
|
+
}) }), mode === 'project-detail' && selectedProject && (_jsxs(_Fragment, { children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: theme.colors.accent, bold: true, children: [theme.name === 'modern' ? '📁 ' : '>> ', selectedProject.title] }), _jsxs(Text, { color: theme.colors.textMuted, children: [" (Esc/b: ", i18n.tui.back || 'back', ")"] })] }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.projectTasks || 'Tasks', " (", projectTasks.length, ")"] }) })] })), (mode === 'task-detail' || mode === 'add-comment') && selectedTask && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: theme.colors.accent, bold: true, children: [theme.name === 'modern' ? '📋 ' : '>> ', i18n.tui.taskDetailTitle || 'Task Details'] }), _jsxs(Text, { color: theme.colors.textMuted, children: [" (Esc/b: ", i18n.tui.back || 'back', ", ", i18n.tui.commentHint || 'i: add comment', ")"] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, marginBottom: 1, 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, { marginTop: 1, children: [_jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.taskDetailStatus, ": "] }), _jsxs(Text, { color: theme.colors.accent, children: [i18n.status[selectedTask.status], selectedTask.waitingFor && ` (${selectedTask.waitingFor})`] })] })] }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.comments || 'Comments', " (", taskComments.length, ")"] }) }), _jsx(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, minHeight: 5, children: taskComments.length === 0 ? (_jsx(Text, { color: theme.colors.textMuted, italic: true, children: i18n.tui.noComments || 'No comments yet' })) : (taskComments.map((comment, index) => {
|
|
706
734
|
const isSelected = index === selectedCommentIndex && mode === 'task-detail';
|
|
707
735
|
return (_jsxs(Box, { flexDirection: "row", marginBottom: 1, children: [_jsx(Text, { color: isSelected ? theme.colors.textSelected : theme.colors.textMuted, children: isSelected ? theme.style.selectedPrefix : theme.style.unselectedPrefix }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.textMuted, children: ["[", comment.createdAt.toLocaleString(), "]"] }), _jsx(Text, { color: isSelected ? theme.colors.textSelected : theme.colors.text, bold: isSelected, children: comment.content })] })] }, comment.id));
|
|
708
736
|
})) }), mode === 'add-comment' && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: i18n.tui.addComment || 'New comment: ' }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: "" }), _jsxs(Text, { color: theme.colors.textMuted, children: [" ", i18n.tui.inputHelp] })] }))] })), mode === 'search' && searchQuery && (_jsx(SearchResults, { results: searchResults, selectedIndex: searchResultIndex, query: searchQuery })), mode !== 'task-detail' && mode !== 'add-comment' && mode !== 'search' && (_jsx(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, minHeight: 10, children: currentTasks.length === 0 ? (_jsx(Text, { color: theme.colors.textMuted, italic: true, children: i18n.tui.noTasks })) : (currentTasks.map((task, index) => {
|
|
@@ -711,7 +739,7 @@ function AppContent() {
|
|
|
711
739
|
return (_jsx(TaskItem, { task: task, isSelected: index === selectedTaskIndex, projectName: parentProject?.title, progress: progress }, task.id));
|
|
712
740
|
})) })), (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
|
|
713
741
|
? `${i18n.tui.newTask}[${selectedProject.title}] `
|
|
714
|
-
: i18n.tui.newTask }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: i18n.tui.placeholder }), _jsxs(Text, { color: theme.colors.textMuted, children: [" ", i18n.tui.inputHelp] })] })), 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: "" }), _jsxs(Text, { color: theme.colors.textMuted, children: [" ", i18n.tui.inputHelp] })] })), mode === 'select-project' && taskToLink && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.selectProject || 'Select project for', ": ", taskToLink.title] }), _jsx(Box, { flexDirection: "column", marginTop: 1, borderStyle: theme.borders.list, borderColor: theme.colors.borderActive, paddingX: 1, children: tasks.projects.map((project, index) => (_jsxs(Text, { color: index === projectSelectIndex ? theme.colors.textSelected : theme.colors.text, bold: index === projectSelectIndex, children: [index === projectSelectIndex ? theme.style.selectedPrefix : theme.style.unselectedPrefix, project.title] }, project.id))) }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.selectProjectHelp || 'j/k: select, Enter: confirm, Esc: cancel' })] })), mode === 'search' && (_jsx(SearchBar, { value: searchQuery, onChange: handleSearchChange, onSubmit: handleInputSubmit })), message && mode === 'normal' && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textHighlight, children: message }) })), _jsx(Box, { marginTop: 1, children: (mode === 'task-detail' || mode === 'add-comment') ? (theme.style.showFunctionKeys ? (_jsx(FunctionKeyBar, { keys: [
|
|
742
|
+
: i18n.tui.newTask }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: i18n.tui.placeholder }), _jsxs(Text, { color: theme.colors.textMuted, children: [" ", i18n.tui.inputHelp] })] })), 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: "" }), _jsxs(Text, { color: theme.colors.textMuted, children: [" ", i18n.tui.inputHelp] })] })), mode === 'select-project' && taskToLink && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.selectProject || 'Select project for', ": ", taskToLink.title] }), _jsx(Box, { flexDirection: "column", marginTop: 1, borderStyle: theme.borders.list, borderColor: theme.colors.borderActive, paddingX: 1, children: tasks.projects.map((project, index) => (_jsxs(Text, { color: index === projectSelectIndex ? theme.colors.textSelected : theme.colors.text, bold: index === projectSelectIndex, children: [index === projectSelectIndex ? theme.style.selectedPrefix : theme.style.unselectedPrefix, project.title] }, project.id))) }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.selectProjectHelp || 'j/k: select, Enter: confirm, Esc: cancel' })] })), mode === 'search' && (_jsx(SearchBar, { value: searchQuery, onChange: handleSearchChange, onSubmit: handleInputSubmit })), mode === 'confirm-delete' && taskToDelete && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.accent, bold: true, children: fmt(i18n.tui.deleteConfirm || 'Delete "{title}"? (y/n)', { title: taskToDelete.title }) }) })), message && mode === 'normal' && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textHighlight, children: message }) })), _jsx(Box, { marginTop: 1, children: (mode === 'task-detail' || mode === 'add-comment') ? (theme.style.showFunctionKeys ? (_jsx(FunctionKeyBar, { keys: [
|
|
715
743
|
{ key: 'i', label: i18n.tui.keyBar.comment },
|
|
716
744
|
{ key: 'd', label: i18n.tui.keyBar.delete },
|
|
717
745
|
{ key: 'P', label: i18n.tui.keyBar.project },
|
|
@@ -4,6 +4,8 @@ import { Box, Text, useInput } from 'ink';
|
|
|
4
4
|
import { t } from '../../i18n/index.js';
|
|
5
5
|
import { useTheme } from '../theme/index.js';
|
|
6
6
|
import { parseChangelog } from '../../changelog.js';
|
|
7
|
+
import { loadConfig, isTursoEnabled, getTursoConfig, getDbPath } from '../../config.js';
|
|
8
|
+
import { CONFIG_FILE, DATA_DIR } from '../../paths.js';
|
|
7
9
|
const VISIBLE_LINES = 12;
|
|
8
10
|
export function HelpModal({ onClose, isKanban = false }) {
|
|
9
11
|
const [activeTab, setActiveTab] = useState('keybindings');
|
|
@@ -27,10 +29,14 @@ export function HelpModal({ onClose, isKanban = false }) {
|
|
|
27
29
|
return items;
|
|
28
30
|
}, []);
|
|
29
31
|
const maxScroll = Math.max(0, changelogItems.length - VISIBLE_LINES);
|
|
32
|
+
const tabs = ['keybindings', 'info', 'whatsNew'];
|
|
30
33
|
useInput((input, key) => {
|
|
31
34
|
// Tab key detection (key.tab or raw tab character)
|
|
32
35
|
if (key.tab || input === '\t') {
|
|
33
|
-
setActiveTab(prev =>
|
|
36
|
+
setActiveTab(prev => {
|
|
37
|
+
const currentIndex = tabs.indexOf(prev);
|
|
38
|
+
return tabs[(currentIndex + 1) % tabs.length];
|
|
39
|
+
});
|
|
34
40
|
setScrollOffset(0);
|
|
35
41
|
return;
|
|
36
42
|
}
|
|
@@ -41,18 +47,29 @@ export function HelpModal({ onClose, isKanban = false }) {
|
|
|
41
47
|
return;
|
|
42
48
|
}
|
|
43
49
|
if (input === '2') {
|
|
50
|
+
setActiveTab('info');
|
|
51
|
+
setScrollOffset(0);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (input === '3') {
|
|
44
55
|
setActiveTab('whatsNew');
|
|
45
56
|
setScrollOffset(0);
|
|
46
57
|
return;
|
|
47
58
|
}
|
|
48
59
|
// Arrow keys and h/l for tab switching
|
|
49
60
|
if (input === 'h' || key.leftArrow) {
|
|
50
|
-
setActiveTab(
|
|
61
|
+
setActiveTab(prev => {
|
|
62
|
+
const currentIndex = tabs.indexOf(prev);
|
|
63
|
+
return tabs[(currentIndex - 1 + tabs.length) % tabs.length];
|
|
64
|
+
});
|
|
51
65
|
setScrollOffset(0);
|
|
52
66
|
return;
|
|
53
67
|
}
|
|
54
68
|
if (input === 'l' || key.rightArrow) {
|
|
55
|
-
setActiveTab(
|
|
69
|
+
setActiveTab(prev => {
|
|
70
|
+
const currentIndex = tabs.indexOf(prev);
|
|
71
|
+
return tabs[(currentIndex + 1) % tabs.length];
|
|
72
|
+
});
|
|
56
73
|
setScrollOffset(0);
|
|
57
74
|
return;
|
|
58
75
|
}
|
|
@@ -73,7 +90,7 @@ export function HelpModal({ onClose, isKanban = false }) {
|
|
|
73
90
|
}
|
|
74
91
|
});
|
|
75
92
|
const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
|
|
76
|
-
return (_jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.modal, borderColor: theme.colors.borderActive, paddingX: 2, paddingY: 1, children: [_jsx(Box, { justifyContent: "center", marginBottom: 1, children: _jsxs(Box, { children: [_jsxs(Text, { bold: activeTab === 'keybindings', color: activeTab === 'keybindings' ? theme.colors.secondary : theme.colors.textMuted, inverse: activeTab === 'keybindings', children: [' ', formatTitle(help.keybindingsTab), ' '] }), _jsx(Text, { color: theme.colors.textMuted, children: " \u2502 " }), _jsxs(Text, { bold: activeTab === 'whatsNew', color: activeTab === 'whatsNew' ? theme.colors.secondary : theme.colors.textMuted, inverse: activeTab === 'whatsNew', children: [' ', formatTitle(help.whatsNewTab), ' '] })] }) }), activeTab === 'keybindings' ? (isKanban ? (_jsx(KanbanKeybindingsContent, {})) : (_jsx(GTDKeybindingsContent, {}))) : (_jsx(WhatsNewContent, { items: changelogItems, scrollOffset: scrollOffset, visibleLines: VISIBLE_LINES, maxScroll: maxScroll })), _jsx(Box, { justifyContent: "center", marginTop: 1, children: _jsx(Text, { color: theme.colors.textMuted, children: activeTab === 'whatsNew' && maxScroll > 0
|
|
93
|
+
return (_jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.modal, borderColor: theme.colors.borderActive, paddingX: 2, paddingY: 1, children: [_jsx(Box, { justifyContent: "center", marginBottom: 1, children: _jsxs(Box, { children: [_jsxs(Text, { bold: activeTab === 'keybindings', color: activeTab === 'keybindings' ? theme.colors.secondary : theme.colors.textMuted, inverse: activeTab === 'keybindings', children: [' ', formatTitle(help.keybindingsTab), ' '] }), _jsx(Text, { color: theme.colors.textMuted, children: " \u2502 " }), _jsxs(Text, { bold: activeTab === 'info', color: activeTab === 'info' ? theme.colors.secondary : theme.colors.textMuted, inverse: activeTab === 'info', children: [' ', formatTitle(help.infoTab), ' '] }), _jsx(Text, { color: theme.colors.textMuted, children: " \u2502 " }), _jsxs(Text, { bold: activeTab === 'whatsNew', color: activeTab === 'whatsNew' ? theme.colors.secondary : theme.colors.textMuted, inverse: activeTab === 'whatsNew', children: [' ', formatTitle(help.whatsNewTab), ' '] })] }) }), activeTab === 'keybindings' ? (isKanban ? (_jsx(KanbanKeybindingsContent, {})) : (_jsx(GTDKeybindingsContent, {}))) : activeTab === 'info' ? (_jsx(InfoContent, {})) : (_jsx(WhatsNewContent, { items: changelogItems, scrollOffset: scrollOffset, visibleLines: VISIBLE_LINES, maxScroll: maxScroll })), _jsx(Box, { justifyContent: "center", marginTop: 1, children: _jsx(Text, { color: theme.colors.textMuted, children: activeTab === 'whatsNew' && maxScroll > 0
|
|
77
94
|
? `j/k: scroll | ${help.tabHint} | ${help.closeHint}`
|
|
78
95
|
: `${help.tabHint} | ${help.closeHint}` }) })] }));
|
|
79
96
|
}
|
|
@@ -91,6 +108,26 @@ function KanbanKeybindingsContent() {
|
|
|
91
108
|
const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
|
|
92
109
|
return (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(help.navigation) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "h/l \u2190/\u2192" }), " ", help.columnSwitch] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "1-3" }), " ", help.columnDirect] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "j/k \u2191/\u2193" }), " ", help.taskSelect] })] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(help.actions) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "a" }), " ", help.addTask] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "d" }), " ", help.completeTask] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "Enter" }), " ", help.moveRight] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "BS" }), " ", help.moveLeft] })] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(help.other) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "/" }), " ", help.searchTasks] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "?" }), " ", help.showHelp] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "q" }), " ", help.quit] })] })] })] }));
|
|
93
110
|
}
|
|
111
|
+
function InfoContent() {
|
|
112
|
+
const i18n = t();
|
|
113
|
+
const info = i18n.tui.info;
|
|
114
|
+
const theme = useTheme();
|
|
115
|
+
const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
|
|
116
|
+
const config = loadConfig();
|
|
117
|
+
const tursoEnabled = isTursoEnabled();
|
|
118
|
+
const tursoConfig = tursoEnabled ? getTursoConfig() : undefined;
|
|
119
|
+
const dbPath = getDbPath();
|
|
120
|
+
// Get Turso host for display
|
|
121
|
+
const tursoUrl = tursoConfig ? (() => {
|
|
122
|
+
try {
|
|
123
|
+
return new URL(tursoConfig.url).host;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return tursoConfig.url;
|
|
127
|
+
}
|
|
128
|
+
})() : '';
|
|
129
|
+
return (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(info.settings) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsxs(Text, { color: theme.colors.textHighlight, children: [info.theme, ":"] }), " ", config.theme] }), _jsxs(Text, { color: theme.colors.text, children: [_jsxs(Text, { color: theme.colors.textHighlight, children: [info.language, ":"] }), " ", config.locale] }), _jsxs(Text, { color: theme.colors.text, children: [_jsxs(Text, { color: theme.colors.textHighlight, children: [info.viewMode, ":"] }), " ", config.viewMode] })] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(info.database) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsxs(Text, { color: theme.colors.textHighlight, children: [info.dbType, ":"] }), " ", tursoEnabled ? info.turso : info.local] }), _jsxs(Text, { color: theme.colors.text, children: [_jsxs(Text, { color: theme.colors.textHighlight, children: [info.dbPath, ":"] }), " ", dbPath] }), tursoEnabled && tursoUrl && (_jsxs(Text, { color: theme.colors.text, children: [_jsxs(Text, { color: theme.colors.textHighlight, children: [info.tursoUrl, ":"] }), " ", tursoUrl] }))] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(info.paths) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsxs(Text, { color: theme.colors.textHighlight, children: [info.configFile, ":"] }), " ", CONFIG_FILE] }), _jsxs(Text, { color: theme.colors.text, children: [_jsxs(Text, { color: theme.colors.textHighlight, children: [info.dataDir, ":"] }), " ", DATA_DIR] })] })] })] }));
|
|
130
|
+
}
|
|
94
131
|
function WhatsNewContent({ items, scrollOffset, visibleLines, maxScroll }) {
|
|
95
132
|
const i18n = t();
|
|
96
133
|
const whatsNew = i18n.tui.whatsNew;
|
|
@@ -12,7 +12,7 @@ import { SearchResults } from './SearchResults.js';
|
|
|
12
12
|
import { getDb, schema } from '../../db/index.js';
|
|
13
13
|
import { t, fmt } from '../../i18n/index.js';
|
|
14
14
|
import { useTheme } from '../theme/index.js';
|
|
15
|
-
import { isTursoEnabled
|
|
15
|
+
import { isTursoEnabled } from '../../config.js';
|
|
16
16
|
import { VERSION } from '../../version.js';
|
|
17
17
|
const COLUMNS = ['todo', 'doing', 'done'];
|
|
18
18
|
export function KanbanBoard({ onSwitchToGtd }) {
|
|
@@ -476,19 +476,9 @@ export function KanbanBoard({ onSwitchToGtd }) {
|
|
|
476
476
|
const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
|
|
477
477
|
// Turso connection info
|
|
478
478
|
const tursoEnabled = isTursoEnabled();
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
try {
|
|
483
|
-
return new URL(config.url).host;
|
|
484
|
-
}
|
|
485
|
-
catch {
|
|
486
|
-
return config.url;
|
|
487
|
-
}
|
|
488
|
-
}
|
|
489
|
-
return '';
|
|
490
|
-
})() : '';
|
|
491
|
-
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.primary, children: formatTitle(i18n.tui.title) }), _jsx(Text, { color: theme.colors.accent, children: " [KANBAN]" }), _jsx(Text, { color: theme.colors.textMuted, children: theme.name === 'modern' ? ` v${VERSION}` : ` VER ${VERSION}` }), tursoEnabled && (_jsxs(Text, { color: theme.colors.accent, children: [theme.name === 'modern' ? ' ☁️ ' : ' [SYNC] ', tursoHost] })), !tursoEnabled && (_jsx(Text, { color: theme.colors.textMuted, children: theme.name === 'modern' ? ' 💾 local' : ' [LOCAL]' }))] }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.helpHint })] }), (mode === 'task-detail' || mode === 'add-comment' || mode === 'select-project') && selectedTask ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: theme.colors.accent, bold: true, children: [theme.name === 'modern' ? '📋 ' : '>> ', i18n.tui.taskDetailTitle || 'Task Details'] }), _jsxs(Text, { color: theme.colors.textMuted, children: [" (Esc/b: ", i18n.tui.back || 'back', ", ", i18n.tui.commentHint || 'i: add comment', ")"] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, marginBottom: 1, children: [_jsx(Text, { color: theme.colors.text, bold: true, children: selectedTask.title }), selectedTask.description && (_jsx(Text, { color: theme.colors.textMuted, children: selectedTask.description })), _jsxs(Text, { color: theme.colors.textMuted, children: [i18n.status[selectedTask.status], selectedTask.waitingFor && ` - ${selectedTask.waitingFor}`, selectedTask.dueDate && ` (${selectedTask.dueDate.toLocaleDateString()})`] })] }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.comments || 'Comments', " (", taskComments.length, ")"] }) }), _jsx(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, minHeight: 5, children: taskComments.length === 0 ? (_jsx(Text, { color: theme.colors.textMuted, italic: true, children: i18n.tui.noComments || 'No comments yet' })) : (taskComments.map((comment, index) => {
|
|
479
|
+
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.primary, children: formatTitle(i18n.tui.title) }), _jsx(Text, { color: theme.colors.accent, children: " [KANBAN]" }), _jsx(Text, { color: theme.colors.textMuted, children: theme.name === 'modern' ? ` v${VERSION}` : ` VER ${VERSION}` }), _jsx(Text, { color: tursoEnabled ? theme.colors.accent : theme.colors.textMuted, children: theme.name === 'modern'
|
|
480
|
+
? (tursoEnabled ? ' ☁️ turso' : ' 💾 local')
|
|
481
|
+
: (tursoEnabled ? ' [DB]turso' : ' [DB]local') })] }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.helpHint })] }), (mode === 'task-detail' || mode === 'add-comment' || mode === 'select-project') && selectedTask ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: theme.colors.accent, bold: true, children: [theme.name === 'modern' ? '📋 ' : '>> ', i18n.tui.taskDetailTitle || 'Task Details'] }), _jsxs(Text, { color: theme.colors.textMuted, children: [" (Esc/b: ", i18n.tui.back || 'back', ", ", i18n.tui.commentHint || 'i: add comment', ")"] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, marginBottom: 1, children: [_jsx(Text, { color: theme.colors.text, bold: true, children: selectedTask.title }), selectedTask.description && (_jsx(Text, { color: theme.colors.textMuted, children: selectedTask.description })), _jsxs(Text, { color: theme.colors.textMuted, children: [i18n.status[selectedTask.status], selectedTask.waitingFor && ` - ${selectedTask.waitingFor}`, selectedTask.dueDate && ` (${selectedTask.dueDate.toLocaleDateString()})`] })] }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.comments || 'Comments', " (", taskComments.length, ")"] }) }), _jsx(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, minHeight: 5, children: taskComments.length === 0 ? (_jsx(Text, { color: theme.colors.textMuted, italic: true, children: i18n.tui.noComments || 'No comments yet' })) : (taskComments.map((comment, index) => {
|
|
492
482
|
const isSelected = index === selectedCommentIndex && mode === 'task-detail';
|
|
493
483
|
return (_jsxs(Box, { flexDirection: "row", marginBottom: 1, children: [_jsx(Text, { color: isSelected ? theme.colors.textSelected : theme.colors.textMuted, children: isSelected ? theme.style.selectedPrefix : theme.style.unselectedPrefix }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.textMuted, children: ["[", comment.createdAt.toLocaleString(), "]"] }), _jsx(Text, { color: isSelected ? theme.colors.textSelected : theme.colors.text, bold: isSelected, children: comment.content })] })] }, comment.id));
|
|
494
484
|
})) }), mode === 'add-comment' && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: i18n.tui.addComment || 'New comment: ' }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: "" }), _jsxs(Text, { color: theme.colors.textMuted, children: [" ", i18n.tui.inputHelp] })] })), mode === 'select-project' && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.selectProject || 'Select project for', ": ", selectedTask.title] }), _jsx(Box, { flexDirection: "column", marginTop: 1, borderStyle: theme.borders.list, borderColor: theme.colors.borderActive, paddingX: 1, children: projects.map((project, index) => (_jsxs(Text, { color: index === projectSelectIndex ? theme.colors.textSelected : theme.colors.text, bold: index === projectSelectIndex, children: [index === projectSelectIndex ? theme.style.selectedPrefix : theme.style.unselectedPrefix, project.title] }, project.id))) }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.selectProjectHelp || 'j/k: select, Enter: confirm, Esc: cancel' })] }))] })) : mode === 'search' ? (_jsx(_Fragment, { children: searchQuery && (_jsx(SearchResults, { results: searchResults, selectedIndex: searchResultIndex, query: searchQuery })) })) : (_jsxs(_Fragment, { children: [_jsx(Box, { marginBottom: 1, children: COLUMNS.map((column, index) => (_jsx(Box, { flexGrow: 1, flexBasis: 0, marginRight: index < 2 ? 1 : 0, children: _jsxs(Text, { color: currentColumnIndex === index ? theme.colors.textHighlight : theme.colors.textMuted, children: [index + 1, ":"] }) }, column))) }), _jsx(Box, { flexDirection: "row", children: COLUMNS.map((column, index) => (_jsx(KanbanColumn, { title: getColumnLabel(column), tasks: tasks[column], isActive: index === currentColumnIndex, selectedTaskIndex: selectedTaskIndices[column], columnIndex: index }, column))) })] })), mode === 'add' && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: i18n.tui.newTask }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleInputSubmit, placeholder: i18n.tui.placeholder }), _jsxs(Text, { color: theme.colors.textMuted, children: [" ", i18n.tui.inputHelp] })] })), mode === 'search' && (_jsx(SearchBar, { value: searchQuery, onChange: handleSearchChange, onSubmit: handleInputSubmit })), message && mode === 'normal' && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textHighlight, children: message }) })), _jsx(Box, { marginTop: 1, children: mode === 'select-project' ? (_jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.selectProjectHelp || 'j/k: select, Enter: confirm, Esc: cancel' })) : (mode === 'task-detail' || mode === 'add-comment') ? (theme.style.showFunctionKeys ? (_jsx(FunctionKeyBar, { keys: [
|