floq 0.0.1 → 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.
- package/README.ja.md +157 -29
- package/README.md +157 -29
- package/dist/changelog.d.ts +13 -0
- package/dist/changelog.js +95 -0
- package/dist/cli.js +70 -1
- package/dist/commands/add.js +5 -6
- package/dist/commands/comment.d.ts +2 -0
- package/dist/commands/comment.js +67 -0
- package/dist/commands/config.d.ts +7 -0
- package/dist/commands/config.js +163 -14
- package/dist/commands/done.js +4 -6
- package/dist/commands/list.js +9 -13
- package/dist/commands/move.js +4 -6
- package/dist/commands/project.js +18 -26
- package/dist/commands/setup.d.ts +1 -0
- package/dist/commands/setup.js +13 -0
- package/dist/config.d.ts +15 -1
- package/dist/config.js +53 -2
- package/dist/db/index.d.ts +5 -4
- package/dist/db/index.js +127 -32
- package/dist/db/schema.d.ts +83 -0
- package/dist/db/schema.js +6 -0
- package/dist/i18n/en.d.ts +258 -0
- package/dist/i18n/en.js +138 -3
- package/dist/i18n/ja.js +138 -3
- package/dist/index.js +33 -1
- package/dist/paths.d.ts +4 -0
- package/dist/paths.js +63 -5
- package/dist/ui/App.js +384 -136
- package/dist/ui/ModeSelector.d.ts +7 -0
- package/dist/ui/ModeSelector.js +37 -0
- package/dist/ui/SetupWizard.d.ts +6 -0
- package/dist/ui/SetupWizard.js +321 -0
- package/dist/ui/ThemeSelector.d.ts +1 -1
- package/dist/ui/ThemeSelector.js +23 -10
- package/dist/ui/components/FunctionKeyBar.d.ts +5 -4
- package/dist/ui/components/FunctionKeyBar.js +19 -15
- package/dist/ui/components/HelpModal.d.ts +2 -1
- package/dist/ui/components/HelpModal.js +118 -4
- package/dist/ui/components/KanbanBoard.d.ts +6 -0
- package/dist/ui/components/KanbanBoard.js +508 -0
- package/dist/ui/components/KanbanColumn.d.ts +12 -0
- package/dist/ui/components/KanbanColumn.js +11 -0
- package/dist/ui/components/ProgressBar.d.ts +7 -0
- package/dist/ui/components/ProgressBar.js +13 -0
- package/dist/ui/components/SearchBar.d.ts +8 -0
- package/dist/ui/components/SearchBar.js +11 -0
- package/dist/ui/components/SearchResults.d.ts +9 -0
- package/dist/ui/components/SearchResults.js +18 -0
- package/dist/ui/components/TaskItem.d.ts +6 -1
- package/dist/ui/components/TaskItem.js +3 -2
- package/dist/ui/theme/themes.d.ts +12 -0
- package/dist/ui/theme/themes.js +495 -3
- package/dist/ui/theme/types.d.ts +1 -1
- package/package.json +2 -1
|
@@ -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 { getViewMode } from '../config.js';
|
|
5
|
+
const VALID_VIEW_MODES = ['gtd', 'kanban'];
|
|
6
|
+
const modeDisplayNames = {
|
|
7
|
+
gtd: 'GTD (Getting Things Done)',
|
|
8
|
+
kanban: 'Kanban Board',
|
|
9
|
+
};
|
|
10
|
+
const modeDescriptions = {
|
|
11
|
+
gtd: 'Classic GTD workflow with Inbox, Next, Waiting, Someday lists',
|
|
12
|
+
kanban: '3-column kanban board view',
|
|
13
|
+
};
|
|
14
|
+
export function ModeSelector({ onSelect }) {
|
|
15
|
+
const currentMode = getViewMode();
|
|
16
|
+
const initialIndex = VALID_VIEW_MODES.indexOf(currentMode);
|
|
17
|
+
const [selectedIndex, setSelectedIndex] = useState(initialIndex >= 0 ? initialIndex : 0);
|
|
18
|
+
useInput((input, key) => {
|
|
19
|
+
// j or down arrow: move down
|
|
20
|
+
if (input === 'j' || key.downArrow) {
|
|
21
|
+
setSelectedIndex((prev) => (prev < VALID_VIEW_MODES.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_VIEW_MODES.length - 1));
|
|
26
|
+
}
|
|
27
|
+
// Enter: select
|
|
28
|
+
if (key.return) {
|
|
29
|
+
onSelect(VALID_VIEW_MODES[selectedIndex]);
|
|
30
|
+
}
|
|
31
|
+
});
|
|
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) => {
|
|
33
|
+
const isSelected = index === selectedIndex;
|
|
34
|
+
const isCurrent = mode === currentMode;
|
|
35
|
+
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));
|
|
36
|
+
}) })] }));
|
|
37
|
+
}
|
|
@@ -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
|
+
}
|
package/dist/ui/ThemeSelector.js
CHANGED
|
@@ -1,17 +1,30 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { useState } from 'react';
|
|
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
6
|
export function ThemeSelector({ onSelect }) {
|
|
7
7
|
const currentTheme = getThemeName();
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
8
|
+
const initialIndex = VALID_THEMES.indexOf(currentTheme);
|
|
9
|
+
const [selectedIndex, setSelectedIndex] = useState(initialIndex >= 0 ? initialIndex : 0);
|
|
10
|
+
useInput((input, key) => {
|
|
11
|
+
// j or down arrow: move down
|
|
12
|
+
if (input === 'j' || key.downArrow) {
|
|
13
|
+
setSelectedIndex((prev) => (prev < VALID_THEMES.length - 1 ? prev + 1 : 0));
|
|
14
|
+
}
|
|
15
|
+
// k or up arrow: move up
|
|
16
|
+
if (input === 'k' || key.upArrow) {
|
|
17
|
+
setSelectedIndex((prev) => (prev > 0 ? prev - 1 : VALID_THEMES.length - 1));
|
|
18
|
+
}
|
|
19
|
+
// Enter: select
|
|
20
|
+
if (key.return) {
|
|
21
|
+
onSelect(VALID_THEMES[selectedIndex]);
|
|
22
|
+
}
|
|
15
23
|
});
|
|
16
|
-
return (_jsxs(Box, { flexDirection: "column", padding: 1, children: [_jsx(Text, { bold: true, children: "Select a theme:" }), _jsx(
|
|
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) => {
|
|
25
|
+
const theme = themes[themeName];
|
|
26
|
+
const isSelected = index === selectedIndex;
|
|
27
|
+
const isCurrent = themeName === currentTheme;
|
|
28
|
+
return (_jsx(Box, { children: _jsxs(Text, { color: isSelected ? 'cyan' : undefined, children: [isSelected ? '› ' : ' ', theme.displayName, isCurrent ? ' (current)' : ''] }) }, themeName));
|
|
29
|
+
}) })] }));
|
|
17
30
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
interface
|
|
2
|
+
interface ActionKey {
|
|
3
3
|
key: string;
|
|
4
4
|
label: string;
|
|
5
5
|
}
|
|
6
|
-
interface
|
|
7
|
-
keys?:
|
|
6
|
+
interface ActionKeyBarProps {
|
|
7
|
+
keys?: ActionKey[];
|
|
8
8
|
}
|
|
9
|
-
export declare function
|
|
9
|
+
export declare function ActionKeyBar({ keys }: ActionKeyBarProps): React.ReactElement;
|
|
10
|
+
export declare const FunctionKeyBar: typeof ActionKeyBar;
|
|
10
11
|
export {};
|
|
@@ -1,19 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { Box, Text } from 'ink';
|
|
3
3
|
import { useTheme } from '../theme/index.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
{ key: 'F2', label: 'Add' },
|
|
7
|
-
{ key: 'F3', label: 'Done' },
|
|
8
|
-
{ key: 'F4', label: 'Next' },
|
|
9
|
-
{ key: 'F5', label: 'Rfrsh' },
|
|
10
|
-
{ key: 'F6', label: '' },
|
|
11
|
-
{ key: 'F7', label: '' },
|
|
12
|
-
{ key: 'F8', label: '' },
|
|
13
|
-
{ key: 'F9', label: '' },
|
|
14
|
-
{ key: 'F10', label: 'Quit' },
|
|
15
|
-
];
|
|
16
|
-
export function FunctionKeyBar({ keys = DEFAULT_KEYS }) {
|
|
4
|
+
import { t } from '../../i18n/index.js';
|
|
5
|
+
export function ActionKeyBar({ keys }) {
|
|
17
6
|
const theme = useTheme();
|
|
18
|
-
|
|
7
|
+
const i18n = t();
|
|
8
|
+
const kb = i18n.tui.keyBar;
|
|
9
|
+
const defaultKeys = [
|
|
10
|
+
{ key: 'a', label: kb.add },
|
|
11
|
+
{ key: 'd', label: kb.done },
|
|
12
|
+
{ key: 'n', label: kb.next },
|
|
13
|
+
{ key: 's', label: kb.someday },
|
|
14
|
+
{ key: 'i', label: kb.inbox },
|
|
15
|
+
{ key: 'p', label: kb.project },
|
|
16
|
+
{ key: '?', label: kb.help },
|
|
17
|
+
{ key: 'q', label: kb.quit },
|
|
18
|
+
];
|
|
19
|
+
const displayKeys = keys || defaultKeys;
|
|
20
|
+
return (_jsx(Box, { children: displayKeys.map((action, index) => (_jsxs(Box, { marginRight: 1, children: [_jsxs(Text, { color: theme.colors.fnKeyLabel, bold: true, children: ["[", action.key, "]"] }), _jsx(Text, { color: theme.colors.fnKeyText, children: action.label })] }, index))) }));
|
|
19
21
|
}
|
|
22
|
+
// Alias for backward compatibility
|
|
23
|
+
export const FunctionKeyBar = ActionKeyBar;
|
|
@@ -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,
|
|
2
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|