floq 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.ja.md +89 -9
  2. package/README.md +89 -9
  3. package/dist/changelog.d.ts +13 -0
  4. package/dist/changelog.js +95 -0
  5. package/dist/cli.js +44 -1
  6. package/dist/commands/comment.d.ts +2 -0
  7. package/dist/commands/comment.js +67 -0
  8. package/dist/commands/config.d.ts +4 -0
  9. package/dist/commands/config.js +123 -1
  10. package/dist/commands/setup.d.ts +1 -0
  11. package/dist/commands/setup.js +13 -0
  12. package/dist/config.d.ts +5 -0
  13. package/dist/config.js +40 -3
  14. package/dist/db/index.js +20 -0
  15. package/dist/db/schema.d.ts +83 -0
  16. package/dist/db/schema.js +6 -0
  17. package/dist/i18n/en.d.ts +237 -0
  18. package/dist/i18n/en.js +127 -3
  19. package/dist/i18n/ja.js +127 -3
  20. package/dist/index.js +14 -4
  21. package/dist/paths.d.ts +4 -0
  22. package/dist/paths.js +63 -5
  23. package/dist/ui/App.js +280 -25
  24. package/dist/ui/ModeSelector.d.ts +7 -0
  25. package/dist/ui/ModeSelector.js +37 -0
  26. package/dist/ui/SetupWizard.d.ts +6 -0
  27. package/dist/ui/SetupWizard.js +321 -0
  28. package/dist/ui/components/HelpModal.d.ts +2 -1
  29. package/dist/ui/components/HelpModal.js +118 -4
  30. package/dist/ui/components/KanbanBoard.d.ts +6 -0
  31. package/dist/ui/components/KanbanBoard.js +508 -0
  32. package/dist/ui/components/KanbanColumn.d.ts +12 -0
  33. package/dist/ui/components/KanbanColumn.js +11 -0
  34. package/dist/ui/components/ProgressBar.d.ts +7 -0
  35. package/dist/ui/components/ProgressBar.js +13 -0
  36. package/dist/ui/components/SearchBar.d.ts +8 -0
  37. package/dist/ui/components/SearchBar.js +11 -0
  38. package/dist/ui/components/SearchResults.d.ts +9 -0
  39. package/dist/ui/components/SearchResults.js +18 -0
  40. package/dist/ui/components/TaskItem.d.ts +6 -1
  41. package/dist/ui/components/TaskItem.js +3 -2
  42. package/package.json +1 -1
@@ -0,0 +1,321 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from 'react';
3
+ import { Box, Text, useInput, useApp } from 'ink';
4
+ import TextInput from 'ink-text-input';
5
+ import { themes, VALID_THEMES } from './theme/themes.js';
6
+ import { saveConfig } from '../config.js';
7
+ import { t } from '../i18n/index.js';
8
+ const LOCALES = [
9
+ { value: 'en', label: 'English', desc: 'English language' },
10
+ { value: 'ja', label: '日本語', desc: 'Japanese language' },
11
+ ];
12
+ const VIEW_MODES = [
13
+ { value: 'gtd', labelKey: 'gtd', descKey: 'gtdDesc' },
14
+ { value: 'kanban', labelKey: 'kanban', descKey: 'kanbanDesc' },
15
+ ];
16
+ const DB_OPTIONS = [
17
+ { value: 'local', labelKey: 'local', descKey: 'localDesc' },
18
+ { value: 'turso', labelKey: 'turso', descKey: 'tursoDesc' },
19
+ ];
20
+ export function SetupWizard({ onComplete }) {
21
+ const { exit } = useApp();
22
+ const [step, setStep] = useState('welcome');
23
+ const [state, setState] = useState({
24
+ locale: 'en',
25
+ theme: 'modern',
26
+ viewMode: 'gtd',
27
+ useTurso: false,
28
+ tursoUrl: '',
29
+ tursoToken: '',
30
+ });
31
+ // Selection indexes for various steps
32
+ const [languageIndex, setLanguageIndex] = useState(0);
33
+ const [themeIndex, setThemeIndex] = useState(0);
34
+ const [viewModeIndex, setViewModeIndex] = useState(0);
35
+ const [dbIndex, setDbIndex] = useState(0);
36
+ // Input values for Turso
37
+ const [inputValue, setInputValue] = useState('');
38
+ const [inputError, setInputError] = useState(null);
39
+ // Get translations - use selected locale for setup wizard
40
+ const [i18n, setI18n] = useState(t());
41
+ // Update translations when locale changes
42
+ useEffect(() => {
43
+ // Re-get translations when state.locale changes
44
+ // We need to temporarily set the config locale to get correct translations
45
+ const translations = t();
46
+ setI18n(translations);
47
+ }, []);
48
+ // Get setup translations based on selected locale
49
+ const getSetupI18n = () => {
50
+ // Use dynamic import to get correct language translations
51
+ if (state.locale === 'ja') {
52
+ return {
53
+ welcome: {
54
+ title: 'Floqへようこそ!',
55
+ subtitle: 'タスクマネージャーを設定しましょう',
56
+ instruction: '任意のキーを押して設定を開始...',
57
+ },
58
+ language: {
59
+ title: '言語を選択',
60
+ hint: 'j/k: 選択, Enter: 確定, Esc: 戻る',
61
+ },
62
+ theme: {
63
+ title: 'テーマを選択',
64
+ hint: 'j/k: 選択, Enter: 確定, Esc: 戻る',
65
+ },
66
+ viewMode: {
67
+ title: '表示モードを選択',
68
+ hint: 'j/k: 選択, Enter: 確定, Esc: 戻る',
69
+ gtd: 'GTD (Getting Things Done)',
70
+ gtdDesc: 'Inbox、次のアクション、連絡待ち、いつかやるリストによるクラシックGTDワークフロー',
71
+ kanban: 'Kanbanボード',
72
+ kanbanDesc: '3カラムのKanbanボード表示',
73
+ },
74
+ database: {
75
+ title: 'データベースモードを選択',
76
+ local: 'ローカル',
77
+ localDesc: 'このデバイスにデータをローカル保存',
78
+ turso: 'Turso Cloud',
79
+ tursoDesc: 'Turso経由でデバイス間でデータを同期',
80
+ hint: 'j/k: 選択, Enter: 確定, Esc: 戻る',
81
+ },
82
+ turso: {
83
+ urlPrompt: 'TursoデータベースURL',
84
+ urlPlaceholder: 'libsql://your-db.turso.io',
85
+ urlError: 'URLはlibsql://で始まる必要があります',
86
+ tokenPrompt: 'Turso認証トークン',
87
+ tokenError: 'トークンは空にできません',
88
+ inputHint: 'Enter: 確定, Esc: 戻る',
89
+ },
90
+ complete: {
91
+ title: '設定完了!',
92
+ summary: '設定内容:',
93
+ language: '言語',
94
+ theme: 'テーマ',
95
+ viewMode: '表示モード',
96
+ database: 'データベース',
97
+ confirm: 'Enterを押してFloqを開始...',
98
+ },
99
+ };
100
+ }
101
+ return i18n.setup;
102
+ };
103
+ const setupI18n = getSetupI18n();
104
+ const handleSave = () => {
105
+ const tursoConfig = state.useTurso
106
+ ? { url: state.tursoUrl, authToken: state.tursoToken }
107
+ : undefined;
108
+ saveConfig({
109
+ locale: state.locale,
110
+ theme: state.theme,
111
+ viewMode: state.viewMode,
112
+ turso: tursoConfig,
113
+ });
114
+ onComplete();
115
+ };
116
+ const goToPrevStep = () => {
117
+ switch (step) {
118
+ case 'welcome':
119
+ exit();
120
+ break;
121
+ case 'language':
122
+ setStep('welcome');
123
+ break;
124
+ case 'theme':
125
+ setStep('language');
126
+ break;
127
+ case 'viewMode':
128
+ setStep('theme');
129
+ break;
130
+ case 'database':
131
+ setStep('viewMode');
132
+ break;
133
+ case 'turso-url':
134
+ setStep('database');
135
+ setInputValue('');
136
+ setInputError(null);
137
+ break;
138
+ case 'turso-token':
139
+ setStep('turso-url');
140
+ setInputValue(state.tursoUrl);
141
+ setInputError(null);
142
+ break;
143
+ case 'complete':
144
+ setStep('database');
145
+ break;
146
+ }
147
+ };
148
+ useInput((input, key) => {
149
+ // Welcome step - any key continues
150
+ if (step === 'welcome') {
151
+ if (key.escape) {
152
+ exit();
153
+ }
154
+ else {
155
+ setStep('language');
156
+ }
157
+ return;
158
+ }
159
+ // Input steps (turso-url and turso-token)
160
+ if (step === 'turso-url' || step === 'turso-token') {
161
+ if (key.escape) {
162
+ goToPrevStep();
163
+ }
164
+ return;
165
+ }
166
+ // Complete step - Enter saves
167
+ if (step === 'complete') {
168
+ if (key.return) {
169
+ handleSave();
170
+ }
171
+ else if (key.escape) {
172
+ goToPrevStep();
173
+ }
174
+ return;
175
+ }
176
+ // Selection steps
177
+ if (key.escape) {
178
+ goToPrevStep();
179
+ return;
180
+ }
181
+ // j/k or arrow navigation
182
+ if (input === 'j' || key.downArrow) {
183
+ switch (step) {
184
+ case 'language':
185
+ setLanguageIndex((prev) => (prev < LOCALES.length - 1 ? prev + 1 : 0));
186
+ break;
187
+ case 'theme':
188
+ setThemeIndex((prev) => (prev < VALID_THEMES.length - 1 ? prev + 1 : 0));
189
+ break;
190
+ case 'viewMode':
191
+ setViewModeIndex((prev) => (prev < VIEW_MODES.length - 1 ? prev + 1 : 0));
192
+ break;
193
+ case 'database':
194
+ setDbIndex((prev) => (prev < DB_OPTIONS.length - 1 ? prev + 1 : 0));
195
+ break;
196
+ }
197
+ return;
198
+ }
199
+ if (input === 'k' || key.upArrow) {
200
+ switch (step) {
201
+ case 'language':
202
+ setLanguageIndex((prev) => (prev > 0 ? prev - 1 : LOCALES.length - 1));
203
+ break;
204
+ case 'theme':
205
+ setThemeIndex((prev) => (prev > 0 ? prev - 1 : VALID_THEMES.length - 1));
206
+ break;
207
+ case 'viewMode':
208
+ setViewModeIndex((prev) => (prev > 0 ? prev - 1 : VIEW_MODES.length - 1));
209
+ break;
210
+ case 'database':
211
+ setDbIndex((prev) => (prev > 0 ? prev - 1 : DB_OPTIONS.length - 1));
212
+ break;
213
+ }
214
+ return;
215
+ }
216
+ // Enter to confirm selection
217
+ if (key.return) {
218
+ switch (step) {
219
+ case 'language':
220
+ setState((prev) => ({ ...prev, locale: LOCALES[languageIndex].value }));
221
+ setStep('theme');
222
+ break;
223
+ case 'theme':
224
+ setState((prev) => ({ ...prev, theme: VALID_THEMES[themeIndex] }));
225
+ setStep('viewMode');
226
+ break;
227
+ case 'viewMode':
228
+ setState((prev) => ({ ...prev, viewMode: VIEW_MODES[viewModeIndex].value }));
229
+ setStep('database');
230
+ break;
231
+ case 'database':
232
+ const useTurso = DB_OPTIONS[dbIndex].value === 'turso';
233
+ setState((prev) => ({ ...prev, useTurso }));
234
+ if (useTurso) {
235
+ setStep('turso-url');
236
+ }
237
+ else {
238
+ setStep('complete');
239
+ }
240
+ break;
241
+ }
242
+ }
243
+ });
244
+ const handleTursoUrlSubmit = (value) => {
245
+ if (!value.startsWith('libsql://')) {
246
+ setInputError(setupI18n.turso.urlError);
247
+ return;
248
+ }
249
+ setState((prev) => ({ ...prev, tursoUrl: value }));
250
+ setInputValue('');
251
+ setInputError(null);
252
+ setStep('turso-token');
253
+ };
254
+ const handleTursoTokenSubmit = (value) => {
255
+ if (!value.trim()) {
256
+ setInputError(setupI18n.turso.tokenError);
257
+ return;
258
+ }
259
+ setState((prev) => ({ ...prev, tursoToken: value.trim() }));
260
+ setInputValue('');
261
+ setInputError(null);
262
+ setStep('complete');
263
+ };
264
+ // Render Welcome step
265
+ if (step === 'welcome') {
266
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, alignItems: "center", children: [_jsx(Box, { marginBottom: 1, children: _jsx(Text, { bold: true, color: "cyan", children: `
267
+ ███████╗██╗ ██████╗ ██████╗
268
+ ██╔════╝██║ ██╔═══██╗██╔═══██╗
269
+ █████╗ ██║ ██║ ██║██║ ██║
270
+ ██╔══╝ ██║ ██║ ██║██║▄▄ ██║
271
+ ██║ ███████╗╚██████╔╝╚██████╔╝
272
+ ╚═╝ ╚══════╝ ╚═════╝ ╚══▀▀═╝
273
+ ` }) }), _jsx(Text, { bold: true, color: "green", children: setupI18n.welcome.title }), _jsx(Text, { color: "white", children: setupI18n.welcome.subtitle }), _jsx(Box, { marginTop: 2, children: _jsx(Text, { dimColor: true, children: setupI18n.welcome.instruction }) })] }));
274
+ }
275
+ // Render Language selection
276
+ if (step === 'language') {
277
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: setupI18n.language.title }), _jsx(Text, { dimColor: true, children: setupI18n.language.hint }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: LOCALES.map((locale, index) => {
278
+ const isSelected = index === languageIndex;
279
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { color: isSelected ? 'cyan' : undefined, children: [isSelected ? '› ' : ' ', locale.label] }), _jsxs(Text, { dimColor: true, children: [" ", locale.desc] })] }, locale.value));
280
+ }) })] }));
281
+ }
282
+ // Render Theme selection
283
+ if (step === 'theme') {
284
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: setupI18n.theme.title }), _jsx(Text, { dimColor: true, children: setupI18n.theme.hint }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: VALID_THEMES.map((themeName, index) => {
285
+ const theme = themes[themeName];
286
+ const isSelected = index === themeIndex;
287
+ return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? 'cyan' : undefined, children: [isSelected ? '› ' : ' ', theme.displayName] }) }, themeName));
288
+ }) })] }));
289
+ }
290
+ // Render View Mode selection
291
+ if (step === 'viewMode') {
292
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: setupI18n.viewMode.title }), _jsx(Text, { dimColor: true, children: setupI18n.viewMode.hint }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: VIEW_MODES.map((mode, index) => {
293
+ const isSelected = index === viewModeIndex;
294
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { color: isSelected ? 'cyan' : undefined, children: [isSelected ? '› ' : ' ', setupI18n.viewMode[mode.labelKey]] }), _jsxs(Text, { dimColor: true, children: [" ", setupI18n.viewMode[mode.descKey]] })] }, mode.value));
295
+ }) })] }));
296
+ }
297
+ // Render Database selection
298
+ if (step === 'database') {
299
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: setupI18n.database.title }), _jsx(Text, { dimColor: true, children: setupI18n.database.hint }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: DB_OPTIONS.map((option, index) => {
300
+ const isSelected = index === dbIndex;
301
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Text, { color: isSelected ? 'cyan' : undefined, children: [isSelected ? '› ' : ' ', setupI18n.database[option.labelKey]] }), _jsxs(Text, { dimColor: true, children: [" ", setupI18n.database[option.descKey]] })] }, option.value));
302
+ }) })] }));
303
+ }
304
+ // Render Turso URL input
305
+ if (step === 'turso-url') {
306
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: setupI18n.turso.urlPrompt }), _jsx(Text, { dimColor: true, children: setupI18n.turso.inputHint }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "\u203A " }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleTursoUrlSubmit, placeholder: setupI18n.turso.urlPlaceholder })] }), inputError && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "red", children: inputError }) }))] }));
307
+ }
308
+ // Render Turso Token input
309
+ if (step === 'turso-token') {
310
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "cyan", children: setupI18n.turso.tokenPrompt }), _jsx(Text, { dimColor: true, children: setupI18n.turso.inputHint }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "\u203A " }), _jsx(TextInput, { value: inputValue, onChange: setInputValue, onSubmit: handleTursoTokenSubmit, placeholder: "" })] }), inputError && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "red", children: inputError }) }))] }));
311
+ }
312
+ // Render Complete step
313
+ if (step === 'complete') {
314
+ const selectedTheme = themes[state.theme];
315
+ const dbLabel = state.useTurso
316
+ ? `Turso (${state.tursoUrl})`
317
+ : setupI18n.database.local;
318
+ return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, color: "green", children: setupI18n.complete.title }), _jsx(Box, { marginTop: 1, marginBottom: 1, children: _jsx(Text, { bold: true, children: setupI18n.complete.summary }) }), _jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsxs(Text, { children: [_jsxs(Text, { color: "cyan", children: [setupI18n.complete.language, ":"] }), " ", LOCALES.find(l => l.value === state.locale)?.label] }), _jsxs(Text, { children: [_jsxs(Text, { color: "cyan", children: [setupI18n.complete.theme, ":"] }), " ", selectedTheme.displayName] }), _jsxs(Text, { children: [_jsxs(Text, { color: "cyan", children: [setupI18n.complete.viewMode, ":"] }), " ", setupI18n.viewMode[state.viewMode === 'gtd' ? 'gtd' : 'kanban']] }), _jsxs(Text, { children: [_jsxs(Text, { color: "cyan", children: [setupI18n.complete.database, ":"] }), " ", dbLabel] })] }), _jsx(Box, { marginTop: 2, children: _jsx(Text, { dimColor: true, children: setupI18n.complete.confirm }) })] }));
319
+ }
320
+ return _jsx(Text, { children: "Unknown step" });
321
+ }
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
2
  interface HelpModalProps {
3
3
  onClose: () => void;
4
+ isKanban?: boolean;
4
5
  }
5
- export declare function HelpModal({ onClose }: HelpModalProps): React.ReactElement;
6
+ export declare function HelpModal({ onClose, isKanban }: HelpModalProps): React.ReactElement;
6
7
  export {};
@@ -1,11 +1,125 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Box, Text } from 'ink';
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, useMemo } from 'react';
3
+ import { Box, Text, useInput } from 'ink';
3
4
  import { t } from '../../i18n/index.js';
4
5
  import { useTheme } from '../theme/index.js';
5
- export function HelpModal({ onClose }) {
6
+ import { parseChangelog } from '../../changelog.js';
7
+ const VISIBLE_LINES = 12;
8
+ export function HelpModal({ onClose, isKanban = false }) {
9
+ const [activeTab, setActiveTab] = useState('keybindings');
10
+ const [scrollOffset, setScrollOffset] = useState(0);
6
11
  const i18n = t();
12
+ const theme = useTheme();
7
13
  const help = i18n.tui.help;
14
+ // Get changelog items for scroll calculation
15
+ const changelogItems = useMemo(() => {
16
+ const changelog = parseChangelog();
17
+ const items = [];
18
+ for (const entry of changelog.entries.slice(0, 5)) {
19
+ items.push({ type: 'version', content: entry.version, date: entry.date });
20
+ for (const section of entry.sections) {
21
+ items.push({ type: 'section', content: section.type });
22
+ for (const item of section.items) {
23
+ items.push({ type: 'item', content: item });
24
+ }
25
+ }
26
+ }
27
+ return items;
28
+ }, []);
29
+ const maxScroll = Math.max(0, changelogItems.length - VISIBLE_LINES);
30
+ useInput((input, key) => {
31
+ // Tab key detection (key.tab or raw tab character)
32
+ if (key.tab || input === '\t') {
33
+ setActiveTab(prev => prev === 'keybindings' ? 'whatsNew' : 'keybindings');
34
+ setScrollOffset(0);
35
+ return;
36
+ }
37
+ // Number keys for direct tab access
38
+ if (input === '1') {
39
+ setActiveTab('keybindings');
40
+ setScrollOffset(0);
41
+ return;
42
+ }
43
+ if (input === '2') {
44
+ setActiveTab('whatsNew');
45
+ setScrollOffset(0);
46
+ return;
47
+ }
48
+ // Arrow keys and h/l for tab switching
49
+ if (input === 'h' || key.leftArrow) {
50
+ setActiveTab('keybindings');
51
+ setScrollOffset(0);
52
+ return;
53
+ }
54
+ if (input === 'l' || key.rightArrow) {
55
+ setActiveTab('whatsNew');
56
+ setScrollOffset(0);
57
+ return;
58
+ }
59
+ // Scroll in What's New tab
60
+ if (activeTab === 'whatsNew') {
61
+ if (input === 'j' || key.downArrow) {
62
+ setScrollOffset(prev => Math.min(prev + 1, maxScroll));
63
+ return;
64
+ }
65
+ if (input === 'k' || key.upArrow) {
66
+ setScrollOffset(prev => Math.max(prev - 1, 0));
67
+ return;
68
+ }
69
+ }
70
+ // Close modal
71
+ if (key.escape || key.return || input === 'q' || input === ' ') {
72
+ onClose();
73
+ }
74
+ });
75
+ const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
76
+ return (_jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.modal, borderColor: theme.colors.borderActive, paddingX: 2, paddingY: 1, children: [_jsx(Box, { justifyContent: "center", marginBottom: 1, children: _jsxs(Box, { children: [_jsxs(Text, { bold: activeTab === 'keybindings', color: activeTab === 'keybindings' ? theme.colors.secondary : theme.colors.textMuted, inverse: activeTab === 'keybindings', children: [' ', formatTitle(help.keybindingsTab), ' '] }), _jsx(Text, { color: theme.colors.textMuted, children: " \u2502 " }), _jsxs(Text, { bold: activeTab === 'whatsNew', color: activeTab === 'whatsNew' ? theme.colors.secondary : theme.colors.textMuted, inverse: activeTab === 'whatsNew', children: [' ', formatTitle(help.whatsNewTab), ' '] })] }) }), activeTab === 'keybindings' ? (isKanban ? (_jsx(KanbanKeybindingsContent, {})) : (_jsx(GTDKeybindingsContent, {}))) : (_jsx(WhatsNewContent, { items: changelogItems, scrollOffset: scrollOffset, visibleLines: VISIBLE_LINES, maxScroll: maxScroll })), _jsx(Box, { justifyContent: "center", marginTop: 1, children: _jsx(Text, { color: theme.colors.textMuted, children: activeTab === 'whatsNew' && maxScroll > 0
77
+ ? `j/k: scroll | ${help.tabHint} | ${help.closeHint}`
78
+ : `${help.tabHint} | ${help.closeHint}` }) })] }));
79
+ }
80
+ function GTDKeybindingsContent() {
81
+ const i18n = t();
82
+ const help = i18n.tui.help;
83
+ const theme = useTheme();
84
+ const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
85
+ 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] })] })] })] }));
86
+ }
87
+ function KanbanKeybindingsContent() {
88
+ const i18n = t();
89
+ const help = i18n.tui.kanbanHelp;
90
+ const theme = useTheme();
91
+ const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
92
+ return (_jsxs(_Fragment, { children: [_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(help.navigation) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "h/l \u2190/\u2192" }), " ", help.columnSwitch] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "1-3" }), " ", help.columnDirect] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "j/k \u2191/\u2193" }), " ", help.taskSelect] })] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(help.actions) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "a" }), " ", help.addTask] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "d" }), " ", help.completeTask] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "Enter" }), " ", help.moveRight] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "BS" }), " ", help.moveLeft] })] })] }), _jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsx(Text, { bold: true, color: theme.colors.accent, children: formatTitle(help.other) }), _jsxs(Box, { paddingLeft: 2, flexDirection: "column", children: [_jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "/" }), " ", help.searchTasks] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "?" }), " ", help.showHelp] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "q" }), " ", help.quit] })] })] })] }));
93
+ }
94
+ function WhatsNewContent({ items, scrollOffset, visibleLines, maxScroll }) {
95
+ const i18n = t();
96
+ const whatsNew = i18n.tui.whatsNew;
8
97
  const theme = useTheme();
9
98
  const formatTitle = (title) => theme.style.headerUppercase ? title.toUpperCase() : title;
10
- return (_jsxs(Box, { flexDirection: "column", borderStyle: theme.borders.modal, borderColor: theme.colors.borderActive, paddingX: 2, paddingY: 1, children: [_jsx(Box, { justifyContent: "center", marginBottom: 1, children: _jsx(Text, { bold: true, color: theme.colors.secondary, children: formatTitle(help.title) }) }), _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-5" }), " ", 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: "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.showHelp] }), _jsxs(Text, { color: theme.colors.text, children: [_jsx(Text, { color: theme.colors.textHighlight, children: "q" }), " ", help.quit] })] })] }), _jsx(Box, { justifyContent: "center", marginTop: 1, children: _jsx(Text, { color: theme.colors.textMuted, children: help.closeHint }) })] }));
99
+ const getSectionLabel = (type) => {
100
+ const labels = {
101
+ Added: whatsNew.added,
102
+ Changed: whatsNew.changed,
103
+ Deprecated: whatsNew.deprecated,
104
+ Removed: whatsNew.removed,
105
+ Fixed: whatsNew.fixed,
106
+ Security: whatsNew.security,
107
+ };
108
+ return labels[type] || type;
109
+ };
110
+ if (items.length === 0) {
111
+ return (_jsx(Box, { justifyContent: "center", paddingY: 2, children: _jsx(Text, { color: theme.colors.textMuted, children: whatsNew.noChanges }) }));
112
+ }
113
+ const visibleItems = items.slice(scrollOffset, scrollOffset + visibleLines);
114
+ const showScrollUp = scrollOffset > 0;
115
+ const showScrollDown = scrollOffset < maxScroll;
116
+ return (_jsxs(Box, { flexDirection: "column", children: [showScrollUp && (_jsx(Text, { color: theme.colors.textMuted, children: " \u25B2 scroll up" })), visibleItems.map((item, index) => {
117
+ if (item.type === 'version') {
118
+ return (_jsxs(Text, { bold: true, color: theme.colors.accent, children: ["v", item.content, " ", item.date && _jsxs(Text, { color: theme.colors.textMuted, children: ["(", item.date, ")"] })] }, `${item.content}-${index}`));
119
+ }
120
+ if (item.type === 'section') {
121
+ return (_jsxs(Text, { color: theme.colors.secondary, children: [' ', formatTitle(getSectionLabel(item.content)), ":"] }, `${item.content}-${index}`));
122
+ }
123
+ return (_jsxs(Text, { color: theme.colors.text, children: [' ', "\u2022 ", item.content] }, `${item.content}-${index}`));
124
+ }), showScrollDown && (_jsx(Text, { color: theme.colors.textMuted, children: " \u25BC scroll down" }))] }));
11
125
  }
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface KanbanBoardProps {
3
+ onSwitchToGtd?: () => void;
4
+ }
5
+ export declare function KanbanBoard({ onSwitchToGtd }: KanbanBoardProps): React.ReactElement;
6
+ export {};