via |
Huh. nativefier is pretty cool and super easy to use to get an app wrapper for a webpage (haven't yet fooled around enough to see how it plays with Apple's usual "do you trust this developer" gatekeeping)
As I make my Atari Tools, the idea of long term support comes up, since some of these projects are one-person-shows, and sometimes folk lose interest, or something happens to them. That's one reason I try to keep my software all browserside, ideally with trivial build processes - it should be pretty easy for a coder to pick up my stuff if they wanted to.
I'm sure I'll have more to say about the UI behind Splash-O-Matic 2600, my upcoming browser-based tool to make large, ready-to-run images for the Atari 2600, but right now I'm chuffed that I got an image "upload" feature working - except it all happens in the browser!
Here's the editor page:
and here it is running on an Atari Emulator -
The trick is using the createFileInput() feature from P5. I didn't get the (probably more performant) loadPixels() version working, but get() let me read from the pixels and take a guess if this square should be black or white.
I was worried this was going to be super-tough to add to my tool, but it turned out to be pretty easy. The trick was not to overthink it: in theory I could let the tool carefully scale and crop etc the uploaded image - but that would really complicate the UI. I decided to let the program just scale the original image to the width and height of the canvas, then read the pixels. (In atari-pixel-size chunks)
I've been thinking a lot lately about The Rise of Worse is Better. This is a good example of the "New Jersey approach" favoring simplicity above completeness - making the implementation simple really made the whole thing possible.
But thinking about the example from the essay - the "interface" for the PC loser-ing was "more complex" because the programmer had to do more checking. And the metaphor for me is, my "interface" is in some ways "more complex" because the user might have to scale/crop the image on their own. But, the UI itself reflects the simplicity of the interface! Which in turn means the user has to know less about my system - it's more transparent, easier to keep a mental model of what's going on.
My sweetie Melissa found some scratchers left over from Christmas - 2 of them, $2,000,000 50x Cashword are a little annoying to check - you have to hunt through a completed crossword to see if you actually had the letters for any of the words (SPOILER: thanks to the missing vowels you will likely complete about a word per puzzle but you need two in a puzzle to win back your money, but if you get up to eleven you win big bucks)
Anyway, in the words of Douglas Adams who said he was "rarely happier than when spending an entire day programming my computer to perform automatically a task that would otherwise take me a good ten seconds to do by hand.” I made a little Javascript checker for it... I really enjoy being able to whip something like this up.
Ran into this a while back: Automating my job by using GPT-3 to generate database-ready SQL to answer business questions. "Natural language programming" has been a long sought-after goal and this is impressive but I think it will be a while before we trust the results!
Still the best diet plan comes from the book "Chubster: A Hipster's Guide to Losing Weight While Staying Cool" and also "The Hacker's Diet": "find a method to hit a daily calorie count without making yourself miserable" I doubt there's an all-in-one for everyone, but so many successful diets boil down to that.
<!doctype html> <html> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <? if(isset($_GET["date"])) { $date = $_GET["date"]; } else { $date = date("Y-m-d"); } if(!preg_match("/^\d\d\d\d\-\d\d\-\d\d$/",$date)) { die; } print "<title>food - $date</title>"; if(file_exists("data/$date")){ $guts = file_get_contents("data/$date"); } ?> <link rel="stylesheet" href="style.css" /> </head> <body> <div class="content"> <h1>food - <? echo $date; ?></h1> <table class="chart"> <? if(isset($guts)) { $lines = explode("\n",$guts); $total = 0; foreach($lines as $line) { if(preg_match("/^(\d*) \- (.*)$/",$line,$matches)){ $cal = $matches[1]; $total += $cal; $width = $cal / 5; $food = $matches[2]; print "<tr><td>$food</td>\n"; print "<td><div style='width:".$width."px'></div></td></tr>\n"; } } print "<tr><td><br><b>total: $total</b></td></tr>"; } ?> </table> <form action="save.php" method="post"> <input name="date" type="date" value="<? echo $date; ?>" onchange="window.location='./?date='+this.value"> <textarea name="guts"><? echo $guts ?></textarea> <button>submit</button> </form> <ul> <? $files = array_values(array_diff(scandir("data"), array('.', '..'))); foreach($files as $date){ print "<li><a href='./?date=$date'>$date</a>"; } ?> </ul> </div> </body> </html>
body { background-color: white; color: black; font-family: sans-serif; } .content { width: 800px; margin: auto; } input,textarea { display:block; margin-bottom:10px; } textarea { width:20em; height:20em; } button { font-size:40px; } .chart div { height:20px; background-color: #ccc; }
<? $date = $_POST["date"]; if(!preg_match("/^\d\d\d\d\-\d\d\-\d\d$/",$date)) { die; } $guts = $_POST["guts"]; file_put_contents("data/$date",$guts); header("Refresh:0; url=./?date=$date"); ?>
UPDATE:
Not that anyone cares but I updated this a little, changed the order of things so the textarea was first in the tab order and did a
onfocus="this.selectionStart = this.selectionEnd = this.value.length;"to hop the cursor to the end, and made the regex more robust:
"/^(\d+) ? ?\-? ? ?(.*)$/"The lets it use space dash space, but omit either the dash or the space, or add an extra space or two.
Continuing my Atari kick... I've started work on title-screen-titan, a browser tool for the esoteric art of making 48-pixel graphics on the Atari 2600.
One thing I like to do with my Atari tools is provide code you can run right away.
This page taught me how to build up one click downloads without hitting my server:
function download(filename, text) { var element = document.createElement('a'); element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)); element.setAttribute('download', filename); element.style.display = 'none'; document.body.appendChild(element); element.click(); document.body.removeChild(element); }
Then I just called download() in the onclick of a button.
I also updated my old batari Basic kickstart tutorial - I do love writing to help people learn to do new stuff.
In 2005 I wrote a sprite editor for the Atari 2600, PlayerPal 2600.
This was before jQuery, so I wrote it in Vanilla Javascript. (And, gasp, tables)
In 2008 I upgraded it so it could output "Batari BASIC" code - so the editor plus bB is probably the easiest way of getting an Atari 2600 program up and running.
Anyway, this year a fellow named Oliver Gross submitted some code improvements allowing height and color info to be preserved when re-importing assembly data.... smartly, my past self tagged each line with color data but dumbly thought "well it's too hard to bring [the real] color data back in so I'm not going to try". Gross' code just reads the comments. It's a great 80/20 solution - if I'm not going to put in a clever parser, this still allows for people to iterate on their sprites!
I wrote it as a single file - no external libraries or graphics. (A concept that puts it in the tradition of type-in games from computer magazines) I think having it as a single file made it very easy for Gross to set up a "dev environment" and then submit changes, even without github! He just had a to view source, copy and paste all into an editor, save it, then load that in a browser. And then send me the end file, which I diff'd with the current code to review the changes.
It's funny how this predated jQuery, and outlived jQuery as well! Vanilla js is cool like that.
As far as I know, Outlook is not widely beloved. Probably not as bad as it used to me, but its like it never quite caught up to the UX Gmail introduced for keeping threads manageable.
So sometimes I'm tearing through my email, delete delete delete... sometimes I hit delete, then an instant later realize no, I wanted to read that one. Ctrl-Z brings it back... but doesn't jump to the thing I just deleted, and because of the weird way things are sorted, I don't always know how to get back to it...
Ctrl-Z should absolutely restore the full state, not just the more important part...
I get a little defensive over my use of PHP (and keep a link Taking PHP Seriously handy) - I sometimes wish my rented server had more options for doing serverside stuff in javascript, say, but PHP is both convenient and performant.
My server-based blog/diary/database still uses a lot of legacy Perl CGI - if it ain't broken why fix it? - but lately it HAS been breaking, logs showing "Cannot allocate memory: couldn't create child process" errors. So I figured it was time to update things to PHP and get rid of the CGI dependency.
(I still owe a debt to Perl - see what perl taught me (in the early-mid-90s))
One thing about PHP - it's such a mature environment, in terms of almost every utility you'd want is baked into the language - no CPAN or npm hell or hacking up some regex, it's just there. Case in point, here's a legacy Perl function for my private diary (a bunch of flat files, one per day) I'm porting now
sub dateGoneOrEmpty{ if(! (-e "entries/$justwhen.txt")){ return 1; } open(READ,"< entries/$justwhen.txt"); my $buf = ""; while(defined($line=<READ>)){ $buf .= $line; } close READ; if($buf =~ /MORE/){ return 1; } if($buf =~ /\S/){ return 0; } return 1; }
The "$justwhen" is a datestamp used as filename... this function returns 1 if the file doesn't exist, is empty, or just has the word (all caps) MORE in it (an old hack I used when I knew I wanted to come back and put in more details in a day's entry)
But the syntax is kind of terrible! It's Perl's wonky/shell based way of passing in variables, rather arcane "-e" for file test, use of 1 or 0 for true/false, and doing everything by regex (well that last is not terrible terrible but still) That all reflects Perl's history drawing from shell programming, which I never did much of.
Here is the same logic recreated in PHP
function dateGoneOrEmpty($justwhen){ $filepath = "entries/$justwhen.txt"; if(!file_exists($filepath)) return true; $buf = file_get_contents($filepath); if(str_contains($buf,"MORE")) return true; if(ctype_space($buf)) return true; return true; }
(Not optimized at all, just trying to formally recreate the old behaviors)
That's so much cleaner! I also love how PHP splits the difference with globals... you can just write a simple script and everything is a global, but if you reference a global in a function - you can do that, you just have to declare it as "global". That's so much better than javascript's "well we'll just throw it on window." and/or "just assume there is a global of this name"
(The one thing I loved in Perl I wished showed up elsewhere: use of "eq" and "ne" "gt" and "lt" for string comparisons... I love the mnemonics - it's a string, so the operator is more string-ish)
I implemented support for customizing font-family's with the assumption I could use the font-family name as a unique key, but that was a misthink; I'm not sure it's frequently advisable, but there's a use case of having different font-families where like you pick one file or another based on, say, a <b> tag:
I made up a simple test page
@font-face { font-family: 'Foo'; src: url('AkayaTelivigala-Regular.woff2') format('woff2'), url('AkayaTelivigala-Regular.woff') format('woff'); font-weight: bold; font-style: normal; font-display: swap; } @font-face { font-family: 'Foo'; src: url('Stick-Regular.woff2') format('woff2'), url('Stick-Regular.woff') format('woff'); font-weight: normal; font-style: normal; font-display: swap; }
Later on then, this HTML uses two different fonts files:
<div class="foo">Same Font Family <b>
But A Different File For Bold</b></div>
Of course, the browser can do a pretty good job faking up a "bold" even if it's coming from the same file.
Scrum, an "agile" methodology to make the rhythm of software development more predictable, tends to be built on some iffy assumptions.
Some of these methodologies assume that any given ticket will be a equally as difficult no matter which dev, qa, or designer works on it. (Kanban, in its purest form of "the top ticket is the most important thing that should be picked up next" also falls prey to that 'everyone is interchangeable' fallacy, but in practice people don't have to scoop the ticket right on the top, they'll go down a few to find one that makes sense for their role.)
Some teams avoid thinking of people as interchangeable parts, but fall prey to completely ignoring the "Gant chart"-like dependencies within a sprint - the way many tickets will need some work by Designers, which will then be handed off to be implemented by Developers, and then finally to QA (with some time needed for Dev to fix what QA finds)
(But at least such teams have QA has its own thing! - the trendy thing to do has been to say "devs QA their own work" - armed with their own sense of diligence and the power of automated tests, but A. it's really hard to get someone to be good at hunting for something they don't want to find, i.e. ego-bruising bugs in their own code and B. for automated tests.... if they could have thought of went wrong ahead of time they would have already fixed it in code! So tests are mostly good to say what was developed hasn't been broken over time. (There are some other things tests are good with, but still, in my experience they aren't as load bearing as people want to pretend, and are much more brittle and prone to needing to be fixed than the code itself.))
Anyway. Here's theoretically what a sprint should look like:
That's a weird setup, right? What is QA doing the first part of the sprint? What is Design doing the last? In practice there are always things to be doing, extra chores, light prep for future sprints or learning or useful tasks, but I find it bizarre this kind of dependency isn't accounted for in the system. And of course, let's be honest, thanks to human psychology, this is more what a sprint actually looks like:
QA always gets squashed! Devs are thinking more in terms of the whole sprint rather than aiming to get done halfway through. And so the system fixes itself by carrying over to the next sprint... but that feels bad, like failure, even though the failure is more in the design of the system than in the people doing the work.
The sanest, most clear-eyed response I've seen to these problems was at Alleyoop, a little subproject of Pearson Education that unfortunately never found its sustainable business model mojo. They would do overlapping springs, something like this:
I'm not sure how the naming actually worked (overlapping full sprints vs "subsprints") but hopefully you get the idea: from the devs point of view, at any given sprint, designers were prepping stuff for dev that would be coded in the next sprint, and QA was checking on what was dev'd up the previous sprint.
The trouble is, most tools companies rely on don't model this particularly well. (Alleyoop was well in pre-COVID times and independent, so it was free to use good old "stickies on a white board" without having to use online tracking tools.) And it's a tough sell. But at least it acknowledges that any given ticket has dependencies in time, and if you want to keep everyone productive, you should include those dependencies in your system.
Sans Forgetica, a font to remember.
I haven't thought much about Apple Pencil tips - honestly they seem to last a long time! And it wasn't clear what was "consumable" about them, but after noticing very bad responsiveness, ordering new ones, and then reading 5 Signs it’s time to change your Apple Pencil Tip - I finally notice what's happening, that you don't want a hard surface that might scratch your iPad screen, so the tips give way first, but slowly.
The "Pencil" name is more appropriate than I realized!
Anyway, they're pretty cheap to order online.