floq 0.2.1 → 0.2.3
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 +20 -1
- package/README.md +20 -1
- package/dist/i18n/en.d.ts +16 -0
- package/dist/i18n/en.js +9 -1
- package/dist/i18n/ja.js +9 -1
- package/dist/ui/App.js +70 -12
- package/dist/ui/LanguageSelector.d.ts +8 -0
- package/dist/ui/LanguageSelector.js +37 -0
- package/dist/ui/ModeSelector.d.ts +2 -1
- package/dist/ui/ModeSelector.js +6 -2
- package/dist/ui/ThemeSelector.d.ts +2 -1
- package/dist/ui/ThemeSelector.js +6 -2
- package/dist/ui/components/HelpModal.js +2 -2
- package/dist/ui/components/KanbanBoard.d.ts +3 -1
- package/dist/ui/components/KanbanBoard.js +45 -8
- package/dist/ui/components/SearchResults.js +2 -2
- package/dist/ui/theme/themes.d.ts +10 -0
- package/dist/ui/theme/themes.js +410 -0
- package/dist/ui/theme/types.d.ts +1 -1
- package/package.json +1 -1
package/README.ja.md
CHANGED
|
@@ -64,6 +64,15 @@ floq
|
|
|
64
64
|
| `?` | ヘルプ |
|
|
65
65
|
| `q` | 終了 |
|
|
66
66
|
|
|
67
|
+
#### 検索
|
|
68
|
+
|
|
69
|
+
| キー | アクション |
|
|
70
|
+
|------|-----------|
|
|
71
|
+
| `/` | 検索モード開始 |
|
|
72
|
+
| `↑/↓` または `Ctrl+j/k` | 検索結果をナビゲート |
|
|
73
|
+
| `Enter` | 選択したタスクのタブに移動して選択 |
|
|
74
|
+
| `Esc` | キャンセル |
|
|
75
|
+
|
|
67
76
|
#### プロジェクト詳細画面
|
|
68
77
|
|
|
69
78
|
| キー | アクション |
|
|
@@ -228,7 +237,7 @@ floq config turso --disable
|
|
|
228
237
|
|
|
229
238
|
## テーマ
|
|
230
239
|
|
|
231
|
-
|
|
240
|
+
26種類のテーマが利用可能。`floq config theme` でインタラクティブに選択(j/kで移動)。
|
|
232
241
|
|
|
233
242
|
| テーマ | 説明 |
|
|
234
243
|
|--------|------|
|
|
@@ -248,6 +257,16 @@ floq config turso --disable
|
|
|
248
257
|
| `synthwave` | ネオン80sスタイル |
|
|
249
258
|
| `paper` | 紙とインク風ライト |
|
|
250
259
|
| `coffee` | 暖かみのある茶系 |
|
|
260
|
+
| `nord` | 北欧風ブルーグレー |
|
|
261
|
+
| `dracula` | ダーク&ビビッドカラー |
|
|
262
|
+
| `monokai` | エディタ定番の鮮やかな配色 |
|
|
263
|
+
| `gruvbox` | レトロな暖色系ダーク |
|
|
264
|
+
| `tokyo-night` | 東京の夜景イメージ |
|
|
265
|
+
| `catppuccin` | パステル系モダン |
|
|
266
|
+
| `ocean` | 深海ブルー |
|
|
267
|
+
| `sakura` | 桜ピンク |
|
|
268
|
+
| `msx` | MSXコンピュータ(TMS9918) |
|
|
269
|
+
| `pc-98` | NEC PC-9801風 |
|
|
251
270
|
|
|
252
271
|
> **注意**: 背景色はターミナルの設定に依存します。
|
|
253
272
|
|
package/README.md
CHANGED
|
@@ -64,6 +64,15 @@ floq
|
|
|
64
64
|
| `?` | Help |
|
|
65
65
|
| `q` | Quit |
|
|
66
66
|
|
|
67
|
+
#### Search
|
|
68
|
+
|
|
69
|
+
| Key | Action |
|
|
70
|
+
|-----|--------|
|
|
71
|
+
| `/` | Start search mode |
|
|
72
|
+
| `↑/↓` or `Ctrl+j/k` | Navigate search results |
|
|
73
|
+
| `Enter` | Jump to selected task's tab and select it |
|
|
74
|
+
| `Esc` | Cancel search |
|
|
75
|
+
|
|
67
76
|
#### Project Detail View
|
|
68
77
|
|
|
69
78
|
| Key | Action |
|
|
@@ -228,7 +237,7 @@ floq config turso --disable
|
|
|
228
237
|
|
|
229
238
|
## Themes
|
|
230
239
|
|
|
231
|
-
|
|
240
|
+
26 themes available. Use `floq config theme` for interactive selection (j/k to navigate).
|
|
232
241
|
|
|
233
242
|
| Theme | Description |
|
|
234
243
|
|-------|-------------|
|
|
@@ -248,6 +257,16 @@ floq config turso --disable
|
|
|
248
257
|
| `synthwave` | Neon 80s aesthetic |
|
|
249
258
|
| `paper` | Light minimal theme |
|
|
250
259
|
| `coffee` | Warm brown tones |
|
|
260
|
+
| `nord` | Arctic, north-bluish palette |
|
|
261
|
+
| `dracula` | Dark theme with vibrant colors |
|
|
262
|
+
| `monokai` | Classic editor vivid colors |
|
|
263
|
+
| `gruvbox` | Retro groove warm tones |
|
|
264
|
+
| `tokyo-night` | Tokyo night lights inspired |
|
|
265
|
+
| `catppuccin` | Soothing pastel theme |
|
|
266
|
+
| `ocean` | Deep sea blue theme |
|
|
267
|
+
| `sakura` | Cherry blossom pink |
|
|
268
|
+
| `msx` | MSX computer (TMS9918) |
|
|
269
|
+
| `pc-98` | NEC PC-9801 style |
|
|
251
270
|
|
|
252
271
|
> **Note**: Background colors depend on your terminal settings.
|
|
253
272
|
|
package/dist/i18n/en.d.ts
CHANGED
|
@@ -129,6 +129,10 @@ export declare const en: {
|
|
|
129
129
|
taskDetail: string;
|
|
130
130
|
addComment: string;
|
|
131
131
|
searchTasks: string;
|
|
132
|
+
settings: string;
|
|
133
|
+
changeTheme: string;
|
|
134
|
+
changeViewMode: string;
|
|
135
|
+
changeLanguage: string;
|
|
132
136
|
other: string;
|
|
133
137
|
showHelp: string;
|
|
134
138
|
quit: string;
|
|
@@ -156,6 +160,10 @@ export declare const en: {
|
|
|
156
160
|
moveRight: string;
|
|
157
161
|
moveLeft: string;
|
|
158
162
|
searchTasks: string;
|
|
163
|
+
settings: string;
|
|
164
|
+
changeTheme: string;
|
|
165
|
+
changeViewMode: string;
|
|
166
|
+
changeLanguage: string;
|
|
159
167
|
other: string;
|
|
160
168
|
showHelp: string;
|
|
161
169
|
quit: string;
|
|
@@ -274,6 +282,10 @@ export type HelpTranslations = {
|
|
|
274
282
|
taskDetail: string;
|
|
275
283
|
addComment: string;
|
|
276
284
|
searchTasks: string;
|
|
285
|
+
settings: string;
|
|
286
|
+
changeTheme: string;
|
|
287
|
+
changeViewMode: string;
|
|
288
|
+
changeLanguage: string;
|
|
277
289
|
other: string;
|
|
278
290
|
showHelp: string;
|
|
279
291
|
quit: string;
|
|
@@ -301,6 +313,10 @@ export type KanbanHelpTranslations = {
|
|
|
301
313
|
moveRight: string;
|
|
302
314
|
moveLeft: string;
|
|
303
315
|
searchTasks: string;
|
|
316
|
+
settings: string;
|
|
317
|
+
changeTheme: string;
|
|
318
|
+
changeViewMode: string;
|
|
319
|
+
changeLanguage: string;
|
|
304
320
|
other: string;
|
|
305
321
|
showHelp: string;
|
|
306
322
|
quit: string;
|
package/dist/i18n/en.js
CHANGED
|
@@ -138,6 +138,10 @@ export const en = {
|
|
|
138
138
|
taskDetail: 'View task details',
|
|
139
139
|
addComment: 'Add comment',
|
|
140
140
|
searchTasks: 'Search tasks',
|
|
141
|
+
settings: 'Settings',
|
|
142
|
+
changeTheme: 'Change theme',
|
|
143
|
+
changeViewMode: 'Change view mode',
|
|
144
|
+
changeLanguage: 'Change language',
|
|
141
145
|
other: 'Other',
|
|
142
146
|
showHelp: 'Show this help',
|
|
143
147
|
quit: 'Quit',
|
|
@@ -167,6 +171,10 @@ export const en = {
|
|
|
167
171
|
moveRight: 'Move task to next column',
|
|
168
172
|
moveLeft: 'Move task to previous column',
|
|
169
173
|
searchTasks: 'Search tasks',
|
|
174
|
+
settings: 'Settings',
|
|
175
|
+
changeTheme: 'Change theme',
|
|
176
|
+
changeViewMode: 'Change view mode',
|
|
177
|
+
changeLanguage: 'Change language',
|
|
170
178
|
other: 'Other',
|
|
171
179
|
showHelp: 'Show this help',
|
|
172
180
|
quit: 'Quit',
|
|
@@ -190,7 +198,7 @@ export const en = {
|
|
|
190
198
|
search: {
|
|
191
199
|
prefix: '/',
|
|
192
200
|
placeholder: 'Search tasks...',
|
|
193
|
-
help: '(Enter to select, Ctrl+j/k to navigate, Esc to cancel)',
|
|
201
|
+
help: '(Enter to select, ↑/↓ or Ctrl+j/k to navigate, Esc to cancel)',
|
|
194
202
|
noResults: 'No matching tasks',
|
|
195
203
|
resultsTitle: 'Search Results',
|
|
196
204
|
searchTasks: 'Search tasks',
|
package/dist/i18n/ja.js
CHANGED
|
@@ -138,6 +138,10 @@ export const ja = {
|
|
|
138
138
|
taskDetail: 'タスク詳細を表示',
|
|
139
139
|
addComment: 'コメント追加',
|
|
140
140
|
searchTasks: 'タスク検索',
|
|
141
|
+
settings: '設定',
|
|
142
|
+
changeTheme: 'テーマ変更',
|
|
143
|
+
changeViewMode: '表示モード変更',
|
|
144
|
+
changeLanguage: '言語変更',
|
|
141
145
|
other: 'その他',
|
|
142
146
|
showHelp: 'このヘルプを表示',
|
|
143
147
|
quit: '終了',
|
|
@@ -167,6 +171,10 @@ export const ja = {
|
|
|
167
171
|
moveRight: '次のカラムへ移動',
|
|
168
172
|
moveLeft: '前のカラムへ戻す',
|
|
169
173
|
searchTasks: 'タスク検索',
|
|
174
|
+
settings: '設定',
|
|
175
|
+
changeTheme: 'テーマ変更',
|
|
176
|
+
changeViewMode: '表示モード変更',
|
|
177
|
+
changeLanguage: '言語変更',
|
|
170
178
|
other: 'その他',
|
|
171
179
|
showHelp: 'このヘルプを表示',
|
|
172
180
|
quit: '終了',
|
|
@@ -190,7 +198,7 @@ export const ja = {
|
|
|
190
198
|
search: {
|
|
191
199
|
prefix: '/',
|
|
192
200
|
placeholder: 'タスクを検索...',
|
|
193
|
-
help: '(Enterで選択, Ctrl+j/kで移動, Escでキャンセル)',
|
|
201
|
+
help: '(Enterで選択, ↑/↓またはCtrl+j/kで移動, Escでキャンセル)',
|
|
194
202
|
noResults: '該当するタスクがありません',
|
|
195
203
|
resultsTitle: '検索結果',
|
|
196
204
|
searchTasks: 'タスク検索',
|
package/dist/ui/App.js
CHANGED
|
@@ -10,19 +10,52 @@ import { FunctionKeyBar } from './components/FunctionKeyBar.js';
|
|
|
10
10
|
import { SearchBar } from './components/SearchBar.js';
|
|
11
11
|
import { SearchResults } from './components/SearchResults.js';
|
|
12
12
|
import { SplashScreen } from './SplashScreen.js';
|
|
13
|
+
import { ThemeSelector } from './ThemeSelector.js';
|
|
14
|
+
import { ModeSelector } from './ModeSelector.js';
|
|
15
|
+
import { LanguageSelector } from './LanguageSelector.js';
|
|
13
16
|
import { getDb, schema } from '../db/index.js';
|
|
14
17
|
import { t, fmt } from '../i18n/index.js';
|
|
15
18
|
import { ThemeProvider, useTheme } from './theme/index.js';
|
|
16
|
-
import { getThemeName, getViewMode, isTursoEnabled } from '../config.js';
|
|
19
|
+
import { getThemeName, getViewMode, setThemeName, setViewMode, setLocale, isTursoEnabled } from '../config.js';
|
|
17
20
|
import { KanbanBoard } from './components/KanbanBoard.js';
|
|
18
21
|
import { VERSION } from '../version.js';
|
|
19
22
|
const TABS = ['inbox', 'next', 'waiting', 'someday', 'projects', 'done'];
|
|
20
23
|
export function App() {
|
|
21
|
-
const themeName = getThemeName
|
|
22
|
-
const viewMode = getViewMode
|
|
23
|
-
|
|
24
|
+
const [themeName, setThemeNameState] = useState(getThemeName);
|
|
25
|
+
const [viewMode, setViewModeState] = useState(getViewMode);
|
|
26
|
+
const [settingsMode, setSettingsMode] = useState('none');
|
|
27
|
+
const [, forceUpdate] = useState({});
|
|
28
|
+
const handleThemeSelect = (theme) => {
|
|
29
|
+
setThemeName(theme);
|
|
30
|
+
setThemeNameState(theme);
|
|
31
|
+
setSettingsMode('none');
|
|
32
|
+
};
|
|
33
|
+
const handleModeSelect = (mode) => {
|
|
34
|
+
setViewMode(mode);
|
|
35
|
+
setViewModeState(mode);
|
|
36
|
+
setSettingsMode('none');
|
|
37
|
+
};
|
|
38
|
+
const handleLocaleSelect = (locale) => {
|
|
39
|
+
setLocale(locale);
|
|
40
|
+
setSettingsMode('none');
|
|
41
|
+
forceUpdate({});
|
|
42
|
+
};
|
|
43
|
+
const handleSettingsCancel = () => {
|
|
44
|
+
setSettingsMode('none');
|
|
45
|
+
};
|
|
46
|
+
// Settings selector screens
|
|
47
|
+
if (settingsMode === 'theme-select') {
|
|
48
|
+
return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(ThemeSelector, { onSelect: handleThemeSelect, onCancel: handleSettingsCancel }) }));
|
|
49
|
+
}
|
|
50
|
+
if (settingsMode === 'mode-select') {
|
|
51
|
+
return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(ModeSelector, { onSelect: handleModeSelect, onCancel: handleSettingsCancel }) }));
|
|
52
|
+
}
|
|
53
|
+
if (settingsMode === 'lang-select') {
|
|
54
|
+
return (_jsx(ThemeProvider, { themeName: themeName, children: _jsx(LanguageSelector, { onSelect: handleLocaleSelect, onCancel: handleSettingsCancel }) }));
|
|
55
|
+
}
|
|
56
|
+
return (_jsx(ThemeProvider, { themeName: themeName, children: viewMode === 'kanban' ? (_jsx(KanbanBoard, { onOpenSettings: setSettingsMode })) : (_jsx(AppContent, { onOpenSettings: setSettingsMode })) }));
|
|
24
57
|
}
|
|
25
|
-
function AppContent() {
|
|
58
|
+
function AppContent({ onOpenSettings }) {
|
|
26
59
|
const theme = useTheme();
|
|
27
60
|
const { exit } = useApp();
|
|
28
61
|
const [mode, setMode] = useState('splash');
|
|
@@ -141,6 +174,18 @@ function AppContent() {
|
|
|
141
174
|
setSearchResults(results);
|
|
142
175
|
setSearchResultIndex(0);
|
|
143
176
|
}, [searchTasks]);
|
|
177
|
+
// Navigate to a task from search results
|
|
178
|
+
const navigateToTask = useCallback((task) => {
|
|
179
|
+
const targetTab = task.isProject ? 'projects' : task.status;
|
|
180
|
+
const tabIndex = TABS.indexOf(targetTab);
|
|
181
|
+
const tabTasks = tasks[targetTab];
|
|
182
|
+
const taskIndex = tabTasks.findIndex(t => t.id === task.id);
|
|
183
|
+
if (tabIndex >= 0 && taskIndex >= 0) {
|
|
184
|
+
setCurrentListIndex(tabIndex);
|
|
185
|
+
setSelectedTaskIndex(taskIndex);
|
|
186
|
+
setMode('normal');
|
|
187
|
+
}
|
|
188
|
+
}, [tasks]);
|
|
144
189
|
const addTask = useCallback(async (title, parentId) => {
|
|
145
190
|
if (!title.trim())
|
|
146
191
|
return;
|
|
@@ -191,9 +236,7 @@ function AppContent() {
|
|
|
191
236
|
if (mode === 'search') {
|
|
192
237
|
if (searchResults.length > 0) {
|
|
193
238
|
const task = searchResults[searchResultIndex];
|
|
194
|
-
|
|
195
|
-
loadTaskComments(task.id);
|
|
196
|
-
setMode('task-detail');
|
|
239
|
+
navigateToTask(task);
|
|
197
240
|
}
|
|
198
241
|
else {
|
|
199
242
|
setMode('normal');
|
|
@@ -312,12 +355,12 @@ function AppContent() {
|
|
|
312
355
|
setMode('normal');
|
|
313
356
|
return;
|
|
314
357
|
}
|
|
315
|
-
// Navigate search results with Ctrl+j/k or Ctrl+n/p
|
|
316
|
-
if (key.ctrl && (input === 'j' || input === 'n')) {
|
|
358
|
+
// Navigate search results with arrow keys, Ctrl+j/k, or Ctrl+n/p
|
|
359
|
+
if (key.downArrow || (key.ctrl && (input === 'j' || input === 'n'))) {
|
|
317
360
|
setSearchResultIndex((prev) => prev < searchResults.length - 1 ? prev + 1 : 0);
|
|
318
361
|
return;
|
|
319
362
|
}
|
|
320
|
-
if (key.ctrl && (input === 'k' || input === 'p')) {
|
|
363
|
+
if (key.upArrow || (key.ctrl && (input === 'k' || input === 'p'))) {
|
|
321
364
|
setSearchResultIndex((prev) => prev > 0 ? prev - 1 : Math.max(0, searchResults.length - 1));
|
|
322
365
|
return;
|
|
323
366
|
}
|
|
@@ -551,6 +594,21 @@ function AppContent() {
|
|
|
551
594
|
setSearchResultIndex(0);
|
|
552
595
|
return;
|
|
553
596
|
}
|
|
597
|
+
// Settings: Theme selector
|
|
598
|
+
if (input === 'T') {
|
|
599
|
+
onOpenSettings('theme-select');
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
// Settings: Mode selector
|
|
603
|
+
if (input === 'V') {
|
|
604
|
+
onOpenSettings('mode-select');
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
607
|
+
// Settings: Language selector
|
|
608
|
+
if (input === 'L') {
|
|
609
|
+
onOpenSettings('lang-select');
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
554
612
|
// Quit
|
|
555
613
|
if (input === 'q' || (key.ctrl && input === 'c')) {
|
|
556
614
|
exit();
|
|
@@ -725,7 +783,7 @@ function AppContent() {
|
|
|
725
783
|
const tursoEnabled = isTursoEnabled();
|
|
726
784
|
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
785
|
? (tursoEnabled ? ' ☁️ turso' : ' 💾 local')
|
|
728
|
-
: (tursoEnabled ? ' [DB]
|
|
786
|
+
: (tursoEnabled ? ' [DB]TURSO' : ' [DB]local') })] }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.helpHint })] }), _jsx(Box, { marginBottom: 1, children: TABS.map((tab, index) => {
|
|
729
787
|
const isActive = index === currentListIndex && mode !== 'project-detail';
|
|
730
788
|
const count = tasks[tab].length;
|
|
731
789
|
const label = `${index + 1}:${getTabLabel(tab)}(${count})`;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type Locale } from '../config.js';
|
|
3
|
+
interface LanguageSelectorProps {
|
|
4
|
+
onSelect: (locale: Locale) => void;
|
|
5
|
+
onCancel: () => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function LanguageSelector({ onSelect, onCancel }: LanguageSelectorProps): React.ReactElement;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import { getLocale } from '../config.js';
|
|
5
|
+
const VALID_LOCALES = ['en', 'ja'];
|
|
6
|
+
const localeDisplayNames = {
|
|
7
|
+
en: 'English',
|
|
8
|
+
ja: '日本語 (Japanese)',
|
|
9
|
+
};
|
|
10
|
+
export function LanguageSelector({ onSelect, onCancel }) {
|
|
11
|
+
const currentLocale = getLocale();
|
|
12
|
+
const initialIndex = VALID_LOCALES.indexOf(currentLocale);
|
|
13
|
+
const [selectedIndex, setSelectedIndex] = useState(initialIndex >= 0 ? initialIndex : 0);
|
|
14
|
+
useInput((input, key) => {
|
|
15
|
+
if (key.escape) {
|
|
16
|
+
onCancel();
|
|
17
|
+
return;
|
|
18
|
+
}
|
|
19
|
+
// j or down arrow: move down
|
|
20
|
+
if (input === 'j' || key.downArrow) {
|
|
21
|
+
setSelectedIndex((prev) => (prev < VALID_LOCALES.length - 1 ? prev + 1 : 0));
|
|
22
|
+
}
|
|
23
|
+
// k or up arrow: move up
|
|
24
|
+
if (input === 'k' || key.upArrow) {
|
|
25
|
+
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : VALID_LOCALES.length - 1));
|
|
26
|
+
}
|
|
27
|
+
// Enter: select
|
|
28
|
+
if (key.return) {
|
|
29
|
+
onSelect(VALID_LOCALES[selectedIndex]);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
32
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: "Select language / \u8A00\u8A9E\u3092\u9078\u629E:" }), _jsx(Text, { dimColor: true, children: "j/k: select, Enter: confirm, Esc: cancel" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: VALID_LOCALES.map((locale, index) => {
|
|
33
|
+
const isSelected = index === selectedIndex;
|
|
34
|
+
const isCurrent = locale === currentLocale;
|
|
35
|
+
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? 'cyan' : undefined, children: [isSelected ? '› ' : ' ', localeDisplayNames[locale], isCurrent ? ' (current)' : ''] }) }, locale));
|
|
36
|
+
}) })] }));
|
|
37
|
+
}
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import { type ViewMode } from '../config.js';
|
|
3
3
|
interface ModeSelectorProps {
|
|
4
4
|
onSelect: (mode: ViewMode) => void;
|
|
5
|
+
onCancel?: () => void;
|
|
5
6
|
}
|
|
6
|
-
export declare function ModeSelector({ onSelect }: ModeSelectorProps): React.ReactElement;
|
|
7
|
+
export declare function ModeSelector({ onSelect, onCancel }: ModeSelectorProps): React.ReactElement;
|
|
7
8
|
export {};
|
package/dist/ui/ModeSelector.js
CHANGED
|
@@ -11,11 +11,15 @@ const modeDescriptions = {
|
|
|
11
11
|
gtd: 'Classic GTD workflow with Inbox, Next, Waiting, Someday lists',
|
|
12
12
|
kanban: '3-column kanban board view',
|
|
13
13
|
};
|
|
14
|
-
export function ModeSelector({ onSelect }) {
|
|
14
|
+
export function ModeSelector({ onSelect, onCancel }) {
|
|
15
15
|
const currentMode = getViewMode();
|
|
16
16
|
const initialIndex = VALID_VIEW_MODES.indexOf(currentMode);
|
|
17
17
|
const [selectedIndex, setSelectedIndex] = useState(initialIndex >= 0 ? initialIndex : 0);
|
|
18
18
|
useInput((input, key) => {
|
|
19
|
+
if (key.escape && onCancel) {
|
|
20
|
+
onCancel();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
19
23
|
// j or down arrow: move down
|
|
20
24
|
if (input === 'j' || key.downArrow) {
|
|
21
25
|
setSelectedIndex((prev) => (prev < VALID_VIEW_MODES.length - 1 ? prev + 1 : 0));
|
|
@@ -29,7 +33,7 @@ export function ModeSelector({ onSelect }) {
|
|
|
29
33
|
onSelect(VALID_VIEW_MODES[selectedIndex]);
|
|
30
34
|
}
|
|
31
35
|
});
|
|
32
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: "Select a view mode:" }), _jsx(Text, { dimColor: true, children: "j/k: select, Enter: confirm" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: VALID_VIEW_MODES.map((mode, index) => {
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: "Select a view mode:" }), _jsx(Text, { dimColor: true, children: "j/k: select, Enter: confirm, Esc: cancel" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: VALID_VIEW_MODES.map((mode, index) => {
|
|
33
37
|
const isSelected = index === selectedIndex;
|
|
34
38
|
const isCurrent = mode === currentMode;
|
|
35
39
|
return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { color: isSelected ? 'cyan' : undefined, children: [isSelected ? '› ' : ' ', modeDisplayNames[mode], isCurrent ? ' (current)' : ''] }), _jsxs(Text, { dimColor: true, children: [' ', modeDescriptions[mode]] })] }, mode));
|
|
@@ -2,6 +2,7 @@ import React from 'react';
|
|
|
2
2
|
import type { ThemeName } from './theme/types.js';
|
|
3
3
|
interface ThemeSelectorProps {
|
|
4
4
|
onSelect: (theme: ThemeName) => void;
|
|
5
|
+
onCancel?: () => void;
|
|
5
6
|
}
|
|
6
|
-
export declare function ThemeSelector({ onSelect }: ThemeSelectorProps): React.ReactElement;
|
|
7
|
+
export declare function ThemeSelector({ onSelect, onCancel }: ThemeSelectorProps): React.ReactElement;
|
|
7
8
|
export {};
|
package/dist/ui/ThemeSelector.js
CHANGED
|
@@ -3,11 +3,15 @@ import { useState } from 'react';
|
|
|
3
3
|
import { Box, Text, useInput } from 'ink';
|
|
4
4
|
import { themes, VALID_THEMES } from './theme/themes.js';
|
|
5
5
|
import { getThemeName } from '../config.js';
|
|
6
|
-
export function ThemeSelector({ onSelect }) {
|
|
6
|
+
export function ThemeSelector({ onSelect, onCancel }) {
|
|
7
7
|
const currentTheme = getThemeName();
|
|
8
8
|
const initialIndex = VALID_THEMES.indexOf(currentTheme);
|
|
9
9
|
const [selectedIndex, setSelectedIndex] = useState(initialIndex >= 0 ? initialIndex : 0);
|
|
10
10
|
useInput((input, key) => {
|
|
11
|
+
if (key.escape && onCancel) {
|
|
12
|
+
onCancel();
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
11
15
|
// j or down arrow: move down
|
|
12
16
|
if (input === 'j' || key.downArrow) {
|
|
13
17
|
setSelectedIndex((prev) => (prev < VALID_THEMES.length - 1 ? prev + 1 : 0));
|
|
@@ -21,7 +25,7 @@ export function ThemeSelector({ onSelect }) {
|
|
|
21
25
|
onSelect(VALID_THEMES[selectedIndex]);
|
|
22
26
|
}
|
|
23
27
|
});
|
|
24
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: "Select a theme:" }), _jsx(Text, { dimColor: true, children: "j/k: select, Enter: confirm" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: VALID_THEMES.map((themeName, index) => {
|
|
28
|
+
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: "Select a theme:" }), _jsx(Text, { dimColor: true, children: "j/k: select, Enter: confirm, Esc: cancel" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: VALID_THEMES.map((themeName, index) => {
|
|
25
29
|
const theme = themes[themeName];
|
|
26
30
|
const isSelected = index === selectedIndex;
|
|
27
31
|
const isCurrent = themeName === currentTheme;
|
|
@@ -99,14 +99,14 @@ function GTDKeybindingsContent() {
|
|
|
99
99
|
const help = i18n.tui.help;
|
|
100
100
|
const theme = useTheme();
|
|
101
101
|
const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
|
|
102
|
-
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: "1-6" }), " ", help.tabSwitch] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "h/l \u2190/\u2192" }), " ", help.prevNextTab] }), _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: "n" }), " ", help.moveToNext] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "s" }), " ", help.moveToSomeday] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "w" }), " ", help.moveToWaiting] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "i" }), " ", help.moveToInbox] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "r" }), " ", help.refresh] })] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(help.projects) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "p" }), " ", help.makeProject] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "P" }), " ", help.linkToProject] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "Enter" }), " ", help.openProject] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "Esc/b" }), " ", help.backFromProject] })] })] }), _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] })] })] })] }));
|
|
102
|
+
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: "1-6" }), " ", help.tabSwitch] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "h/l \u2190/\u2192" }), " ", help.prevNextTab] }), _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: "n" }), " ", help.moveToNext] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "s" }), " ", help.moveToSomeday] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "w" }), " ", help.moveToWaiting] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "i" }), " ", help.moveToInbox] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "r" }), " ", help.refresh] })] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(help.projects) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "p" }), " ", help.makeProject] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "P" }), " ", help.linkToProject] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "Enter" }), " ", help.openProject] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "Esc/b" }), " ", help.backFromProject] })] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(help.settings) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "T" }), " ", help.changeTheme] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "V" }), " ", help.changeViewMode] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "L" }), " ", help.changeLanguage] })] })] }), _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] })] })] })] }));
|
|
103
103
|
}
|
|
104
104
|
function KanbanKeybindingsContent() {
|
|
105
105
|
const i18n = t();
|
|
106
106
|
const help = i18n.tui.kanbanHelp;
|
|
107
107
|
const theme = useTheme();
|
|
108
108
|
const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
|
|
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] })] })] })] }));
|
|
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.settings) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "T" }), " ", help.changeTheme] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "V" }), " ", help.changeViewMode] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "L" }), " ", help.changeLanguage] })] })] }), _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] })] })] })] }));
|
|
110
110
|
}
|
|
111
111
|
function InfoContent() {
|
|
112
112
|
const i18n = t();
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
type SettingsMode = 'none' | 'theme-select' | 'mode-select' | 'lang-select';
|
|
2
3
|
interface KanbanBoardProps {
|
|
3
4
|
onSwitchToGtd?: () => void;
|
|
5
|
+
onOpenSettings?: (mode: SettingsMode) => void;
|
|
4
6
|
}
|
|
5
|
-
export declare function KanbanBoard({ onSwitchToGtd }: KanbanBoardProps): React.ReactElement;
|
|
7
|
+
export declare function KanbanBoard({ onSwitchToGtd, onOpenSettings }: KanbanBoardProps): React.ReactElement;
|
|
6
8
|
export {};
|
|
@@ -15,7 +15,7 @@ import { useTheme } from '../theme/index.js';
|
|
|
15
15
|
import { isTursoEnabled } from '../../config.js';
|
|
16
16
|
import { VERSION } from '../../version.js';
|
|
17
17
|
const COLUMNS = ['todo', 'doing', 'done'];
|
|
18
|
-
export function KanbanBoard({ onSwitchToGtd }) {
|
|
18
|
+
export function KanbanBoard({ onSwitchToGtd, onOpenSettings }) {
|
|
19
19
|
const theme = useTheme();
|
|
20
20
|
const { exit } = useApp();
|
|
21
21
|
const [mode, setMode] = useState('normal');
|
|
@@ -140,6 +140,30 @@ export function KanbanBoard({ onSwitchToGtd }) {
|
|
|
140
140
|
setSearchResults(results);
|
|
141
141
|
setSearchResultIndex(0);
|
|
142
142
|
}, [searchTasks]);
|
|
143
|
+
// Navigate to a task from search results
|
|
144
|
+
const navigateToTask = useCallback((task) => {
|
|
145
|
+
// Determine which column the task belongs to based on status
|
|
146
|
+
let targetColumn;
|
|
147
|
+
if (task.status === 'inbox' || task.status === 'someday') {
|
|
148
|
+
targetColumn = 'todo';
|
|
149
|
+
}
|
|
150
|
+
else if (task.status === 'next' || task.status === 'waiting') {
|
|
151
|
+
targetColumn = 'doing';
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
targetColumn = 'done';
|
|
155
|
+
}
|
|
156
|
+
const columnIndex = COLUMNS.indexOf(targetColumn);
|
|
157
|
+
const taskIndex = tasks[targetColumn].findIndex(t => t.id === task.id);
|
|
158
|
+
if (columnIndex >= 0 && taskIndex >= 0) {
|
|
159
|
+
setCurrentColumnIndex(columnIndex);
|
|
160
|
+
setSelectedTaskIndices(prev => ({
|
|
161
|
+
...prev,
|
|
162
|
+
[targetColumn]: taskIndex,
|
|
163
|
+
}));
|
|
164
|
+
setMode('normal');
|
|
165
|
+
}
|
|
166
|
+
}, [tasks]);
|
|
143
167
|
const addTask = useCallback(async (title) => {
|
|
144
168
|
if (!title.trim())
|
|
145
169
|
return;
|
|
@@ -169,9 +193,7 @@ export function KanbanBoard({ onSwitchToGtd }) {
|
|
|
169
193
|
if (mode === 'search') {
|
|
170
194
|
if (searchResults.length > 0) {
|
|
171
195
|
const task = searchResults[searchResultIndex];
|
|
172
|
-
|
|
173
|
-
loadTaskComments(task.id);
|
|
174
|
-
setMode('task-detail');
|
|
196
|
+
navigateToTask(task);
|
|
175
197
|
}
|
|
176
198
|
else {
|
|
177
199
|
setMode('normal');
|
|
@@ -257,12 +279,12 @@ export function KanbanBoard({ onSwitchToGtd }) {
|
|
|
257
279
|
setMode('normal');
|
|
258
280
|
return;
|
|
259
281
|
}
|
|
260
|
-
// Navigate search results with Ctrl+j/k or Ctrl+n/p
|
|
261
|
-
if (key.ctrl && (input === 'j' || input === 'n')) {
|
|
282
|
+
// Navigate search results with arrow keys, Ctrl+j/k, or Ctrl+n/p
|
|
283
|
+
if (key.downArrow || (key.ctrl && (input === 'j' || input === 'n'))) {
|
|
262
284
|
setSearchResultIndex((prev) => prev < searchResults.length - 1 ? prev + 1 : 0);
|
|
263
285
|
return;
|
|
264
286
|
}
|
|
265
|
-
if (key.ctrl && (input === 'k' || input === 'p')) {
|
|
287
|
+
if (key.upArrow || (key.ctrl && (input === 'k' || input === 'p'))) {
|
|
266
288
|
setSearchResultIndex((prev) => prev > 0 ? prev - 1 : Math.max(0, searchResults.length - 1));
|
|
267
289
|
return;
|
|
268
290
|
}
|
|
@@ -366,6 +388,21 @@ export function KanbanBoard({ onSwitchToGtd }) {
|
|
|
366
388
|
setSearchResultIndex(0);
|
|
367
389
|
return;
|
|
368
390
|
}
|
|
391
|
+
// Settings: Theme selector
|
|
392
|
+
if (input === 'T' && onOpenSettings) {
|
|
393
|
+
onOpenSettings('theme-select');
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
// Settings: Mode selector
|
|
397
|
+
if (input === 'V' && onOpenSettings) {
|
|
398
|
+
onOpenSettings('mode-select');
|
|
399
|
+
return;
|
|
400
|
+
}
|
|
401
|
+
// Settings: Language selector
|
|
402
|
+
if (input === 'L' && onOpenSettings) {
|
|
403
|
+
onOpenSettings('lang-select');
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
369
406
|
// Quit
|
|
370
407
|
if (input === 'q' || (key.ctrl && input === 'c')) {
|
|
371
408
|
exit();
|
|
@@ -478,7 +515,7 @@ export function KanbanBoard({ onSwitchToGtd }) {
|
|
|
478
515
|
const tursoEnabled = isTursoEnabled();
|
|
479
516
|
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
517
|
? (tursoEnabled ? ' ☁️ turso' : ' 💾 local')
|
|
481
|
-
: (tursoEnabled ? ' [DB]
|
|
518
|
+
: (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) => {
|
|
482
519
|
const isSelected = index === selectedCommentIndex && mode === 'task-detail';
|
|
483
520
|
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));
|
|
484
521
|
})) }), 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: [
|
|
@@ -12,7 +12,7 @@ export function SearchResults({ results, selectedIndex, query }) {
|
|
|
12
12
|
return (_jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.borderActive, paddingX: 1, paddingY: 1, minHeight: 5, children: [_jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: theme.colors.secondary, bold: true, children: ["[", search.resultsTitle, "] (", results.length, ")"] }) }), results.length === 0 ? (_jsx(Text, { color: theme.colors.textMuted, italic: true, children: search.noResults })) : (results.slice(0, 10).map((task, index) => {
|
|
13
13
|
const isSelected = index === selectedIndex;
|
|
14
14
|
const shortId = task.id.slice(0, 8);
|
|
15
|
-
const
|
|
16
|
-
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? theme.colors.textSelected : theme.colors.text, bold: isSelected, children: [isSelected ? theme.style.selectedPrefix : theme.style.unselectedPrefix, "[", shortId, "] ", task.title, _jsxs(Text, { color: theme.colors.textMuted, children: [" (",
|
|
15
|
+
const displayLabel = task.isProject ? i18n.tui.keyBar.project : i18n.status[task.status];
|
|
16
|
+
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? theme.colors.textSelected : theme.colors.text, bold: isSelected, children: [isSelected ? theme.style.selectedPrefix : theme.style.unselectedPrefix, "[", shortId, "] ", task.title, _jsxs(Text, { color: theme.colors.textMuted, children: [" (", displayLabel, ")"] }), task.waitingFor && (_jsxs(Text, { color: theme.colors.statusWaiting, children: [" - ", task.waitingFor] }))] }) }, task.id));
|
|
17
17
|
})), results.length > 10 && (_jsxs(Text, { color: theme.colors.textMuted, italic: true, children: ["... and ", results.length - 10, " more"] }))] }));
|
|
18
18
|
}
|
|
@@ -15,6 +15,16 @@ export declare const solarizedLightTheme: Theme;
|
|
|
15
15
|
export declare const synthwaveTheme: Theme;
|
|
16
16
|
export declare const paperTheme: Theme;
|
|
17
17
|
export declare const coffeeTheme: Theme;
|
|
18
|
+
export declare const nordTheme: Theme;
|
|
19
|
+
export declare const draculaTheme: Theme;
|
|
20
|
+
export declare const monokaiTheme: Theme;
|
|
21
|
+
export declare const gruvboxTheme: Theme;
|
|
22
|
+
export declare const tokyoNightTheme: Theme;
|
|
23
|
+
export declare const catppuccinTheme: Theme;
|
|
24
|
+
export declare const oceanTheme: Theme;
|
|
25
|
+
export declare const sakuraTheme: Theme;
|
|
26
|
+
export declare const msxTheme: Theme;
|
|
27
|
+
export declare const pc98Theme: Theme;
|
|
18
28
|
export declare const themes: Record<ThemeName, Theme>;
|
|
19
29
|
export declare const VALID_THEMES: ThemeName[];
|
|
20
30
|
export declare function getTheme(name: ThemeName): Theme;
|
package/dist/ui/theme/themes.js
CHANGED
|
@@ -611,6 +611,396 @@ export const coffeeTheme = {
|
|
|
611
611
|
loadingChars: ['☕', '○'],
|
|
612
612
|
},
|
|
613
613
|
};
|
|
614
|
+
// Nord - Arctic, north-bluish color palette
|
|
615
|
+
export const nordTheme = {
|
|
616
|
+
name: 'nord',
|
|
617
|
+
displayName: 'Nord',
|
|
618
|
+
colors: {
|
|
619
|
+
primary: '#88c0d0',
|
|
620
|
+
secondary: '#81a1c1',
|
|
621
|
+
accent: '#ebcb8b',
|
|
622
|
+
muted: '#4c566a',
|
|
623
|
+
border: '#4c566a',
|
|
624
|
+
borderActive: '#88c0d0',
|
|
625
|
+
background: '#2e3440',
|
|
626
|
+
statusInbox: '#88c0d0',
|
|
627
|
+
statusNext: '#a3be8c',
|
|
628
|
+
statusWaiting: '#ebcb8b',
|
|
629
|
+
statusSomeday: '#b48ead',
|
|
630
|
+
statusDone: '#4c566a',
|
|
631
|
+
text: '#eceff4',
|
|
632
|
+
textMuted: '#4c566a',
|
|
633
|
+
textSelected: '#88c0d0',
|
|
634
|
+
textHighlight: '#a3be8c',
|
|
635
|
+
fnKeyLabel: '#3b4252',
|
|
636
|
+
fnKeyText: '#d8dee9',
|
|
637
|
+
},
|
|
638
|
+
borders: {
|
|
639
|
+
main: 'round',
|
|
640
|
+
modal: 'round',
|
|
641
|
+
list: 'single',
|
|
642
|
+
},
|
|
643
|
+
style: {
|
|
644
|
+
selectedPrefix: '❯ ',
|
|
645
|
+
unselectedPrefix: ' ',
|
|
646
|
+
tabActiveInverse: true,
|
|
647
|
+
tabBrackets: ['', ''],
|
|
648
|
+
headerUppercase: false,
|
|
649
|
+
showFunctionKeys: false,
|
|
650
|
+
loadingChars: ['●', '○'],
|
|
651
|
+
},
|
|
652
|
+
};
|
|
653
|
+
// Dracula - Dark theme with vibrant colors
|
|
654
|
+
export const draculaTheme = {
|
|
655
|
+
name: 'dracula',
|
|
656
|
+
displayName: 'Dracula',
|
|
657
|
+
colors: {
|
|
658
|
+
primary: '#bd93f9',
|
|
659
|
+
secondary: '#8be9fd',
|
|
660
|
+
accent: '#ff79c6',
|
|
661
|
+
muted: '#6272a4',
|
|
662
|
+
border: '#6272a4',
|
|
663
|
+
borderActive: '#bd93f9',
|
|
664
|
+
background: '#282a36',
|
|
665
|
+
statusInbox: '#8be9fd',
|
|
666
|
+
statusNext: '#50fa7b',
|
|
667
|
+
statusWaiting: '#ffb86c',
|
|
668
|
+
statusSomeday: '#ff79c6',
|
|
669
|
+
statusDone: '#6272a4',
|
|
670
|
+
text: '#f8f8f2',
|
|
671
|
+
textMuted: '#6272a4',
|
|
672
|
+
textSelected: '#bd93f9',
|
|
673
|
+
textHighlight: '#50fa7b',
|
|
674
|
+
fnKeyLabel: '#44475a',
|
|
675
|
+
fnKeyText: '#f8f8f2',
|
|
676
|
+
},
|
|
677
|
+
borders: {
|
|
678
|
+
main: 'round',
|
|
679
|
+
modal: 'round',
|
|
680
|
+
list: 'single',
|
|
681
|
+
},
|
|
682
|
+
style: {
|
|
683
|
+
selectedPrefix: '▸ ',
|
|
684
|
+
unselectedPrefix: ' ',
|
|
685
|
+
tabActiveInverse: true,
|
|
686
|
+
tabBrackets: ['', ''],
|
|
687
|
+
headerUppercase: false,
|
|
688
|
+
showFunctionKeys: false,
|
|
689
|
+
loadingChars: ['◆', '◇'],
|
|
690
|
+
},
|
|
691
|
+
};
|
|
692
|
+
// Monokai - Classic editor theme with vibrant syntax colors
|
|
693
|
+
export const monokaiTheme = {
|
|
694
|
+
name: 'monokai',
|
|
695
|
+
displayName: 'Monokai',
|
|
696
|
+
colors: {
|
|
697
|
+
primary: '#f92672',
|
|
698
|
+
secondary: '#66d9ef',
|
|
699
|
+
accent: '#a6e22e',
|
|
700
|
+
muted: '#75715e',
|
|
701
|
+
border: '#75715e',
|
|
702
|
+
borderActive: '#f92672',
|
|
703
|
+
background: '#272822',
|
|
704
|
+
statusInbox: '#66d9ef',
|
|
705
|
+
statusNext: '#a6e22e',
|
|
706
|
+
statusWaiting: '#e6db74',
|
|
707
|
+
statusSomeday: '#ae81ff',
|
|
708
|
+
statusDone: '#75715e',
|
|
709
|
+
text: '#f8f8f2',
|
|
710
|
+
textMuted: '#75715e',
|
|
711
|
+
textSelected: '#f92672',
|
|
712
|
+
textHighlight: '#a6e22e',
|
|
713
|
+
fnKeyLabel: '#3e3d32',
|
|
714
|
+
fnKeyText: '#f8f8f2',
|
|
715
|
+
},
|
|
716
|
+
borders: {
|
|
717
|
+
main: 'single',
|
|
718
|
+
modal: 'round',
|
|
719
|
+
list: 'single',
|
|
720
|
+
},
|
|
721
|
+
style: {
|
|
722
|
+
selectedPrefix: '» ',
|
|
723
|
+
unselectedPrefix: ' ',
|
|
724
|
+
tabActiveInverse: true,
|
|
725
|
+
tabBrackets: ['', ''],
|
|
726
|
+
headerUppercase: false,
|
|
727
|
+
showFunctionKeys: false,
|
|
728
|
+
loadingChars: ['▓', '░'],
|
|
729
|
+
},
|
|
730
|
+
};
|
|
731
|
+
// Gruvbox - Retro groove color scheme
|
|
732
|
+
export const gruvboxTheme = {
|
|
733
|
+
name: 'gruvbox',
|
|
734
|
+
displayName: 'Gruvbox',
|
|
735
|
+
colors: {
|
|
736
|
+
primary: '#fe8019',
|
|
737
|
+
secondary: '#fabd2f',
|
|
738
|
+
accent: '#b8bb26',
|
|
739
|
+
muted: '#665c54',
|
|
740
|
+
border: '#665c54',
|
|
741
|
+
borderActive: '#fe8019',
|
|
742
|
+
background: '#282828',
|
|
743
|
+
statusInbox: '#83a598',
|
|
744
|
+
statusNext: '#b8bb26',
|
|
745
|
+
statusWaiting: '#fabd2f',
|
|
746
|
+
statusSomeday: '#d3869b',
|
|
747
|
+
statusDone: '#665c54',
|
|
748
|
+
text: '#ebdbb2',
|
|
749
|
+
textMuted: '#928374',
|
|
750
|
+
textSelected: '#fe8019',
|
|
751
|
+
textHighlight: '#b8bb26',
|
|
752
|
+
fnKeyLabel: '#3c3836',
|
|
753
|
+
fnKeyText: '#ebdbb2',
|
|
754
|
+
},
|
|
755
|
+
borders: {
|
|
756
|
+
main: 'single',
|
|
757
|
+
modal: 'single',
|
|
758
|
+
list: 'single',
|
|
759
|
+
},
|
|
760
|
+
style: {
|
|
761
|
+
selectedPrefix: '▶ ',
|
|
762
|
+
unselectedPrefix: ' ',
|
|
763
|
+
tabActiveInverse: true,
|
|
764
|
+
tabBrackets: ['[', ']'],
|
|
765
|
+
headerUppercase: false,
|
|
766
|
+
showFunctionKeys: false,
|
|
767
|
+
loadingChars: ['█', '░'],
|
|
768
|
+
},
|
|
769
|
+
};
|
|
770
|
+
// Tokyo Night - A clean dark theme inspired by Tokyo's night lights
|
|
771
|
+
export const tokyoNightTheme = {
|
|
772
|
+
name: 'tokyo-night',
|
|
773
|
+
displayName: 'Tokyo Night',
|
|
774
|
+
colors: {
|
|
775
|
+
primary: '#7aa2f7',
|
|
776
|
+
secondary: '#bb9af7',
|
|
777
|
+
accent: '#7dcfff',
|
|
778
|
+
muted: '#565f89',
|
|
779
|
+
border: '#565f89',
|
|
780
|
+
borderActive: '#7aa2f7',
|
|
781
|
+
background: '#1a1b26',
|
|
782
|
+
statusInbox: '#7aa2f7',
|
|
783
|
+
statusNext: '#9ece6a',
|
|
784
|
+
statusWaiting: '#e0af68',
|
|
785
|
+
statusSomeday: '#bb9af7',
|
|
786
|
+
statusDone: '#565f89',
|
|
787
|
+
text: '#c0caf5',
|
|
788
|
+
textMuted: '#565f89',
|
|
789
|
+
textSelected: '#7dcfff',
|
|
790
|
+
textHighlight: '#9ece6a',
|
|
791
|
+
fnKeyLabel: '#24283b',
|
|
792
|
+
fnKeyText: '#c0caf5',
|
|
793
|
+
},
|
|
794
|
+
borders: {
|
|
795
|
+
main: 'round',
|
|
796
|
+
modal: 'round',
|
|
797
|
+
list: 'single',
|
|
798
|
+
},
|
|
799
|
+
style: {
|
|
800
|
+
selectedPrefix: '› ',
|
|
801
|
+
unselectedPrefix: ' ',
|
|
802
|
+
tabActiveInverse: true,
|
|
803
|
+
tabBrackets: ['', ''],
|
|
804
|
+
headerUppercase: false,
|
|
805
|
+
showFunctionKeys: false,
|
|
806
|
+
loadingChars: ['◉', '◎'],
|
|
807
|
+
},
|
|
808
|
+
};
|
|
809
|
+
// Catppuccin - Soothing pastel theme (Mocha variant)
|
|
810
|
+
export const catppuccinTheme = {
|
|
811
|
+
name: 'catppuccin',
|
|
812
|
+
displayName: 'Catppuccin',
|
|
813
|
+
colors: {
|
|
814
|
+
primary: '#cba6f7',
|
|
815
|
+
secondary: '#89b4fa',
|
|
816
|
+
accent: '#f5c2e7',
|
|
817
|
+
muted: '#6c7086',
|
|
818
|
+
border: '#6c7086',
|
|
819
|
+
borderActive: '#cba6f7',
|
|
820
|
+
background: '#1e1e2e',
|
|
821
|
+
statusInbox: '#89b4fa',
|
|
822
|
+
statusNext: '#a6e3a1',
|
|
823
|
+
statusWaiting: '#f9e2af',
|
|
824
|
+
statusSomeday: '#f5c2e7',
|
|
825
|
+
statusDone: '#6c7086',
|
|
826
|
+
text: '#cdd6f4',
|
|
827
|
+
textMuted: '#6c7086',
|
|
828
|
+
textSelected: '#cba6f7',
|
|
829
|
+
textHighlight: '#a6e3a1',
|
|
830
|
+
fnKeyLabel: '#313244',
|
|
831
|
+
fnKeyText: '#cdd6f4',
|
|
832
|
+
},
|
|
833
|
+
borders: {
|
|
834
|
+
main: 'round',
|
|
835
|
+
modal: 'round',
|
|
836
|
+
list: 'single',
|
|
837
|
+
},
|
|
838
|
+
style: {
|
|
839
|
+
selectedPrefix: '◆ ',
|
|
840
|
+
unselectedPrefix: ' ',
|
|
841
|
+
tabActiveInverse: true,
|
|
842
|
+
tabBrackets: ['', ''],
|
|
843
|
+
headerUppercase: false,
|
|
844
|
+
showFunctionKeys: false,
|
|
845
|
+
loadingChars: ['●', '○'],
|
|
846
|
+
},
|
|
847
|
+
};
|
|
848
|
+
// Ocean - Deep sea inspired blue theme
|
|
849
|
+
export const oceanTheme = {
|
|
850
|
+
name: 'ocean',
|
|
851
|
+
displayName: 'Ocean',
|
|
852
|
+
colors: {
|
|
853
|
+
primary: '#00b4d8',
|
|
854
|
+
secondary: '#0077b6',
|
|
855
|
+
accent: '#90e0ef',
|
|
856
|
+
muted: '#023e8a',
|
|
857
|
+
border: '#0077b6',
|
|
858
|
+
borderActive: '#00b4d8',
|
|
859
|
+
background: '#03045e',
|
|
860
|
+
statusInbox: '#00b4d8',
|
|
861
|
+
statusNext: '#48cae4',
|
|
862
|
+
statusWaiting: '#90e0ef',
|
|
863
|
+
statusSomeday: '#ade8f4',
|
|
864
|
+
statusDone: '#023e8a',
|
|
865
|
+
text: '#caf0f8',
|
|
866
|
+
textMuted: '#0077b6',
|
|
867
|
+
textSelected: '#90e0ef',
|
|
868
|
+
textHighlight: '#48cae4',
|
|
869
|
+
fnKeyLabel: '#023e8a',
|
|
870
|
+
fnKeyText: '#caf0f8',
|
|
871
|
+
},
|
|
872
|
+
borders: {
|
|
873
|
+
main: 'round',
|
|
874
|
+
modal: 'round',
|
|
875
|
+
list: 'single',
|
|
876
|
+
},
|
|
877
|
+
style: {
|
|
878
|
+
selectedPrefix: '≋ ',
|
|
879
|
+
unselectedPrefix: ' ',
|
|
880
|
+
tabActiveInverse: true,
|
|
881
|
+
tabBrackets: ['〔', '〕'],
|
|
882
|
+
headerUppercase: false,
|
|
883
|
+
showFunctionKeys: false,
|
|
884
|
+
loadingChars: ['◐', '◑'],
|
|
885
|
+
},
|
|
886
|
+
};
|
|
887
|
+
// Sakura - Cherry blossom inspired pink theme
|
|
888
|
+
export const sakuraTheme = {
|
|
889
|
+
name: 'sakura',
|
|
890
|
+
displayName: 'Sakura',
|
|
891
|
+
colors: {
|
|
892
|
+
primary: '#ffb7c5',
|
|
893
|
+
secondary: '#ff8fa3',
|
|
894
|
+
accent: '#ffffff',
|
|
895
|
+
muted: '#c9184a',
|
|
896
|
+
border: '#ff8fa3',
|
|
897
|
+
borderActive: '#ffb7c5',
|
|
898
|
+
background: '#590d22',
|
|
899
|
+
statusInbox: '#ffb7c5',
|
|
900
|
+
statusNext: '#ff758f',
|
|
901
|
+
statusWaiting: '#ffccd5',
|
|
902
|
+
statusSomeday: '#ff4d6d',
|
|
903
|
+
statusDone: '#800f2f',
|
|
904
|
+
text: '#fff0f3',
|
|
905
|
+
textMuted: '#a4133c',
|
|
906
|
+
textSelected: '#ffffff',
|
|
907
|
+
textHighlight: '#ff758f',
|
|
908
|
+
fnKeyLabel: '#800f2f',
|
|
909
|
+
fnKeyText: '#ffb7c5',
|
|
910
|
+
},
|
|
911
|
+
borders: {
|
|
912
|
+
main: 'round',
|
|
913
|
+
modal: 'round',
|
|
914
|
+
list: 'single',
|
|
915
|
+
},
|
|
916
|
+
style: {
|
|
917
|
+
selectedPrefix: '❀ ',
|
|
918
|
+
unselectedPrefix: ' ',
|
|
919
|
+
tabActiveInverse: true,
|
|
920
|
+
tabBrackets: ['『', '』'],
|
|
921
|
+
headerUppercase: false,
|
|
922
|
+
showFunctionKeys: false,
|
|
923
|
+
loadingChars: ['✿', '❀'],
|
|
924
|
+
},
|
|
925
|
+
};
|
|
926
|
+
// MSX - Japanese retro computer style (TMS9918 palette)
|
|
927
|
+
export const msxTheme = {
|
|
928
|
+
name: 'msx',
|
|
929
|
+
displayName: 'MSX',
|
|
930
|
+
colors: {
|
|
931
|
+
primary: '#40b64a',
|
|
932
|
+
secondary: '#5955df',
|
|
933
|
+
accent: '#ffffff',
|
|
934
|
+
muted: '#cacaca',
|
|
935
|
+
border: '#40b64a',
|
|
936
|
+
borderActive: '#73ce7c',
|
|
937
|
+
background: '#000000',
|
|
938
|
+
statusInbox: '#64daee',
|
|
939
|
+
statusNext: '#40b64a',
|
|
940
|
+
statusWaiting: '#ddce85',
|
|
941
|
+
statusSomeday: '#b565b3',
|
|
942
|
+
statusDone: '#cacaca',
|
|
943
|
+
text: '#40b64a',
|
|
944
|
+
textMuted: '#cacaca',
|
|
945
|
+
textSelected: '#ffffff',
|
|
946
|
+
textHighlight: '#73ce7c',
|
|
947
|
+
fnKeyLabel: '#64daee',
|
|
948
|
+
fnKeyText: '#000000',
|
|
949
|
+
},
|
|
950
|
+
borders: {
|
|
951
|
+
main: 'single',
|
|
952
|
+
modal: 'single',
|
|
953
|
+
list: 'single',
|
|
954
|
+
},
|
|
955
|
+
style: {
|
|
956
|
+
selectedPrefix: '> ',
|
|
957
|
+
unselectedPrefix: ' ',
|
|
958
|
+
tabActiveInverse: true,
|
|
959
|
+
tabBrackets: ['[', ']'],
|
|
960
|
+
headerUppercase: true,
|
|
961
|
+
showFunctionKeys: true,
|
|
962
|
+
loadingChars: ['■', '□'],
|
|
963
|
+
},
|
|
964
|
+
};
|
|
965
|
+
// PC-98 - NEC PC-9801 style
|
|
966
|
+
export const pc98Theme = {
|
|
967
|
+
name: 'pc-98',
|
|
968
|
+
displayName: 'PC-98',
|
|
969
|
+
colors: {
|
|
970
|
+
primary: '#ffffff',
|
|
971
|
+
secondary: '#00ffff',
|
|
972
|
+
accent: '#ffff00',
|
|
973
|
+
muted: '#808080',
|
|
974
|
+
border: '#ffffff',
|
|
975
|
+
borderActive: '#00ffff',
|
|
976
|
+
background: '#000080',
|
|
977
|
+
statusInbox: '#00ffff',
|
|
978
|
+
statusNext: '#00ff00',
|
|
979
|
+
statusWaiting: '#ffff00',
|
|
980
|
+
statusSomeday: '#ff00ff',
|
|
981
|
+
statusDone: '#808080',
|
|
982
|
+
text: '#ffffff',
|
|
983
|
+
textMuted: '#808080',
|
|
984
|
+
textSelected: '#ffff00',
|
|
985
|
+
textHighlight: '#00ffff',
|
|
986
|
+
fnKeyLabel: '#00ffff',
|
|
987
|
+
fnKeyText: '#ffffff',
|
|
988
|
+
},
|
|
989
|
+
borders: {
|
|
990
|
+
main: 'single',
|
|
991
|
+
modal: 'double',
|
|
992
|
+
list: 'single',
|
|
993
|
+
},
|
|
994
|
+
style: {
|
|
995
|
+
selectedPrefix: '▶ ',
|
|
996
|
+
unselectedPrefix: ' ',
|
|
997
|
+
tabActiveInverse: true,
|
|
998
|
+
tabBrackets: ['【', '】'],
|
|
999
|
+
headerUppercase: false,
|
|
1000
|
+
showFunctionKeys: true,
|
|
1001
|
+
loadingChars: ['●', '○'],
|
|
1002
|
+
},
|
|
1003
|
+
};
|
|
614
1004
|
export const themes = {
|
|
615
1005
|
'modern': modernTheme,
|
|
616
1006
|
'norton-commander': nortonCommanderTheme,
|
|
@@ -628,6 +1018,16 @@ export const themes = {
|
|
|
628
1018
|
'synthwave': synthwaveTheme,
|
|
629
1019
|
'paper': paperTheme,
|
|
630
1020
|
'coffee': coffeeTheme,
|
|
1021
|
+
'nord': nordTheme,
|
|
1022
|
+
'dracula': draculaTheme,
|
|
1023
|
+
'monokai': monokaiTheme,
|
|
1024
|
+
'gruvbox': gruvboxTheme,
|
|
1025
|
+
'tokyo-night': tokyoNightTheme,
|
|
1026
|
+
'catppuccin': catppuccinTheme,
|
|
1027
|
+
'ocean': oceanTheme,
|
|
1028
|
+
'sakura': sakuraTheme,
|
|
1029
|
+
'msx': msxTheme,
|
|
1030
|
+
'pc-98': pc98Theme,
|
|
631
1031
|
};
|
|
632
1032
|
export const VALID_THEMES = [
|
|
633
1033
|
'modern',
|
|
@@ -646,6 +1046,16 @@ export const VALID_THEMES = [
|
|
|
646
1046
|
'synthwave',
|
|
647
1047
|
'paper',
|
|
648
1048
|
'coffee',
|
|
1049
|
+
'nord',
|
|
1050
|
+
'dracula',
|
|
1051
|
+
'monokai',
|
|
1052
|
+
'gruvbox',
|
|
1053
|
+
'tokyo-night',
|
|
1054
|
+
'catppuccin',
|
|
1055
|
+
'ocean',
|
|
1056
|
+
'sakura',
|
|
1057
|
+
'msx',
|
|
1058
|
+
'pc-98',
|
|
649
1059
|
];
|
|
650
1060
|
export function getTheme(name) {
|
|
651
1061
|
return themes[name] || modernTheme;
|
package/dist/ui/theme/types.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type ThemeName = 'modern' | 'norton-commander' | 'dos-prompt' | 'turbo-pascal' | 'classic-mac' | 'apple-ii' | 'commodore-64' | 'amiga-workbench' | 'matrix' | 'amber-crt' | 'phosphor' | 'solarized-dark' | 'solarized-light' | 'synthwave' | 'paper' | 'coffee';
|
|
1
|
+
export type ThemeName = 'modern' | 'norton-commander' | 'dos-prompt' | 'turbo-pascal' | 'classic-mac' | 'apple-ii' | 'commodore-64' | 'amiga-workbench' | 'matrix' | 'amber-crt' | 'phosphor' | 'solarized-dark' | 'solarized-light' | 'synthwave' | 'paper' | 'coffee' | 'nord' | 'dracula' | 'monokai' | 'gruvbox' | 'tokyo-night' | 'catppuccin' | 'ocean' | 'sakura' | 'msx' | 'pc-98';
|
|
2
2
|
export type BorderStyleType = 'single' | 'double' | 'round' | 'bold' | 'singleDouble' | 'doubleSingle' | 'classic';
|
|
3
3
|
export interface ThemeColors {
|
|
4
4
|
primary: string;
|