@aral@mastodon.ar.al avatar aral , to random

I love how simple Kitten’s Streaming HTML workflow makes building features like this, especially when using class-based Kitten pages and components :)

https://kitten.small-web.org

:kitten:💕

Screenshot of code, continuation of the same class, starting with the end of the html() method from the previous screenshot and going till the end of the class (the table[inert] style and the onToggle() method are highlighted: <style> table[inert] { opacity: 0.9; filter: grayscale(100%); } </style> </table> </section> } onToggle () { this.editable = !this.editable console.log(this.editable) this.update() } }](https://s3-eu-central-1.amazonaws.com/mastodon-aral/media_attachments/files/115/701/475/591/066/309/original/0dd9da4ffbc5e31c.png) ![Screenshot of code (the <${TeamSchedule.connectedTo(this)} /> line is highlighted): export default class InterviewsPage extends kitten.Page { html () { const today = db.calendar.today const futureDays = db.calendar.futureDays const pastDays = db.calendar.pastDays return kitten.html <${MainLayout} page='/admin/interviews/'> <h2>Interviews</h2> <${SignUpsSwitch.connectedTo(this)} /> <${InternalNav} /> <${TeamSchedule.connectedTo(this)} /> <h3 id='today'>Today</h3> <if ${today !== undefined}> <then>

Screen capture of the Team schedule interface from the Gaza Verified admin site. It is a table of names and days (Aral, Casey, Joy, Aseel, Fabio And Mondays … Fridays) with checkboxes at each name/day pair. It starts out greyed out. The Unlock to edit button is clicked and the interface comes alive (opacity returns to full and the checkboxes become green and clickable). The person toggles a few checkboxes and clicks the Lock button to lock the interface again and then repeats the process to undo the changes they just made.

ALT
@aral@mastodon.ar.al avatar aral , (edited ) to random

You can really get a feel for what authoring a simple web app is like in Kitten (with the latest techniques/features), by checking out the source code for Look Over There!, the multi-site forwarding app with TLS forwarding support that I built recently:

https://codeberg.org/small-web/look-over-there

There’s an instance of it I’m hosting for us at https://look-over-there.small-web.org to forward our archived sites – e.g., https://web0.small-web.org – to archive.org so we don’t break links on the web.

If you want to run it locally, install Kitten (https://kitten.small-web.org) and then type: kitten run https://codeberg.org/small-web/look-over-there.git (you can also clone the git repo manually and just run kitten from the working directory).

Interesting places to look:

• The index page (shows use of a Markdown page that imports and uses Kitten components) https://codeberg.org/small-web/look-over-there/raw/branch/main/index.page.md

• The admin page (notice the lock emoji at the end of the name? That’s all you need to add to a route to make use of Kitten’s automatic authentication. Since every Kitten site/app is protected by public-key encryption and we don’t have the concept of users (each site/app on the Small Web is owned by one person), the whole process can be automated for you. https://codeberg.org/small-web/look-over-there/src/branch/main/admin%F0%9F%94%92.page.js

• The Redirection component. This is what does all the hard work on the admin page. Along with the admin page, they showcase Kitten’s new/alternative (and as-of-yet mostly undocumented) class-based component model with event bubbling on the component hierarchy on the server. (Since this is resource intensive, it’s recommended you use it only when implementing authenticated routes where you’re sure only the site’s owner will be accessing the route.) https://codeberg.org/small-web/look-over-there/src/branch/main/Redirection.component.js

In any case, as Kitten and the rest of the Small Web ecosystem matures further, I’ll be documenting all this better and there will be even more examples and tutorials but, in case you’re one of those inquisitive types and you want to see what web development can be like if you’re not building centralised people farming machinery for Big Tech, have a play with Kitten.

:kitten:💕

aral OP ,
@aral@mastodon.ar.al avatar

…And there are more interesting tid-bits in there too:

• See how I’m pushing Kitten’s Streaming HTML to its logical conclusion and streaming JavaScript from the server to the client to keep all logic on the server while implementing a client-side feature (copy to clipboard): https://codeberg.org/small-web/look-over-there/src/branch/main/CopyButton.component.js

• Following on from that, note how the Toast component that’s triggered when something is copied looks (under the hood, Streaming HTML is htmx + WebSockets + some Kitten-specific magic and glues it all together and adds syntactic sugar): https://codeberg.org/small-web/look-over-there/src/branch/main/Toast.fragment.js

• Finally, check out how layout components and slots work: https://codeberg.org/small-web/look-over-there/src/branch/main/Site.layout.js

I think that’s all the intersting stuff I can spot at the moment.

Have fun!

:kitten:💕

@aral@mastodon.ar.al avatar aral , to random

I just whipped up a simple icon search for Kitten Icons (based on Phosphor Icons). It’s not complete yet – I have a few other things to do before I can get back to it today – but you can play with it here:

https://kitten.small-web.org/reference/#finding-icons

And see the code for it here:

https://codeberg.org/kitten/site/src/branch/main/reference/IconExplorer.fragment.js

I popped the event handler into the Markdown page itself. Not the cleanest but works in a pinch:

https://codeberg.org/kitten/site/raw/branch/main/reference/index.page.md

ALT
aral OP ,
@aral@mastodon.ar.al avatar

Right, the Kitten icon explorer is now fully functional.

• Type to filter results (searches component names, categories, and tags).
• Press the component button to copy the component code to your clipboard.

https://kitten.small-web.org/reference/#finding-icons

I quickly whipped this up using the new generic script in Markdown frontmatter feature I added to Kitten. And I’d imagine this is about the most amount of functionality you’d add using it before refactoring to something more maintainable. (Hey, just because I’m the author of Kitten doesn’t mean I’m not also learning how to use it as build it.) :)

Source: https://codeberg.org/kitten/site/raw/branch/main/reference/index.page.md

Enjoy!

:kitten:💕

ALT
  • Reply
  • Loading...
  • @aral@mastodon.ar.al avatar aral , to random

    New Kitten release 🎉

    https://kitten.small-web.org

    • New: Lovely new icons¹ and new callouts in Kitten Settings²

    • New: Markdown now supports attributes and bracketed spans³

    • New: client-side kitten global with trigger function for triggering events on the server from the client. (Useful when streaming client-side JavaScript when using Kitten’s Streaming HTML⁴ workflow. e.g., when you have to use a client-only web API like the Clipboard API but you want to keep all your logic on your server-side page.⁵)

    • Fixed: The bound render function returned by KittenComponent class’s component getter now correctly awaits asynchronous templates. (In Kitten, you don’t have to care whether your templates contain promises. Kitten handles all that for you.)

    Enjoy! :kitten:💕

    ¹ https://kitten.small-web.org/reference/#icons

    ² https://mastodon.ar.al/@aral/114381983893061099

    ³ https://kitten.small-web.org/reference/#markdown-support (also see https://mastodon.ar.al/@aral/114381462302862256)

    https://kitten.small-web.org/tutorials/streaming-html/

    ⁵ e.g., See how I use this to implement a copy to clipboard button in the database page of Kitten’s Settings: https://codeberg.org/kitten/app/src/branch/main/web/%F0%9F%90%B1/settings%F0%9F%94%92/db/index.page.js#L33 Of course, you don’t have to use this and you can just write client-side JavaScript or use the built-in Alpine.js integration. e.g., how I do it on the (older) settings/identity page: https://codeberg.org/kitten/app/src/branch/main/web/%F0%9F%90%B1/settings%F0%9F%94%92/identity/index.page.js#L7

    @aral@mastodon.ar.al avatar aral , to random

    New Kitten release

    • Fix: messages that are promises are properly awaited before being sent.

    https://kitten.small-web.org

    Kitten automatically and transparently handles asynchronous content in your templates for you so you don’t have to worry about it. One place where this wasn’t working properly is if you addressed this.component to stream a custom update of your component manually instead of calling the this.update() method of Kitten component instances.

    e.g.,

    export default class AdminPage extends kitten.Page {  
     // …  
     onSelectedPost (data) {  
     this.send(kitten.html`&lt;${this.component} postId='${data.selectedPost.postId}' /&gt;`)  
     }  
    }  
    

    :kitten:💕

    @aral@mastodon.ar.al avatar aral , to random

    Coming tomorrow to Kitten… Kitten icons!

    Kitten will have built-in support for the Phosphor icons set with full authoring-time language intelligence where you can search for icons via category and tag (in addition to the canonical alphabetical categorisation).

    Thought this was going to take me a few hours but it took a few days thanks to running into issues with size limits, type inference from JavaScript types in modules, etc., with the TypeScript language server but I believe I’ve finally cracked it :)

    :kitten: 💕

    ALT
    aral OP ,
    @aral@mastodon.ar.al avatar

    👆 Sorry, life got in the way; just deployed this but I haven’t had a chance to document it properly yet. I’ll make a proper announcement when I do.

    Also, you can now use the delete attribute on a DOM node/Kitten component you’re streaming back from the server to have it removed from the DOM on the client. It’s syntactic sugar for hx-swap-oob='delete' in htmx.

    https://kitten.small-web.org

    @aral@mastodon.ar.al avatar aral , to random

    New Kitten release

    • Socket routes now have precendence in the router.

    This stops wildcard page routes from capturing the default socket routes that Kitten creates to enable the Streaming HTML workflow.

    e.g., Previously, the following route:

    /videos/index_[slug].page.js

    Could not connect to its default socket (/videos/default.socket) because default.socket would be captured by the [slug] parameter.

    Now, it will work as intended as the /videos/default.socket (a socket route) has precendence over index_[slug].page.js (a page route).

    Learn more about Kitten’s Streaming HTML workflow here:
    https://kitten.small-web.org/tutorials/streaming-html/

    Enjoy!

    :kitten:💕

    @aral@mastodon.ar.al avatar aral , to random

    New Kitten Release

    • Automatic message routing: if the element that triggers an event on the client does not have a name attribute, Kitten now falls back to using its id instead to route the event to the correct server-side event handler on your live Kitten pages.

    If neither attribute exists, Kitten will fail to route the message but no longer crash as it was due to a regression introduced when I implemented support for colons in element names.¹

    https://kitten.small-web.org

    For more details on Kitten’s live pages and automatic message routing, please see the Streaming HTML tutorial:

    https://kitten.small-web.org/tutorials/streaming-html/

    Enjoy!

    :kitten:💕

    ¹ A colon in an element name is ignored for message routing purposes, letting you, for example, give unique names to <details> elements, allowing more than one to be open at a time, while having their events be handled by the same handler.

    @aral@mastodon.ar.al avatar aral , to random

    New Kitten release

    • Fixes issue with routes where dynamic routes with file names that had more than two extensions were not recognised as the correct type of route. e.g., A route called index.xml.get.js would previously have been treated as a static route instead of a dynamic GET route.

    https://kitten.small-web.org

    For more details, see the Valid File Types section of the Kitten reference¹ and the Dynamic Pages tutorial².

    Enjoy!

    :kitten:💕

    ¹ https://kitten.small-web.org/reference/#valid-file-types
    ² https://kitten.small-web.org/tutorials/dynamic-pages/

    @aral@mastodon.ar.al avatar aral , (edited ) to random

    New Kitten release

    • You can now use key paths in the names of your client-side live components and these will automatically be transformed into object hierarchies on the server for you.¹

    • Self heals zombie live pages (see Streaming HTML workflow²) if they return to life due to client-side browser cache.³

    • Removes htmx⁴ headers from data property into separate header property in Kitten Page events and the data your Kitten Page message handlers receive.

    • Automatically passes references to the live page object (if any) and the request and response objects to the layout templates of Markdown pages⁵ (so you can, for example, check if request.session.authenticated⁶ is true from the your layout template and customise the layout accordingly).

    https://kitten.small-web.org

    Enjoy!

    :kitten:💕

    ¹ e.g., See https://codeberg.org/small-tech/site/src/branch/kitten/admin%F0%9F%94%92/news/index_%5Boptional-postId%5D.page.js#L356 (markup) and https://codeberg.org/small-tech/site/src/branch/kitten/admin%F0%9F%94%92/news/index_%5Boptional-postId%5D.page.js#L173 (handler) and https://codeberg.org/small-tech/site/src/branch/kitten/app_modules/database/database.js#L95 (model class method).

    ² See Streaming HTML tutorial: https://kitten.small-web.org/tutorials/streaming-html/ (There’s actually more to it now but I haven’t had a chance to document the new class-based and event-driven live page workflow yet. It’s experimental but working very well for me so far so I will do so shortly.)

    ³ When a person leaves a live/connected page (a page connected to its default web socket), we clean up and remove that live page from memory. However, browsers being what they are, cache the page on the client. If a person uses the back/forward buttons to return to the page, the browser will serve the cached source from memory, which has the old page ID, for the page that no longer exists in Kitten’s memory. So now we have a problem. The only way to recover from this is to tell the page to reload itself. So we accept the WebSocket connection, send a command to the page for it to reload itself, and then close the socket. That makes the stale page self heal by replacing itself with a fresh one. Yay, go us!

    ⁴ HTMX: https://htmx.org

    ⁵ Kitten Markdown pages reference: https://kitten.small-web.org/reference/#markdown-pages-page-md-files

    ⁶ See Session tutorial: https://kitten.small-web.org/tutorials/sessions/

    @aral@mastodon.ar.al avatar aral , to random

    New Kitten update

    https://kitten.small-web.org

    • Added remove() method to kitten.Component class. Use this when working with live pages and components and you want to remove a component from the page (or its parent). It will handle removing event listeners for you so you don’t end up with any memory leaks.

    • Improved update() method so it similarly removes listeners on child components before updating the component itself in case you have class-based child components that will be reinstantiated on render.

    • Updated the send() methods on page.everyone and page.everyoneElse so you can pass a swap target to insert the element being streamed to the page before, after, asFirstChildOf, or asLastChildOf another. (This was already there for the page.send() but now the two broadcast objects have the same consistent interface.

    The @small-web/kitten npm package (Kitten’s types package) has also been updated to version 5.1.0 to reflect the latest changes.

    (Remember that the new class and event-based page and component model is still experimental and largely undocumented and fully backwards compatible with the classic functional way of authoring your page routes and components.)

    Enjoy!

    :kitten:💕

    @aral@mastodon.ar.al avatar aral , to random

    A quick demonstration of using the State: Overview page in Kitten’s¹ settings while developing to keep an eye on your event and event listener counts to avoid memory leaks.

    Notice how the events and listeners counts change as I navigate between the People and Settings pages in my Place² node and that they are consistent. If they were rising as I navigated back and forth I’d know I had a memory leak somewhere.

    If you use Kitten’s built-in features (e.g., the addEventHandler() method on your kitten.Component subclasses, Kitten will handle adding and removing listeners for you automatically during your component’s lifecycle. You can also do so manually in your component’s automatically-called onConnect() and onDisconnect() event handlers.

    This view is useful during development to ensure you don’t have any memory leaks as pages are loaded and unloaded.

    https://vimeo.com/1050714714

    ¹ https://kitten.small-web.org
    ² Place is in early development at the moment (https://codeberg.org/place/app)

    @aral@mastodon.ar.al avatar aral , to random

    New Kitten¹ update

    Experimental:

    • Adds data property to Kitten components

    • Adds swap target to page.send so you can have an element added before, after, as first child of, or as last child of another (this is syntactic sugar over htmx and works around some of the complexities with out-of-band swaps in htmx, especially when streaming table rows to tables).

    Also, check out the latest live page and page events state view in Kitten’s Settings (every Kitten app has this settings view).

    https://vimeo.com/1050348456

    ¹ https://kitten.small-web.org

    @aral@mastodon.ar.al avatar aral , to random

    Rather loving the umpteenth iteration of my streaming setup :)

    (Just recorded a preview of Kitten’s improved component model and will share it once it’s uploaded.)

    ALT
    aral OP ,
    @aral@mastodon.ar.al avatar

    Little preview video: Kitten’s improved component model

    • Class-based page routes and components
    • Object-oriented
    • Event-based
    • Seamless hypermedia-driven WebSocket-based event mapping and interface updates (Streaming HTML)
    • A light server-side live component hierarchy with event bubbling
    • Almost as if you’re building a desktop or mobile app instead of a web app…

    … another authoring simplification made possible because on the Small Web – which is a peer-to-peer web – you build a web app/site as a tool for one person (the owner of the site/app) instead of as a tool for you to farm millions of people.

    … still experimental ;)

    https://vimeo.com/1049055406

    Learn more about Kitten:

    https://kitten.small-web.org

    If you like what you see and want us to keep existing, we could definitely use your support:

    https://small-tech.org/fund-us/

    :kitten:💕

    aral OP ,
    @aral@mastodon.ar.al avatar

    So last night, while recording the preview of Kitten’s¹ improved component model², I made a silly mistake (copying raw HTML into a JavaScript function instead of wrapping it in a kitten.html`` tagged template, easy to do when you’re refactoring to pull out components from pages).

    Then, once I figured out what I’d done, I made another one by forgetting to return the value from the function (easy to do when you’re used to using one-line closures as render functions).

    I would have caught both of those so much faster if Kitten had helpful error messages for those two pitfalls. And guess what, this morning, it does :)

    Attached are screenshot showing the before and after error messages.

    Enjoy!

    :kitten:💕

    ¹ https://kitten.small-web.org
    ² Scroll up the thread to watch the video.

    Screenshot of first error message (after): Error: Render function did not return kitten.html class Count extends kitten.Component { html { kitten.html`<h1 morph>${kitten.db.counter.count}</h1>` } onIncrement { kitten.db.counter.count++ this.update } }](https://s3-eu-central-1.amazonaws.com/mastodon-aral/media_attachments/files/113/871/665/803/469/776/original/b8b240204c08abd6.png) ![Screenshot of second error message (before): 500 SyntaxError: Unexpected token '<](https://s3-eu-central-1.amazonaws.com/mastodon-aral/media_attachments/files/113/871/671/171/833/538/original/5089824c084db1a9.png) ![Screenshot of second error message (after): 500 SyntaxError: Unexpected token '< (Did you put raw HTML inside a function by mistake instead of wrapping it in a kitten.html tagged template?)

    @aral@mastodon.ar.al avatar aral , to random

    Feeling more like myself today after getting my ass kicked by this bug for the past few weeks and I’m finally making progress on improving Kitten’s¹ component model.

    Soon, you’ll be able to implement page event handlers in an even simpler way than before. Is your connected DOM element named pixel? Then export an event handler called onPixel() on your page route and it’ll get called when that element triggers its event.

    And there’s so much more coming, including a class-based page and component model with DOM-style event bubbling on the server. This should really let you make the most of the Streaming HTML² workflow in Kitten by writing event-driven GUI-like code but for a web app without really caring about the client/server separation.

    Need to update the examples, test, test, test, and update docs but this had me stumped for a while now with several false starts and I feel this implementation might just be it 🤞

    :kitten: 💕

    ¹ https://kitten.small-web.org
    ² https://kitten.small-web.org/tutorials/streaming-html/

    ALT
    @aral@mastodon.ar.al avatar aral , (edited ) to random

    New Kitten update

    Adds :unique-suffix support to automatic client-side element name to server-side event name mapping.

    (This lets you give unique names to your elements while having them share the same event name.)

    Read more in the “a magic connection” section of the Kitten Streaming HTML tutorial: https://kitten.small-web.org/tutorials/streaming-html/#a-magic-connection

    :kitten: 💕

    @aral@mastodon.ar.al avatar aral , to random

    Today @ 18:30 in Dublin: Come hear me talk about the Small Web and watch me demonstrate the current state of Kitten, Domain, and Place.

    https://meetu.ps/e/Ny9vK/H1WXJ/i

    @aral@mastodon.ar.al avatar aral , to random

    Just added a code breakdown for the Draw Together¹ Kitten² app to my blog post with the tutorial video:

    https://ar.al/2024/03/26/draw-together/

    Draw Together, in ~50 lines of code, is a real-time collaborative drawing tool on a 20×20 pixel grid where people can click to toggle the colour of each pixel.

    (The full source and explanations fit the four screenshots on this post.)

    ¹ https://draw-together.small-web.org
    ² https://kitten.small-web.org

    Screenshot of web page (detail, cont.): rows[data.rowIndex][data.columnIndex]++ if (rows[data.rowIndex][data.columnIndex] === colours.length) { rows[data.rowIndex][data.columnIndex] = 0 } page.everyone.send(kitten.html <${Pixel} rowIndex=${data.rowIndex} columnIndex=${data.columnIndex} pixelColourIndex=${rows[data.rowIndex][data.columnIndex]} /> ) }) } Listens for pixel updates, validates data, updates the grid, and sends the updated pixel state to everyone connected. Pixel component const Pixel = ({ rowIndex, columnIndex, pixelColourIndex }) => kitten.html <button id='pixel-${rowIndex}-${columnIndex}' aria-label='pixel-${rowIndex}-${columnIndex}' name='pixel' style=' background-color: ${colours[pixelColourIndex]}; top: calc(5% * ${rowIndex}); left: calc(5% * ${columnIndex}); ' connect data='{rowIndex: ${rowIndex}, columnIndex: ${columnIndex}}' morph ></button> Defines a button element representing a pixel, styled according to
    Screenshot of web page (detail, cont.): its colour and position. Canvas component const Canvas = () => kitten.html${ rows.map((row, rowIndex) => row.map((pixelColourIndex, columnIndex) => kitten.html <${Pixel} rowIndex=${rowIndex} columnIndex=${columnIndex} pixelColourIndex=${pixelColourIndex} /> )) } Renders the entire grid by mapping over each row and column, creating Pixel components. Styles component const Styles = () => kitten.css [name='pixel'] { position: fixed; width: 5%; height: 5%; border: 0; } Applies CSS styles to the pixel buttons to size and position them correctly. Main export export default () => kitten.html <page title='Draw Together'>](https://s3-eu-central-1.amazonaws.com/mastodon-aral/media_attachments/files/113/323/790/831/119/935/original/b4d009e120774c56.png) ![Screenshot of web page (detail, cont., last): Main export export default () => kitten.html <page title='Draw Together'> <${Canvas} /> <${Styles} /> ` Constructs the main page with the canvas and styles. Like this? Fund us! Small Technology Foundation is a tiny, independent not-for-profit. We exist in part thanks to patronage by people like you. If you share our vision and want to support our work, please become a patron or donate to us today and help us continue to exist. Aral Balkan About the author I’m an activist, designer, and developer. I’m one-half of Small Technology Foundation, a tiny and independent two-person not-for-profit based in Ireland. We advocate for and build small technology to protect personhood and democracy in the digital network age. To support my work, become a patron of our foundation. I’m available for public speaking and media inquiries.

    ALT