Solo with Aaron Francis
Joe (00:06)
Welcome to Side Project. I am your host, Joe Tannenbaum, and with me today we have a man of the internet, Aaron Francis, co-founder of Try Hard Studios. How are you doing today, Aaron?
Aaron Francis (00:18)
Good, I am honored to be your guest on side project today.
Joe (00:22)
don't know if honor is the right word, but I'll take it. will, I will accept that. okay. That is, that is fair. you have another podcast, which is excellent called mostly technical. And on that, that podcast, although I am an Ian Stansman, I am a huge Stan. you get a little bit of shame. there's a little side project shame on that and we don't have any of that here.
Aaron Francis (00:25)
I get to decide. I am honored. So that it is the right word.
Tell him that.
Yeah,
Joe (00:46)
You
Aaron Francis (00:46)
that's good.
Joe (00:47)
can work on whatever you want without guilt, without shame. Doesn't matter if anything comes out of it. Doesn't matter if there's a publish button hit, whatever you want. And you seem to have a lot of those going on lately. And so I'm curious which one of the thousand you'd like to talk about today.
Aaron Francis (00:50)
I appreciate that.
Thank you.
Yeah.
Well, I just first want to acknowledge that I'm glad that we acknowledge that Ian is a no man. He's always bringing me down. He's like, don't do that, Aaron. I'm like, what in the world? I got to do stuff. so yeah, I appreciate having a yes man on the show today. I kind of want to talk about, I kind of want to talk about solo. So we got a lot of projects going on.
Joe (01:11)
Always. Yeah. Joyless. Joyless, that man.
Aaron Francis (01:26)
one of which is probably near and dear to your heart, and that's this new library Laravel package called Solo. So let's talk about that one.
Joe (01:36)
Let's talk about it. this correct me if I'm wrong. This came from that new composer run dev. Is that what it was? Composer run dev. And you said that's cute. Let's go a hundred times further with that. So tell me, tell me the process. Tell me how this came to be.
Aaron Francis (01:41)
It did. Mm-hmm. Yep.
Mm-hmm. Exactly. Yeah,
so I was creating a new Laravel project for this game show that I was on, Jason Lingstorf's channel. Super fun. And so this was the first...
Joe (02:02)
I love that. He does a great job
of that. Yeah.
Aaron Francis (02:04)
was awesome. This was the first
new Laravel project that I had created since the introduction of Composer Run Dev, which Taylor introduced a few weeks ago. That is basically a PHP entry point around NPX concurrently. So NPX concurrently, they set it up to run, know, npm run dev, phpartisan serve, PHP queue work and maybe
or tail the logs or something. And so I ran that and I was like, this is kind of nice. know, first time experiencing like the combined command to run the whole thing, kind of nice. However, as I was like developing the application, I needed to add reverb and I was like, hmm. So now I got to go muck around with, you know, NPX concurrently and the escape, like all the escaping and adding of the new colors. And I was like, that kind of sucks. And then you switch over to the place where it's running.
and the logs are interspersed with HTTP responses or interspersed with reverb errors. And I'm like, this is not great. We could do so much better. This is not great. And so I came home after that and on a lark watched your CLI experiments, Laracast's course, which is very good. I highly recommend it. And so I'm laying in bed. It's like 1030.
Joe (03:08)
Yeah.
Thank you.
Aaron Francis (03:24)
I got the phone sideways and I'm just watching you just type stuff. I'm like, man, this is really good. I could do this. And so the next day I came in and was like, I'm going to, I'm going to make a better version of this composer run dev. And I'm to do it as like a CLI tool as a, as a twoie. And as things do, it just kind of spiraled from there. It just kind of, you know, it was like, this will be simple. And now, you know, we're several, several weeks later and.
open issues and dozens to a few hundred commits and it's not finished yet so hoisted by my own petard once again but it's kind awesome.
Joe (03:59)
Yeah.
It's kind of incredible. You took it so far. You took it further than I even, I think you mentioned sort of in the beginning, like, I'm thinking of doing this and here's like a prototype. And I was like, that's amazing. That's great. And then you like started sprinting, just sprinting down the road. So you have accidentally recreated TMUX via PHP, which I'm not like super familiar with TMUX, but I know enough about it to know that's basically what you did.
Aaron Francis (04:06)
Yeah
Yeah.
Yeah.
know that you
shouldn't do that. Yeah, same.
Joe (04:32)
And I also
want to like emphasize that your learning curve is like enormous you went from I have virtually zero knowledge of this space I don't know how to put together like a comprehensive TUI slash CLI app and then you went I am building a full screen interactive multi-process TUI Outside of the course. I mean there's you're doing way more than what's in the course Where where were your stumbling blocks like where where did you go? I don't know what this is and
Aaron Francis (04:50)
huh.
Joe (05:01)
I wish I knew more about this or because a lot of it I find in this space is not even knowing the term to search for yet. You've got to figure out what you're looking for before you even get there. like, can you do have any of those identified like what the stumbling blocks were in the beginning process?
Aaron Francis (05:07)
Correct.
Yep. Yes.
Yeah, mean, where
were the stumbling blocks? Everywhere, all over the place. Everything was a stumbling block. It's all stumbling blocks all the way down. So I think of myself as a very working class developer. And so when people talk about TMUX, I'm like, sorry, friendo. I don't know what that means. Sounds awesome for you. That sounds great. But I don't know what that is.
Joe (05:24)
It's all stumbling blocks. Yeah.
Yeah.
Aaron Francis (05:41)
They're like, I gotta, you know, set up my tree sitter and my LSP and Neo of them. And I'm like, is that PHP storm? Cause that's what I use and PHP storm rules. and so once you like, once you start mucking around with accidentally inventing T mucks, there's a whole layer down there that I'm just not terribly familiar with. mean, so we can talk about, we can talk about, a terminal in a shell.
Joe (05:45)
Yeah
Yeah. Yeah.
Aaron Francis (06:07)
What are those things? I still don't know. How are they different? I have no idea. And so it's like, how am I supposed to even do any of this when I can't describe to you even now the difference between a terminal and a shell? Beats me. And so then we start.
Joe (06:20)
I know it
every two weeks when I have to relook it up. That's when I know it and then the information just disappears from my head. I know shockingly little about what I'm actually doing. I'm really just doing what you're doing, stumbling along. Yeah, okay.
Aaron Francis (06:24)
No clue.
Yes. Correct. Yes. I
am like a plumber that doesn't know theory and only knows practice. It's like, well, water flows downhill. Make sure it doesn't leak. I'm like, hey, yeah, that's pretty good. I'm a plumber now. And so, you know, you start mucking around with like dealing with the output of other processes and you're in the wild, wild west. Cause it's like, they could do anything and you don't
Joe (06:42)
Yeah. Yeah.
Aaron Francis (06:57)
you don't really have control over it, but you gotta be prepared for it. And so then you enter into the whole world of ANSI codes and stuff. Fortunately, I have found ANSI codes have been around for 100 years and therefore, Chet GPT is very well versed in what ANSI codes are, what they do, how to work with them. And so I spend a lot of time being like, hey, this is happening, I don't know what this means, can you help me?
Like, why did my whole screen just turn blue? Can you help me? Cause I don't, I can't figure that out. Yeah. It's like, you have a ANSI code that never got reset. Okay. Tell me more about ANSI codes. What does that mean? And so, you know, it kind of, the guy that created SQLite has said in later years that if he had known you weren't supposed to write a database from scratch, he wouldn't have done it, but he didn't know that.
Joe (07:29)
Right.
Yeah.
Aaron Francis (07:48)
And that's really, that's like, that's how I feel. Like if I had known that this was stupid, I wouldn't have done it, but because I didn't, I did it. And it's still, it's awesome. But I ended up, yes, exactly. And it's great, it works. I ended up, you know, in the fullness of time, I've ended up creating basically like a, I don't know what, a terminal emulator, a screen emulator basically, because one of the problems is like,
Joe (07:48)
Great.
You're describing my career, basically. Yeah.
Mm-hmm.
Aaron Francis (08:17)
I'm gathering all of this output from these underlying processes and then they're full of ANSI codes. But some of the ANSI codes, I'm looking at you, Jess Archer, some of the Laravel prompts, for example, is like, hey, what if we, as you're typing, we move up five lines and clear down and then redraw from the five lines that we moved up? And I'm like, guys, I wasn't prepared for that. I started with the idea.
that you would be tailing a log and I could just be like, haha, I got the output there, I'm gonna write it over there. And then Jess comes along, she's like, no, Laravel prompts are awesome and we do partial screen clearing and you gotta be ready for that. And so that is the point when I moved into, all right, I have to take all of this output from these underlying processes, I have to take it all, I have to run it through an emulator to figure out what the final representable state should be.
and then give that to Solo to represent. And so I'm like doing all of this stuff in memory. dude.
Joe (09:18)
And that's an enormous task. That's that's like, you're,
that's like parsing level 1000, because like, when you look at ANSI codes, it's not like HTML, like HTML, can kind of suss out. can say like, I see tags here, whatever. ANSI amidst content is a jumbled non-human parsable mess. You've got to like really go through it. And the thing you just talked about, that mental leap of saying like, I just printed these, these, sequence to the terminal and it actually,
Aaron Francis (09:29)
Mm-mm.
Joe (09:44)
is not about formatting, it's gonna move the cursor around and erase things. And that's not, to me, that was not intuitive in the beginning. I was like, I'm just printing something to the screen and it's it's controlling the terminal. And so that is a mental hurdle that you have to sort of like get over and figure out and like grok in order to move forward, right? That's a huge one in my, because if it was just formatting, you're like, okay, I just need to like sort of parse out the formatting, but there's actions here, you know?
Aaron Francis (09:47)
gonna move the cursor.
No.
Yes.
Yes. In my...
Exactly.
In my adorable naivete, I thought ANSI codes are for colors. And like, it's just, it makes everything pretty. And then you realize it's like, no, this manipulates the cursor and it clears the screen and it sets settings and retrieves settings. And you're like, well, F, that means like, I'm, if I am in control of a process and I'm just randomly or rather faithfully printing out
the output from the underlying process, it's gonna muck about with my process. It's gonna muck about with what I'm trying to show on the screen. So I'm drawing this beautiful chrome around this little pane of output, but this output and this little pane is now reaching outside and clearing the screen. And it's like, God freaking dang it. now I run it all through a buffer, but then you get into the situation where it's like, all right, well, how do you keep track of content and ANSI?
Joe (10:57)
You
Aaron Francis (11:06)
kind of separately but kind of together because you need to do like, you need to do word wrapping and you need to keep track of like, what colors this line should be up to this character but then turn it off. And then the insidious part is, let's say you've got a string of red text, right? And your cursor's at the end and somebody decides, all right, let's turn the text back to black for the next few characters, right?
string a red text, turn off red, go back to black, let's start typing again. here's something fun. Let's move the cursor all the way back to the middle of the red text and keep typing. What color should it be? Well, whatever it was at the end. It doesn't become red. The cursor carries these ANSI codes back in time. And it's like, I'm gonna start writing, but you need to respect my ANSI codes. But also, once I'm done writing and I move the cursor away,
the very next character has to retain the fact that it was red before you came in here and overwrote it. And so now it's like, you can't just say, all right, red goes from position zero to position 20. You have to say red is position zero through 20. Every single character is red such that if you bring the cursor and start overwriting it, doesn't matter because positions 17, 18, 19, and 20 are still red, even though you overwrote in the middle with black. It's like,
shoot, so now I have to keep track of every ANSI code for every single character? And unless I'm an idiot, the answer is yes. I don't know, man. I don't know.
Joe (12:32)
anybody listening to this would go, why would you do this? Why would you put yourself through this? Because that's
like a recipe for insanity. That's wild. And actually, I'm not even sure I knew that before you told me about that a couple of weeks ago. And it does make sense because all you're doing is just continually printing things and there is a sequence to it. It's just that visually it's doing something else. But...
Aaron Francis (12:41)
It is, it is.
Mm-hmm. Mm-hmm.
Joe (12:57)
So it does sort of make sense. is logic there, but it's not to our human brains. It's just a scramble. Like that's insane.
Aaron Francis (13:04)
it's insane.
Yeah. And so I was like, you know, when I, when I realized that when I, was, it was truly a Laravel prompts thing, cause it was the one moving the cursor around in a way that I hadn't experienced before. And that's when I started to think like, crap. I, I have to keep track of a buffer of characters and a buffer of like display, decoration. and I like,
Joe (13:16)
Mm-hmm.
Mm-hmm.
Aaron Francis (13:32)
I felt like I reached a new or a level of like oneness with the machine. And I felt like, what's his name? Jeff Daniel. Is that his name? Jeff Daniels. When he goes into Tron, he's like inside the computer and you can see how it's working. I don't know if that's his name. Somebody's going to make fun of me for that. But I felt like I was inside the computer. This is so bizarre. I was in Boise and
Joe (13:44)
huh. huh.
Aaron Francis (13:55)
know, hanging out with Steve all day, Steve and Kelsey all day. And then at night I'm in the hotel and it's like quiet and there are no children there. And I'm like, what if I just like worked on something? And so I'm laying in bed, I've got music playing and my eyes are closed as I'm trying to figure out how am I going to move a cursor and take information with it and like insert it without having to overwrite. It was just the weirdest thing. It was a delight. It was so fun.
Joe (14:04)
I could lock in right now.
Aaron Francis (14:23)
but it was one of those things where like you close your eyes for 30 minutes and then you open it and write it down and see if it works. And then I just haven't done that in forever. it felt awesome. So satisfying.
Joe (14:32)
And it felt great, right? Isn't that so satisfying? It just like poured out of you
and you're like, even if it's not 100%, we probably just got 85 % there and now I just need to tweak it a little bit, right? It just sort of like float. I love that. So this is one of those projects where it's like virtually 100 % learning, probably 110 % learning. There's no like...
Aaron Francis (14:40)
Yes. Yes. So fun.
Yes, dude, totally.
Joe (14:54)
There's no just like, I'm in a familiar space and I just know like the thing I want to do. You're just you're doing both things at the same time. You're you have an objective, but you have to learn everything to get there. But that being said, you did this all within, I want to say, three weeks. Is that the correct timeline? It's less. I mean, that's wild. That's not bad at all. And are you using this now on like a regular basis? Are you using your own package?
Aaron Francis (15:09)
Yeah, I think that's about right. Yeah. Not bad. Yeah.
I am, yeah.
Yeah.
Joe (15:19)
And
and you're you're happy with it or you're finding more things with it. Do you want to tweak her? How's that looking? But.
Aaron Francis (15:24)
Yes, both. Yes and.
His name is Jeff Bridges. I looked it up. So I had Jeff right. So yes. No, Jeff Daniels is dumb and dumber. know, six to one, half a dozen to the other. So I am using this package. Some of the things that I knew were going to be issues have become issues. I guess congrats to me. I knew there were going to be problems and I was right. The biggest...
Joe (15:30)
Now, yeah, I was like, Jeff Daniels. I don't know about that. Correct.
Okay.
You
Aaron Francis (15:48)
The biggest one that we found early on was stubborn processes or processes that the sub processes spawned, like many layers deep. And so then when you quit solo, it left a few of these lingering processes around, which is not great. Like if the whole point is like start a bunch of processes and when you stop, it stops a bunch. Well, you've only delivered half of the promise if you don't stop them all. So it was stuff like,
Joe (16:01)
Some zombie processes in there. Yeah. Yeah, that's tricky
Right.
Aaron Francis (16:15)
You know, was stuff like npm run dev actually spawns Vite and PHP artisan serve actually spawns a few other, you know, underlying processes depending on the platform you're on, which has been another big problem. And so being able to consistently kill all of the underlying processes was the first kind of like major hurdle that took us from like 0.1 to 0.2 after I fixed it.
Joe (16:25)
Yeah.
Aaron Francis (16:43)
And the solution there was to do a little bit, and this is again above my pay grade slash below the layer of the stack that I normally work on. I had to like do a little bit of command line foo to figure out, all right, based on my PID of Solo, give me all the PIDs that I spawned and all the PIDs that they spawned just forever and ever and ever. And so the solution there was,
Joe (17:04)
Yeah.
Aaron Francis (17:08)
continue to try to kill them gracefully from PHP, just like say, you know, the symphony process, just tell the symphony underlying symphony process to stop. Give it a few seconds and then just like, you know, basically bail out, kill yourself. Yeah, so then, then after the main solo process stops, there is now a basically a monitoring process. So when you start solo,
Joe (17:21)
Double check? Yeah.
Aaron Francis (17:33)
it starts the TUI process, but it also itself spawns an underlying process that is the monitoring process. And so it's running these two processes the whole time. One is like rendering the 2e and doing all the work and everything, and others just kind of hanging out and watching. And the one that's hanging out and watching, every like second or something like that, it says, all right, what are all the processes that have been spawned? Let me just hang on to those. Then,
Joe (17:49)
Right.
Aaron Francis (18:02)
when Solo, the main process dies, that monitor process kicks into gear and says, all right, the main tui, the thing that you've been looking at is dead, let's go see if anybody else is still alive and kill them all. And then after it does that, it then exits. And so that's just been totally foolproof. And so that's been incredibly helpful to have, because there are some weird pail Nuno's tailing package.
Joe (18:18)
Fascinating. Okay.
Mm-hmm.
Aaron Francis (18:27)
doesn't
respond to SIGTERM or something like that. And so it's like, it just doesn't die. And so this monitoring process, once Solo is dead, it assumes that anyone that's left over is pretty stubborn and it just brute forces them. And so that's now foolproof, which is quite nice.
Joe (18:31)
Excellent. Yeah.
That's a tricky one to solve, that's a clever, unshockingly clever solution. I was doing...
What was it for? my US talk. I was doing the FFmpeg stuff and I was spawning all of these child FFmpeg processes and I wasn't consistently cleaning them up and I would constantly PSAux, know, pipe grep, you know, what am I looking for? And it was just like so many on the screen. So I had to figure out how to effectively clean that up, but that was a much more contained situation. can't, are you allowing people to...
Aaron Francis (18:57)
Yeah.
Yeah.
I know.
Joe (19:15)
add their own processes into the mix. I know that was an idea at some point and, and it's, they can just say like, this command is running, pipe it into a new tab and it just works. Dude, you're a wizard. That's incredible. That's amazing.
Aaron Francis (19:18)
Yep.
Yep, exactly. Yep, exactly.
yeah, so I ship with, it ships with this app or solo service provider. And in the solo service provider, it's got like NPM run dev, tail F storage logs, and then maybe one more that's like commented out. Like PHP Artisan Serve, I think is commented out.
But I also, so those are all the ones, cause I use herd, I don't need phpartisan serve, but a lot of people do, so I just left it in there and commented out. Those are set up as like auto starting commands. So if you run solo and you've uncommented that one, it's gonna start three commands in three different tabs running those processes. There's also the notion of lazy commands, which would be like phpartisan cue work and phpartisan reverb start.
So you can add those as lazy commands, but they don't auto start. So this is a nice situation if you're like, I'm just working on the front end, I'm gonna run solo, I don't need a key worker, I don't need reverb, I don't need whatever else, but they're there such that if you're working on the backend, you can just hit the right arrow, go over to reverb and hit S and reverb will start. But it doesn't auto start, because that could be annoying and you might need it one tenth of the time or something.
And so we have those two affordances, lazy commands don't auto start, but they show up in your provider or like on your screen. And then regular commands just show up and start themselves. And it's just an array. Like you could just say, you know, Joe's command as the key and then any, literally any command that you wanna run as the value. And then that will be running in solo with the output.
piping in and showing you, and you can tab over to it and pause it and that sort of stuff.
Joe (21:09)
The notion of just saying run any command and I'll figure out how to properly output it feels stressful in my entire body. And I'm so impressed that you are doing it. That's amazing. You're like, I haven't slept in weeks. What is on the docket? Are you pushing this further? Are you saying this is kind of done? Where are you at with it?
Aaron Francis (21:18)
Yeah. Yeah, it's stressful. It's very stressful. Yeah. yeah, truly.
Yeah!
So the final thing that I have, I think there are two final things that both kind of got munged into one PR, because I was like manic. So the final things, the first would be custom hotkeys. So right now, you can do hotkeys to like scroll the logs up if you missed something, switch tabs, stop a process, restart a process, that sort of stuff.
Joe (21:46)
I was gonna ask you. Yeah.
Aaron Francis (21:56)
And somebody was like, I want to use Vim hotkeys. was like, that's awesome. What are you talking about? What is Vim hotkeys? I don't know what Vim hotkeys is. Dude. Yep. Vim hotkeys. I'm like, eh, sounds cool. What are, what does that mean? So instead of...
Joe (22:01)
If you put out a 2e, it's t-negative five seconds before somebody says, Vim hotkeys? And I'm like, Vim key bindings? Yeah.
Aaron Francis (22:13)
instead of doing the easy thing, is like, yeah, we can just, instead of arrow up, you can also add J or K or whatever it is. Yeah, we'll just make that an alias. No, no, no, no, of course not, of course not, of course not. We have to have an entire way of setting up your own custom hotkeys. And so we're close. I've got that fully working on a PR that allows you to basically register like a key provider. So you can have default keys.
Joe (22:21)
It's kind of alias things.
Aaron Francis (22:38)
or you can have Vim hotkeys or you could extend either one of those or some combination of those and have like Joe's favorite hotkeys or something. And a lot of what I'm trying to do in this is set it up such that teams can customize it for themselves without stomping over each other's setups. And so like you may want Vim, I certainly don't. How do we give that affordance in the package such that two developers working on the same repo
can have differences like that. And so it ends up being a lot of like, let's register a thing, let's register a class, whether that's a theme, dark and light modes, both supported, a theme or a hotkey provider or something, and let's register it under a key and then use your .env to pick which key you want. And so maybe everyone on the team has registered in their service provider
Joe's awesome hotkeys, but nobody else uses it because their .env is set to default mode or Vim keys or whatever. And so that's the way that I've kind of figured out to like, let's have 15 different options and each one, each person is to choose their own individually. So in that PR, we've got global custom global hotkeys, but also command specific hotkeys, which is very interesting because let's say you're on like,
So the logs command that I wrote, it tails the Laravel log file, but in the process of mucking about with the output that it received from tail storage logs, it collapses vendor frames. So you don't get 18 vendor frames going through the service container and you're like, God freaking dang it, I can't see what I'm working with here.
Joe (24:04)
Okay.
Yeah, yeah,
Aaron Francis (24:21)
And so it's like an enhanced log command. Now you can imagine that sometimes you might wanna see the vendor frames depending on the error that you're debugging, but you're kinda hosed unless this command has its own hotkeys. So when you like arrow over to logs, you see a new row of hotkeys and one of them is V and that says vendor frames and you can like, and it shows the vendor frames. You're like, I got it. It is in the container. Hit V again and the vendor frames go away.
Joe (24:43)
Yessss!
Aaron Francis (24:51)
So now commands themselves can register hotkeys. Cause you can also imagine on the logs, you might want to hit T to truncate, be like, I don't need to see all this crap. It's fine. Just truncate it. Let me start over. And so now commands can register hotkeys. So we have global and local hotkeys in that PR. And then there's one other thing, but I'll pause there.
Joe (25:00)
Yeah. Yeah.
So if you, so let's say I have my own command I'm running. I can register hotkeys that do custom behavior on that screen. How does that?
How am I tapping into the command? Am I just running other commands? Are you providing an interface to interact with that command? How is that happening in a generic way? I'm just curious.
Aaron Francis (25:28)
Mm-hmm.
Yeah, so in a generic way, the enhanced, let's say the enhanced log command, which is my wrapper around tail, that is a subclass of the command class that Solo ships with. in the command class, I've left a few basically like no op methods where it's like, hey, I've got all of the lines of the log.
Joe (25:42)
Great.
Aaron Francis (25:54)
Like everything that has been output, I have all of it right here. I'm gonna pass it through this method called modify output, and then I will put it out to the screen. Modify output doesn't do anything, it just returns what you gave it. Except in the enhanced log command, modify output walks the lines looking for vendor frames, modifies it, and then gives it back. And so there are a few affordances like that, that like the super or the parent just left blank for the sake of the children, but.
Joe (26:07)
It's...
Mm-hmm.
Aaron Francis (26:22)
You can also imagine like, let's say the truncate hotkey, which I haven't written yet, but you have access in the subclass, you have access to the command as it was written. So you can see like, what is the log file that they're working with? When I hit T, I can just do whatever I want inside of PHP and just say like, file, put contents, nothing, and put it into that file. That's totally fine. There is also a way,
Joe (26:44)
Right.
Aaron Francis (26:48)
because Symphony gives you, Symphony, so things I don't know, TTY and PTY. Don't know what those things are, but I do know that PTY is like pretend, or I think it's actually pseudo. Yeah, I like pretend a little better. But it's like a, it's a pretend TTY or a pseudo TTY. And this is the method by which you can give input to the underlying process from like the PHP slash Symphony layer, right?
Joe (26:55)
Mm-hmm.
Mm-hmm.
Aaron Francis (27:15)
And so interactive commands are coming, but you don't actually have to go interactive. You could have a hotkey that says, send this string, and the string could be like type A-A-R-O-N, enter. Send that string to the underlying process. And so you can imagine that if you have a certain command that pauses for yes or pauses for are you sure or something,
and you don't wanna go full interactive mode because you're like, I don't actually need it. You can just add hotkeys of Y and N and then in your PHP class, you can say when the person hits Y, just send through the input the character Y or send through the input the character N and then just like, we'll just keep going. And so those are kind of the three methods by which I think somebody would hook into local hotkeys.
That would be like modifying something, some affordance that the parent class has given you, run some just arbitrary command like file put contents or arbitrary PHP or send input into the underlying running process through symphony's PTY thing.
Joe (28:25)
here's what I like about all this. And I think it's maybe, correct me if I'm wrong, indicative of your personality. You were like, I'm not in control of this, so I'm taking control of all of this. I, no, nothing gets through the gates without my stamp of approval, like without running through my code base, which I love. And I've never, I've never really thought about approaching it that way, but it affords basically like unlimited flexibility. You've done a very...
Aaron Francis (28:40)
Yeah... Mm-hmm.
Mm-hmm.
Joe (28:50)
smart thing here that's a very cool dude
Aaron Francis (28:52)
Mm-hmm. Yeah, and I'm always
thinking about like, because I am like a working man developer, I spend a ton of time just like looking around in source code and being like, where can I my grubby little paws on this process? Or like, how can I like, yeah, Laravel does that. I wanna do something different. Like what is some weird way that I can like hook into what's about to happen to affect my will? And so when I'm...
Joe (29:07)
Yeah, yeah, yeah, yeah.
Mm-hmm.
Aaron Francis (29:19)
when I'm writing a library, I'm also thinking that I'm like, how do people want to hook into this? Well, how would I want to hook into it? A hundred different ways. And so let me count them. And so that's kind of like where all of that comes from.
Joe (29:24)
What are the hook points? Yeah, Yep, yep.
I saw, I was source having a little bit cause I was trying to figure out a better way to, to re-render on terminal resize. That was a little more effective. And I found that I think prompts itself is hooking into the symphony class, but it's accessing a protected method through reflection and firing. I was like, yeah, that's what I'm talking about. Good. And by any means necessary, get in there, you know, it was great.
Aaron Francis (29:39)
Mm-hmm
Mm-hmm. Mm-hmm. Mm-hmm. Yep. Yep. I've got
the solution for that, by the way. I can show you after the code that does that.
Joe (29:59)
I
have one too, I'm curious what you came up with, but mine's is gnarly, mine's pretty clean, mine's pretty chill, maybe, yeah, let's compare notes, I would love to see that. Okay.
Aaron Francis (30:02)
It's gnarly. Mine is.
Mine's not a chill guy. I think it's,
what is it, the sig winch command or sig winch signal? And then I basically, I barely remember, but I basically have to do get nv and put nv to rewrite the amount of calls that, like I basically create a new terminal, measure it, or I clear out the get nv and put nv so that it's not cached, get a new terminal, measure it, and then like rerender and then put it back just in case.
Joe (30:14)
It's Sigwin's share. Yeah.
Yes.
Aaron Francis (30:36)
So it feels a little bit wonky to me.
Joe (30:38)
I did the same thing, except that you can just do after you clear out those, first of all, those ENVs were really messed with me for a while. Cause I didn't realize that those were being put somewhere. And I didn't realize it was like a cash situation because I was like, why is this not happening? I know what's happening. I spent a lot of time logging and dumping on that one. But, yeah, if you, if you clear out the ENV and then you just say this in it dimensions again, you just get it. It's just fresh. just like, it's a, it's a, like a four liner or something like that.
Aaron Francis (30:48)
Yes. Yup. I know. I know.
Mm-hmm.
Yeah, I think that's close to what I do. may steal. But yeah, and the thing I remember one time I was showing you interactive commands, which we can talk about in a second, and I resized, and that was, it was the sig winch along with the whatever it is, like stream choose or whatever. Those things, yeah, stream select, those things working together, the sig winch killed the stream select, and that was when the error blew up. And so I have to have like a.
Joe (31:06)
Yeah, I think that sounds about like what you did.
Yeah.
Stream selects, yeah.
Aaron Francis (31:31)
I have to have like a process that's like, if you're in interactive mode and you're resizing, we're gonna need to suppress this error, eat the error and redo it. Yeah, exactly. Friggin' nightmare. So, yeah.
Joe (31:39)
Keep all the balls in the air. Keep all the balls in the air. Yeah. my God. Yeah, you
cracked the interactive thing, which was something I was trying to do for a while. And you went to completely, because you were now in control of all of these processes and all of these output, you could say, I say what goes on this terminal. You don't get to change things. And so.
Aaron Francis (31:47)
Cracked it.
Mm-hmm.
Exactly.
Joe (32:00)
Yeah, you you cracked something that I had been trying to crack for a while in a in an elegant way. I cracked it in a very inelegant way and it was very gross. yeah, so that was that was basically a result of you basically creating your own pseudo terminal, your own emulator and.
Aaron Francis (32:03)
Mm-hmm.
Jury's out, but yeah.
Mm-hmm.
Joe (32:17)
and controlling the situation, right? So as soon as you did that, anything goes underneath, right? And you're just sending. So basically just as a background, like if you have basically one tab that allows you to run like, know, make model or make controller or like all of the artisan make commands and you can interact with them.
Aaron Francis (32:22)
Yeah. Yeah.
Mm-hmm. Mm-hmm.
Joe (32:35)
in that tab without messing with the outside process and having it all in line and clean. And that is that may not sound hard, but it's very difficult and it's very difficult. And so was was this controlling in this sort of emulator thing born out of that or this was already there and you said, now that I have that, can do this.
Aaron Francis (32:45)
Turns out it's very difficult.
No, the emulator came as a necessity to handle Laravel prompts, which came from the interactive part. I watched Josh Siri, friend of show, of ours, Josh Siri did a good YouTube video as he has wanted to do on Solo. And the first thing I think that he tried was like, wanna run, I wanna have my make command constantly running so I can just hop over and make new stuff. And it didn't work. I was like,
Joe (33:02)
Yeah. Okay.
Aaron Francis (33:24)
shoot, it's not interactive. What the hell? And so I watched that and I was like, yeah, shoot, interactive is a good idea. And so that's when I like went down the deep dark rabbit hole of TTY PTY, figured out that symphony has this input thing. So I'm like deep in the bowels of the computer and got it working, right? So I get it working and I'm like, I'm going to hit the I key to go interactive and then I'm going to start typing.
Joe (33:25)
UGH!
Hmm.
Aaron Francis (33:51)
And so I hit the I key and I was like, it's working. And I started typing and my screen just got shorter and shorter and shorter and shorter because Jess was like moving five up, clearing, moving five up, clearing. And I'm like, no, this is bad. And so that's when the interactive mode necessitated writing a screen emulator such that I could calculate what is the final representable state and then feed that out to solo.
Joe (33:58)
Uh-huh. Uh-huh.
Aaron Francis (34:17)
And so that was the part that led me down the whole like, have to write these buffers to keep track of it all so that I could, you know, tab over, hit I and start typing and everything just looks exactly as it should. And the kind of like, we're going like six levels deep because that command that I run in the make tab, may know that you can type phprs and make colon model test factory enum.
Joe (34:43)
Mm-hmm.
Aaron Francis (34:45)
you can't type phpartisan make and get a Laravel prompt that says what do want to make. Unless you're using solo, because now you can. Because what I do is...
Joe (34:55)
For the low, low price
of $99.99 a month, you can get a make-a-man.
Aaron Francis (34:58)
Yes, exactly. It's available
for free on GitHub, but you can Venmo me 99.99 a month and I will accept it. what you can do, so what I did was I wrote just a honest to goodness Laravel command. And then that's the command that I like register with solo. So that's kind of a nice thing. It's like, I want some advanced functionality. I'll just write it as a traditional Laravel command and then I'll run that in solo. But this,
Joe (35:04)
That's right. That's right.
Mm-hmm.
pipe it in.
Aaron Francis (35:28)
solo colon make command, what it does, so we're going like literally three levels deep. So the solo make command under the hood, when you run solo make, it runs PHP artisan make. And it gets back a list of did you mean, and it's like, well, actually, I don't know what I meant, but I'm gonna parse all of that out, take all of those, put those into a Laravel type of head search select, yes, autocomplete or whatever.
Joe (35:54)
Select. Yeah.
Aaron Francis (35:57)
prompt and so I can run solo make and it says what do want to make and I can type and model and migration and like all that stuff shows up and so I type model then what happens is I now have to spawn the proper command so like I'm in I'm in solo make I've run the underlying artisan process gathered up all of the options put those into a prompt that is inside of my control now the user says I want to make a model
Joe (36:22)
Mm-hmm.
Aaron Francis (36:26)
And I, as solo, am like, not my responsibility. But what I can do is I can run phpartisan make model under the hood, and then I need to gather all of the output from that underlying process and proxy all of the input to that underlying process. And so within my solo make command, I am now running a sub-process of phpartisan make model, and I'm
Joe (36:29)
Yeah.
Aaron Francis (36:52)
I'm whenever I type into solo make, it receives that input and forwards it to the underlying process, which is make model. And so now I'm typing inside the sub process and it's doing all of its stuff. And then finally, when that sub process completes, just, that sub process dies, it exits and it comes back up to the solo control and says, all right, what do want to make next? And so it just runs in an infinite loop such that you can leave it open in the solo tab.
Joe (37:20)
Right.
Aaron Francis (37:22)
when you run this inside of Solo, the stupid thing that is happening is you're running PHP Artisan Solo. Inside of Solo, you're running PHP Artisan Solo Make. Inside of Solo Make, you're running PHP Artisan Make Model. And you're proxying, truly, you're proxying the input from Solo to Solo Make to Make Model. So you're three levels deep. Yes, yes! And so...
Joe (37:34)
You
Yeah.
from the attic to the basement. Yeah, yeah.
Aaron Francis (37:48)
And then all that output has to make its way all the way back out to the top and then run through my screen emulator and then be put to the screen. But now you can just leave a make tab open all the time. You can enter, it's great, you can enter interactive mode, do all your stuff and then exit interactive mode without killing the process. And so the nice thing about that is you can exit interactive mode mid command and be like F, what was the name of that thing? Hop over to PHP Storm.
Be like, that's right, okay, we use plural or we call it this way. Hop back to solo, hit interactive again and pick up right where you left off inside that command, which is two levels deep and continue typing. And then all of your enters and your control Cs, all of that is proxied all the way down. So you can like leave it and pause and come back later. You can actually kill it and it'll restart. You can let it run and loop and it just sits there forever.
It's great, it's great. And I did all of that accidentally on one PR that was supposed to be just about hotkeys, but I kind of munched it. Munched it all together and I've apologized to the people, but it's like, you know, hundreds of changes. no, so that is open in PR number four and it's just a matter of being brave enough to merge it. I know, I know.
Joe (38:45)
Yes!
Is that out yet? Is that something upcoming or is that out yet?
people are gonna flip their lids.
I feel like, do you know that GIF of John C. Reilly where he's like, like just confused consistently? it's like, John C. Reilly? Step brothers? Will Ferrell and John C. Reilly?
Aaron Francis (39:05)
I don't know who John C Reilly is. Is that Jeff Daniels friend?
yeah yeah yeah yeah yeah I know who that is. Yeah. huh. Yep.
Joe (39:12)
Yeah, he's got this like weird like little fro and he's just like confusing it's smash panning in.
That's what I felt like as that was like happening as you were laying all that information at me. was that I I if there was a crown to give, it's yours. I don't I don't it ain't me anymore. I'll tell you that much. Wow, dude. I could talk to you all day, but I also know we have things to do and we're coming up on a holiday. So I'm going to.
Aaron Francis (39:21)
I know.
Yeah.
Hehehehehe
Joe (39:37)
Unfortunately, wrap this up. But if you ever want to come and talk about a frivolous side project again, you are always welcome to. I think you could get more frivolous than this. think we this this is actually a side project with.
Aaron Francis (39:42)
Mmm. I don't know if it can get more frivolous than this, but I would love to come back at some point.
Okay, I will see.
Joe (39:53)
tangible output. mean, this is really genuinely helpful. I think it's, it's not actually, I don't think it's frivolous at all. think it's over-engineered in a wonderful way, but I think the end product is genuinely useful. mean, I, I think I told you this, but like when Taylor popped that into Slack the first time, the composer run dev thing.
Aaron Francis (39:56)
I hope so, yeah.
Yes.
Mm-hmm.
Joe (40:12)
I was like, this is awesome. I hate this output. I am not a fan. And I was like, I was like, is this why I was hired? I maybe and then, and I was like, I don't have the time. have so many things to do. And then you come along and you're like, I got you. No problem solved. Problem solved.
Aaron Francis (40:15)
You got hives. You're like, I could do it better.
Mm-hmm.
Mm-hmm. Yeah, some people have asked like,
why isn't this merged into core? And I was like, guys, do you know how stupid this is? Like, do you understand all of this garbage that I'm having to do? I don't want to put, I don't want to foist that upon the shoulders of a different set of maintainers. So if, you know, if you ever get the inkling, you can lobby, but I don't want to give this to y'all.
Joe (40:45)
It is...
Yeah, it's
as a concept and as a construct, which you've created is very fascinating. And I think I've seen some of the code that looks very clean and I think it's like pretty maintainable and you've abstracted it away enough where I feel like you could apply this to other things in other ways. So it is fascinating. I, you know, I've thought about it. I'm not, I'm not, I'm, this is a zero promises. I I'm not even authorized to do this, but I think it's.
Aaron Francis (41:07)
Mm-hmm.
Yeah, don't worry.
Joe (41:19)
I think what you've done here is beyond fascinating and incredibly smart. And congrats. Congrats on being so smart. All right. So Solo is the project. That's up on GitHub. And then where can people find you or where should people find you? Where would you like people to find you?
Aaron Francis (41:24)
Thanks. Thanks. Thanks.
Solos the project. GitHub.
Well, I'm still in the good or bad place depending on your point of view. So you can find me on Twitter.com, X is a stupid name, Twitter.com, Aaron D. Francis. I will dip my toes into the bluer skies after Thanksgiving perhaps. Or you can just go to AaronFrancis.com. Everything should be there.
Joe (41:55)
That's where I've been pointing people to just the site. I'm like, I don't know exactly where you're going to find me right now. Just go to the site and you'll figure it out from there. It's pretty obvious.
Aaron Francis (41:58)
Mm-hmm. Exactly. That's right. Own your stuff,
people. Regardless of where you hang out, you gotta own your stuff.
Joe (42:08)
All right, so AaronFrancis.com is the, no day, correct. That's where people should go as a statement of record. Okay, amazing. Thank you so much, Aaron. I really appreciate it. And enjoy the rest of the day.
Aaron Francis (42:10)
AaronFrancis.com, no D, yeah. No D, yeah.
Yes.
Yeah, thanks for having me. All right, you too,
ya.