Episode

Side Project Podcast · Dec 23, 2024

Side Project Podcast: Aaron Francis

Show notes

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

Transcript
00:06

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?

00:17

Aaron Francis

Good. I am honored to be your guest on side projects today.

00:21

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.

00:25

Aaron Francis

I I am honored. So that it is That is the right word.

00:29

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,

00:40

Aaron Francis

you get a little bit of shame.

00:42

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.

00:56

Aaron Francis

Thank you.

00:56

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.

01:05

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

01:13

Joe Tannenbaum

the world?

01:13

Aaron Francis

I gotta do stuff.

01:15

Joe Tannenbaum

Joyless. Joyless. So, yeah,

01:16

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.

01:35

Joe Tannenbaum

Let's talk about it. So this correct me if I'm wrong. This came from that new composer run dev.

01:41

Aaron Francis

Is that

01:42

Joe Tannenbaum

what it was? Composer run dev?

01:43

Aaron Francis

Mhmm.

01:43

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.

01:51

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.

02:02

Joe Tannenbaum

Why won't you say

02:02

Aaron Francis

he didn't get a great

02:03

Joe Tannenbaum

job with that?

02:04

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.

02:40

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.

03:06

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.

03:20

Joe Tannenbaum

Thank you.

03:21

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.

03:30

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.

04:01

But it's kinda awesome.

04:03

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.

04:14

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.

04:29

Aaron Francis

That you shouldn't do that. Yeah. Same.

04:32

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.

04:55

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?

05:17

Aaron Francis

Where were the stumbling blocks? Everywhere. All over the place. Everything was a stumbling block.

05:24

Joe Tannenbaum

It's all stumbling blocks.

05:25

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.

05:40

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.

05:50

Joe Tannenbaum

Yeah.

05:51

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?

06:10

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

06:20

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.

06:30

I'm really just doing what you're doing stumbling along.

06:32

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.

06:42

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.

06:56

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.

07:19

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?

07:27

Because I I can't figure that out. Yeah. It's like, oh, you have a ANSI code that never got reset. Okay.

07:33

Joe Tannenbaum

Yeah.

07:34

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.

07:53

But because I didn't, I did it, and it still is awesome.

07:56

Joe Tannenbaum

You're describing my career.

07:58

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.

08:13

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.

08:43

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.

08:53

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.

09:17

And so I'm, like, doing all the

09:18

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.

09:28

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.

09:48

Aaron Francis

The cursor.

09:48

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?

10:03

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?

10:11

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.

10:42

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.

11:03

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.

11:30

Right? String of red text. Turn off red. Go back to black. Let's start typing again.

11:35

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.

11:45

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.

12:20

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.

12:31

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.

12:42

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

12:56

Aaron Francis

Mhmm. So it

12:57

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.

13:05

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

13:16

Joe Tannenbaum

Mhmm.

13:16

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.

13:38

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.

13:47

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.

13:57

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.

14:21

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.

14:32

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.

14:42

Right? It just sort of, like, flowed. I love that.

14:45

Aaron Francis

So this is fun.

14:46

Joe Tannenbaum

This is one of those projects where it's, like, virtually 100% learning, probably a 10% learning. Yes. Dude.

14:53

Aaron Francis

Totally.

14:54

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?

15:09

It's some

15:09

Aaron Francis

Yeah. I think that's about right. I mean,

15:11

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?

15:17

Are you using

15:18

Aaron Francis

your own package?

15:19

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?

15:25

Aaron Francis

Yes. Both. Both? Yes. Yes.

15:27

And. His name is Jeff Bridges. I looked it up. So I had Jeff right. Oh.

15:30

So I was like, Jeff Daniels?

15:31

Joe Tannenbaum

I don't know what that No.

15:32

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.

15:44

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

16:02

Joe Tannenbaum

around. Processes. Yeah.

16:03

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.

16:29

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.

17:05

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.

17:23

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.

17:49

Joe Tannenbaum

And the

17:49

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.

18:08

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.

18:27

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.

18:44

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.

18:55

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.

19:11

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.

19:25

Exactly. And you're a wizard. That's

19:26

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.

19:33

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.

19:53

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.

20:20

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.

20:34

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.

20:52

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.

21:09

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.

21:20

Aaron Francis

It's stressful. It's very stressful.

21:22

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?

21:30

Where are you at with it?

21:32

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

21:46

Joe Tannenbaum

I was gonna

21:47

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.

21:59

What are you talking about? What is VIM hot keys? I don't know what VIM hot keys is.

22:02

Joe Tannenbaum

You put out a TUI. It's t minus negative five seconds before somebody says VIM hot keys question mark. VIM

22:07

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.

22:20

We'll just make that an alias. No. No. No. No.

22:22

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.

22:29

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.

22:58

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.

23:19

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.

24:04

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.

24:35

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.

24:43

Joe Tannenbaum

Yeah. Yes.

24:44

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.

24:54

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.

25:01

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.

25:09

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?

25:27

Like, how how is that happening to in a generic way? I'm just curious.

25:31

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.

25:57

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.

26:21

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.

26:45

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.

27:01

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.

27:29

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.

28:00

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.

28:25

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.

28:49

You've done a very, very smart thing here. That's very cool.

28:53

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.

29:07

Joe Tannenbaum

Yeah. Yeah.

29:08

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.

29:21

I'm, like, how do people wanna hook into this? Well What

29:24

Joe Tannenbaum

are the hook how I wanna

29:25

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.

29:31

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.

29:49

Aaron Francis

And I

29:50

Joe Tannenbaum

was like, yeah. That's what I'm talking about. Yep. And by any means necessary, get in there. You know?

29:54

It was great.

29:55

Aaron Francis

I got the solution for that, by the way. I can I can show you after the code that does that?

29:59

Joe Tannenbaum

I have one too. I'm I'm curious what you came up with, but mine

30:03

Aaron Francis

It's gnarly.

30:04

Joe Tannenbaum

It's gnarly? Mine's pretty clean. Mine's pretty chill. So maybe yeah. I was comparing that.

30:08

Aaron Francis

So Mine's not a chill guy. Mine

30:10

Joe Tannenbaum

Okay.

30:11

Aaron Francis

I think it's what is what is it? The sig winch command or sig winch signal?

30:15

Joe Tannenbaum

Yeah.

30:15

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.

30:38

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.

30:52

Aaron Francis

I know.

30:52

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.

31:04

Aaron Francis

Yeah. I think I think that's close to what I do. Yeah. I think that sounds

31:07

Joe Tannenbaum

like what you did.

31:09

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.

31:24

Joe Tannenbaum

Stream select. Yeah.

31:24

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.

31:40

Joe Tannenbaum

Keep all the balls there.

31:41

Aaron Francis

Freaking nightmare. My god. Yeah.

31:44

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.

31:57

Aaron Francis

Exactly.

31:57

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.

32:07

Aaron Francis

Jury's out. But yeah.

32:10

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?

32:22

Aaron Francis

Yeah.

32:22

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

32:45

Aaron Francis

Turns out it's very difficult.

32:47

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?

32:56

Aaron Francis

No. The emulator came as a necessity to handle Laravel prompts, which Yeah. Came from the interactive part.

33:04

Joe Tannenbaum

So Okay.

33:05

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.

33:24

It's not interactive. Alright. What the hell? And so I watched that and I was like, yeah. Shoot.

33:29

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.

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, 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.

34:30

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

34:55

Joe Tannenbaum

is price of $99.99 a month.

34:58

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.

35:05

Joe Tannenbaum

That's right.

35:06

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.

35:25

But this

35:25

Joe Tannenbaum

Type it in.

35:26

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.

35:50

Put those into a Laravel like So what? Ahead search select. Yes.

35:55

Joe Tannenbaum

Yeah.

35:55

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.

36:11

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.

36:22

Joe Tannenbaum

Mhmm.

36:22

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.

37:01

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.

37:29

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

37:43

Joe Tannenbaum

from the activity

37:44

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.

37:55

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.

38:11

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.

38:18

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.

38:34

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.

38:48

Joe Tannenbaum

Is that out yet? Is that something upcoming, or is that out yet?

38:51

Aaron Francis

No. So that is open in PR number four, and it's just a matter of being brave enough to merge it.

38:57

Joe Tannenbaum

People are gonna

38:58

Aaron Francis

flip their heads. I know.

39:00

Joe Tannenbaum

I feel like do you know that GIF of John c Riley where he's, like, like, just confused, consistent with John

39:06

Aaron Francis

c Riley?

39:07

Joe Tannenbaum

John c Riley? Stepbrothers? Will Will Carroll and John c Riley?

39:11

Aaron Francis

Oh, yeah. Yeah. Yeah. Yeah. Yeah.

39:12

Yeah. He's got

39:13

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.

39:27

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.

39:38

But if you ever wanna come and talk about a frivolous side project again, you're always welcome to.

39:43

Aaron Francis

I don't know if it can get more frivolous than this, but I would love to come back at some point.

39:47

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

39:56

Aaron Francis

hope so. Actually,

39:57

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

40:15

Aaron Francis

have a fan. You're like,

40:16

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.

40:23

I have so many things to do. And then you come along and you're like, I got you. No problem solved.

40:28

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.

40:44

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.

40:51

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.

41:07

Aaron Francis

Mhmm.

41:09

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.

41:17

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.

41:25

Aaron Francis

Thanks. Thanks.

41:26

Joe Tannenbaum

Alright. So solo is the project. That's

41:29

Aaron Francis

up on project.

41:30

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?

41:38

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.

41:53

Everything should be there.

41:55

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

42:02

Aaron Francis

it out from there. It's pretty obvious. That's right. Own your stuff, people. Regardless of where you hang out,

42:07

Joe Tannenbaum

you gotta own your stuff. Alright. So aaron aaron francis

42:10

Aaron Francis

dot com? Aaron francis dot com. No d.

42:12

Joe Tannenbaum

Yeah. No no d. Correct.

42:14

Aaron Francis

No d.

42:14

Joe Tannenbaum

That's that's where people should go as as a statement of record. Yes. Okay. Amazing. Thank you so much, Aaron.

42:21

I really appreciate it.

42:22

Aaron Francis

For having me.

42:23

Joe Tannenbaum

And, enjoy the rest of the day.

42:25

Aaron Francis

Alright. You too. See you.

Details
Show
Side Project Podcast
Date
Dec 23, 2024
Role
Guest
All podcasts
Podcasts
Newsletter

Every week: the best things I've found and what I've been thinking about.