@manthank/mgit 1.0.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/LICENSE +21 -0
- package/README.md +96 -0
- package/dist/app.js +468 -0
- package/dist/catalog.js +585 -0
- package/dist/cli.js +48 -0
- package/dist/components/Footer.js +38 -0
- package/dist/components/Header.js +46 -0
- package/dist/components/Menu.js +56 -0
- package/dist/config.js +40 -0
- package/dist/git.js +263 -0
- package/dist/hooks.js +23 -0
- package/dist/recipes.js +96 -0
- package/dist/themes.js +89 -0
- package/dist/views/ActionRunner.js +175 -0
- package/dist/views/BranchView.js +84 -0
- package/dist/views/ConfigView.js +60 -0
- package/dist/views/ContribView.js +80 -0
- package/dist/views/GitignoreView.js +85 -0
- package/dist/views/LogGraphView.js +116 -0
- package/dist/views/RecipeView.js +153 -0
- package/dist/views/SettingsView.js +66 -0
- package/dist/views/SetupWizard.js +120 -0
- package/dist/views/StatusView.js +121 -0
- package/package.json +52 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
// Walks the user through a multi-step recipe one command at a time.
|
|
2
|
+
import React, { useState } from 'react';
|
|
3
|
+
import { Box, Text, useInput } from 'ink';
|
|
4
|
+
import TextInput from 'ink-text-input';
|
|
5
|
+
import Spinner from 'ink-spinner';
|
|
6
|
+
import { git } from '../git.js';
|
|
7
|
+
import { recipes } from '../recipes.js';
|
|
8
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
9
|
+
export default function RecipeView({
|
|
10
|
+
recipeId,
|
|
11
|
+
theme,
|
|
12
|
+
config
|
|
13
|
+
}) {
|
|
14
|
+
const recipe = recipes[recipeId];
|
|
15
|
+
const inputs = recipe.inputs || [];
|
|
16
|
+
const [values, setValues] = useState(() => {
|
|
17
|
+
const init = {};
|
|
18
|
+
for (const inp of inputs) init[inp.name] = inp.default || '';
|
|
19
|
+
return init;
|
|
20
|
+
});
|
|
21
|
+
const [idx, setIdx] = useState(0);
|
|
22
|
+
const [phase, setPhase] = useState(inputs.length ? 'input' : 'run'); // input | run | busy | done
|
|
23
|
+
const [step, setStep] = useState(0);
|
|
24
|
+
const [log, setLog] = useState([]); // {title, ok, out}
|
|
25
|
+
|
|
26
|
+
const steps = phase !== 'input' ? recipe.steps(values) : [];
|
|
27
|
+
async function runStep() {
|
|
28
|
+
setPhase('busy');
|
|
29
|
+
const s = steps[step];
|
|
30
|
+
const r = await git(s.args);
|
|
31
|
+
const entry = {
|
|
32
|
+
title: s.title,
|
|
33
|
+
ok: r.ok,
|
|
34
|
+
out: r.stdout || r.stderr,
|
|
35
|
+
cmd: 'git ' + s.args.join(' ')
|
|
36
|
+
};
|
|
37
|
+
const nextLog = [...log, entry];
|
|
38
|
+
setLog(nextLog);
|
|
39
|
+
if (step < steps.length - 1) {
|
|
40
|
+
setStep(step + 1);
|
|
41
|
+
setPhase('run');
|
|
42
|
+
} else {
|
|
43
|
+
setPhase('done');
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function skipStep() {
|
|
47
|
+
const s = steps[step];
|
|
48
|
+
const entry = {
|
|
49
|
+
title: s.title,
|
|
50
|
+
ok: true,
|
|
51
|
+
out: '(skipped)',
|
|
52
|
+
cmd: 'git ' + s.args.join(' '),
|
|
53
|
+
skipped: true
|
|
54
|
+
};
|
|
55
|
+
setLog([...log, entry]);
|
|
56
|
+
if (step < steps.length - 1) setStep(step + 1);else setPhase('done');
|
|
57
|
+
}
|
|
58
|
+
useInput((input, key) => {
|
|
59
|
+
if (phase === 'run') {
|
|
60
|
+
if (key.return) runStep();else if (input.toLowerCase() === 's') skipStep();
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
if (phase === 'input') {
|
|
64
|
+
const inp = inputs[idx];
|
|
65
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
66
|
+
flexDirection: "column",
|
|
67
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
68
|
+
color: theme.secondary,
|
|
69
|
+
bold: true,
|
|
70
|
+
children: [recipe.emoji, " ", recipe.title]
|
|
71
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
72
|
+
color: theme.muted,
|
|
73
|
+
children: recipe.intro
|
|
74
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
75
|
+
marginTop: 1,
|
|
76
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
77
|
+
color: theme.accent,
|
|
78
|
+
children: [inp.label.padEnd(24), " "]
|
|
79
|
+
}), /*#__PURE__*/_jsx(TextInput, {
|
|
80
|
+
value: values[inp.name],
|
|
81
|
+
placeholder: inp.placeholder || '',
|
|
82
|
+
onChange: val => setValues(v => ({
|
|
83
|
+
...v,
|
|
84
|
+
[inp.name]: val
|
|
85
|
+
})),
|
|
86
|
+
onSubmit: () => {
|
|
87
|
+
if (!values[inp.name].trim() && !inp.default) return;
|
|
88
|
+
if (idx < inputs.length - 1) setIdx(idx + 1);else setPhase('run');
|
|
89
|
+
}
|
|
90
|
+
})]
|
|
91
|
+
})]
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
95
|
+
flexDirection: "column",
|
|
96
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
97
|
+
color: theme.secondary,
|
|
98
|
+
bold: true,
|
|
99
|
+
children: [recipe.emoji, " ", recipe.title]
|
|
100
|
+
}), log.map((l, i) => /*#__PURE__*/_jsx(Box, {
|
|
101
|
+
children: /*#__PURE__*/_jsxs(Text, {
|
|
102
|
+
color: l.skipped ? theme.muted : l.ok ? theme.success : theme.danger,
|
|
103
|
+
children: [l.skipped ? '↷' : l.ok ? '✓' : '✗', " Step ", i + 1, ": ", l.title]
|
|
104
|
+
})
|
|
105
|
+
}, i)), phase === 'busy' && /*#__PURE__*/_jsxs(Text, {
|
|
106
|
+
color: theme.accent,
|
|
107
|
+
children: [/*#__PURE__*/_jsx(Spinner, {
|
|
108
|
+
type: "dots"
|
|
109
|
+
}), " Running step ", step + 1, "\u2026"]
|
|
110
|
+
}), phase === 'run' && steps[step] && /*#__PURE__*/_jsxs(Box, {
|
|
111
|
+
flexDirection: "column",
|
|
112
|
+
marginTop: 1,
|
|
113
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
114
|
+
color: theme.accent,
|
|
115
|
+
bold: true,
|
|
116
|
+
children: ["Step ", step + 1, " of ", steps.length, ": ", steps[step].title]
|
|
117
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
118
|
+
color: theme.muted,
|
|
119
|
+
children: steps[step].why
|
|
120
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
121
|
+
marginTop: 1,
|
|
122
|
+
borderStyle: "round",
|
|
123
|
+
borderColor: theme.accent,
|
|
124
|
+
paddingX: 1,
|
|
125
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
126
|
+
color: theme.muted,
|
|
127
|
+
children: "$ "
|
|
128
|
+
}), /*#__PURE__*/_jsxs(Text, {
|
|
129
|
+
color: theme.secondary,
|
|
130
|
+
bold: true,
|
|
131
|
+
children: ["git ", steps[step].args.map(a => /\s/.test(a) ? `"${a}"` : a).join(' ')]
|
|
132
|
+
})]
|
|
133
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
134
|
+
color: theme.muted,
|
|
135
|
+
children: "Enter \u2192 run this step \xB7 S \u2192 skip it \xB7 Esc \u2192 leave recipe"
|
|
136
|
+
})]
|
|
137
|
+
}), phase === 'done' && /*#__PURE__*/_jsxs(Box, {
|
|
138
|
+
flexDirection: "column",
|
|
139
|
+
marginTop: 1,
|
|
140
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
141
|
+
color: theme.success,
|
|
142
|
+
bold: true,
|
|
143
|
+
children: "\uD83C\uDF89 Recipe complete!"
|
|
144
|
+
}), log.some(l => !l.ok && !l.skipped) && /*#__PURE__*/_jsx(Text, {
|
|
145
|
+
color: theme.danger,
|
|
146
|
+
children: "Some steps failed \u2014 scroll up to review. That's normal while learning!"
|
|
147
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
148
|
+
color: theme.muted,
|
|
149
|
+
children: "Press Esc / \u2190 to go back."
|
|
150
|
+
})]
|
|
151
|
+
})]
|
|
152
|
+
});
|
|
153
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Customisation: pick a theme (with live preview) and toggle UI options.
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import Gradient from 'ink-gradient';
|
|
5
|
+
import Menu from '../components/Menu.js';
|
|
6
|
+
import { themes, themeOrder } from '../themes.js';
|
|
7
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
export default function SettingsView({
|
|
9
|
+
theme,
|
|
10
|
+
config,
|
|
11
|
+
onChange
|
|
12
|
+
}) {
|
|
13
|
+
const items = [...themeOrder.map(key => ({
|
|
14
|
+
label: `Theme: ${themes[key].emoji} ${themes[key].label}${key === config.theme ? ' ✓' : ''}`,
|
|
15
|
+
value: 'theme:' + key
|
|
16
|
+
})), {
|
|
17
|
+
label: `Animations: ${config.animations ? 'ON ✨' : 'OFF'}`,
|
|
18
|
+
value: 'toggle:animations'
|
|
19
|
+
}, {
|
|
20
|
+
label: `Confirm dangerous actions: ${config.confirmDanger ? 'ON 🛡' : 'OFF'}`,
|
|
21
|
+
value: 'toggle:confirmDanger'
|
|
22
|
+
}, {
|
|
23
|
+
label: `Show command hints: ${config.showHints ? 'ON 💡' : 'OFF'}`,
|
|
24
|
+
value: 'toggle:showHints'
|
|
25
|
+
}];
|
|
26
|
+
function handle({
|
|
27
|
+
value
|
|
28
|
+
}) {
|
|
29
|
+
const [kind, key] = value.split(':');
|
|
30
|
+
if (kind === 'theme') onChange({
|
|
31
|
+
...config,
|
|
32
|
+
theme: key
|
|
33
|
+
});else onChange({
|
|
34
|
+
...config,
|
|
35
|
+
[key]: !config[key]
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
39
|
+
flexDirection: "column",
|
|
40
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
41
|
+
color: theme.muted,
|
|
42
|
+
children: "Pick a theme or toggle options. Changes save automatically."
|
|
43
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
44
|
+
marginTop: 1,
|
|
45
|
+
marginBottom: 1,
|
|
46
|
+
children: [themeOrder.map(key => /*#__PURE__*/_jsx(Box, {
|
|
47
|
+
marginRight: 1,
|
|
48
|
+
children: /*#__PURE__*/_jsx(Gradient, {
|
|
49
|
+
colors: themes[key].gradient,
|
|
50
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
51
|
+
children: key === config.theme ? '◉' : '●'
|
|
52
|
+
})
|
|
53
|
+
})
|
|
54
|
+
}, key)), /*#__PURE__*/_jsx(Text, {
|
|
55
|
+
color: theme.muted,
|
|
56
|
+
children: " live palette"
|
|
57
|
+
})]
|
|
58
|
+
}), /*#__PURE__*/_jsx(Menu, {
|
|
59
|
+
items: items,
|
|
60
|
+
onSelect: handle,
|
|
61
|
+
theme: theme,
|
|
62
|
+
animations: config.animations,
|
|
63
|
+
limit: 12
|
|
64
|
+
})]
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
// One-time identity setup: name, email, default branch = main.
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import TextInput from 'ink-text-input';
|
|
5
|
+
import Spinner from 'ink-spinner';
|
|
6
|
+
import { git, getGlobalIdentity } from '../git.js';
|
|
7
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
8
|
+
export default function SetupWizard({
|
|
9
|
+
theme,
|
|
10
|
+
onDone
|
|
11
|
+
}) {
|
|
12
|
+
const [phase, setPhase] = useState('load'); // load | name | email | running | done
|
|
13
|
+
const [name, setName] = useState('');
|
|
14
|
+
const [email, setEmail] = useState('');
|
|
15
|
+
const [results, setResults] = useState([]);
|
|
16
|
+
useEffect(() => {
|
|
17
|
+
let alive = true;
|
|
18
|
+
getGlobalIdentity().then(id => {
|
|
19
|
+
if (!alive) return;
|
|
20
|
+
setName(id.name);
|
|
21
|
+
setEmail(id.email);
|
|
22
|
+
setPhase('name');
|
|
23
|
+
});
|
|
24
|
+
return () => {
|
|
25
|
+
alive = false;
|
|
26
|
+
};
|
|
27
|
+
}, []);
|
|
28
|
+
async function run() {
|
|
29
|
+
setPhase('running');
|
|
30
|
+
const steps = [{
|
|
31
|
+
label: 'Set your name',
|
|
32
|
+
args: ['config', '--global', 'user.name', name]
|
|
33
|
+
}, {
|
|
34
|
+
label: 'Set your email',
|
|
35
|
+
args: ['config', '--global', 'user.email', email]
|
|
36
|
+
}, {
|
|
37
|
+
label: 'Default branch → main',
|
|
38
|
+
args: ['config', '--global', 'init.defaultBranch', 'main']
|
|
39
|
+
}];
|
|
40
|
+
const out = [];
|
|
41
|
+
for (const s of steps) {
|
|
42
|
+
const r = await git(s.args);
|
|
43
|
+
out.push({
|
|
44
|
+
label: s.label,
|
|
45
|
+
ok: r.ok,
|
|
46
|
+
msg: r.ok ? 'done' : r.stderr
|
|
47
|
+
});
|
|
48
|
+
setResults([...out]);
|
|
49
|
+
}
|
|
50
|
+
setPhase('done');
|
|
51
|
+
}
|
|
52
|
+
if (phase === 'load') {
|
|
53
|
+
return /*#__PURE__*/_jsxs(Text, {
|
|
54
|
+
color: theme.accent,
|
|
55
|
+
children: [/*#__PURE__*/_jsx(Spinner, {
|
|
56
|
+
type: "dots"
|
|
57
|
+
}), " Checking existing config\u2026"]
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
61
|
+
flexDirection: "column",
|
|
62
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
63
|
+
color: theme.muted,
|
|
64
|
+
children: "Tell git who you are. This is stored globally and only needs doing once."
|
|
65
|
+
}), /*#__PURE__*/_jsxs(Box, {
|
|
66
|
+
marginTop: 1,
|
|
67
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
68
|
+
color: theme.accent,
|
|
69
|
+
children: "Name "
|
|
70
|
+
}), phase === 'name' ? /*#__PURE__*/_jsx(TextInput, {
|
|
71
|
+
value: name,
|
|
72
|
+
onChange: setName,
|
|
73
|
+
onSubmit: () => name.trim() && setPhase('email'),
|
|
74
|
+
placeholder: "Your Name"
|
|
75
|
+
}) : /*#__PURE__*/_jsx(Text, {
|
|
76
|
+
color: "white",
|
|
77
|
+
children: name
|
|
78
|
+
})]
|
|
79
|
+
}), phase !== 'name' && /*#__PURE__*/_jsxs(Box, {
|
|
80
|
+
children: [/*#__PURE__*/_jsx(Text, {
|
|
81
|
+
color: theme.accent,
|
|
82
|
+
children: "Email "
|
|
83
|
+
}), phase === 'email' ? /*#__PURE__*/_jsx(TextInput, {
|
|
84
|
+
value: email,
|
|
85
|
+
onChange: setEmail,
|
|
86
|
+
onSubmit: () => email.trim() && run(),
|
|
87
|
+
placeholder: "you@example.com"
|
|
88
|
+
}) : /*#__PURE__*/_jsx(Text, {
|
|
89
|
+
color: "white",
|
|
90
|
+
children: email
|
|
91
|
+
})]
|
|
92
|
+
}), (phase === 'running' || phase === 'done') && /*#__PURE__*/_jsxs(Box, {
|
|
93
|
+
flexDirection: "column",
|
|
94
|
+
marginTop: 1,
|
|
95
|
+
children: [results.map((r, i) => /*#__PURE__*/_jsxs(Text, {
|
|
96
|
+
color: r.ok ? theme.success : theme.danger,
|
|
97
|
+
children: [r.ok ? '✓' : '✗', " ", r.label, " ", r.ok ? '' : '— ' + r.msg]
|
|
98
|
+
}, i)), phase === 'done' && /*#__PURE__*/_jsx(Box, {
|
|
99
|
+
marginTop: 1,
|
|
100
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
101
|
+
color: theme.success,
|
|
102
|
+
bold: true,
|
|
103
|
+
children: "\uD83C\uDF89 All set! Press Esc / \u2190 to go back."
|
|
104
|
+
})
|
|
105
|
+
})]
|
|
106
|
+
}), phase === 'name' && /*#__PURE__*/_jsx(Box, {
|
|
107
|
+
marginTop: 1,
|
|
108
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
109
|
+
color: theme.muted,
|
|
110
|
+
children: "Type your name, press Enter \u2192"
|
|
111
|
+
})
|
|
112
|
+
}), phase === 'email' && /*#__PURE__*/_jsx(Box, {
|
|
113
|
+
marginTop: 1,
|
|
114
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
115
|
+
color: theme.muted,
|
|
116
|
+
children: "Type your email, press Enter to save \u2192"
|
|
117
|
+
})
|
|
118
|
+
})]
|
|
119
|
+
});
|
|
120
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Colour-coded working-tree dashboard.
|
|
2
|
+
import React, { useState, useEffect } from 'react';
|
|
3
|
+
import { Box, Text } from 'ink';
|
|
4
|
+
import Spinner from 'ink-spinner';
|
|
5
|
+
import { getStatus, getCurrentBranch } from '../git.js';
|
|
6
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
7
|
+
function Row({
|
|
8
|
+
theme,
|
|
9
|
+
file
|
|
10
|
+
}) {
|
|
11
|
+
const color = file.untracked ? theme.warn : file.label === 'Deleted' ? theme.danger : file.staged ? theme.success : theme.secondary;
|
|
12
|
+
const mark = file.staged ? '✓' : file.untracked ? '?' : '•';
|
|
13
|
+
return /*#__PURE__*/_jsxs(Text, {
|
|
14
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
15
|
+
color: color,
|
|
16
|
+
children: [mark, " "]
|
|
17
|
+
}), /*#__PURE__*/_jsxs(Text, {
|
|
18
|
+
color: theme.muted,
|
|
19
|
+
children: ["[", file.label.padEnd(9), "] "]
|
|
20
|
+
}), /*#__PURE__*/_jsx(Text, {
|
|
21
|
+
color: color,
|
|
22
|
+
children: file.name
|
|
23
|
+
})]
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
export default function StatusView({
|
|
27
|
+
theme
|
|
28
|
+
}) {
|
|
29
|
+
const [state, setState] = useState({
|
|
30
|
+
loading: true
|
|
31
|
+
});
|
|
32
|
+
const [branch, setBranch] = useState('');
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
let alive = true;
|
|
35
|
+
(async () => {
|
|
36
|
+
const [s, b] = [await getStatus(), await getCurrentBranch()];
|
|
37
|
+
if (!alive) return;
|
|
38
|
+
setState({
|
|
39
|
+
loading: false,
|
|
40
|
+
...s
|
|
41
|
+
});
|
|
42
|
+
setBranch(b);
|
|
43
|
+
})();
|
|
44
|
+
return () => {
|
|
45
|
+
alive = false;
|
|
46
|
+
};
|
|
47
|
+
}, []);
|
|
48
|
+
if (state.loading) {
|
|
49
|
+
return /*#__PURE__*/_jsxs(Text, {
|
|
50
|
+
color: theme.accent,
|
|
51
|
+
children: [/*#__PURE__*/_jsx(Spinner, {
|
|
52
|
+
type: "dots"
|
|
53
|
+
}), " Reading working tree\u2026"]
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
if (!state.ok) {
|
|
57
|
+
return /*#__PURE__*/_jsxs(Text, {
|
|
58
|
+
color: theme.danger,
|
|
59
|
+
children: ["Could not read status: ", state.error]
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const clean = state.files.length === 0;
|
|
63
|
+
return /*#__PURE__*/_jsxs(Box, {
|
|
64
|
+
flexDirection: "column",
|
|
65
|
+
children: [/*#__PURE__*/_jsxs(Box, {
|
|
66
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
67
|
+
color: theme.accent,
|
|
68
|
+
bold: true,
|
|
69
|
+
children: ["On branch ", branch || '(detached)', ' ']
|
|
70
|
+
}), state.ahead > 0 && /*#__PURE__*/_jsxs(Text, {
|
|
71
|
+
color: theme.warn,
|
|
72
|
+
children: ["\u2191", state.ahead, " ahead "]
|
|
73
|
+
}), state.behind > 0 && /*#__PURE__*/_jsxs(Text, {
|
|
74
|
+
color: theme.danger,
|
|
75
|
+
children: ["\u2193", state.behind, " behind "]
|
|
76
|
+
})]
|
|
77
|
+
}), clean ? /*#__PURE__*/_jsx(Box, {
|
|
78
|
+
marginTop: 1,
|
|
79
|
+
children: /*#__PURE__*/_jsx(Text, {
|
|
80
|
+
color: theme.success,
|
|
81
|
+
children: "\u2728 Working tree clean \u2014 nothing to commit."
|
|
82
|
+
})
|
|
83
|
+
}) : /*#__PURE__*/_jsxs(Box, {
|
|
84
|
+
flexDirection: "column",
|
|
85
|
+
marginTop: 1,
|
|
86
|
+
children: [state.staged.length > 0 && /*#__PURE__*/_jsxs(Box, {
|
|
87
|
+
flexDirection: "column",
|
|
88
|
+
marginBottom: 1,
|
|
89
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
90
|
+
color: theme.success,
|
|
91
|
+
bold: true,
|
|
92
|
+
children: ["\u25CF Staged (", state.staged.length, ") \u2014 will be committed"]
|
|
93
|
+
}), state.staged.map((f, i) => /*#__PURE__*/_jsx(Row, {
|
|
94
|
+
theme: theme,
|
|
95
|
+
file: f
|
|
96
|
+
}, 's' + i))]
|
|
97
|
+
}), state.unstaged.length > 0 && /*#__PURE__*/_jsxs(Box, {
|
|
98
|
+
flexDirection: "column",
|
|
99
|
+
marginBottom: 1,
|
|
100
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
101
|
+
color: theme.secondary,
|
|
102
|
+
bold: true,
|
|
103
|
+
children: ["\u25CF Changed but not staged (", state.unstaged.length, ")"]
|
|
104
|
+
}), state.unstaged.map((f, i) => /*#__PURE__*/_jsx(Row, {
|
|
105
|
+
theme: theme,
|
|
106
|
+
file: f
|
|
107
|
+
}, 'u' + i))]
|
|
108
|
+
}), state.untracked.length > 0 && /*#__PURE__*/_jsxs(Box, {
|
|
109
|
+
flexDirection: "column",
|
|
110
|
+
children: [/*#__PURE__*/_jsxs(Text, {
|
|
111
|
+
color: theme.warn,
|
|
112
|
+
bold: true,
|
|
113
|
+
children: ["\u25CF Untracked (", state.untracked.length, ") \u2014 new files git isn't watching yet"]
|
|
114
|
+
}), state.untracked.map((f, i) => /*#__PURE__*/_jsx(Row, {
|
|
115
|
+
theme: theme,
|
|
116
|
+
file: f
|
|
117
|
+
}, 'n' + i))]
|
|
118
|
+
})]
|
|
119
|
+
})]
|
|
120
|
+
});
|
|
121
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@manthank/mgit",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A friendly, animated terminal UI that teaches beginners how to use git & GitHub.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"mgit": "dist/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist",
|
|
11
|
+
"README.md",
|
|
12
|
+
"LICENSE"
|
|
13
|
+
],
|
|
14
|
+
"engines": {
|
|
15
|
+
"node": ">=18"
|
|
16
|
+
},
|
|
17
|
+
"scripts": {
|
|
18
|
+
"build": "babel source --out-dir dist --extensions .js,.jsx --copy-files",
|
|
19
|
+
"dev": "npm run build && node ./dist/cli.js",
|
|
20
|
+
"start": "node ./dist/cli.js",
|
|
21
|
+
"prepare": "npm run build"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"git",
|
|
25
|
+
"github",
|
|
26
|
+
"cli",
|
|
27
|
+
"tui",
|
|
28
|
+
"ink",
|
|
29
|
+
"beginner",
|
|
30
|
+
"interactive",
|
|
31
|
+
"learn-git",
|
|
32
|
+
"terminal",
|
|
33
|
+
"learning"
|
|
34
|
+
],
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"execa": "^9.5.2",
|
|
38
|
+
"ink": "^5.1.0",
|
|
39
|
+
"ink-big-text": "^2.0.0",
|
|
40
|
+
"ink-gradient": "^3.0.0",
|
|
41
|
+
"ink-select-input": "^6.0.0",
|
|
42
|
+
"ink-spinner": "^5.0.0",
|
|
43
|
+
"ink-text-input": "^6.0.0",
|
|
44
|
+
"react": "^18.3.1"
|
|
45
|
+
},
|
|
46
|
+
"devDependencies": {
|
|
47
|
+
"@babel/cli": "^7.26.4",
|
|
48
|
+
"@babel/core": "^7.26.0",
|
|
49
|
+
"@babel/preset-react": "^7.26.3",
|
|
50
|
+
"ink-testing-library": "^4.0.0"
|
|
51
|
+
}
|
|
52
|
+
}
|