floq 0.2.1 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.ja.md CHANGED
@@ -228,7 +228,7 @@ floq config turso --disable
228
228
 
229
229
  ## テーマ
230
230
 
231
- 16種類のテーマが利用可能。`floq config theme` でインタラクティブに選択(j/kで移動)。
231
+ 26種類のテーマが利用可能。`floq config theme` でインタラクティブに選択(j/kで移動)。
232
232
 
233
233
  | テーマ | 説明 |
234
234
  |--------|------|
@@ -248,6 +248,16 @@ floq config turso --disable
248
248
  | `synthwave` | ネオン80sスタイル |
249
249
  | `paper` | 紙とインク風ライト |
250
250
  | `coffee` | 暖かみのある茶系 |
251
+ | `nord` | 北欧風ブルーグレー |
252
+ | `dracula` | ダーク&ビビッドカラー |
253
+ | `monokai` | エディタ定番の鮮やかな配色 |
254
+ | `gruvbox` | レトロな暖色系ダーク |
255
+ | `tokyo-night` | 東京の夜景イメージ |
256
+ | `catppuccin` | パステル系モダン |
257
+ | `ocean` | 深海ブルー |
258
+ | `sakura` | 桜ピンク |
259
+ | `msx` | MSXコンピュータ(TMS9918) |
260
+ | `pc-98` | NEC PC-9801風 |
251
261
 
252
262
  > **注意**: 背景色はターミナルの設定に依存します。
253
263
 
package/README.md CHANGED
@@ -228,7 +228,7 @@ floq config turso --disable
228
228
 
229
229
  ## Themes
230
230
 
231
- 16 themes available. Use `floq config theme` for interactive selection (j/k to navigate).
231
+ 26 themes available. Use `floq config theme` for interactive selection (j/k to navigate).
232
232
 
233
233
  | Theme | Description |
234
234
  |-------|-------------|
@@ -248,6 +248,16 @@ floq config turso --disable
248
248
  | `synthwave` | Neon 80s aesthetic |
249
249
  | `paper` | Light minimal theme |
250
250
  | `coffee` | Warm brown tones |
251
+ | `nord` | Arctic, north-bluish palette |
252
+ | `dracula` | Dark theme with vibrant colors |
253
+ | `monokai` | Classic editor vivid colors |
254
+ | `gruvbox` | Retro groove warm tones |
255
+ | `tokyo-night` | Tokyo night lights inspired |
256
+ | `catppuccin` | Soothing pastel theme |
257
+ | `ocean` | Deep sea blue theme |
258
+ | `sakura` | Cherry blossom pink |
259
+ | `msx` | MSX computer (TMS9918) |
260
+ | `pc-98` | NEC PC-9801 style |
251
261
 
252
262
  > **Note**: Background colors depend on your terminal settings.
253
263
 
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',
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: '終了',
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
- return (_jsx(ThemeProvider, { themeName: themeName, children: viewMode === 'kanban' ? _jsx(KanbanBoard, {}) : _jsx(AppContent, {}) }));
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');
@@ -551,6 +584,21 @@ function AppContent() {
551
584
  setSearchResultIndex(0);
552
585
  return;
553
586
  }
587
+ // Settings: Theme selector
588
+ if (input === 'T') {
589
+ onOpenSettings('theme-select');
590
+ return;
591
+ }
592
+ // Settings: Mode selector
593
+ if (input === 'V') {
594
+ onOpenSettings('mode-select');
595
+ return;
596
+ }
597
+ // Settings: Language selector
598
+ if (input === 'L') {
599
+ onOpenSettings('lang-select');
600
+ return;
601
+ }
554
602
  // Quit
555
603
  if (input === 'q' || (key.ctrl && input === 'c')) {
556
604
  exit();
@@ -725,7 +773,7 @@ function AppContent() {
725
773
  const tursoEnabled = isTursoEnabled();
726
774
  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
775
  ? (tursoEnabled ? ' ☁️ turso' : ' 💾 local')
728
- : (tursoEnabled ? ' [DB]turso' : ' [DB]local') })] }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.helpHint })] }), _jsx(Box, { marginBottom: 1, children: TABS.map((tab, index) => {
776
+ : (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
777
  const isActive = index === currentListIndex && mode !== 'project-detail';
730
778
  const count = tasks[tab].length;
731
779
  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 {};
@@ -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 {};
@@ -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');
@@ -366,6 +366,21 @@ export function KanbanBoard({ onSwitchToGtd }) {
366
366
  setSearchResultIndex(0);
367
367
  return;
368
368
  }
369
+ // Settings: Theme selector
370
+ if (input === 'T' && onOpenSettings) {
371
+ onOpenSettings('theme-select');
372
+ return;
373
+ }
374
+ // Settings: Mode selector
375
+ if (input === 'V' && onOpenSettings) {
376
+ onOpenSettings('mode-select');
377
+ return;
378
+ }
379
+ // Settings: Language selector
380
+ if (input === 'L' && onOpenSettings) {
381
+ onOpenSettings('lang-select');
382
+ return;
383
+ }
369
384
  // Quit
370
385
  if (input === 'q' || (key.ctrl && input === 'c')) {
371
386
  exit();
@@ -478,7 +493,7 @@ export function KanbanBoard({ onSwitchToGtd }) {
478
493
  const tursoEnabled = isTursoEnabled();
479
494
  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
495
  ? (tursoEnabled ? ' ☁️ turso' : ' 💾 local')
481
- : (tursoEnabled ? ' [DB]turso' : ' [DB]local') })] }), _jsx(Text, { color: theme.colors.textMuted, children: i18n.tui.helpHint })] }), (mode === 'task-detail' || mode === 'add-comment' || mode === 'select-project') && selectedTask ? (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsxs(Text, { color: theme.colors.accent, bold: true, children: [theme.name === 'modern' ? '📋 ' : '>> ', i18n.tui.taskDetailTitle || 'Task Details'] }), _jsxs(Text, { color: theme.colors.textMuted, children: [" (Esc/b: ", i18n.tui.back || 'back', ", ", i18n.tui.commentHint || 'i: add comment', ")"] })] }), _jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, marginBottom: 1, children: [_jsx(Text, { color: theme.colors.text, bold: true, children: selectedTask.title }), selectedTask.description && (_jsx(Text, { color: theme.colors.textMuted, children: selectedTask.description })), _jsxs(Text, { color: theme.colors.textMuted, children: [i18n.status[selectedTask.status], selectedTask.waitingFor && ` - ${selectedTask.waitingFor}`, selectedTask.dueDate && ` (${selectedTask.dueDate.toLocaleDateString()})`] })] }), _jsx(Box, { marginBottom: 1, children: _jsxs(Text, { color: theme.colors.secondary, bold: true, children: [i18n.tui.comments || 'Comments', " (", taskComments.length, ")"] }) }), _jsx(Box, { flexDirection: "column", borderStyle: theme.borders.list, borderColor: theme.colors.border, paddingX: 1, paddingY: 1, minHeight: 5, children: taskComments.length === 0 ? (_jsx(Text, { color: theme.colors.textMuted, italic: true, children: i18n.tui.noComments || 'No comments yet' })) : (taskComments.map((comment, index) => {
496
+ : (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
497
  const isSelected = index === selectedCommentIndex && mode === 'task-detail';
483
498
  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
499
  })) }), 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: [
@@ -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;
@@ -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;
@@ -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;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "floq",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Floq - Getting Things Done Task Manager with MS-DOS style themes",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",