-
-
Notifications
You must be signed in to change notification settings - Fork 261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
buttons executing custom commands or user menu like in mc #221
Comments
As I understood what you want is ability to define shell commands, and run them somehow not opening console, from user menu, for example. To implement such feature a couple things should be solved:
If all you need is to run a command, and see output, in this case it will be something like You can use The tool can be built using terminal-kit that will contain menu options and run what you need. You can read more about it here. Here is example code: const {terminal} = require('terminal-kit');
terminal.cyan('The hall is spacious. Someone lighted few chandeliers.\n') ;
terminal.cyan('There are doorways south and west.\n');
const items = [
'a. Go south' ,
'b. Go west' ,
'c. Go back to the street',
] ;
terminal.singleColumnMenu( items, (error , response) => {
terminal('\n').eraseLineAfter.green(
"#%s selected: %s (%s,%s)\n" ,
response.selectedIndex ,
response.selectedText ,
response.x ,
response.y,
) ;
process.exit() ;
}); There is a couple benefits of using
You can use you own repo to implement proof of concept with predefined scripts, and latter it can be extended to ability to add and remove scripts in a comfortable way. And added to cloudcmd organization. In a future user menu can be started with help of a hot key, like What do you think about such solution? |
Thanks for that idea - it sounds like a good first step. The only worry I have with this approach is: can I get the currently selected path in cloudcmd somehow (is the cloudcmd path environment variable updated all the time and I can just read it out before I execute the user-command)? I would also need to make the choices - mouse clickable, but I think ncurses in the terminal should be my friend there (either terminal-kit or any other console-ncurses menu should work there I assume, I am more a python guy than a java-script guy, so my choice might be ptk). Coudl even initially just make a shell escape. |
client/modules/terminal.js passes env variables with currently selected path to executable command and it is updated.
|
I will give it a go the coming week, using your js-skeleton and terminal-kit and report back here. |
This is incorrect. The variable is set only once (first start of terminal) and never updated. Worse, when the shell or command in terminal quits/ends the respawned process doesn't have the environment variables anymore at all. You have to refresh the cloudcmd and terminal browser page to access the variable again. Also, when the path in cloudcmd is changed after terminal was started, the variable doesn't change in the started program (of course - a fork has a copy of the environment of the process it was started from). If we can't find a quick fix for this, this renders all my efforts useless - see below for some ideas.
This works pretty well, I have spent a lot of time to convince terminal-kit to do something decent here and was kind of proud of my results (compare: https://github.com/ulno/ulnoiot/blob/master/bin/simple_shell.js, I wasted also a lot of time as I wanted to do it in python first, but python and ncurses is too slow for the raspberry pi, terminal-kit works good enough). Unfortunately, this all falls apart as there is no way for me to get an updated path from cloudcmd. Is there (1) a way to close the terminal window after an action is done (eventually adding a menu for different terminal sessions there - 1a) or (2) writing the paths always to a tmp-file or /var/run/cloudcmd that can be read before I execute something in the terminal, maybe the terminal should have an environment variable with an ID (2a) that refers to the temp file or a location in the tmp-file to allow multiple sessions? I (and my students) would be very thankful if there would be a solution to this. I will start looking at cloudcmd code, if I can quickly hack solution 2 (2a can wait after my next class). |
You right, this is how You can set
Do not forgot to add shebang and make And modify client side part to start menu on every show and hide terminal on every exit.
This is great 👍, we can use it as a user menu in
Yes gritty uses socket.on('exit', hide); To close terminal window on program exit.
I think would be simpler to run menu every time, and get data from environment variables, what do you think about it? I don't think this will be very slow, but communication thru environment is the simplest cross operational future proof way of doing things (12 factor app agree). |
I did already toy with the no-auto-restart-option, running this script: #!/bin/bash
for i in {1..5}; do
echo "$i. $(date +%H:%M:%S) >$ACTIVE_DIR<"
sleep 5
done
exit 0 However, it does not close and restart...
... which you must be hinting at here
... and here So, I changed the code this way: // const {socket, terminal} = gritty(document.body, options);
var {socket, terminal} = gritty(document.body, options); // make sure to open new each time
Terminal = terminal;
socket.on('exit', hide); // ulno: hint from coderaiser for closing all the time // TODO: kill menu too? and copied it over an obfuscated file into a new local installation in (I tried to create the dist folder locally, but I have no idea how - The resulting cloudcmd starts and looks ok, but does not start the terminal anymore, I assume I have somehow correctly build the package, how can I do that? Thanks for all the help and sorry for my lack of nodejs knowledge. |
Update1: managed to create dist folder with yarn. Update2: only managed it once and might be an accidental call of webpack, however can't reproduce, still always failing creating the dist folder and can't start cloudcmd at all from my local directory neither on the pi nor on my desktop. webpack always fails now (several times) with:
Update3: |
Yep that works, now the hide-part, when shell-command ends, works already, however the terminal does not restart yet, my trick using However, I don't find anything to destroy the old Terminal (and view) and where I can then call On another note, for others, I add here a quick summary how to make cloudcmd development possible in a local directory in a Linux environment: Make sure you have nodejs, npm, yarn, and webpack globally installed, then ... cd <personal-development-folder> # adjust!
git clone git clone ssh://[email protected]/coderaiser/cloudcmd # or your personal fork
cd cloudcmd
npm i gritty
npm i rimraf
npm i madrun
export NODE_PATH="$(pwd)/node_modules:/usr/lib/node_modules"
alias redrun="node $(pwd)/node_modules/redrun/bin/redrun.js"
alias rimraf="node $(pwd)/node_modules/rimraf/rimraf.js"
yarn # install webpack-cli, when asked
webpack --mode development
node bin/cloudcmd.js Maybe that needs to go somewhere or is there an easier solution? |
Yes this could be simpler: git clone https://github.com/coderaiser/cloudcmd.git
cd cloudcmd
npm i
npm run build:start:dev
Looks like this will be a little bit harder then I thought, and gritty should be modified to have ability to spawn different programs, or to destroy itself. I should think about it. You moving in a right direction, thank you for all your work. |
Ok, thanks for the development shortcut. I guess, now I am back to square one. I have a small workshop Wednesday at a Makerspace and would have liked to show off the user menu there, so I think I have to proceed with step (2) (at least temporarily, if there is a chance to finish this with a couple of hours of coding investment):
Could you point me to a position in one of the server modules, where I could write to such a file? Anything that is triggered when the directory or focus is switched? Or is that all happening on client side? How could I trigger then an event from client side to execute something in the server code each time the path is changed? I appreciate all your help, I hope I can help soon to provide some "nicer" code for the user menu when we straighten out the workflow between cloudcmd and gritty to allow new processes to be started. |
Look, it is much harder, because server side doesn't know what file is selected, and what directory you observing. --- a/client/modules/terminal.js
+++ b/client/modules/terminal.js
@@ -52,7 +52,6 @@ module.exports.init = async () => {
await CloudCmd.View();
await loadAll();
- await create();
};
module.exports.show = show;
@@ -87,6 +86,7 @@ function create() {
fontFamily: 'Droid Sans Mono',
};
+ delete window.IntersectionObserver;
const {socket, terminal} = gritty(document.body, options);
Terminal = terminal;
@@ -115,6 +115,7 @@ function show(callback) {
if (!config('terminal'))
return;
+ create();
CloudCmd.View.show(Terminal.element, {
afterShow: () => {
if (Terminal)
Then run cloudcmd --terminal-command env --no-terminal-auto-restart And every time you will open Here is what I see when I in
And here is what I see when I in
To socket.on('connect', exec.with(authCheck, socket));
+ socket.on('exit', hide); About this error
It is related to terser/terser#251 and should be fixed already. |
Thanks a lot! This starts to work quite ok (I updated to your latest version from github and manually applied the patches). I can now see the different paths, when restarting Terminal from a different folder. However, adding Update: it can be actually any folder and it does not happen all the time. It seems to happen more often in Firefox and Chromium and less often on Chrome. However, on all browsers it happens usually before you change into 10 different folders - on the desktop. I will try to see if it is an issue at all on the pi (which is much slower than the desktop). A reload of the current window restores the icons again but just navigating into a folder and back up again destroys them again. |
This is a hot fix for
I can be related to #224, could you please tell me names of this directories? I can't reproduce this with my directories. |
In Firefox, for example, I just need to open http://localhost:8000 (I end in /), then I double-click (also when I use the keybaord and press enter)on home, and I can only navigate back (icons are gone), this happened 5/5 times I just tested it. It happens in both: one column and two column mode. Only change I made is, adding the delete window.IntersectionObserver patch. |
Yes, issue #224 seems to be related, however, navigation problems stop for me if I take the delete out - was that ever there upstream? I also have the same problems in Firefox incognito mode. Same problems on Android with Firefox and Chrome. Same problems with and without authentication enabled. |
I can't reproduce this anyway. No it wasn't upstream, I just today figure out what broke first show of a |
Positive Update: running on the raspberry pi in latest raspbian, I don't have the problems (and terminal re-opening in new environment works, too -> which means I can move ahead, yeah, thanks so much). So, why does it not work on my desktop? As mentioned in thread beginning, cloudcmd pi-configuration is this:
now upgraded to v11.8.3 and patched as advised (cloned from current http://github.com/ulno/cloudcmd)
My desktop:
Any idea, what could cause this and let me know if I can help to track this down - but apart from that, I am pretty happy at this point as it seems to work well on the pi and I might show off cloudcmd in my IoT framework on Wednesday with some simple user menus! Update: issues with icons are gone in 11.8.5 |
Things are working out mostly fine with the user-menu on the pi, just one very small issue: Often a program that ran in the user-menu is modifying the directory that it was started in (creating for example new files or directories). How can I trigger a refresh on exit of the terminal of the current file column to reflect th echange (like pressing on the two small errors or doing ctrl-r)? What do I have to call for that? |
Just call |
so where to do that |
At the moment, you need to make the changes to the source mentioned hereto be able to use that feature (you lose though the terminal). It works great for my students and has simplified the workflow greatly. @coderaiser: How much effort would it be to clone the current terminal for this "User-Menu" functionality" and offer another optional button for this? Or even just have a Here is a summary of the changes I made to my version of cloudcmd: Enable closing of terminal after command is run and refresh when opening again-------------------- client/modules/terminal.js --------------------------
index a635e939..d971c854 100644
@@ -52,7 +52,6 @@ module.exports.init = async () => {
await CloudCmd.View();
await loadAll();
- await create();
};
module.exports.show = show;
@@ -87,13 +86,10 @@ function create() {
fontFamily: 'Droid Sans Mono',
};
-
-// const {socket, terminal} = gritty(document.body, options);
- var {socket, terminal} = gritty(document.body, options); // make sure to open new each time
-
- Terminal = terminal;
+ delete window.IntersectionObserver;
+ const {socket, terminal} = gritty(document.body, options);
- socket.on('exit', hide); // ulno: hint from coderaiser for closing all the time // TODO: kill menu too?
+ Terminal = terminal;
terminal.on('key', (char, {keyCode, shiftKey}) => {
if (shiftKey && keyCode === Key.ESC) {
@@ -118,6 +114,8 @@ function show(callback) {
if (!config('terminal'))
return;
+
+ create();
CloudCmd.View.show(Terminal.element, { Autohide after execution finishes-------------------------- client/modules/terminal.js --------------------------
index d971c854..c15cb3c3 100644
@@ -98,6 +98,7 @@ function create() {
});
socket.on('connect', exec.with(authCheck, socket));
+ socket.on('exit', hide);
}
function authCheck(spawn) { Refresh of directory after close of terminal-------------------------- client/modules/terminal.js --------------------------
index c15cb3c3..69a605ce 100644
@@ -94,11 +94,12 @@ function create() {
terminal.on('key', (char, {keyCode, shiftKey}) => {
if (shiftKey && keyCode === Key.ESC) {
hide();
+ CloudCmd.refresh();
}
});
socket.on('connect', exec.with(authCheck, socket));
- socket.on('exit', hide);
+ socket.on('exit', function() { hide(); CloudCmd.refresh(); });
}
function authCheck(spawn) { Renaming the terminal button into User Menu------------------------------- html/index.html -------------------------------
index e0fd8595..7a3bdc62 100644
@@ -30,7 +30,7 @@
<button id=f9 class="cmd-button reduce-text icon-menu" title="Menu" >F9</button>
<button id=f10 class="cmd-button reduce-text icon-config" title="Config" >F10</button>
<button id=~ class="cmd-button reduce-text icon-console" title="Console" >~</button>
- <button id=shift~ class="cmd-button reduce-text icon-terminal" title="Terminal" >⇧ ~</button>
+ <button id=shift~ class="cmd-button reduce-text icon-terminal" title="User Menu" >⇧ ~</button>
<button id=contact class="cmd-button reduce-text icon-contact" title="Contact" ></button>
</div>
<script src="{{ prefix }}/dist/cloudcmd.common.js"></script> |
I think we can keep
Arguments with @ulno what do you think about it? |
Sounds like a great idea to me. Do you need any help to make that happen - maybe in terms of documentation/youtube usage video? We can use my user-menu from ulnoiot/iot (https://github.com/iotempire/iotempower/blob/master/bin/user_menu.js) empower as an example? Or shall I strip out just the button list? Could even make that a bit more generic and take a json file with button titles and respective commands as as input? |
Yes, I need help with a pull request that contains changes described above :)
Yes, would be great if user menu would be as generic as possible. Ideally would be great if our format will be compatible to mc. But we can start from generic format comfortable for you, |
Ok, you asked for it. I will be on vacation for 2 weeks now, but I will give it a shot the following week.
Hmm, if it should be compatible to mc, shouldn't it be also called with F3? (Btw. would it make sense to make the keys and buttons customizable in a configuration file? Probably should file that as new issue.) |
Do you mean as
We can make it in a future, when main functionality will be ready. |
Yes, F2, I am using shift-f6 for rename in mc, but this leads off topic, can work on having a user-menu first. |
That's interesting, I should think about it, maybe there is a reason to bind keys in a similar way as |
@ulno don't rush with a pull request, I have an idea, how all the things can be simplified a lot. User menu can work just like Also I have an idea about simplest {
"create list file": {
"key": "c",
"command": "ls > 1.txt"
},
"convert mp3 to flac": {
"key": "m",
"command": "mp3toFlac %s"
}
} For first implementation we can avoid {
"create list file": {
"command": "ls > 1.txt"
},
"convert mp3 to flac": {
"command": "mp3toFlac %s"
}
} The field We can even use: {
"create list file": "ls > 1.txt",
"convert mp3 to flac": "mp3toFlac %s"
} But in this case there won't be ability to set key, just like in |
I prefer version 1 (or the abbreviated version in 2) to be closer to mc's format and being able to add a key or even a nested option. (Sorry for late answer still on vacation) |
That's OK, I'm trying to determine what format would fit your and everyone else needs and will be simple enough to parse :). Latest version, I came up with, to handle your case and previous with file rename is module.exports = {
'F2 - Rename file': () => {
const {element} = DOM.CurrentInfo;
DOM.renameCurrent(element);
},
'D - Deploy': async ({DOM, push}) => {
const {dirPath} = DOM.CurrentInfo;
const msg = `You are about to deploy from the following path: ${dirPath}.\n Are you sure?`;
await DOM.Dialog.confirm(msg);
push('deploy');
},
}; In case of With this format file rename can be kept, with help of |
Having the option of a native confirmation dialog would of course be very awesome (that might be even superior to mc - but of course you can simulate that in mc). Also being able to influence all the buttons this way would of course offer a host of options. How much of this configuration should be exposed to the user? |
Actually not all the buttons, the idea is to not use modifiers like
All of this can be exposed, it can be any JavaScript code that will be executed in the browser. |
Just added a base implementation of 'use strict';
module.exports = {
'F2 - Rename file': ({DOM}) => {
const {element} = DOM.CurrentInfo;
DOM.renameCurrent(element);
},
'D - Deploy': async ({DOM}) => {
const {dirPath} = DOM.CurrentInfo;
const msg = `You are about to deploy from the following path: ${dirPath}.\n Are you sure?`;
await DOM.Dialog.confirm(msg);
return 'deploy';
},
'L - List files': () => {
return 'ls';
},
}; Here is how it works. When you access the user menu, the file It's very raw solution, WIP, any comments would be appropriate :). |
@ulno could you please tell me, do you show any output of your commands when running via user menu? What do you think should we show any output of commands, or it is doesn't matter? |
I do show a lot of output. For example In a super long-term perspective it would be cool to call this compilation directly from the editor and maybe then later see the errors highlighted in the editor, but I don't know what the best way forward here is. Also the option to press ctrl-c to interrupt (or kill or suspend) the started process is kind of useful here. I guess, I could wrap my scripts into something which generates some dedicated percentage values -> if you want even somehow "tagged" and some "log-information" (tagged differently), but maybe that is starting getting too far away from the terminal-based approach? |
Yes, this makes things harder. Maybe would be better to call terminal for executing command selected from user menu. What do you think about this approach? Would this feet the needs for most users of module.exports = {
'D - Deploy': async ({DOM}) => {
const {dirPath} = DOM.CurrentInfo;
const msg = `You are about to deploy from the following path: ${dirPath}.\n Are you sure?`;
await DOM.Dialog.confirm(msg);
CloudCmd.Terminal.show({
execute: 'deploy'
});
},
}; It wan't close terminal on end of executing, user must close it by himself. Should we provide option for this? Or it is OK? |
On my way back, so I will be able to respond quicker and hopefully even be able to do some testing and coding tomorrow (looking forward to trying out your test branch). I think we need to be able to use terminal as discussed for showing command execution. This opens automation of all kind of different things for potential users. I can imagine quick solutions for tons of small local personalized small businesses or school or univetsity class networks, where the file metaphor and some customized commands would be extremely useful. But I also think it should close automatically or this should be at least configurable. If it closes automatically, I can delay the closing with a small terminal dialog ("press enter or click here"), but if it stays open, I cannot force-close it from the terminal. |
Looks nice, but my lack of experience with nodejs and npm is getting the better of me again. I am able to start the git-branch version of cloudcmd with Any hints how I can give this |
After building with |
I think this can be possible. All we need is modify gritty to add support of this. |
That works well. Very nice! I think, I kind of like the double F2 press for rename idea - maybe shift-f2 as short cut to just rename without user-menu? In user-menu, I tried running the build development as example and that shows the problem not using a terminal directly going into hiding for a while and then suddenly showing a huge dialog with lots of tagged output (though getting the result of a short running command in a dialog is cool too!). So what is needed to have the option of running things in a potentially closing terminal? Why would it be needed to patch gritty? With the formerly mentioned patches that was already working? Is it an issue in having two instances of gritty running at the same time? Or did you mean patching gritty to allow a force-close with a shell command - does that make sense if we just need it to be allowed to be closed with destroying its view after the command in there finishes? |
Just updated feature/user-menu branch. Here is how module.exports = {
'F2 - Rename file': ({DOM}) => {
const {element} = DOM.CurrentInfo;
DOM.renameCurrent(element);
},
'D - Build Dev': ({CloudCmd}) => {
CloudCmd.TerminalRun.show({
command: 'npm run build:client:dev',
});
},
'P - Build Prod': ({CloudCmd}) => {
CloudCmd.TerminalRun.show({
command: 'npm run build:client',
});
},
}; You have
Also updated gritty to get things working. What do you think about it? |
I think it's great - and I would be willing to start using it already as is for production environment. However, there are some small things that should still be considered:
|
It is a good point. I added this and now it is possible with
This is not obvious behavior to use
It is a good point about Here is how module.exports = {
'F2 - Rename file': async ({DOM}) => {
await DOM.renameCurrent();
},
'D - Build Dev': async ({CloudCmd}) => {
await CloudCmd.TerminalRun.show({
command: 'npm run build:client:dev',
closeMessage: 'Press any button to close Terminal',
});
CloudCmd.refresh();
},
'P - Build Prod': async ({CloudCmd}) => {
await CloudCmd.TerminalRun.show({
command: 'npm run build:client',
autoClose: true,
});
CloudCmd.refresh();
},
}; |
Added User Menu Cookbook, you can add your own scripts, as examples 🙂. |
cloudcmd -v
): v10.8.3node -v
: v8.15.0uname -a
on Linux): Linux ulnoiotgw 4.14.79-v7+ #1159 SMP Sun Nov 4 17:50:20 GMT 2018 armv7l GNU/LinuxAs most of my users completely fear the command line, I would like to be able to implement buttons (like for copy, mkdir, or console start) or a user menu (like happening on right click or like seen in midnight commander pressing F3) to execute some pre-defined commands in the respectively activated folder and showing their output while they are executing in the console.
How much effort would this be? Where would I have to start?
Usecase example
I have cloudcmd running on a headless raspberry pi that I control via the web using cloudcmd.
I just made a copy of a template source folder and now want to use the
initialize
command to write this configuration to a microcontroller connected to the raspberry pi (or gateway computer). Now, I am starting the console per button press and typeinitialize
and look at the output. It would be nicer if I had some kind of deploy button executinginitialize
in the right folder directly for me.If there were multiple commands possible (initialize, deploy, upgrade), maybe a pop-up menu would make sense.
The text was updated successfully, but these errors were encountered: