floq 0.4.0 → 0.6.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/README.ja.md CHANGED
@@ -4,17 +4,20 @@
4
4
 
5
5
  MS-DOSスタイルのテーマを備えたターミナルベースのGTD(Getting Things Done)タスクマネージャー。
6
6
 
7
+ ![Floq Demo](./assets/demo_ja.gif)
8
+
7
9
  ## 特徴
8
10
 
9
11
  - **TUIインターフェース**: Ink(CLI用React)で構築されたインタラクティブなターミナルUI
10
- - **GTDワークフロー**: Inbox、Next Actions、Waiting For、Someday/Maybe、Done
12
+ - **GTDワークフロー**: Inbox、Next Actions、Waiting For、Someday/Maybe、Done(過去7日間表示)
11
13
  - **カンバンモード**: 3カラムのカンバンボード表示(TODO、Doing、Done)
12
14
  - **プロジェクト**: タスクをプロジェクトに整理(進捗バー表示付き)
13
- - **コンテキスト**: タスクにコンテキスト(@work、@homeなど)を設定してフィルタリング
15
+ - **コンテキスト**: タスクにコンテキスト(@work、@homeなど)を設定してフィルタリング。タスク追加時は現在のフィルターを自動継承
14
16
  - **タスク検索**: `/` キーで全タスクを素早く検索
15
17
  - **コメント**: タスクにメモやコメントを追加
16
18
  - **クラウド同期**: [Turso](https://turso.tech/)のembedded replicasによるオプションの同期機能
17
- - **テーマ**: MS-DOSノスタルジックスタイルを含む複数テーマ
19
+ - **テーマ**: MS-DOSノスタルジックスタイルやドラクエRPG風を含む複数テーマ
20
+ - **スプラッシュ画面**: 起動時のスプラッシュ画面(レトロテーマではドラクエ風)
18
21
  - **多言語対応**: 英語・日本語サポート
19
22
  - **Vimスタイルナビゲーション**: hjklまたは矢印キーで操作
20
23
  - **セットアップウィザード**: 初回起動時の簡単設定
@@ -209,6 +212,12 @@ floq config mode kanban # カンバンボード
209
212
  floq config db /path/to/custom.db
210
213
  floq config db # デフォルトに戻す
211
214
 
215
+ # スプラッシュ画面設定
216
+ floq config splash # 現在の設定を表示
217
+ floq config splash 3000 # 3秒に設定
218
+ floq config splash off # スプラッシュ無効化
219
+ floq config splash key # キー入力待ちモード
220
+
212
221
  # データベースリセット(全データ削除)
213
222
  floq db reset # 確認あり
214
223
  floq db reset --force # 確認なし
@@ -254,6 +263,10 @@ floq config turso --disable
254
263
 
255
264
  26種類のテーマが利用可能。`floq config theme` でインタラクティブに選択(j/kで移動)。
256
265
 
266
+ 一部のテーマは**ドラクエRPG風UI**を採用しており、タイトル付きメッセージボックス、2カラムレイアウト、レトロなスプラッシュ画面が表示されます。DQ風テーマ: `turbo-pascal`、`msx`、`pc-98`
267
+
268
+ ![ドラクエ風UI](./assets/demo_dq.gif)
269
+
257
270
  | テーマ | 説明 |
258
271
  |--------|------|
259
272
  | `modern` | シンプルでクリーン(デフォルト) |
package/README.md CHANGED
@@ -4,17 +4,20 @@
4
4
 
5
5
  A terminal-based GTD (Getting Things Done) task manager with MS-DOS style themes.
6
6
 
7
+ ![Floq Demo](./assets/demo.gif)
8
+
7
9
  ## Features
8
10
 
9
11
  - **TUI Interface**: Interactive terminal UI built with Ink (React for CLI)
10
- - **GTD Workflow**: Inbox, Next Actions, Waiting For, Someday/Maybe, Done
12
+ - **GTD Workflow**: Inbox, Next Actions, Waiting For, Someday/Maybe, Done (shows last 7 days)
11
13
  - **Kanban Mode**: 3-column kanban board view (TODO, Doing, Done)
12
14
  - **Projects**: Organize tasks into projects with progress tracking
13
- - **Contexts**: Tag tasks with contexts (@work, @home, etc.) and filter by context
15
+ - **Contexts**: Tag tasks with contexts (@work, @home, etc.) and filter by context. New tasks inherit the active context filter
14
16
  - **Task Search**: Quick search across all tasks with `/`
15
17
  - **Comments**: Add notes and comments to tasks
16
18
  - **Cloud Sync**: Optional sync with [Turso](https://turso.tech/) using embedded replicas
17
- - **Themes**: Multiple themes including MS-DOS nostalgic styles
19
+ - **Themes**: Multiple themes including MS-DOS nostalgic styles and Dragon Quest RPG style
20
+ - **Splash Screen**: Configurable startup splash with Dragon Quest style for retro themes
18
21
  - **i18n**: English and Japanese support
19
22
  - **Vim-style Navigation**: Use hjkl or arrow keys
20
23
  - **Setup Wizard**: First-run wizard for easy configuration
@@ -209,6 +212,12 @@ floq config mode kanban # Kanban board
209
212
  floq config db /path/to/custom.db
210
213
  floq config db # Reset to default
211
214
 
215
+ # Splash screen settings
216
+ floq config splash # Show current setting
217
+ floq config splash 3000 # Set to 3 seconds
218
+ floq config splash off # Disable splash screen
219
+ floq config splash key # Wait for key press
220
+
212
221
  # Reset database (delete all data)
213
222
  floq db reset # With confirmation
214
223
  floq db reset --force # Skip confirmation
@@ -254,6 +263,10 @@ floq config turso --disable
254
263
 
255
264
  26 themes available. Use `floq config theme` for interactive selection (j/k to navigate).
256
265
 
266
+ Some themes feature a **Dragon Quest RPG style UI** with titled message boxes, 2-column layouts, and retro splash screens. Themes with DQ-style: `turbo-pascal`, `msx`, `pc-98`.
267
+
268
+ ![Dragon Quest Style UI](./assets/demo_dq.gif)
269
+
257
270
  | Theme | Description |
258
271
  |-------|-------------|
259
272
  | `modern` | Clean, minimal style (default) |
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { listTasks, listProjects } from './commands/list.js';
7
7
  import { moveTask } from './commands/move.js';
8
8
  import { markDone } from './commands/done.js';
9
9
  import { addProject, listProjectsCommand, showProject, completeProject, } from './commands/project.js';
10
- import { showConfig, setLanguage, setDbPath, resetDbPath, setTheme, selectTheme, setViewModeCommand, selectMode, setTurso, disableTurso, syncCommand, resetDatabase } from './commands/config.js';
10
+ import { showConfig, setLanguage, setDbPath, resetDbPath, setTheme, selectTheme, setViewModeCommand, selectMode, setTurso, disableTurso, syncCommand, resetDatabase, setSplashCommand, showSplash } from './commands/config.js';
11
11
  import { addComment, listComments } from './commands/comment.js';
12
12
  import { listContexts, addContextCommand, removeContextCommand } from './commands/context.js';
13
13
  import { runSetupWizard } from './commands/setup.js';
@@ -155,6 +155,17 @@ configCmd
155
155
  process.exit(1);
156
156
  }
157
157
  });
158
+ configCmd
159
+ .command('splash [duration]')
160
+ .description('Set splash screen duration (ms, off=disable, key=wait for key)')
161
+ .action(async (duration) => {
162
+ if (duration !== undefined) {
163
+ await setSplashCommand(duration);
164
+ }
165
+ else {
166
+ await showSplash();
167
+ }
168
+ });
158
169
  // Sync command
159
170
  program
160
171
  .command('sync')
@@ -9,5 +9,7 @@ export declare function setViewModeCommand(mode: string): Promise<void>;
9
9
  export declare function selectMode(): Promise<void>;
10
10
  export declare function setTurso(url: string, token: string): Promise<void>;
11
11
  export declare function disableTurso(): Promise<void>;
12
+ export declare function setSplashCommand(duration: string): Promise<void>;
13
+ export declare function showSplash(): Promise<void>;
12
14
  export declare function syncCommand(): Promise<void>;
13
15
  export declare function resetDatabase(force: boolean): Promise<void>;
@@ -3,7 +3,7 @@ import React from 'react';
3
3
  import { createInterface } from 'readline';
4
4
  import { unlinkSync, existsSync, readdirSync } from 'fs';
5
5
  import { dirname, basename, join } from 'path';
6
- import { loadConfig, saveConfig, getDbPath, getViewMode, setViewMode, isTursoEnabled, getTursoConfig, setTursoConfig } from '../config.js';
6
+ import { loadConfig, saveConfig, getDbPath, getViewMode, setViewMode, isTursoEnabled, getTursoConfig, setTursoConfig, getSplashDuration, setSplashDuration } from '../config.js';
7
7
  import { CONFIG_FILE } from '../paths.js';
8
8
  import { ThemeSelector } from '../ui/ThemeSelector.js';
9
9
  import { ModeSelector } from '../ui/ModeSelector.js';
@@ -13,6 +13,7 @@ const VALID_LOCALES = ['en', 'ja'];
13
13
  const VALID_VIEW_MODES = ['gtd', 'kanban'];
14
14
  export async function showConfig() {
15
15
  const config = loadConfig();
16
+ const splashDuration = getSplashDuration();
16
17
  console.log('GTD CLI Configuration');
17
18
  console.log('─'.repeat(40));
18
19
  console.log(`Config file: ${CONFIG_FILE}`);
@@ -20,6 +21,7 @@ export async function showConfig() {
20
21
  console.log(`Database: ${getDbPath()}`);
21
22
  console.log(`Theme: ${config.theme || 'modern'}`);
22
23
  console.log(`View Mode: ${config.viewMode || 'gtd'}`);
24
+ console.log(`Splash: ${splashDuration === 0 ? 'disabled' : splashDuration === -1 ? 'wait for key' : `${splashDuration}ms`}`);
23
25
  console.log(`Turso: ${isTursoEnabled() ? 'enabled' : 'disabled'}`);
24
26
  if (config.db_path) {
25
27
  console.log(` (custom: ${config.db_path})`);
@@ -115,6 +117,54 @@ export async function disableTurso() {
115
117
  setTursoConfig(undefined);
116
118
  console.log('Turso sync disabled');
117
119
  }
120
+ export async function setSplashCommand(duration) {
121
+ let value;
122
+ // Handle special keywords
123
+ if (duration === 'key' || duration === 'wait') {
124
+ value = -1;
125
+ }
126
+ else if (duration === 'off' || duration === 'disable') {
127
+ value = 0;
128
+ }
129
+ else {
130
+ value = parseInt(duration, 10);
131
+ if (isNaN(value)) {
132
+ console.error(`Invalid duration: ${duration}`);
133
+ console.error('Usage: floq config splash <milliseconds|key|off>');
134
+ console.error(' off/0 = disable splash screen');
135
+ console.error(' key = wait for key press');
136
+ console.error(' positive number = duration in milliseconds');
137
+ process.exit(1);
138
+ }
139
+ if (value < 0) {
140
+ console.error('Duration must be 0 (disabled) or positive number');
141
+ console.error('Use "key" for wait-for-keypress mode');
142
+ process.exit(1);
143
+ }
144
+ }
145
+ setSplashDuration(value);
146
+ if (value === 0) {
147
+ console.log('Splash screen disabled');
148
+ }
149
+ else if (value === -1) {
150
+ console.log('Splash screen set to wait for key press');
151
+ }
152
+ else {
153
+ console.log(`Splash screen duration set to ${value}ms`);
154
+ }
155
+ }
156
+ export async function showSplash() {
157
+ const duration = getSplashDuration();
158
+ if (duration === 0) {
159
+ console.log('Splash screen: disabled');
160
+ }
161
+ else if (duration === -1) {
162
+ console.log('Splash screen: wait for key press');
163
+ }
164
+ else {
165
+ console.log(`Splash screen: ${duration}ms`);
166
+ }
167
+ }
118
168
  export async function syncCommand() {
119
169
  if (!isTursoEnabled()) {
120
170
  console.error('Turso sync is not enabled.');
@@ -1,4 +1,4 @@
1
- import { eq } from 'drizzle-orm';
1
+ import { eq, and, gte } from 'drizzle-orm';
2
2
  import { getDb, schema } from '../db/index.js';
3
3
  import { t, fmt } from '../i18n/index.js';
4
4
  export async function listTasks(status) {
@@ -11,10 +11,22 @@ export async function listTasks(status) {
11
11
  console.error(fmt(i18n.commands.list.validStatuses, { statuses: validStatuses.join(', ') }));
12
12
  process.exit(1);
13
13
  }
14
- const tasks = await db
15
- .select()
16
- .from(schema.tasks)
17
- .where(eq(schema.tasks.status, status));
14
+ // For done status, only show tasks from the last week by default
15
+ let tasks;
16
+ if (status === 'done') {
17
+ const oneWeekAgo = new Date();
18
+ oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
19
+ tasks = await db
20
+ .select()
21
+ .from(schema.tasks)
22
+ .where(and(eq(schema.tasks.status, 'done'), gte(schema.tasks.updatedAt, oneWeekAgo)));
23
+ }
24
+ else {
25
+ tasks = await db
26
+ .select()
27
+ .from(schema.tasks)
28
+ .where(eq(schema.tasks.status, status));
29
+ }
18
30
  console.log(`\n${i18n.status[status]} (${tasks.length})`);
19
31
  console.log('─'.repeat(40));
20
32
  if (tasks.length === 0) {
package/dist/config.d.ts CHANGED
@@ -13,6 +13,7 @@ export interface Config {
13
13
  viewMode: ViewMode;
14
14
  turso?: TursoConfig;
15
15
  contexts?: string[];
16
+ splashDuration?: number;
16
17
  }
17
18
  export declare function loadConfig(): Config;
18
19
  export declare function saveConfig(updates: Partial<Config>): void;
@@ -30,3 +31,5 @@ export declare function isFirstRun(): boolean;
30
31
  export declare function getContexts(): string[];
31
32
  export declare function addContext(context: string): boolean;
32
33
  export declare function removeContext(context: string): boolean;
34
+ export declare function getSplashDuration(): number;
35
+ export declare function setSplashDuration(duration: number): void;
package/dist/config.js CHANGED
@@ -138,3 +138,16 @@ export function removeContext(context) {
138
138
  saveConfig({ contexts: newContexts });
139
139
  return true;
140
140
  }
141
+ const DEFAULT_SPLASH_DURATION = 2500; // 2.5 seconds
142
+ export function getSplashDuration() {
143
+ const duration = loadConfig().splashDuration;
144
+ // undefined means use default, 0 means disabled
145
+ if (duration === undefined) {
146
+ return DEFAULT_SPLASH_DURATION;
147
+ }
148
+ return duration;
149
+ }
150
+ export function setSplashDuration(duration) {
151
+ // Allow -1 (wait for key), 0 (disabled), or positive values
152
+ saveConfig({ splashDuration: duration >= 0 ? duration : -1 });
153
+ }
package/dist/i18n/en.d.ts CHANGED
@@ -240,6 +240,12 @@ export declare const en: {
240
240
  turso: string;
241
241
  };
242
242
  };
243
+ splash: {
244
+ welcome: string;
245
+ subtitle: string;
246
+ subtitleKanban: string;
247
+ pressKey: string;
248
+ };
243
249
  setup: {
244
250
  welcome: {
245
251
  title: string;
@@ -517,6 +523,12 @@ export type SetupTranslations = {
517
523
  confirm: string;
518
524
  };
519
525
  };
526
+ export type SplashTranslations = {
527
+ welcome: string;
528
+ subtitle: string;
529
+ subtitleKanban: string;
530
+ pressKey: string;
531
+ };
520
532
  export type Translations = {
521
533
  status: Record<string, string>;
522
534
  kanban: KanbanTranslations;
@@ -531,5 +543,6 @@ export type Translations = {
531
543
  context: Record<string, string>;
532
544
  };
533
545
  tui: TuiTranslations;
546
+ splash?: SplashTranslations;
534
547
  setup: SetupTranslations;
535
548
  };
package/dist/i18n/en.js CHANGED
@@ -255,6 +255,13 @@ export const en = {
255
255
  turso: 'turso',
256
256
  },
257
257
  },
258
+ // Splash screen (Dragon Quest style)
259
+ splash: {
260
+ welcome: 'Welcome!',
261
+ subtitle: 'Your GTD adventure begins',
262
+ subtitleKanban: 'Your Kanban adventure begins',
263
+ pressKey: '▼ Press any key',
264
+ },
258
265
  // Setup wizard
259
266
  setup: {
260
267
  welcome: {
package/dist/i18n/ja.js CHANGED
@@ -255,6 +255,13 @@ export const ja = {
255
255
  turso: 'turso',
256
256
  },
257
257
  },
258
+ // Splash screen (Dragon Quest style)
259
+ splash: {
260
+ welcome: 'ようこそ!',
261
+ subtitle: 'GTDの冒険がはじまる',
262
+ subtitleKanban: 'Kanbanの冒険がはじまる',
263
+ pressKey: '▼ なにかキーをおしてください',
264
+ },
258
265
  // Setup wizard
259
266
  setup: {
260
267
  welcome: {
package/dist/ui/App.js CHANGED
@@ -2,22 +2,25 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
2
2
  import { useState, useEffect, useCallback } from 'react';
3
3
  import { Box, Text, useInput, useApp } from 'ink';
4
4
  import TextInput from 'ink-text-input';
5
- import { eq, and } from 'drizzle-orm';
5
+ import { eq, and, gte } from 'drizzle-orm';
6
6
  import { v4 as uuidv4 } from 'uuid';
7
7
  import { TaskItem } from './components/TaskItem.js';
8
8
  import { HelpModal } from './components/HelpModal.js';
9
9
  import { FunctionKeyBar } from './components/FunctionKeyBar.js';
10
10
  import { SearchBar } from './components/SearchBar.js';
11
11
  import { SearchResults } from './components/SearchResults.js';
12
+ import { TitledBox } from './components/TitledBox.js';
12
13
  import { SplashScreen } from './SplashScreen.js';
13
14
  import { ThemeSelector } from './ThemeSelector.js';
14
15
  import { ModeSelector } from './ModeSelector.js';
15
16
  import { LanguageSelector } from './LanguageSelector.js';
16
17
  import { getDb, schema } from '../db/index.js';
17
18
  import { t, fmt } from '../i18n/index.js';
18
- import { ThemeProvider, useTheme } from './theme/index.js';
19
- import { getThemeName, getViewMode, setThemeName, setViewMode, setLocale, isTursoEnabled, getContexts, addContext } from '../config.js';
19
+ import { ThemeProvider, useTheme, getTheme } from './theme/index.js';
20
+ import { getThemeName, getViewMode, setThemeName, setViewMode, setLocale, isTursoEnabled, getContexts, addContext, getSplashDuration } from '../config.js';
20
21
  import { KanbanBoard } from './components/KanbanBoard.js';
22
+ import { KanbanDQ } from './components/KanbanDQ.js';
23
+ import { GtdDQ } from './components/GtdDQ.js';
21
24
  import { VERSION } from '../version.js';
22
25
  import { HistoryProvider, useHistory, CreateTaskCommand, DeleteTaskCommand, MoveTaskCommand, LinkTaskCommand, ConvertToProjectCommand, CreateCommentCommand, DeleteCommentCommand, SetContextCommand, } from './history/index.js';
23
26
  const TABS = ['inbox', 'next', 'waiting', 'someday', 'projects', 'done'];
@@ -25,6 +28,8 @@ export function App() {
25
28
  const [themeName, setThemeNameState] = useState(getThemeName);
26
29
  const [viewMode, setViewModeState] = useState(getViewMode);
27
30
  const [settingsMode, setSettingsMode] = useState('none');
31
+ const splashDuration = getSplashDuration();
32
+ const [showSplash, setShowSplash] = useState(splashDuration !== 0);
28
33
  const [, forceUpdate] = useState({});
29
34
  const handleThemeSelect = (theme) => {
30
35
  setThemeName(theme);
@@ -44,6 +49,12 @@ export function App() {
44
49
  const handleSettingsCancel = () => {
45
50
  setSettingsMode('none');
46
51
  };
52
+ const currentTheme = getTheme(themeName);
53
+ const useDQStyle = currentTheme.uiStyle === 'titled-box';
54
+ // Show splash screen (all themes, configurable duration)
55
+ if (showSplash) {
56
+ return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(SplashScreen, { onComplete: () => setShowSplash(false), duration: splashDuration, viewMode: viewMode }) }));
57
+ }
47
58
  // Settings selector screens
48
59
  if (settingsMode === 'theme-select') {
49
60
  return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(ThemeSelector, { onSelect: handleThemeSelect, onCancel: handleSettingsCancel }) }));
@@ -54,13 +65,13 @@ export function App() {
54
65
  if (settingsMode === 'lang-select') {
55
66
  return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(LanguageSelector, { onSelect: handleLocaleSelect, onCancel: handleSettingsCancel }) }));
56
67
  }
57
- return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(HistoryProvider, { children: viewMode === 'kanban' ? (_jsx(KanbanBoard, { onOpenSettings: setSettingsMode })) : (_jsx(AppContent, { onOpenSettings: setSettingsMode })) }) }));
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 }))) }) }));
58
69
  }
59
70
  function AppContent({ onOpenSettings }) {
60
71
  const theme = useTheme();
61
72
  const { exit } = useApp();
62
73
  const history = useHistory();
63
- const [mode, setMode] = useState('splash');
74
+ const [mode, setMode] = useState('normal');
64
75
  const [inputValue, setInputValue] = useState('');
65
76
  const [currentListIndex, setCurrentListIndex] = useState(0);
66
77
  const [selectedTaskIndex, setSelectedTaskIndex] = useState(0);
@@ -105,10 +116,21 @@ function AppContent({ onOpenSettings }) {
105
116
  // Load all tasks (including project children) by status
106
117
  const statusList = ['inbox', 'next', 'waiting', 'someday', 'done'];
107
118
  for (const status of statusList) {
119
+ // For done tasks, only show those completed in the last week
120
+ const oneWeekAgo = new Date();
121
+ oneWeekAgo.setDate(oneWeekAgo.getDate() - 7);
122
+ const conditions = [
123
+ eq(schema.tasks.status, status),
124
+ eq(schema.tasks.isProject, false),
125
+ ];
126
+ // Filter done tasks to last week only
127
+ if (status === 'done') {
128
+ conditions.push(gte(schema.tasks.updatedAt, oneWeekAgo));
129
+ }
108
130
  let allTasks = await db
109
131
  .select()
110
132
  .from(schema.tasks)
111
- .where(and(eq(schema.tasks.status, status), eq(schema.tasks.isProject, false)));
133
+ .where(and(...conditions));
112
134
  // Apply context filter
113
135
  if (contextFilter !== null) {
114
136
  if (contextFilter === '') {
@@ -205,7 +227,7 @@ function AppContent({ onOpenSettings }) {
205
227
  setMode('normal');
206
228
  }
207
229
  }, [tasks]);
208
- const addTask = useCallback(async (title, parentId) => {
230
+ const addTask = useCallback(async (title, parentId, context) => {
209
231
  if (!title.trim())
210
232
  return;
211
233
  const now = new Date();
@@ -216,6 +238,7 @@ function AppContent({ onOpenSettings }) {
216
238
  title: title.trim(),
217
239
  status: parentId ? 'next' : 'inbox',
218
240
  parentId: parentId || null,
241
+ context: context || null,
219
242
  createdAt: now,
220
243
  updatedAt: now,
221
244
  },
@@ -298,12 +321,13 @@ function AppContent({ onOpenSettings }) {
298
321
  setMode('task-detail');
299
322
  }
300
323
  else if (mode === 'add-to-project' && selectedProject) {
301
- await addTask(value, selectedProject.id);
324
+ await addTask(value, selectedProject.id, contextFilter && contextFilter !== '' ? contextFilter : null);
302
325
  await loadProjectTasks(selectedProject.id);
303
326
  setMode('project-detail');
304
327
  }
305
328
  else {
306
- await addTask(value);
329
+ // Pass contextFilter when adding a task, so it inherits the current filter context
330
+ await addTask(value, undefined, contextFilter && contextFilter !== '' ? contextFilter : null);
307
331
  setMode('normal');
308
332
  }
309
333
  }
@@ -417,11 +441,6 @@ function AppContent({ onOpenSettings }) {
417
441
  }
418
442
  };
419
443
  useInput((input, key) => {
420
- // Skip splash screen on any key
421
- if (mode === 'splash') {
422
- setMode('normal');
423
- return;
424
- }
425
444
  // Handle search mode
426
445
  if (mode === 'search') {
427
446
  if (key.escape) {
@@ -965,10 +984,6 @@ function AppContent({ onOpenSettings }) {
965
984
  return;
966
985
  }
967
986
  }, { isActive: mode !== 'help' });
968
- // Splash screen
969
- if (mode === 'splash') {
970
- return _jsx(SplashScreen, { onComplete: () => setMode('normal') });
971
- }
972
987
  // Help modal overlay
973
988
  if (mode === 'help') {
974
989
  return (_jsx(Box, { flexDirection: "column", padding: 1, children: _jsx(HelpModal, { onClose: () => setMode('normal') }) }));
@@ -990,11 +1005,15 @@ function AppContent({ onOpenSettings }) {
990
1005
  }) }), 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})`] })] }), _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, { 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) => {
991
1006
  const isSelected = index === selectedCommentIndex && mode === 'task-detail';
992
1007
  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));
993
- })) }), 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) => {
1008
+ })) }), 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' && (theme.uiStyle === 'titled-box' ? (_jsx(TitledBox, { title: mode === 'project-detail' && selectedProject ? selectedProject.title : getTabLabel(currentTab), borderColor: theme.colors.border, minHeight: 10, children: currentTasks.length === 0 ? (_jsx(Text, { color: theme.colors.textMuted, italic: true, children: i18n.tui.noTasks })) : (currentTasks.map((task, index) => {
1009
+ const parentProject = getParentProject(task.parentId);
1010
+ const progress = currentTab === 'projects' ? projectProgress[task.id] : undefined;
1011
+ return (_jsx(TaskItem, { task: task, isSelected: index === selectedTaskIndex, projectName: parentProject?.title, progress: progress }, task.id));
1012
+ })) })) : (_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) => {
994
1013
  const parentProject = getParentProject(task.parentId);
995
1014
  const progress = currentTab === 'projects' ? projectProgress[task.id] : undefined;
996
1015
  return (_jsx(TaskItem, { task: task, isSelected: index === selectedTaskIndex, projectName: parentProject?.title, progress: progress }, task.id));
997
- })) })), (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
1016
+ })) }))), (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
998
1017
  ? `${i18n.tui.newTask}[${selectedProject.title}] `
999
1018
  : 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 === 'context-filter' && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: theme.colors.secondary, bold: true, children: i18n.tui.context?.filter || 'Filter by context' }), _jsx(Box, { flexDirection: "column", marginTop: 1, borderStyle: theme.borders.list, borderColor: theme.colors.borderActive, paddingX: 1, children: ['all', 'none', ...availableContexts].map((ctx, index) => {
1000
1019
  const label = ctx === 'all'
@@ -2,6 +2,7 @@ import React from 'react';
2
2
  interface SplashScreenProps {
3
3
  onComplete: () => void;
4
4
  duration?: number;
5
+ viewMode?: 'gtd' | 'kanban';
5
6
  }
6
- export declare function SplashScreen({ onComplete, duration }: SplashScreenProps): React.ReactElement;
7
+ export declare function SplashScreen({ onComplete, duration, viewMode }: SplashScreenProps): React.ReactElement;
7
8
  export {};