Side Project Podcast · Dec 23, 2024
Aaron Francis hops on to go full galaxy brain on his interactive TUI Solo.
00:00 - Introduction to Side Projects and Aaron Francis 01:05 - Exploring the Laravel Package: Solo 03:58 - The Development Journey of Solo 07:38 - Understanding ANSI Codes and Terminal Manipulation 11:36 - The Challenges of Process Management 15:14 - Enhancements and Future Directions for Solo 22:26 - Customizing Development Environments 25:14 - Enhanced Logging and Command Hotkeys 28:30 - Flexibility in Command Customization 32:21 - Interactive Command Handling 37:26 - Deep Integration of Commands 41:24 - Project Overview and Future Directions
All things Aaron: https://aaronfrancis.com https://github.com/aarondfrancis/solo https://tryhardstudios.com https://x.com/aarondfrancis https://bsky.app/profile/aaronfrancis.com
https://sideprojectpodcast.com https://bsky.app/profile/sideprojectpodcast.com
Joe Tannenbaum
Welcome to Side Projects. I am your host, Joe Tannenbaum. And with me today, we have a man of the Internet, Aaron Francis, cofounder of TryHard Studios. How you doing today, Aaron?
Aaron Francis
Good. I am honored to be your guest on side projects today.
Joe Tannenbaum
I don't know if honor is the right word, but I'll take it. I will I will accept that. Yeah. I get to decide.
Aaron Francis
I I am honored. So that it is That is the right word.
Joe Tannenbaum
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 I'm a huge fan,
Aaron Francis
you get a little bit of shame.
Joe Tannenbaum
There's a little side project shame on that, and we don't have any of that here. You That's good. You can walk 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.
Aaron Francis
Thank you.
Joe Tannenbaum
And you seem to have a lot of those going on lately. And so I'm Yeah. I'm curious which one of the thousand you'd like to talk about today.
Aaron Francis
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
Joe Tannenbaum
the world?
Aaron Francis
I gotta do stuff.
Joe Tannenbaum
Joyless. Joyless. So, yeah,
Aaron Francis
I appreciate having, a yes man on the show today. I kinda wanna talk about I kinda wanna talk about solo. So we got a lot a lot of projects going on, 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 Tannenbaum
Let's talk about it. So this correct me if I'm wrong. This came from that new composer run dev.
Aaron Francis
Is that
Joe Tannenbaum
what it was? Composer run dev?
Aaron Francis
Mhmm.
Joe Tannenbaum
Yep. 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.
Aaron Francis
So I was creating a new Laravel project for, this, like, game show that I was on, Jason Lingsdorf's channel. Super fun.
Joe Tannenbaum
Why won't you say
Aaron Francis
he didn't get a great
Joe Tannenbaum
job with that?
Aaron Francis
It 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 n p x concurrently. So n p x concurrently, they set it up to run, you know, npm run dev, PHP artisan serve, PHP q work, and maybe pale or tail the logs or something. And so I ran that and I was like, oh, this is kinda nice. You know, first time experiencing like the combined command to run the whole thing.
Kinda nice. However, as I was like developing the application, I needed to add reverb. And I was like, so now I gotta go muck around with, you know, n p x concurrently and the escape like, all the escaping and adding of the new colors. And I was like, oh, that kinda 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 Laracasts course, which is very good. I highly recommend it.
Joe Tannenbaum
Thank you.
Aaron Francis
And so I'm I'm laying in bed. It's, like, 10:30, and I got the phone sideways, and I'm just watching you just type stuff. Like, man, this is really good. I could do this. This.
And so the next day, I came in and was like, I'm gonna I'm gonna make, a better version of this composer run dev, and I'm gonna do it as, like, a a CLI tool, as a as a TUI. And as things do, it just kinda spiraled from there. It just kinda you know, it was like, oh, 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 kinda awesome.
Joe Tannenbaum
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, oh, 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 do.
Aaron Francis
That you shouldn't do that. Yeah. Same.
Joe Tannenbaum
And I also wanna, 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 2e slash CLI app. And then you went, I am building a full screen interactive multi process 2e. 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 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 gotta figure out what you're looking for before you even get there. So, like Yes. Can you I do you have any of those identified, like, what the stumbling blocks were in the beginning process?
Aaron Francis
Where were the stumbling blocks? Everywhere. All over the place. Everything was a stumbling block.
Joe Tannenbaum
It's all stumbling blocks.
Aaron Francis
It's all stumbling blocks all the way down. It so I I I think of myself as a very, like, working class developer. And so when people talk about, Tmux, I'm like, I don't I don't know what that means. Sounds awesome for you. That sounds great.
But I don't know what that is. They're like, oh, I gotta, you know, set up my tree sitter and my LSP and Neovim. I'm like, is that PHPStorm? Because that's what I use, and PHPStorm rules.
Joe Tannenbaum
Yeah.
Aaron Francis
And so once you, like, once you start mucking around with accidentally inventing tmux, there's a whole layer down there that I'm just not terribly familiar with. I mean, so we can talk about we can talk about a terminal and a shell. 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
Joe Tannenbaum
start every two weeks when I have to relook it up. That's that's when I know it. Okay. And then it the the information just disappears from my head. I I know shockingly little about what I'm actually doing.
I'm really just doing what you're doing stumbling along.
Aaron Francis
Correct. Yes. I I am I am like a plumber that doesn't know theory and only knows practice. It's like, well, if 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 so, you know, you start mucking around with, like, dealing with the output of other processes. And you're in the wild, wild west because it's like, they could do anything.
And you don't 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, you know, ANSI codes have been around for one hundred years. And therefore, ChetGPT is very well versed in, like, what ANSI codes are, what they do, like, how to work with them. And so I spent 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?
Because I I can't figure that out. Yeah. It's like, oh, you have a ANSI code that never got reset. Okay.
Joe Tannenbaum
Yeah.
Aaron Francis
Tell me more about ANSI codes. What does that mean? And so, you know, it kinda the guy that created SQLite has said in 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. 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 still is awesome.
Joe Tannenbaum
You're describing my career.
Aaron Francis
Yes. Exactly. And it 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 a screen emulator, basically.
Because one of the problems is, like, 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 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, 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 alright. 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 the
Joe Tannenbaum
stuff that's an enormous task. That's like a that's like Dude. You're that's like parsing level 1,000. Because, like, when you look at ANSI codes, it's not like HTML. Like HTML, you can kinda suss out.
You can say, like, I see tags here or whatever. ANSI amidst content is a jumbled nonhuman parsable mess. You've gotta, like, really go through it. And the thing you just talked about, that mental leap of saying, like, I just printed these these this sequence to the terminal, and it actually is not about formatting. It's gonna move the cursor around and erase things.
Aaron Francis
The cursor.
Joe Tannenbaum
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 like it's controlling Yes. The terminal. And so that is a mental hurdle that you have to sort of, like, get over and and figure out and, like, grok in order to move forward. Right?
That's a huge one in my because In my it was just formatting. You're like, okay. I just need to, like, sort of parse out before I but there's actions here. You know?
Aaron Francis
In my in my adorable naivety, I thought ANSI codes are for for colors. And, like, it's just it makes everything pretty. And then you realize it's like, no. This is 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, if I'm if I am in control of a process and I'm just randomly or or rather, faithfully printing out the code or the output from the underlying process, it's gonna muck about with my process.
Like, it's gonna muck about with what I'm trying to show on the screen. So I'm drawing this, you know, beautiful chrome around this little pane of, like, output. But this output in this little pane is now reaching outside and clearing the screen. It's like, god freaking dang it. So now I run it all I run it all through a buffer, but then you get into the situation where it's like, alright.
Well, how do you keep track of content and ANSI kinda separately but kinda 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 then turn it off. And then the insidious part is, let's say you've got a string of red text. Right? And your cursors at the end and then somebody decides, alright. Let's turn the text back to black for the next few characters.
Right? String of 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 the very next character has to retain the fact that it was read before you came in here and overwrote it. And so now, it's like you can't just say, alright, 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 seventeen, eighteen, 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.
Joe Tannenbaum
And anybody listening to this would go answers to this. Would you do this? Why would you put yourself through this? Because that's that's like a recipe for insanity. That's It is.
Wild. And, actually, I'm not even sure I knew that before you told me about that a couple weeks ago. And it does make sense because all you're doing is just continually printing things, and and there is a sequence to it. It's just that, visually, it's doing something else. But
Aaron Francis
Mhmm. So it
Joe Tannenbaum
does it does sort of make sense. There is there is logic there, but it's not to our human brains, it's just a scramble. Like, that's It's insane.
Aaron Francis
Yeah. And so I was like you know, when I when I realized that, when I it was it was truly, a Laravel prompts thing because it was the one moving the cursor around
Joe Tannenbaum
Mhmm.
Aaron Francis
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. Mhmm. And I like I felt I felt like I reached a new or a, a level of, like, oneness with the machine.
And I felt like what's his name? Jeff Daniels? 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 gonna make fun of me for that. But I felt like I was inside the computer. This is so bizarre. I was in Boise and, you 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 I'm 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 override. It's it was just the weirdest thing.
It was a delight. It was so fun. But it was one of those things where, like, you close your eyes for thirty 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.
Joe Tannenbaum
And it felt great. Right? Isn't that isn't that so satisfying? It just, like, poured out of you, and you're, like, even if it's not a %, we probably just got 85% there. And now we just need to tweak it a little bit.
Right? It just sort of, like, flowed. I love that.
Aaron Francis
So this is fun.
Joe Tannenbaum
This is one of those projects where it's, like, virtually 100% learning, probably a 10% learning. Yes. Dude.
Aaron Francis
Totally.
Joe Tannenbaum
There's no just, like, I'm in a familiar space, and I just know, like, the thing I wanna 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 wanna say, three weeks. Is that the correct timeline?
It's some
Aaron Francis
Yeah. I think that's about right. I mean,
Joe Tannenbaum
that's wild. Bad. Yeah. That's not bad at all. And are you using this now on a on, like, a regular basis?
Are you using
Aaron Francis
your own package?
Joe Tannenbaum
Yeah. And and you're you're happy with it or you're finding more things with it? Do you wanna tweak or how's that looking?
Aaron Francis
Yes. Both. Both? Yes. Yes.
And. His name is Jeff Bridges. I looked it up. So I had Jeff right. Oh.
So I was like, Jeff Daniels?
Joe Tannenbaum
I don't know what that No.
Aaron Francis
Jeff Daniels is dumb and dumber. So, you 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 Okay.
Congrats to me. I knew there were going to be problems, and I was right. The biggest 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
Joe Tannenbaum
around. Processes. Yeah.
Aaron Francis
Which is not quite. 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 Right. It was stuff like, you know, it was stuff like npm run dev actually spawns Vite, and PHP artisan serve 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 kinda like major hurdle that took us from like 0.1 to 0.2 after I fixed it. 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 food to figure out, alright. 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 the solution there was 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. Don't care. Yeah.
So then then after the main solo process stops, there is now a basically a monitoring process. So when you start solo, it starts the 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.
Joe Tannenbaum
And the
Aaron Francis
other is just kinda hanging out and watching. And the one that's hanging out and watching every, like, second or something like that, it says, alright. What are all the processes that have been spawned? Let me just hang on to those. Then when solo, the main process dies, that monitor process kicks into gear and says, alright.
The main Tooie, 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 has been that's just been totally foolproof. And so that's been incredibly helpful to have because there are there are some weird, like, pale, Nuno's tailing package.
Mhmm. Doesn't respond to sigterm or something like that. And so it's like, it just doesn't 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, like, brute forces them. And so that that's now foolproof, which is which is quite nice.
Joe Tannenbaum
That's a that's a tricky one. That's a tricky one. That's a tricky one to solve, but that's that's a clever unshockingly a clever solution. I was doing what was it for? My US talk.
I was doing the FFmpeg stuff. Yeah. I was spawning all of these child FFmpeg processes, and I wasn't consistently cleaning them up. And I I would constantly PSOX, you 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. I can't are you allowing people to add their own processes into the mix? I know that was an idea at some point. And Yeah. And it's and they can just say, like, this command is running, pipe it into a new tab, and it just works.
Exactly. And you're a wizard. That's
Aaron Francis
incredible. Exactly. That's amazing. Yeah. So I ship with you know, it ships with this, like, app or 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, has commented out. But I also so those are all the ones because I use herd. I don't need php artisan serve, but a lot of people do. So I just left it in there, 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 PHP artisan queue work and PHP artisan reverb start. So you can add those as lazy commands but they don't auto start. So So this is a nice, 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, you know, whatever else. But they're there such that if, you know, you're working on the back end, 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, you know, 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 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 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 output piping in and showing you. And you can tab over to it and pause it and that sort of stuff.
Joe Tannenbaum
The notion of just saying run any command and off 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 that's amazing.
Aaron Francis
It's stressful. It's very stressful.
Joe Tannenbaum
You're like, I haven't slept in in weeks. Truly. What is what is on the docket? Are you pushing this further? Are you saying this is kinda done?
Where are you at with it?
Aaron Francis
So the final thing that I have I think there are two final things that both kinda got munched into one PR because I was, like, manic. So the final things, the first would be custom, hot keys. So right now
Joe Tannenbaum
I was gonna
Aaron Francis
ask you. Yeah. You can do hot keys to, like, scroll the logs up if you miss something, switch tabs, stop a process, restart a process, that sort of stuff. Then somebody was, like, I wanna use VIM hot keys. I was, like, that's awesome.
What are you talking about? What is VIM hot keys? I don't know what VIM hot keys is.
Joe Tannenbaum
You put out a TUI. It's t minus negative five seconds before somebody says VIM hot keys question mark. VIM
Aaron Francis
hot keys. I'm, like, that's cool. What are what does that mean? So instead of instead of doing the easy thing, which 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. Of course not. We have to have an entire way of setting up your own custom hot keys.
And so we're close. Now 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, or you can have VIM hot keys or you could extend either one of those or some combination of those and have like Joe's favorite hot keys 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 them.
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 hot key provider or something.
And let's register it under a key and then use your, dot 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 dot 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 gets to choose their own individually. So in that PR, we've got global custom global hot keys, but also command specific hot keys, which is very interesting. Because let's say you're on, like so the the logs command that I wrote, it, like, tails the Laravel log file.
But in the process of of, like, mucking about with the output that it received from tail storage logs, it collapses vendor frames so you don't get, you know, 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. 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 host unless this command has its own hot key.
So when you, like, arrow over to logs, you see a new row of hot keys and one of them is v and that says vendor frames.
Joe Tannenbaum
Yeah. Yes.
Aaron Francis
And it shows the vendor frames. You're like, ah, I got it. It is in the container. Hit v again, and the vendor frames go away. So now commands themselves can register hot keys.
Because you can also imagine on the logs, you might wanna hit t to truncate. Be like, I've done I don't need to see all this crap. It's fine. Yeah. Just truncate it.
Let me start over. And so now commands can register hot keys. So we have global and local hot keys in that PR and then there's one other thing, but I'll pause there.
Joe Tannenbaum
So if you so let's say I have my own command I'm running. I can register hot keys that do custom behavior on that screen. How does that how am I tapping into the command? Am I just running other commands? Am I are you providing an interface to interact with that command?
Like, how how is that happening to in a generic way? I'm just curious.
Aaron Francis
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 Right. Solo ships with. And in 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, I've got all of 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, the like, the super or the or the parent just left blank for the sake of the children.
But you can also imagine, like, let's say, the truncate hot key, 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 because Symfony gives you Symfony 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 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 Symfony layer. Right? And so interactive commands are coming, but you don't actually have to go interactive. You could have a hot key 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 hot keys 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 we'll just keep going.
And so those are kind of the three methods by which I think somebody would hook into local hot keys. 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 Symfony's PTY thing.
Joe Tannenbaum
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. Like, no. Nothing gets through the gates without my stamp of approval, like, without 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, very smart thing here. That's very cool.
Aaron Francis
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 be like, where can I get my grab your little pause on this post? Yeah.
Joe Tannenbaum
Yeah. Yeah.
Aaron Francis
How 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 when I'm writing a library, I'm also thinking that.
I'm, like, how do people wanna hook into this? Well What
Joe Tannenbaum
are the hook how I wanna
Aaron Francis
hook into it? Yep. A hundred different ways, and so let me count them. And so that's kind of, like, where all of that comes from.
Joe Tannenbaum
I saw I I was sore siding a little bit because I was trying to figure out a better way to to to rerender 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.
Aaron Francis
And I
Joe Tannenbaum
was like, yeah. That's what I'm talking about. Yep. And by any means necessary, get in there. You know?
It was great.
Aaron Francis
I got the solution for that, by the way. I can I can show you after the code that does that?
Joe Tannenbaum
I have one too. I'm I'm curious what you came up with, but mine
Aaron Francis
It's gnarly.
Joe Tannenbaum
It's gnarly? Mine's pretty clean. Mine's pretty chill. So maybe yeah. I was comparing that.
Aaron Francis
So Mine's not a chill guy. Mine
Joe Tannenbaum
Okay.
Aaron Francis
I think it's what is what is it? The sig winch command or sig winch signal?
Joe Tannenbaum
Yeah.
Aaron Francis
And then I basically I barely remember, but I basically have to do get env and put env to rewrite the amount of calls that like, I I basically create a new terminal, measure it, or I clear out the get in for put in so that it's not cached, get a new terminal, measure it, and then, like, rerender, and then put it back just in case. So it feels a little bit wonky to me.
Joe Tannenbaum
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 because I didn't realize that those were being put somewhere, and I I didn't realize it was like a cash situation. Yep. I was like, why is this not happening? I know it's happening.
Aaron Francis
I know.
Joe Tannenbaum
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 save this in it dimensions again, you just get it. It's just fresh. It's just like it's a fits a, like, a four liner or something like that.
Aaron Francis
Yeah. I think I think that's close to what I do. Yeah. I think that sounds
Joe Tannenbaum
like what you did.
Aaron Francis
But yeah. And the thing I remember one time I was showing you interactive commands, which show which we can talk about in a second, and I resized. And that was it was the sequinched along with the whatever it is, like stream choose or whatever. Stream selects. Yeah.
Joe Tannenbaum
Stream select. Yeah.
Aaron Francis
Those things working together, the sequence killed the stream select, and that was when the error blew up. And so I have to have, like, a I have to have, like, a process that's like, hey. If if you're in interactive mode and you're resizing, we're gonna need to suppress this error or eat the error. Keep all the balls there.
Joe Tannenbaum
Keep all the balls there.
Aaron Francis
Freaking nightmare. My god. Yeah.
Joe Tannenbaum
Yeah. You cracked the interactive thing, which was something I was trying to do for a while, and you went in control of all of these processes and all of these output, you could say, I I say what goes on this terminal.
Aaron Francis
Exactly.
Joe Tannenbaum
You don't get to change things. And so, 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.
Aaron Francis
Jury's out. But yeah.
Joe Tannenbaum
Yeah. So that was that was basically a result of you basically creating your own pseudo terminal, your own emulator, and and controlling the situation. Right? So as as soon as you did that, anything goes underneath. Right?
Aaron Francis
Yeah.
Joe Tannenbaum
And you're just sending so, basically, just as a background, like, if you you have basically one tab that allows you to run, like, you know, make model or or make controller or, like, all of the artisan make commands, and you can interact with them 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
Aaron Francis
Turns out it's very difficult.
Joe Tannenbaum
Very difficult. And so was was this controlling in this sort of a emulator thing born out of that? Or this was already there and you said, oh, now that I have that, I can do this?
Aaron Francis
No. The emulator came as a necessity to handle Laravel prompts, which Yeah. Came from the interactive part.
Joe Tannenbaum
So Okay.
Aaron Francis
I watched, Josh Siri, friend of friend of the show, friend of ours. Josh Siri did a a good, YouTube video as he is want to do on solo. And the first thing I think that he tried was, like, I 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, oh, shoot.
It's not interactive. Alright. What the hell? And so I watched that and I was like, yeah. Shoot.
Interactive's 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 gonna hit the I key to go interactive, and then I'm gonna start typing.
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, oh, 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. And so that was the part that led me down the whole like I have to write I 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 commands that I run-in the make tab, you may you may know that you can type PHP artisan make colon model test factory enum. You can't type PHP artisan make and get a Laravel prompt that says, what do you want to make? Unless you're using solo because now you can. Because what what I do
Joe Tannenbaum
is price of $99.99 a month.
Aaron Francis
You can get a make Exactly. It's available for free on GitHub, but you can Venmo me $99.99 a month, and I will accept it.
Joe Tannenbaum
That's right.
Aaron Francis
What you can do so what I did was I wrote just a honest to goodness Laravel, like, 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 tip traditional Laravel command, and then I'll, you know, run that in solo.
But this
Joe Tannenbaum
Type it in.
Aaron Francis
This this 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, 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 like So what? Ahead search select. Yes.
Joe Tannenbaum
Yeah.
Aaron Francis
Auto complete or whatever prompt. And so I can run solo make and it says, what do you want to make? And I can type in 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.
Joe Tannenbaum
Mhmm.
Aaron Francis
Now the user says, I wanna make a model. And I, as solo, am, like, not my responsibility. But what I can do is I can run PHP artisan 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 subprocess of PHP artisan make model. And I'm 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 subprocess, and it's doing all of its stuff. And then finally, when that sub process completes, it just that sub process dies and exits and it comes back up to the solo control and says, alright. What do you wanna make next? And so it just runs in an infinite loop such that you can leave it open in the solo tabs. 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
Joe Tannenbaum
from the activity
Aaron Francis
deep. Yes. Yeah. Yes. And so 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. 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 PHPStorm. Be like, like, that's right. Okay. We use plural or we you you know, 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 c's, all that is proxied all the way down. 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 then 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 hot keys, but I kinda munched it Munched it all together, and I've apologized to people, but it's, like, you know, hundreds of changes.
Joe Tannenbaum
Is that out yet? Is that something upcoming, or is that out yet?
Aaron Francis
No. So that is open in PR number four, and it's just a matter of being brave enough to merge it.
Joe Tannenbaum
People are gonna
Aaron Francis
flip their heads. I know.
Joe Tannenbaum
I feel like do you know that GIF of John c Riley where he's, like, like, just confused, consistent with John
Aaron Francis
c Riley?
Joe Tannenbaum
John c Riley? Stepbrothers? Will Will Carroll and John c Riley?
Aaron Francis
Oh, yeah. Yeah. Yeah. Yeah. Yeah.
Yeah. He's got
Joe Tannenbaum
this, like, weird, like, little throw, and he's just, like, confused and it's smash panning in. Yep. That's what I felt like as that was, like, happening as you were laying all that information at me. It was Yeah. 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 unfortunately wrap this up.
But if you ever wanna come and talk about a frivolous side project again, you're always welcome to.
Aaron Francis
I don't know if it can get more frivolous than this, but I would love to come back at some point.
Joe Tannenbaum
I think you could get more frivolous than this. I think we could. This is actually a side project with tangible output. I mean, this is really genuinely helpful. I think it's it's not I
Aaron Francis
hope so. Actually,
Joe Tannenbaum
I don't think it's frivolous at all. I think it's overengineered in a wonderful way, but I think the end product is genuinely useful. I mean, I I I think I told you this, but, like, when Taylor popped that into Slack the first time, the composer run dev thing, I was like, this is awesome. I hate this output. I
Aaron Francis
have a fan. You're like,
Joe Tannenbaum
I could do it better. I was like, is this why I was hired? I maybe. And then Mhmm. And I was like, I don't have the time.
I have so many things to do. And then you come along and you're like, I got you. No problem solved.
Aaron Francis
Problem solved. People have asked, like, why why is 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 wanna put I don't wanna foist that upon the shoulders of a different set of maintainers.
So if, you know, if you ever get the the inkling, you can lobby, but I I don't wanna give this to y'all.
Joe Tannenbaum
As a concept and as a construct, what you've created is very fascinating. And I think I've seen some of the code. It 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.
Aaron Francis
Mhmm.
Joe Tannenbaum
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 Yeah. Authorized to do this.
But, I I I think it's I think what you've done here is, like, beyond fascinating and, like, incredibly smart. And congrats congrats on business work.
Aaron Francis
Thanks. Thanks.
Joe Tannenbaum
Alright. So solo is the project. That's
Aaron Francis
up on project.
Joe Tannenbaum
GitHub. GitHub. And then where where can people find you or where should people find you? Where would you like people to find you?
Aaron Francis
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..comaarondfrancis. 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 Tannenbaum
That's where I've been point I've been pointing people to just the site. I'm like, I don't know exactly where you're gonna find me right now. Just go to the site, and you'll you'll figure
Aaron Francis
it out from there. It's pretty obvious. That's right. Own your stuff, people. Regardless of where you hang out,
Joe Tannenbaum
you gotta own your stuff. Alright. So aaron aaron francis
Aaron Francis
dot com? Aaron francis dot com. No d.
Joe Tannenbaum
Yeah. No no d. Correct.
Aaron Francis
No d.
Joe Tannenbaum
That's that's where people should go as as a statement of record. Yes. Okay. Amazing. Thank you so much, Aaron.
I really appreciate it.
Aaron Francis
For having me.
Joe Tannenbaum
And, enjoy the rest of the day.
Aaron Francis
Alright. You too. See you.