floq 0.5.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 +14 -1
- package/README.md +14 -1
- package/dist/cli.js +12 -1
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.js +51 -1
- package/dist/config.d.ts +3 -0
- package/dist/config.js +13 -0
- package/dist/i18n/en.d.ts +13 -0
- package/dist/i18n/en.js +7 -0
- package/dist/i18n/ja.js +7 -0
- package/dist/ui/App.js +21 -15
- package/dist/ui/SplashScreen.d.ts +2 -1
- package/dist/ui/SplashScreen.js +109 -10
- package/dist/ui/components/DQLayout.d.ts +36 -0
- package/dist/ui/components/DQLayout.js +53 -0
- package/dist/ui/components/DQTaskList.d.ts +53 -0
- package/dist/ui/components/DQTaskList.js +48 -0
- package/dist/ui/components/DQWindow.d.ts +19 -0
- package/dist/ui/components/DQWindow.js +33 -0
- package/dist/ui/components/GtdDQ.d.ts +7 -0
- package/dist/ui/components/GtdDQ.js +773 -0
- package/dist/ui/components/HelpModal.js +136 -102
- package/dist/ui/components/KanbanColumn.js +53 -1
- package/dist/ui/components/KanbanDQ.d.ts +7 -0
- package/dist/ui/components/KanbanDQ.js +470 -0
- package/dist/ui/components/TitledBox.d.ts +11 -0
- package/dist/ui/components/TitledBox.js +66 -0
- package/dist/ui/theme/themes.d.ts +1 -0
- package/dist/ui/theme/themes.js +43 -1
- package/dist/ui/theme/types.d.ts +3 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
MS-DOSスタイルのテーマを備えたターミナルベースのGTD(Getting Things Done)タスクマネージャー。
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
## 特徴
|
|
8
10
|
|
|
9
11
|
- **TUIインターフェース**: Ink(CLI用React)で構築されたインタラクティブなターミナルUI
|
|
@@ -14,7 +16,8 @@ MS-DOSスタイルのテーマを備えたターミナルベースのGTD(Getti
|
|
|
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
|
+

|
|
269
|
+
|
|
257
270
|
| テーマ | 説明 |
|
|
258
271
|
|--------|------|
|
|
259
272
|
| `modern` | シンプルでクリーン(デフォルト) |
|
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
A terminal-based GTD (Getting Things Done) task manager with MS-DOS style themes.
|
|
6
6
|
|
|
7
|
+

|
|
8
|
+
|
|
7
9
|
## Features
|
|
8
10
|
|
|
9
11
|
- **TUI Interface**: Interactive terminal UI built with Ink (React for CLI)
|
|
@@ -14,7 +16,8 @@ A terminal-based GTD (Getting Things Done) task manager with MS-DOS style themes
|
|
|
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
|
+

|
|
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>;
|
package/dist/commands/config.js
CHANGED
|
@@ -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.');
|
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
|
@@ -9,15 +9,18 @@ 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('
|
|
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);
|
|
@@ -430,11 +441,6 @@ function AppContent({ onOpenSettings }) {
|
|
|
430
441
|
}
|
|
431
442
|
};
|
|
432
443
|
useInput((input, key) => {
|
|
433
|
-
// Skip splash screen on any key
|
|
434
|
-
if (mode === 'splash') {
|
|
435
|
-
setMode('normal');
|
|
436
|
-
return;
|
|
437
|
-
}
|
|
438
444
|
// Handle search mode
|
|
439
445
|
if (mode === 'search') {
|
|
440
446
|
if (key.escape) {
|
|
@@ -978,10 +984,6 @@ function AppContent({ onOpenSettings }) {
|
|
|
978
984
|
return;
|
|
979
985
|
}
|
|
980
986
|
}, { isActive: mode !== 'help' });
|
|
981
|
-
// Splash screen
|
|
982
|
-
if (mode === 'splash') {
|
|
983
|
-
return _jsx(SplashScreen, { onComplete: () => setMode('normal') });
|
|
984
|
-
}
|
|
985
987
|
// Help modal overlay
|
|
986
988
|
if (mode === 'help') {
|
|
987
989
|
return (_jsx(Box, { flexDirection: "column", padding: 1, children: _jsx(HelpModal, { onClose: () => setMode('normal') }) }));
|
|
@@ -1003,11 +1005,15 @@ function AppContent({ onOpenSettings }) {
|
|
|
1003
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) => {
|
|
1004
1006
|
const isSelected = index === selectedCommentIndex && mode === 'task-detail';
|
|
1005
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));
|
|
1006
|
-
})) }), 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(
|
|
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) => {
|
|
1007
1013
|
const parentProject = getParentProject(task.parentId);
|
|
1008
1014
|
const progress = currentTab === 'projects' ? projectProgress[task.id] : undefined;
|
|
1009
1015
|
return (_jsx(TaskItem, { task: task, isSelected: index === selectedTaskIndex, projectName: parentProject?.title, progress: progress }, task.id));
|
|
1010
|
-
})) })), (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
|
|
1011
1017
|
? `${i18n.tui.newTask}[${selectedProject.title}] `
|
|
1012
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) => {
|
|
1013
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 {};
|
package/dist/ui/SplashScreen.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
2
|
import { useState, useEffect } from 'react';
|
|
3
|
-
import { Box, Text } from 'ink';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import { useTheme } from './theme/index.js';
|
|
5
5
|
import { VERSION } from '../version.js';
|
|
6
|
+
import { t } from '../i18n/index.js';
|
|
6
7
|
const LOGO_MODERN = `
|
|
7
8
|
███████╗██╗ ██████╗ ██████╗
|
|
8
9
|
██╔════╝██║ ██╔═══██╗██╔═══██╗
|
|
@@ -20,14 +21,88 @@ const LOGO_DOS = `
|
|
|
20
21
|
║ ██ ███████ ██████ ██████ ║
|
|
21
22
|
╚═══════════════════════════════════╝
|
|
22
23
|
`;
|
|
24
|
+
// Dragon Quest style FLOQ logo with wings and sword
|
|
25
|
+
const FLOQ_LOGO = [
|
|
26
|
+
' /\\',
|
|
27
|
+
' __ / \\ __',
|
|
28
|
+
' / \\ / || \\ / \\',
|
|
29
|
+
' / /\\ \\ / || \\ / /\\ \\',
|
|
30
|
+
' / / \\ \\ / || \\ / / \\ \\',
|
|
31
|
+
' / / \\ \\_/ || \\_/ / \\ \\',
|
|
32
|
+
' /_/ \\__/ || \\__/ \\_\\',
|
|
33
|
+
' ||',
|
|
34
|
+
' =================[##]=================',
|
|
35
|
+
'',
|
|
36
|
+
' ######## ## ###### ######',
|
|
37
|
+
' ## ## ## ## ## ##',
|
|
38
|
+
' ###### ## ## ## ## ##',
|
|
39
|
+
' ## ## ## ## ## # ##',
|
|
40
|
+
' ## ######## ###### #### ##',
|
|
41
|
+
'',
|
|
42
|
+
' =====================================',
|
|
43
|
+
' ~ Flow Your Tasks ~',
|
|
44
|
+
];
|
|
45
|
+
// Dragon Quest style border characters
|
|
46
|
+
const DQ_BORDER = {
|
|
47
|
+
topLeft: '╭',
|
|
48
|
+
topRight: '╮',
|
|
49
|
+
bottomLeft: '╰',
|
|
50
|
+
bottomRight: '╯',
|
|
51
|
+
horizontal: '─',
|
|
52
|
+
vertical: '│',
|
|
53
|
+
};
|
|
23
54
|
const TAGLINE = 'Flow your tasks, clear your mind';
|
|
24
|
-
|
|
55
|
+
// Dragon Quest famous quotes for splash screen
|
|
56
|
+
const DQ_QUOTES_JA = [
|
|
57
|
+
'へんじがない。ただのしかばねのようだ。',
|
|
58
|
+
'おきのどくですが ぼうけんのしょは きえてしまいました。',
|
|
59
|
+
'レベルがあがった!',
|
|
60
|
+
'しかし まわりこまれてしまった!',
|
|
61
|
+
'たたかいに やぶれた...',
|
|
62
|
+
'どうぐがいっぱいで もてません。',
|
|
63
|
+
'やくそうを つかった!',
|
|
64
|
+
'かいしんのいちげき!',
|
|
65
|
+
'しかし なにも おこらなかった',
|
|
66
|
+
'そのほうこうには すすめません',
|
|
67
|
+
];
|
|
68
|
+
const DQ_QUOTES_EN = [
|
|
69
|
+
'No response. It seems to be just a corpse.',
|
|
70
|
+
'Unfortunately, your adventure log has been erased.',
|
|
71
|
+
'Level up!',
|
|
72
|
+
'But you were surrounded!',
|
|
73
|
+
'You have been defeated...',
|
|
74
|
+
'Your inventory is full.',
|
|
75
|
+
'Used an herb!',
|
|
76
|
+
'Critical hit!',
|
|
77
|
+
'But nothing happened.',
|
|
78
|
+
'You cannot go that way.',
|
|
79
|
+
];
|
|
80
|
+
export function SplashScreen({ onComplete, duration = 1500, viewMode = 'gtd' }) {
|
|
25
81
|
const [frame, setFrame] = useState(0);
|
|
26
82
|
const [showTagline, setShowTagline] = useState(false);
|
|
83
|
+
const [blinkVisible, setBlinkVisible] = useState(true);
|
|
27
84
|
const theme = useTheme();
|
|
85
|
+
const i18n = t();
|
|
86
|
+
const isDqStyle = theme.uiStyle === 'titled-box';
|
|
28
87
|
const isDosStyle = theme.name !== 'modern';
|
|
29
88
|
const logo = isDosStyle ? LOGO_DOS : LOGO_MODERN;
|
|
30
89
|
const [filled, empty] = theme.style.loadingChars;
|
|
90
|
+
// Pick a random quote (stable across re-renders)
|
|
91
|
+
const [randomQuote] = useState(() => {
|
|
92
|
+
const isJapanese = i18n.splash?.welcome === 'ようこそ!';
|
|
93
|
+
const quotes = isJapanese ? DQ_QUOTES_JA : DQ_QUOTES_EN;
|
|
94
|
+
return quotes[Math.floor(Math.random() * quotes.length)];
|
|
95
|
+
});
|
|
96
|
+
// Adventure subtitle based on view mode
|
|
97
|
+
const adventureSubtitle = viewMode === 'kanban'
|
|
98
|
+
? (i18n.splash?.subtitleKanban || 'Kanbanの冒険がはじまる')
|
|
99
|
+
: (i18n.splash?.subtitle || 'GTDの冒険がはじまる');
|
|
100
|
+
// Wait for key press mode (duration = -1)
|
|
101
|
+
const waitForKeyPress = duration < 0;
|
|
102
|
+
// Handle key press to skip splash
|
|
103
|
+
useInput(() => {
|
|
104
|
+
onComplete();
|
|
105
|
+
});
|
|
31
106
|
useEffect(() => {
|
|
32
107
|
// Animate logo appearance
|
|
33
108
|
const frameInterval = setInterval(() => {
|
|
@@ -43,19 +118,43 @@ export function SplashScreen({ onComplete, duration = 1500 }) {
|
|
|
43
118
|
const taglineTimer = setTimeout(() => {
|
|
44
119
|
setShowTagline(true);
|
|
45
120
|
}, 600);
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
121
|
+
// Blink effect for DQ style or wait-for-key mode
|
|
122
|
+
let blinkInterval = null;
|
|
123
|
+
if (isDqStyle || waitForKeyPress) {
|
|
124
|
+
blinkInterval = setInterval(() => {
|
|
125
|
+
setBlinkVisible((prev) => !prev);
|
|
126
|
+
}, 500);
|
|
127
|
+
}
|
|
128
|
+
// Complete splash screen (only if not waiting for key press)
|
|
129
|
+
let completeTimer = null;
|
|
130
|
+
if (!waitForKeyPress) {
|
|
131
|
+
completeTimer = setTimeout(() => {
|
|
132
|
+
onComplete();
|
|
133
|
+
}, duration);
|
|
134
|
+
}
|
|
50
135
|
return () => {
|
|
51
136
|
clearInterval(frameInterval);
|
|
52
137
|
clearTimeout(taglineTimer);
|
|
53
|
-
|
|
138
|
+
if (completeTimer)
|
|
139
|
+
clearTimeout(completeTimer);
|
|
140
|
+
if (blinkInterval)
|
|
141
|
+
clearInterval(blinkInterval);
|
|
54
142
|
};
|
|
55
|
-
}, [onComplete, duration]);
|
|
143
|
+
}, [onComplete, duration, isDqStyle, waitForKeyPress]);
|
|
144
|
+
// Dragon Quest style splash
|
|
145
|
+
if (isDqStyle) {
|
|
146
|
+
const boxWidth = 40;
|
|
147
|
+
const innerWidth = boxWidth - 2;
|
|
148
|
+
const title = 'FLOQ';
|
|
149
|
+
const leftDashes = 3;
|
|
150
|
+
const rightDashes = innerWidth - leftDashes - title.length - 2;
|
|
151
|
+
const shadowColor = theme.colors.muted;
|
|
152
|
+
return (_jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", padding: 2, children: [_jsx(Box, { flexDirection: "column", alignItems: "center", marginBottom: 1, children: FLOQ_LOGO.map((line, index) => (_jsx(Text, { color: index === 4 ? theme.colors.text : theme.colors.accent, bold: index === 4, children: line }, index))) }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: theme.colors.border, children: DQ_BORDER.topLeft }), _jsxs(Text, { color: theme.colors.border, children: [DQ_BORDER.horizontal.repeat(leftDashes), " "] }), _jsx(Text, { color: theme.colors.accent, bold: true, children: title }), _jsxs(Text, { color: theme.colors.border, children: [" ", DQ_BORDER.horizontal.repeat(rightDashes)] }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.topRight }), _jsx(Text, { children: " " })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Box, { width: innerWidth, justifyContent: "center", children: _jsx(Text, { color: theme.colors.text, children: " " }) }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Text, { color: shadowColor, children: "\u2591" })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Box, { width: innerWidth, justifyContent: "center", children: _jsx(Text, { color: theme.colors.text, bold: true, children: adventureSubtitle }) }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Text, { color: shadowColor, children: "\u2591" })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Box, { width: innerWidth, justifyContent: "center", children: _jsx(Text, { color: theme.colors.text, children: " " }) }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Text, { color: shadowColor, children: "\u2591" })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Box, { width: innerWidth, justifyContent: "center", children: _jsx(Text, { color: theme.colors.textMuted, children: randomQuote }) }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Text, { color: shadowColor, children: "\u2591" })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Box, { width: innerWidth, justifyContent: "center", children: _jsx(Text, { color: theme.colors.text, children: " " }) }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Text, { color: shadowColor, children: "\u2591" })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Box, { width: innerWidth, justifyContent: "center", children: _jsx(Text, { color: theme.colors.textMuted, children: blinkVisible ? (i18n.splash?.pressKey || '▼ キーを押してください') : ' ' }) }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Text, { color: shadowColor, children: "\u2591" })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Box, { width: innerWidth, justifyContent: "center", children: _jsx(Text, { color: theme.colors.text, children: " " }) }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.vertical }), _jsx(Text, { color: shadowColor, children: "\u2591" })] }), _jsxs(Box, { children: [_jsx(Text, { color: theme.colors.border, children: DQ_BORDER.bottomLeft }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.horizontal.repeat(innerWidth) }), _jsx(Text, { color: theme.colors.border, children: DQ_BORDER.bottomRight }), _jsx(Text, { color: shadowColor, children: "\u2591" })] }), _jsxs(Box, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: shadowColor, children: '░'.repeat(boxWidth) })] })] }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: theme.colors.textMuted, children: ["VER ", VERSION] }) })] }));
|
|
153
|
+
}
|
|
154
|
+
// Standard splash (modern / DOS style)
|
|
56
155
|
const logoLines = logo.split('\n');
|
|
57
156
|
const visibleLines = Math.min(Math.floor(frame * logoLines.length / 10), logoLines.length);
|
|
58
|
-
return (_jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", padding: 2, children: [_jsx(Box, { flexDirection: "column", alignItems: "center", children: logoLines.slice(0, visibleLines).map((line, index) => (_jsx(Text, { color: theme.colors.secondary, bold: true, children: line }, index))) }), showTagline && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textMuted, italic: !isDosStyle, children: isDosStyle ? `[ ${TAGLINE.toUpperCase()} ]` : TAGLINE }) })), _jsx(Box, { marginTop: 2, children: _jsx(Text, { color: theme.colors.primary, children: frame < 10
|
|
157
|
+
return (_jsxs(Box, { flexDirection: "column", alignItems: "center", justifyContent: "center", padding: 2, children: [_jsx(Box, { flexDirection: "column", alignItems: "center", children: logoLines.slice(0, visibleLines).map((line, index) => (_jsx(Text, { color: theme.colors.secondary, bold: true, children: line }, index))) }), showTagline && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: theme.colors.textMuted, italic: !isDosStyle, children: isDosStyle ? `[ ${TAGLINE.toUpperCase()} ]` : TAGLINE }) })), _jsx(Box, { marginTop: 2, children: waitForKeyPress ? (_jsx(Text, { color: theme.colors.textMuted, children: blinkVisible ? (i18n.splash?.pressKey || '▼ Press any key') : ' ' })) : (_jsx(Text, { color: theme.colors.primary, children: frame < 10
|
|
59
158
|
? filled.repeat(frame) + empty.repeat(10 - frame)
|
|
60
|
-
: filled.repeat(10) }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: isDosStyle ? theme.colors.textMuted : theme.colors.muted, children: isDosStyle ? `VER ${VERSION}` : `v${VERSION}` }) })] }));
|
|
159
|
+
: filled.repeat(10) })) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: isDosStyle ? theme.colors.textMuted : theme.colors.muted, children: isDosStyle ? `VER ${VERSION}` : `v${VERSION}` }) })] }));
|
|
61
160
|
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
interface DQLayoutProps {
|
|
3
|
+
title: string;
|
|
4
|
+
subtitle?: string;
|
|
5
|
+
menuTitle: string;
|
|
6
|
+
menuItems: Array<{
|
|
7
|
+
label: string;
|
|
8
|
+
count?: number;
|
|
9
|
+
isActive: boolean;
|
|
10
|
+
}>;
|
|
11
|
+
onMenuSelect?: (index: number) => void;
|
|
12
|
+
contentTitle: string;
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
statusTitle?: string;
|
|
15
|
+
statusItems?: Array<{
|
|
16
|
+
label: string;
|
|
17
|
+
value: string;
|
|
18
|
+
}>;
|
|
19
|
+
footer?: string;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Dragon Quest style multi-window layout
|
|
23
|
+
*
|
|
24
|
+
* ╭────────────────────────────────────────────╮
|
|
25
|
+
* │ FLOQ - GTD Manager │
|
|
26
|
+
* ╰────────────────────────────────────────────╯
|
|
27
|
+
* ╭─ コマンド ─╮ ╭─ Inbox ──────────────────────╮
|
|
28
|
+
* │ ▶ Inbox │ │ ▶ タスク1 │
|
|
29
|
+
* │ 次 │ │ タスク2 │
|
|
30
|
+
* │ 待ち │ │ タスク3 │
|
|
31
|
+
* │ いつか │ ╰────────────────────────────────╯
|
|
32
|
+
* │ 完了 │
|
|
33
|
+
* ╰───────────╯
|
|
34
|
+
*/
|
|
35
|
+
export declare function DQLayout({ title, subtitle, menuTitle, menuItems, contentTitle, children, statusTitle, statusItems, footer, }: DQLayoutProps): React.ReactElement;
|
|
36
|
+
export {};
|