<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/css/simple-atom.xslt"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title>Death.au's Articles</title>
  <subtitle>Thoughts, stories and ideas.</subtitle>
  <link rel="self" href="https://death.id.au/tag/articles/atom.xml" type="application/atom+xml" />
  <updated>2026-03-23T05:11:12Z</updated>
  <author>
    <name>Death.au</name>
  </author>
  
  <id>https://death.id.au//tag/articles/</id>
  
  <entry>
    <title>Service Worker Side Template Rendering</title>
    <id>https://death.id.au/b4.0018/</id>
    <updated>2025-09-30T04:05:43Z</updated>
    <published>2025-09-30T04:05:43Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Service Worker Side template rendering</h1>
<p>I've recently been inspired by <a href="https://ahastack.dev/">The AHA Stack</a> and wanted to look into building something with it. Astro in particular reminds me of building with 11ty, it's just not (necessarily) building a static site. But I also want to look into more offline-capable web development, which is pretty much impossible with server-side rendering like Astro. If only I could get Astro to run on the client side in a service worker...</p>
<p>I did look into that possibility and found <a href="https://dev.to/thepassle/service-worker-side-rendering-swsr-cb1">one attempt</a>, which, if I'm honest, went mostly over my head. My takeaway is that it wasn't yet feasible back in 2022 and while things may have changed now, no one else has pursued the idea. I don't think I have the expertise to pull something like that off, but I was inspired to look into perhaps doing something similar, but more simply.</p>
<p>So, I've taken a crack at something... I don't know how useful it is to everyone else, but I think it might have some legs. I've got a server set up with some <a href="https://mustache.github.io/">Mustache</a> templates, then I have a web page with <a href="https://htmx.org/">HTMX</a> set up to request those templates alongside data from a JSON endpoint. For this example I'm using the omg.lol API to get statuses (I'm toying with the idea of rebuilding neighbourhood.omg.lol with this stack, but that's another story). So on my HTMX-enabled HTML page, I have included this:</p>
<pre><code class="language-HTML" data-lang="HTML">&lt;section id=&quot;statuses&quot; hx-trigger=&quot;load&quot; hx-swap=&quot;innerHTML&quot; hx-get=&quot;/templates/statuses.mustache&quot; hx-vals='{&quot;@url&quot;:&quot;https://api.omg.lol/statuslog/latest&quot;}'&gt;&lt;/section&gt;
</code></pre>
<p>This triggers a request to <code>/templates/statuses.mustache</code>, with <code>@url=https://api.omg.lol/statuslog/latest</code> encoded into the query parameters (if I'd used <code>hx-post</code>, it would be passed through the body). This request will be picked up and intercepted by my service worker like this:</p>
<pre><code class="language-JavaScript" data-lang="JavaScript">self.addEventListener('fetch', async event =&gt; {
  if(new URL(event.request.url).pathname.startsWith('/templates')){
    event.respondWith(templates(event.request))
  }
  else {
    //TODO: serve other stuff from cache
    event.respondWith(fetch(event.request));
  }
});
</code></pre>
<p>(I could and perhaps <em>should</em> change this to check for a pathname ending in <code>.mustache</code> rather than starting with <code>/templates</code>, but I'm still just playing around here).<br />
The important thing here is obviously the call to <code>templates</code>, which I will break down into pieces. The first thing it does is ditch any parameters and get the template file itself:</p>
<pre><code class="language-JavaScript" data-lang="JavaScript">async function templates(request) {
  const url = new URL(request.url)
  const templateUrl = new URL(url.pathname, url.origin)
  
  // first try and get the template
  const templateres = await getTemplate(templateUrl) 
  if(!templateres.ok) return templateres // return the result on failure
  let template = await templateres.text()
  
  ...
</code></pre>
<p>The <code>getTemplate</code> function call there is just a basic &quot;check if it's in the cache, if not go fetch it&quot; kind of function:</p>
<pre><code class="language-JavaScript" data-lang="JavaScript">async function getTemplate(url) {
  // first check the cache
  const cache = await caches.open(TEMPLATE_CACHE)
  let response = await cache.match(url)
  // if not in cache, fetch it and put it in the cache
  if(!response){ 
    response = await fetch(url)
    if(response.ok) cache.put(url, response.clone())
  }

  return response
}
</code></pre>
<p>Once I have the mustache template in hand, I need the data to populate the template. I've built it so that you can either pass data through the request, or fetch the data from elsewhere using <code>@url</code> and other <code>@</code>-prefixed parameters to build up a request. At the moment I'm assuming such a request will return JSON, but this should probably be made more explicit in the future.</p>
<pre><code class="language-JavaScript" data-lang="JavaScript">  ...

  let data = {}
  let requrl = null
  let reqinit = {}
  let params = {}
  // check if anything was passed through search params
  if(url.searchParams.size &gt; 0) params = parseSearchParams(url.search)
  // check if anything was passed through the body
  const body = await request.text().trim()
  if(body) params = {...params, ...parseSearchParams(body)}
  
  // @url is a url to fetch data from
  // @&lt;other&gt; is a request init param
  // everything else is data for the template
  Object.entries(params).forEach(([key, value]) =&gt; {
    if(key == '@url') requrl = value
    else if(key.startsWith('@')) reqinit[key.slice(1)] = value
    else data[key] = value
  })

  // if we have a request url, go fetch data from the request
  // TODO: caching?
  if(requrl) {
    const responsedata = await fetch(requrl, reqinit).then(r =&gt; r.json())
    data = {...data, ...responsedata}
  }
  
  ...
</code></pre>
<p>finally, once we have the data (either from the request or passed through), we can use that to render the template and return the rendered HTML as a response for HTMX to swap in</p>
<pre><code class="language-JavaScript" data-lang="JavaScript">  ...
  // render the template with any data we have
  const output = Mustache.render(template, data)

  // finally, create and return the response
  return new Response(output, {
    status:200,
    headers:{ &quot;Content-Type&quot;: &quot;text/html&quot; }
  })
}
</code></pre>
<p><em>Et voilà</em>, service-worker-side rendering from templates stored on the server and cached locally. Obviously there's a lot of room for improvement here, especially to do with the caching strategies. But I can imagine building a PWA like this, with locally cached templates and the ability to fetch data from an external API.</p>
<p>I am very tempted to continue working on this, but I would also like to know how useful this is. Is this just something fun, or are there practical applications? Is there a future for service workers beyond caching strategies? Please, let me know. These are discussions I want to be having.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b4.0018/" />
    <summary type="html">HTMX + Mustache with a service worker fetching external data and rendering HTML from a template. Fun? Practical? You decide!</summary>
  </entry>
  
  <entry>
    <title>My new website (again)  - 11ty+Friendica</title>
    <id>https://death.id.au/b4.0017/</id>
    <updated>2025-09-22T00:33:00Z</updated>
    <published>2025-09-22T00:33:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>My new website (again) - 11ty+Friendica</h1>
<p>So, I've re-jiggered my website <em>again</em>, and moved away from Ghost, back to 11ty. As I mused in a <a href="/B4.0016">previous blog post</a>, I wanted a simpler ActivityPub id, and more control over the handle (i.e. the ability to use <code>death.au</code> as the preferred username). I already had that in a self-hosted Friendica instance, but if I moved that to my main domain, that would prevent my website from showing up at the main domain. Well, with a little help from Cloudflare, I've managed to work around that.</p>
<p>I wanted to write about what I've set up here. I'll start with the basic part:</p>
<h2>Eleventy static website</h2>
<p>I've used 11ty in the past, and I really like the flexibility it gives me in the output. I can keep my pages and articles as simple markdown, or go for an all out html bonanza and 11ty will assemble the pieces and spit out html files (or other kinds of files) for me, so I end up with a collection of files that can just be served on the web in the most basic of manners.</p>
<p>Also, it allows me to set up page metadata for routing. I've got a URL scheme of my own I'm using, very loosely inspired by Johnny Decimal, whereby all my blog posts are at <code>/b4.xxxx</code> (where <code>xxxx</code> is just an incremental id). I've also got some small portfolio pages (that I still want to expand on) under <code>cv.yy</code> and short-form microblog-style posts under <code>/n.zzzzzzzz</code>(where <code>zzzzzzzz</code> is actually a base 36 encoded timestamp). Alongside some other things like a summary and featured images (if applicable) I have a lot of control, with very little markup.</p>
<p>Setting up hosting for the pages is simple on Cloudflare. I set up a pages project pointing at my 11ty repo, tell it what command to use to build and what folder the resulting files will be in to upload and now every time I commit and push, my website is automatically built and published. I'm already using Cloudflare's DNS, so it's trivial to point <a href="https://www.death.id.au">https://www.death.id.au</a> to this pages project and my website is good to go.</p>
<h2>Friendica for the Fediverse</h2>
<p>As stated above, I was already hosting a Friendica instance as my primary window into the Fediverse. I have it set up in a docker container (via docker compose) on a webserver I'm paying for, behind a reverse proxy. With that all set up it was surprisingly easy to add a route for the reverse proxy to serve up Friendica on <a href="https://death.id.au">https://death.id.au</a>. To reconfigure Friendica itself for the new domain, there's just a command that needs to be run in a console to update the database and send information out about the new domain. And because I'm still hosting it on the old domain as well, I shouldn't be losing anything coming in, even if my followers' details don't get updated right away.</p>
<p>Friendica is an amazing piece of software. Not only does it federate with other ActivityPub services like Mastodon, it also supports &quot;Groups&quot; to follow Lemmy boards, there's a plugin for BlueSky, it supports OStatus and diaspora (not that I follow anyone on those protocols at the moment), it even supports following basic RSS feeds; all in the same interface. On top of all that, it even has Mastodon-compatible API endpoints, meaning I can use most Mastodon apps/clients to interface with it, including my favourite app, Fedilab.</p>
<p>I have my instance set up as a single-user instance, which basically just means that the home page redirects to my Friendica profile page, rather than login/sign up. But that's not really want I wanted for my root domain name. I wanted it to point to my website. So how can I get Friendica to work with my static website? I don't think I can. But I <em>can</em> work around it...</p>
<h2>Cloudflare workers to serve them both</h2>
<p>So, as outlined above, I have my static website set up at <a href="https://www.death.id.au">https://www.death.id.au</a>, and my Friendica instance set up at <a href="https://death.id.au">https://death.id.au</a>. Cloudflare is already hosting my static website for free, and I can also set up &quot;workers&quot; for free as well. Workers is, in Cloudflare's own words, &quot;A serverless platform for building, deploying, and scaling apps across Cloudflare's global network...&quot;.</p>
<p>The upshot is, I have set up a worker that intercepts all requests to <a href="https://death.id.au">https://death.id.au</a>. I've got some basic javascript code to check against a list of regular expressions for the paths I've used in my static website and if it matches one of them, return the data from <a href="https://www.death.id.au">https://www.death.id.au</a> instead. This has potential to lead to conflicts; if a URL on my static website matches one on Friendica, it's going to prioritise my static site instead. But my site's pretty basic, I think it should be fine.</p>
<p>And now, like magic, <a href="https://death.id.au/">https://death.id.au/</a> will serve up my static website, <a href="https://death.id.au/now/">https://death.id.au/now/</a> should serve my now page, etc. etc. Meanwhile, <a href="https://death.id.au/profile/death.au">https://death.id.au/profile/death.au</a> will serve up my profile page on Friendica and, most importantly, any and all ActivityPub, webfinger, API calls, etc will be served on Friendica and my social handle of <code>@death.au@death.id.au</code> is all working fine. The best of both worlds!</p>
<h2>Omg.lol  - How does this fit in?</h2>
<p>A previous version of my website was built on the omg.lol platform. That place is great, and I plan to use it well into the future. But for me to have the control I wanted over this setup, I had to abandon weblog.lol, the blogging service provided by omg.lol. However, I am still heavily utilizing other aspects of the service. I've decided that most, if not all, of the images I'm using on my website are hosted at some.pics. I have plans and ideas to perhaps integrate the omg.lol provided profile and now pages into my website. They are, after all, managed by markdown. I just have to figure out how to trigger the data to be dumped into my github repository when the data changes.</p>
<p>But the biggest part I'm using at the moment is status.lol for microblogging. It's just simple and fun. You write a message (less than 500 characters), choose an emoji to display alongside it, and send it out. There's no likes or anything on the platform, but there is a checkbox to also post the status to Mastodon. As Friendica has Mastodon-compatible APIs, this works on Friendica, too. Status.lol also supports webhooks, meaning whenever I post a status, I can get this sent to an arbitrary URL.</p>
<p>So, I set up another Cloudflare Worker to accept the data from status.lol, format an appropriate markdown file and push that into my repository. That triggers a new build of the website and within seconds, that status is also visible on my website!</p>
<h2>The future</h2>
<p>I am very happy with how all this turned out. There's nothing I love more than tinkering, and the nature of this setup allows me endless possibilities for tinkering. I have Fediverse &quot;integration&quot; via some javascript I wrote which fetches interaction data on articles and posts from Friendica, and allows someone to &quot;log in&quot; with their ActivityPub handle, and if the script can obtain a valid sharing URL, provides the ability to link directly to posts and statuses on your own instance for replying, liking, boosting, etc. As mentioned earlier, I also want to integrate omg.lol's profile and now page functionality to make it easy to modify those pages through either omg.lol or my <a href="https://neighbourhood.omg.lol">neighbourhood.omg.lol</a> app without having to manually check out and push a git repository.</p>
<p>I probably want to look into a headless CMS in the future, but I can also manage my blog posts and pages through Obsidian, as they're all basically in Markdown anyway. I set up a synced vault in my content folder, so I can edit blog posts and pages in Obsidian, on my mobile or anywhere, and then when I'm back at my computer I can review, update some metadata and then push the markdown into my git repository to be published on my website.</p>
<p>Most importantly, I think this setup is pretty stable, and most importantly, fun to tinker with. My biggest hurdle is actually just thinking up things to write about in the first place 😅</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b4.0017/" />
    <summary type="html">So, I&#39;ve re-jiggered my website *again*, and moved away from Ghost, back to 11ty, with a Friendica instance along for the ride...</summary>
  </entry>
  
  <entry>
    <title>What I Want From the Fediverse</title>
    <id>https://death.id.au/b4.0016/</id>
    <updated>2025-08-27T07:08:09Z</updated>
    <published>2025-08-27T07:08:09Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>What I Want From the Fediverse</h1>
<p>Was going to Ghost the right choice for me?</p>
<p>I thought it was a good move. My website, blog, and Fediverse presence are all on the same platform and all on the same domain. But it's not quite what I wanted...</p>
<p>For starters, my fediverse &quot;handle&quot; is <code>@death_au@death.id.au</code>. I want to use <code>.au</code> instead of <code>_au</code>, but Ghost won't let me. Secondly, (and I know this is not important in the slightest, but it matters to me for some reason), my actual ActivityPub ID is <code>https://death.id.au/.ghost/activitypub/users/index</code> which seems pretty cumbersome when it could be my ideal of <code>https://death.id.au</code>, or at least more like <code>https://death.id.au/profile/death.au</code>...</p>
<p>Another thing that's bothering me is having to use the Ghost admin interface to look at my feed of people I'm following. I liked the experience of using the Fedilab app connected to my Friendica instance. But that's because Fedilab uses the Mastodon APIs, which Friendica also implements for compatibility's sake.</p>
<p>It all seems so... messy.</p>
<p>What seems like an obvious thing to me, but doesn't seem to exist for a wide variety of technical reasons, is a personal, single-user ActivityPub server. Connected to that server, a variety of &quot;clients&quot; doing things in different ways. For example, my website could pull objects (i.e. Notes, Articles... even pictures or videos, maybe?) directly from my ActivityPub outbox and display them. I've toyed with the idea of using a static site generator like Eleventy to pull and format the data into HTML files. I could also have a separate web interface to interact with the Fediverse, i.e. to do the actual posting.</p>
<p>It could even be multiple clients! Imagine having something like the Ghost back-end for posting articles, separate from a YouTube/PeerTube-like back-end for posting videos, separate again from a feed reader that allows me to like/comment/subscribe to posts of people I'm following on Mastodon or Ghost or any other Fediverse platform. I could even set up a Mastodon API client layer so that Fedilab (or other Mastodon apps) could connect to the very same ActivityPub server as the rest of it. All posts would be from the same handle/ID. All activities from people I follow would end up in the same inbox.</p>
<p>But that's a dream at this stage. What can I achieve right now? Is there a way to install Friendica — with its wide compatibility — at my root domain, while still serving up a static website at that very same domain? It's something that seems like it should be possible with the right redirects and DNS rules, but it's something I haven't been able to crack yet...</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b4.0016/" />
    <summary type="html">Was going to Ghost the right choice for me? I thought it was a good move. My website, blog, and Fediverse presence are all on the same platform and all on the same domain. But it&#39;s not quite what I wanted...</summary>
  </entry>
  
  <entry>
    <title>Going on Safari (Chapter Two)</title>
    <id>https://death.id.au/b4.0015/</id>
    <updated>2024-09-06T00:00:00Z</updated>
    <published>2024-09-06T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Going on Safari (Chapter Two)</h1>
<p><em>This is part of a series I'm writing documenting my efforts to establish native messaging communication between my MarkDownload web extension and a native app. Go check out the <a href="https://death.id.au/b4.0013">motivation</a> or my <a href="https://death.id.au/b4.0014">previous efforts</a></em></p>
<p>Okay, so, where I left off last time was being able to communicate from the swift app → web extension (via a web view in the app) with no issues. However, communicating back to the app was a problem, in no small part because the web extension only ever sends messages back to an &quot;app extension&quot;, which is sandboxed separately from the app itself. I set both targets to be part of the same app group, but still had troubles getting any sort of data through the App Defaults back to the app.</p>
<h2>Sending message from app extension → app (for real this time)</h2>
<p>I was attempting to google my way through the issues I was having, when I discovered <a href="https://www.atomicbird.com/blog/sharing-with-app-extensions/">this post</a> by <a href="https://mastodon.social/@atomicbird">Tom Harrington</a>, which among other things, suggested using <code>NSFileCoordinator</code> and <code>NSFilePresenter</code> to write to, read from, and get notified of changes to a file which lives in a folder shared within the app group. So what does this look like in the actual code? Something like this:</p>
<pre><code class="language-swift" data-lang="swift">func beginRequest(with context: NSExtensionContext) {
	let request = context.inputItems.first as? NSExtensionItem
	let message: Any? = request?.userInfo?[SFExtensionMessageKey]
	
	var dict = message as? [String:Any] ?? [:] // assuming I'm sending an object from javascript and not just a plain string
	
	let groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: &quot;group.au.death.MarkDownload&quot;)
    let fileURL = groupURL?.appendingPathComponent(&quot;message.json&quot;)

	do {
		let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
		try jsonData.write(to: fileURL!)
	}
	catch {
		// do something with the error
		os_log(.error, &quot;Error serializing json (or saving file): %@\n\n%@&quot;, error.localizedDescription, String(describing: message))
	}
}
</code></pre>
<p>... not really much different from putting the data in the User Defaults, but I somehow feel better writing to a plain text file than some arbitrary data store (which turns out to be a .plist file in the shared folder, but I digress).</p>
<p>The differences show up mainly in the View Controller code, where I have to implement the <code>NSFilePresenter</code> protocol. This requires two new parameters: the url of the file I'm watching, and an operation queue. I'm still just getting my feet wet with swift, so I don't actually know anything about operation queues. I just copied some code I found online to simply use the <code>main</code> operation queue:</p>
<pre><code class="language-swift" data-lang="swift">class ViewController: PlatformViewController, WKNavigationDelegate, WKScriptMessageHandler, NSFilePresenter {
    @IBOutlet var webView: WKWebView!
    /// Shared URL for the message json
    var presentedItemURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: &quot;group.au.death.MarkDownload&quot;)?.appendingPathComponent(&quot;message.json&quot;)
    lazy var presentedItemOperationQueue = OperationQueue.main
    ...
}
</code></pre>
<p>To actually start getting notified that something changed, I also have to implement the <code>presentedItemDidChange</code> function. In my case, as I've written json to a file, I can just read that and pass it directly to the javascript code in my app's web view like so:</p>
<pre><code class="language-swift" data-lang="swift">func presentedItemDidChange() {
	do {
		let jsonString = try String.init(contentsOf: self.presentedItemURL!, encoding: .utf8)
		DispatchQueue.main.async {
           self.webView.evaluateJavaScript(&quot;recieveMessage(\(jsonString!))&quot;)
        }
	}
	catch {
		debugPrint(&quot;Error reading file: \(error.localizedDescription)\nfile name: \(self.presentedItemURL?.absoluteString ?? &quot;nil&quot;)&quot;)
	}
}
</code></pre>
<p>I'm not sure how thread-safe the notifications are, so I decided to hedge my bets and specifically send the code to the web view on the main thread. I don't actually know if this is necessary, but it makes me feel a little better.</p>
<p>So, this seems simple enough. Time to run it and... it still doesn't work. It turns out I've missed one very important part: I have to register the View Controller object as a file presenter via the File Coordinator. After googling for this, I also found that it's required to remove the file presenter when the app goes into the background or closes, so I have this code:</p>
<pre><code class="language-swift" data-lang="swift">override func viewWillAppear() {
	super.viewWillAppear()
	NSFileCoordinator.addFilePresenter(self)
}

override func viewWillDisappear() {
	super.viewWillDisappear()
	NSFileCoordinator.removeFilePresenter(self)
}
</code></pre>
<p>And guess what? It actually works this time!</p>
<h2>Putting it together</h2>
<p>Now, I can achieve my main aim. I have a web view inside a native app that can send a message to a web extension, and a web extension can send a message back all the way to the web view.</p>
<p>Here's a basic recap:</p>
<ul>
<li>Web View → App:</li>
</ul>
<pre><code class="language-javascript" data-lang="javascript">webkit.messageHandlers.controller.postMessage(jsonString)
</code></pre>
<pre><code class="language-swift" data-lang="swift">class ViewController: ..., WKScriptMessageHandler {
	override func viewDidLoad() {
		...
		self.webView.configuration.userContentController.add(self, name: &quot;controller&quot;)
		...
	}
	func userContentController(_ userContentController: WKUserContentController, didReceive message: WKScriptMessage) {
		let body = message.body as? String
		...
	}
}
</code></pre>
<ul>
<li>App → Web Extension</li>
</ul>
<pre><code class="language-swift" data-lang="swift">let jsonObject = try JSONSerialization.jsonObject(with: jsonString!.data(using: .utf8)) as? [String:Any]
SFSafariApplication.dispatchMessage(withName: &quot;message&quot;, toExtensionWithIdentifier: extensionBundleIdentifier, userInfo: jsonObject)
</code></pre>
<pre><code class="language-javascript" data-lang="javascript">const port = browser.runtime.connectNative(&quot;au.death.MarkDownload&quot;)
port.onMessage.addListener(message =&gt; {
	if(message.name == &quot;message&quot;) const jsonObject = message.userInfo
})
</code></pre>
<ul>
<li>Web Extension → App Extension</li>
</ul>
<pre><code class="language-javascript" data-lang="javascript">browser.runtime.sendNativeMessage(&quot;au.death.MarkDownload&quot;, jsonObject)
// or:
const port = browser.runtime.connectNative(&quot;au.death.MarkDownload&quot;)
port.postMessage(jsonObject)
</code></pre>
<pre><code class="language-swift" data-lang="swift">class SafariWebExtensionHandler: NSObject, NSExtensionRequestHandling {
    func beginRequest(with context: NSExtensionContext) {
        let request = context.inputItems.first as? NSExtensionItem
        let message: Any?
        if #available(iOS 15.0, macOS 11.0, *) {
            message = request?.userInfo?[SFExtensionMessageKey]
        } else {
            message = request?.userInfo?[&quot;message&quot;]
        }
        let jsonObject = message as? [String:Any] ?? [:]
        ...
	}
}
</code></pre>
<ul>
<li>App Extension → App (via shared file)</li>
</ul>
<pre><code class="language-swift" data-lang="swift">do {
	let jsonData = try JSONSerialization.data(withJSONObject: jsonObject, options: .prettyPrinted)
	let groupURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: &quot;group.au.death.MarkDownload&quot;)
	let fileURL = groupURL?.appendingPathComponent(&quot;message.json&quot;)
	try jsonData.write(to: fileURL!)
}
catch {...}
</code></pre>
<pre><code class="language-swift" data-lang="swift">class ViewController: ..., NSFilePresenter {
    var presentedItemURL = FileManager.default.containerURL(forSecurityApplicationGroupIdentifier: &quot;group.au.death.MarkDownload&quot;)?.appendingPathComponent(&quot;message.json&quot;)
    lazy var presentedItemOperationQueue = OperationQueue.main
    ...
    override func viewWillAppear() {
		super.viewWillAppear()
		NSFileCoordinator.addFilePresenter(self)
	}
	override func viewWillDisappear() {
		super.viewWillDisappear()
		NSFileCoordinator.removeFilePresenter(self)
	}
	func presentedItemDidChange() {
		do {
			let jsonString = try String.init(contentsOf: self.presentedItemURL!, encoding: .utf8)
			...
		}
		catch {...}
	}
}
</code></pre>
<ul>
<li>App → Web View</li>
</ul>
<pre><code class="language-swift" data-lang="swift">self.webView.evaluateJavaScript(&quot;recieveMessage(\(jsonString!))&quot;)
</code></pre>
<pre><code class="language-javascript" data-lang="javascript">function recieveMessage(jsonObject) {
	...
}
</code></pre>
<h2>Checkpoint!</h2>
<p>Does this mean my safari is over? Of course not. I still have to implement / bring in code from my existing MarkDownload extension. And because I'm still aiming for cross-platform, cross-browser, I have to figure out ways of detecting whether the native app approach is necessary / viable. If the extension is running in Chrome on a Mac, with the app installed, should it communicate with the app, or stick to using the side panel? If so, how does the communication work? It's not sending message to the app extension, but the app itself, via the app's standard input stream. So how does that work?</p>
<p>While I have hit a major milestone, it's still time to set up camp. To see what the next leg of my journey will look like. Native app messaging between other browsers and the mac os app? How does the extension work on iOS Safari? Is there a potential need for Windows and Linux versions of the native app? What about Android?</p>
<p>As hinted in the previous blog post, I do have a way of listening to the standard input stream and write to the standard output stream in the swift mac app. This <em>should</em> mean I can communicate between the app and other browsers like Chrome and Firefox. This could be the next step.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b4.0015/" />
    <summary type="html">This is part of a series I&#39;m writing documenting my efforts to establish native messaging communication between my MarkDownload web extension and a native app. Go check out the motivation or my previous efforts</summary>
  </entry>
  
  <entry>
    <title>Going on Safari (Chapter One)</title>
    <id>https://death.id.au/b4.0014/</id>
    <updated>2024-09-05T00:00:00Z</updated>
    <published>2024-09-05T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Going on Safari (Chapter One)</h1>
<p>As mentioned in my <a href="https://death.id.au/B4.0013">previous blog post</a>, I went down a bit of a rabbit hole regarding native messaging. I couldn't figure out how to communicate properly via stdin/stdout from a MacOS Swift app. If anyone has any pointers, that would greatly simplify the process; because oh boy is this a process.</p>
<p>In this post I'm discussing the prototype mentioned in the previous post. One that does allow for data to be passed back and forth, but it's convoluted. Part of that is my fault. I thought it would make sense to re-use the sidebar code in the native app, so I set up my shared ViewController with a WKWebView that loads the sidebar code.</p>
<p>I'm getting ahead of myself. I first built myself a prototype of the new MarkDownload extension utilising Manifest V3. Most of the work was handled in the sidebar code itself, calling a client script to get the html of the current page, then doing all the transformation logic in the sidebar JavaScript code. It uses ES6 modules and modern standards and works great, as a prototype. But of course, when I converted it to a Safari extension, following <a href="https://developer.apple.com/documentation/safariservices/converting-a-web-extension-for-safari">the instructions from Apple</a> it obviously couldn't open a sidebar and therefore would do nothing.</p>
<p>The instructions linked above lead to the creation of an XCode project made up of multiple parts. There's the extension Info.plist and entitlements, for both macOS and iOS (yes, iOS. Hopefully, as a result of this, we can have an iOS version of MarkDownload 🥳). Then there's the AppDelegate and storyboards for the macOS and iOS apps. There's the shared code for the extension which includes a Resources folder referencing my existing extension code, and a SafariWebExtensionHandler (which we will come back to later). Finally there's the shared app code, which includes a ViewController with a WKWebView and a html page with associated JavaScript to communicate with the ViewController to get and display the current state of the Safari extension. It also has the button to close the app and open the Safari preferences.</p>
<p>And so this is where we begin. I already have a template built in for communicating from the web view to the view controller and back. Also a handler for messages from the extension, which returns replies. This is going to be a piece of cake, right? Well...</p>
<p>Let's go through the main parts of what Apple already provided.</p>
<h2>Sending a message from web view → app</h2>
<p>This is pretty simple and already set up in the template. From the javascript side of the web view, there is this line of code:</p>
<pre><code class="language-js" data-lang="js">webkit.messageHandlers.controller.postMessage(&quot;message-string&quot;);
</code></pre>
<p>To receive this in the ViewController, inside the <code>viewDidLoad()</code> function, this line sets up the web view to be able to receive messages and load the html file:</p>
<pre><code class="language-swift" data-lang="swift">self.webView.configuration.userContentController.add(self, name: &quot;controller&quot;)
self.webView.loadFileURL(Bundle.main.url(forResource: &quot;Main&quot;, withExtension: &quot;html&quot;)!, allowingReadAccessTo: Bundle.main.resourceURL!)
</code></pre>
<p>And for this to work we also have to implement the <code>WKScriptMessageHandler</code> protocol, like so:</p>
<pre><code class="language-swift" data-lang="swift">class ViewController: PlatformViewController, ..., WKScriptMessageHandler {
...
    func userContentController(_ userContentController: WKUserContentController, didReceive scriptMessage: WKScriptMessage) {
        let body = scriptMessage.body as! String
        // process and act on message
    }
}
</code></pre>
<p>So far, so simple. It's basically trivial to substitute <code>&quot;Main&quot;</code> with <code>&quot;sidepanel&quot;</code>, add everything in the shared extension &quot;Resources&quot; directory to the macOS build target and have my sidebar magically appear inside an app. This is going great!</p>
<p>My sidebar currently sends messages to the background page, etc. by using <code>browser.runtime.sendMessage</code>, as well as using <code>browser.runtime.onMessage.addListener</code> to receive messages back, where applicable. To cope with the change necessary in the app, I made myself a <code>messenger.js</code> which contains a class like this:</p>
<pre><code class="language-javascript" data-lang="javascript">globalThis.browser ??= chrome

export default class Messenger {
  static addListener = browser.runtime.onMessage.addListener
  static removeListener = browser.runtime.onMessage.removeListener
  static sendMessage = async (data) =&gt; await browser.runtime.sendMessage(data)
}
</code></pre>
<p>However, I specifically <em>do not</em> include this file in the macOS target, and instead create a new version that does get included which contains code similar to the following:</p>
<pre><code class="language-javascript" data-lang="javascript">class Messenger {
  static addListener = //??
  static removeListener = //??
  static sendMessage = (data) =&gt; webkit.messageHandlers.controller.postMessage(JSON.stringify(data))
}
</code></pre>
<h2>Sending message from app → web view</h2>
<p>There are some question marks in the above. How do I send messages from app to the web view? In the template provided by Apple, they... don't. They just do <code>webView.evaluateJavaScript(&quot;//javascript code&quot;)</code>.</p>
<p>So, I used that to my advantage and came up with the following solution. Modifying the app-based messenger.js above:</p>
<pre><code class="language-javascript" data-lang="javascript">class Messenger {
  static listeners = []
  static addListener = (x) =&gt; Messenger.listeners.push(x)
  static removeListener = (x) =&gt; Messenger.listeners = Messenger.listeners.filter(y =&gt; y !== x)
  static sendMessage = async (data) =&gt; await webkit.messageHandlers.controller.postMessage(JSON.stringify(data))
  static recieveMessage = (message) =&gt; Messenger.listeners.forEach(x =&gt; x(message))
}
</code></pre>
<p>I just keep a static list of listeners, and add a new <code>recieveMessage</code> function that calls all the functions in the list of listeners. This allows me to do the following in my ViewController:</p>
<pre><code class="language-swift" data-lang="swift">self.webView.evaluateJavaScript(&quot;Messenger.recieveMessage(\(String(decoding: try JSONEncoder().encode(message), as: UTF8.self)))&quot;)
</code></pre>
<p>Neat!</p>
<p>Although, so far, none of that communication is going to my extension.</p>
<h2>Sending message from app → extension</h2>
<p>There's an example of this built into the template, too. Easy peasy. The ViewController contains this function:</p>
<pre><code class="language-swift" data-lang="swift">    func sendMessageToExtension(name: String!, userInfo: [String:String]) {
#if os(macOS)
        SFSafariApplication.dispatchMessage(withName: name, toExtensionWithIdentifier: extensionBundleIdentifier, userInfo: userInfo) { error in
            debugPrint(&quot;Message attempted. Error info: \(String.init(describing: error))&quot;)
        }
#endif
    }
</code></pre>
<p>(I'm ignoring the <code>#if os(macOS)</code> for now. I'm going to cross the iOS bridge when I come to it)</p>
<p>To receive this message in the javascript I need to implement this in my background.js</p>
<pre><code class="language-javascript" data-lang="javascript">let port = browser.runtime.connectNative(&quot;au.death.MarkDownload&quot;) // the identifier doesn't matter. It will only connect to the related app
port.onMessage.addListener((message, sender) =&gt; {
	// process the message
	// if I want to send a message back, I can use
	// sender.postMessage(response)
	// ... right?
});
</code></pre>
<h2>Sending message from extension → app?</h2>
<p>This is where things start to get a little shaky. As you can see in the comments above, I'm questioning <code>sender.postMessage</code>. <em>In theory</em> that sends a message back through the port to the macOS app. In practice, I have no idea how to listen for this message on the app side. If anyone has any knowledge of this, I would really appreciate it. It might render the rest of this post moot.</p>
<p>Surely Apple provides an example for this? Well, they do, but there's a catch. Let's get into the example code. Rather then sending a message through the open port, they do this:</p>
<pre><code class="language-javascript" data-lang="javascript">browser.runtime.sendNativeMessage(&quot;application.id&quot;, {message: &quot;Hello from background page&quot;}, function(response) {
  console.log(&quot;Received sendNativeMessage response:&quot;);
  console.log(response); 
});
</code></pre>
<p>Again, the application ID doesn't matter - Safari will only communicate with the linked app. To receive the message, there is this code...</p>
<pre><code class="language-swift" data-lang="swift">func beginRequest(with context: NSExtensionContext) {
	let request = context.inputItems.first as? NSExtensionItem

	let profile: UUID?
	if #available(iOS 17.0, macOS 14.0, *) {
		profile = request?.userInfo?[SFExtensionProfileKey] as? UUID
	} else {
		profile = request?.userInfo?[&quot;profile&quot;] as? UUID
	}

	let message: Any?
	if #available(iOS 15.0, macOS 11.0, *) {
		message = request?.userInfo?[SFExtensionMessageKey]
	} else {
		message = request?.userInfo?[&quot;message&quot;]
	}

	os_log(.default, &quot;Received message from browser.runtime.sendNativeMessage: %@ (profile: %@)&quot;, String(describing: message), profile?.uuidString ?? &quot;none&quot;)

	let response = NSExtensionItem()
	if #available(iOS 15.0, macOS 11.0, *) {
		response.userInfo = [ SFExtensionMessageKey: [ &quot;echo&quot;: message ] ]
	} else {
		response.userInfo = [ &quot;message&quot;: [ &quot;echo&quot;: message ] ]
	}

	context.completeRequest(returningItems: [ response ], completionHandler: nil)
}
</code></pre>
<p>... in the <code>SafariWebExtensionHandler</code>. And the thing about that class is that it has a target membership of (that is to say, it is included in) the Extension, but <em>not</em> the App. So wait, there's another layer here? The javascript is sending a message to a piece of native swift code that is not part of my app. So, the title for this section should have been <em>Sending message from extension → extension</em>? Or perhaps <em>... javascript extension → native extension</em></p>
<p>Okay then, how do I <em>actually</em> send data to my app then?</p>
<h2>Sending message from (native) extension → app</h2>
<p>There is no example for this. <a href="https://developer.apple.com/documentation/safariservices/messaging-between-the-app-and-javascript-in-a-safari-web-extension">Check out the documentation</a>. It covers app → (javascript) extension and (javascript) extension → (native) extension, but nowhere does that data actually make it back to the app. In fact, the documentation does actually contain a paragraph hinting at this:</p>
<blockquote>
<p>Because the macOS or iOS app and the native app extension each run in their own sandboxed environments, they cannot share data in their respective containers. You can store data in a shared space that both the macOS or iOS app and the native app extension can access and update, by enabling app groups. For more information, see <a href="https://developer.apple.com/library/archive/documentation/General/Conceptual/ExtensibilityPG/ExtensionScenarios.html#//apple_ref/doc/uid/TP40014214-CH21-SW6">Sharing Data with Your Containing App</a>.</p>
</blockquote>
<p>I didn't see this at first, and did a whole bunch of googling, the result of which was often replies to the effect of &quot;use <code>UserDefaults</code>&quot;. The link in that quote does point to a page describing how the app and the app extension run in different sandboxed contexts, and therefore both need to be added to a shared &quot;app group&quot;. This way there is a shared container which both the app and extension can read and write to.</p>
<figure>
  <img src="https://cdn.some.pics/deathau/66d93f2f2a0fb.png" alt="An image showing how the app and extension processes are separate and have separate containers, but can optionally communicate with a shared container as well."/>
  <figcaption><strong>Figure 4-1</strong> An app extension’s container is distinct from its containing app’s container</figcaption>
</figure>
Okay, so... First thing, I have to add in the App Groups capability to each of the targets in my XCode project
![A partial screenshot showing the adding of an "App Groups" capability to the "Signing &amp; Capabilities" of a target in XCode.](https://cdn.some.pics/deathau/66d93f8128d16.png)
Then add in an identifier for the group that will be identical across targets:
![A partial screenshot showing the set up of an identifier for an "App Group" named `my.group.identifier`](https://cdn.some.pics/deathau/66d93fdd9be7b.png)
Side note: I found that for the iOS targets, I had to use `group.` as the first part of my identifier.
<p>Once that is in place, I can modify the native extension code to store the data in the shared User Defaults</p>
<pre><code class="language-swift" data-lang="swift">func beginRequest(with context: NSExtensionContext) {
	let request = context.inputItems.first as? NSExtensionItem
	...
	let message: Any?
	...
	var dict = message as? [String:Any] ?? [:] // assuming I'm sending an object from javascript and not just a plain string
	let defaults = UserDefaults(suiteName: &quot;my.group.identifier&quot;)
	do {
            let jsonData = try JSONSerialization.data(withJSONObject: dict, options: .prettyPrinted)
            defaults?.set(jsonData, forKey: &quot;message&quot;)
        }
        catch {
			// do something with the error?
        }
	...
	context.completeRequest(returningItems: [ response ], completionHandler: nil)
}
</code></pre>
<p>I can also set up an observer in my ViewController like so:</p>
<pre><code class="language-swift" data-lang="swift">class ViewController: PlatformViewController, WKNavigationDelegate, WKScriptMessageHandler {
	override func viewDidLoad() {
        super.viewDidLoad()   
        UserDefaults(suiteName: &quot;my.group.identifier&quot;)?.addObserver(self, forKeyPath: &quot;message&quot;, options: .new, context: nil)
        ...
	}

	override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
        if object is UserDefaults {
            // Here you can grab the values or just respond to it with an action.
        }
    }

	deinit {
        UserDefaults(suiteName: &quot;my.group.identifier&quot;)?.removeObserver(self, forKeyPath: &quot;message&quot;)
    }
}
</code></pre>
<p>However, take the above with a grain of salt, because on my machine, it <em>doesn't</em> work. I'm not sure why. The observer never gets triggered, and accessing the User Defaults by a button press or something just returns <code>nil</code> for the &quot;message&quot; key, every time. This is despite being able to retrieve it in the SafariWebExtensionHandler (where it was written in the first place). It even persists between runs!</p>
<p>I can't figure this out. So, on to something else...</p>
<h2>Sending message from (native) extension → app? Please?</h2>
<p>At this point, I could think of only one way to get a message from my extension to my app: A custom url protocol handler. This is far from ideal, as I don't think I can send an entire webpage converted to markdown via url. There's a cap on the length. But, if I can get it working, perhaps there's something else I can do? Maybe write the data to a temp file then notify the app of said file's existence via pinging a url?</p>
<p>Well, either way, to wrap this post up, here's how I managed to get url handling working.</p>
<p>The first step is to update the Info.plist with a new url type. In my case, I chose <code>markdownload</code> as my url scheme
<img src="https://cdn.some.pics/deathau/66d9404ee4cea.png" alt="A partial screenshot showing the setup of a custom  url type for a macOS target in XCode." /></p>
<p>To handle the url, I need to add a function to my AppDelegate. From there I need to get the ViewController, which is theoretically attached to the main window, so I can pass the data there (as is the whole point of this exercise).</p>
<pre><code class="language-swift" data-lang="swift">@main
class AppDelegate: NSObject, NSApplicationDelegate {
	...
    func application(_ application: NSApplication, open urls: [URL]) {
        let url = urls.first
        if(url != nil) {
            let mainVC = application.mainWindow?.contentViewController
            ( mainVC as! ViewController).handleUrl(url: url!)
        }
    }
}
</code></pre>
<p>In the ViewController, I've created that <code>handleUrl</code> function to get data if it receives a url of the form <code>markdownload://message-recieved</code> and fetch the data that was saved in the User Defaults. (As stated above, this isn't working for me, so I'm not actually doing anything with the data apart from forwarding it to the web view, but I thought I'd document it anyway)</p>
<pre><code class="language-swift" data-lang="swift">    func handleUrl(url: URL) {
        print(url)
        var host:String?
        if #available(macOS 13.0, iOS 16.0, *) {
            host = url.host()
        } else {
            // Fallback on earlier versions
            host = url.host
        }
        
        if(host == &quot;message-recieved&quot;) {
            let defaults = UserDefaults(suiteName: &quot;my.group.identifier&quot;)
            let message = defaults?.string(forKey: &quot;message&quot;)
            if(message != nil) {
				defaults?.removeObject(forKey: &quot;message&quot;) // don't need it anymore
				self.webView.evaluateJavaScript(&quot;Messenger.recieveMessage(\(String(decoding: message!, as: UTF8.self)))&quot;) // send it to the webview javascript for processing
            }
        }
    }
</code></pre>
<p>And now that is all in place I can modify the web extension handler to ping the url after adding the data into the defaults</p>
<pre><code class="language-swift" data-lang="swift">func beginRequest(with context: NSExtensionContext) {
	...
	defaults?.set(jsonData, forKey: &quot;message&quot;)
	NSWorkspace.shared.open(URL(string:&quot;markdownload://message-recieved&quot;)!)
    ...
}
</code></pre>
<p>That part of it actually works! I can hit breakpoints in the <code>handleUrl</code> function I made (something I found I <em>can't</em> do in the web extension handler, what with it being a separate process/sandbox)</p>
<h2>First leg complete</h2>
<p>Even though I never really got anything working to the extent I wanted to, I learned a lot in this first leg of my Safari journey. About how Apple handles extensions, inter-process communication... Would you believe I didn't even know Swift before starting this? Thankfully, I did know Objective-C, C# and JavaScript, which Swift feels like a mashup of, so it was easy to follow and understand the code samples provided by Apple.</p>
<p>My code so far is an absolute shambles, and I think I'm going to have to start my &quot;from scratch&quot; branch all over again from scratch. But I've learned, I've grown, and in my googling, I may have stumbled upon an approach that could work for browsers <em>other than</em> Safari, which opens the possibility of bringing whatever app functionality I end up creating to other browsers. We shall see.</p>
<p>For now, I'm tired from cutting through all this jungle, and need to set up camp, have a rest and consult my map and compass before setting out on the next leg of my Safari safari.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b4.0014/" />
    <summary type="html">As mentioned in my previous blog post, I went down a bit of a rabbit hole regarding native messaging. I couldn&#39;t figure out how to communicate properly via stdin/stdout from a MacOS Swift app. If anyone has any pointers, that would greatly simplify the process; because oh boy is this a process.</summary>
  </entry>
  
  <entry>
    <title>Going on Safari (Prologue)</title>
    <id>https://death.id.au/b4.0013/</id>
    <updated>2024-09-03T00:00:00Z</updated>
    <published>2024-09-03T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Going on Safari (Prologue)</h1>
<p>I'm currently working on a complete rewrite of my browser extension, MarkDownload. In the current version, most of my logic and processing takes place on a persistent background page. An invisible webpage, just sitting there taking up memory, waiting for someone to click that button and get the process started.</p>
<p>Google Chrome decided to change all that, and get rid of background pages in favour of service workers, which — long story short — <em>don't</em> persist in the background. They wake up when given certain signals then disappear again. It's certainly not a worse way of doing things, but it's very different to what's working now, so it's a good time to re-think and re-write the entire extension.</p>
<p>One thing that's kind of annoying about the current version is the ephemeral nature of the popup. If you clip a page, make some modifications and then accidentally click off the popup, it's all gone and you have to start over again. Well, Chrome (and most chromium-derived browsers) has a side panel now, and Firefox already had a sidebar, so now seems like a good time to take advantage of all that. There's just one major problem with all this: Safari does not have a sidebar of any sort.</p>
<p>Something else that's at the back of my mind is the image downloading, and especially how it doesn't really work well on Safari browsers. These things are important, because the Safari version of the extension is the only one I charge for (I have to pay for my Apple developer license somehow), but it currently has a worse experience than the free versions.</p>
<p>Due to the way Apple goes about Safari extensions, there is a MacOS app attached to it. The current version of the app is just the templated default from Apple. It's useless. It just pops up a window to tell you the extension is installed and there's a button to quit the app and open Safari's extension options. I've had the idea for a long time that I should be able to fix image downloading on Safari by having that MacOS app somehow handle the image downloads. And also, Safari does not have a sidebar, but I could run that in the native app and have it communicate with the extension — that is the point of having an app attached to an extension, after all.</p>
<p>I started writing this blog post as a way to explain the technical stuff I've learned so far, because the process for getting data between the extension and the app was (and is) not obvious or easy. I have a prototype solution at the moment involving native messaging, webkit message handlers, User Defaults and custom protocol handlers. It's convoluted. But I've waffled on enough about the reasoning that I think this should become a multi-part blog post with the technical stuff separate.</p>
<p>Also, I spy another rabbit hole over there to do with native messaging... If I can elevate the experience for Safari with a native app, could I do the same for other browsers and platforms? Could I offer a paid-for version of a desktop app that enhances the extension's functionality with previously impossible features? Like, at least, the ability to save clipped websites and images <em>outside</em> the download folder (the single most-requested and most impossible feature)?</p>
<p>We shall see. For now, I'm going to do a little more research and tinkering while I write up a more technical blog post on how to achieve what I want. See you then.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b4.0013/" />
    <summary type="html">I&#39;m currently working on a complete rewrite of my browser extension, MarkDownload. In the current version, most of my logic and processing takes place on a persistent background page. An invisible webpage, just sitting there taking up memory, waiting for someone to click that button and get the process started.</summary>
  </entry>
  
  <entry>
    <title>Johnny Decimal (Part One)</title>
    <id>https://death.id.au/b4.0012/</id>
    <updated>2024-04-09T00:00:00Z</updated>
    <published>2024-04-09T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Johnny Decimal (Part One)</h1>
<p>I've taken a leap and have started organising all the things with a Johnny Decimal system.
It's been something I've been mulling over for some time, and with the Johnny Decimal workshop coming soon and an attempt to switch to a new OS, it seems like a perfect time to get organised.</p>
<p>I've read through the Johnny Decimal website a few times over the years, and I've been reading the workbook as well. But as with anything organisational, it's deeply personal and you have to take what works for you and throw away what doesn't. For example, the recommended process for organising with Johnny Decimal is to write a scope statement and brain dump all of your related stuff for a week before moving to the organisation process.
Well, my brain is impatient, and if I were to follow that I'd probably spend an hour writing down a bunch of stuff on the first day, write a few more on the second, and then probably forget about the process altogether until a couple of weeks later, at which point I feel guilty about it and don't want to touch it. It might sound overdramatic, but I'm pretty sure that's what happened when I tried to use Johnny Decimal to organise my Obsidian Vault in the past.
So, screw that, I just jumped into creating Areas and Categories straight away.</p>
<iframe src="https://coub.com/embed/3e6xav" allowfullscreen="true" frameborder="0" width="640" height="354" allow="autoplay"></iframe><script async="true" src="https://c-cdn.coub.com/embed-runner.js"></script>
<p>To start with, I created a new Obsidian vault (knowing full well that this time it's going to be much more than just Obsidian). Rather than making an index note or a canvas (which I did start playing with, to be fair), I decided to just start with folders. I created folders for all the projects I've got going on from the top of my head. I created folders for documents and important resources. As I went, I grouped some things together that made sense, not worrying about how deep or broad anything went, yet. I went through my cloud storage folder for things I wasn't remembering and added those folders in.
When I thought I had enough stuff, I put more effort into the grouping. I realised I had a lot of categories and I didn't feel like I had enough room for only 10 areas. But I quickly noticed that a lot of the areas and categories were specifically for software development work, while others were more personal, life management stuff. I considered splitting each of the dev areas out into their own systems, but that seemed like overkill. Instead, I settled on a system for dev stuff, and a system for life, etc.</p>
<p>And so, I had my first base for IDs: <code>W01</code> for all my work and dev related stuff, and <code>L42</code> for Life, the Universe and Everything (geddit?).
The way I'd split up my areas, <code>W01</code> was still looking a bit tight for areas, but workable. But I noticed something else. Quite a lot of the areas were for developments and projects that were out there in the open for everyone to see, while there were a few areas that were related to actual employed work that was <em>not</em> out in the open. So I had an idea: What if I split the work system into two: One for public and one for private? I could potentially publish notes in an Obsidian vault with the public stuff (Working with the garage door open), while not risking publishing private stuff, because it's entirely separate.
Rather than boring old <code>W01</code> and <code>W02</code>, I couldn't help but think of HTTP Code 401 — meaning &quot;Unauthorised&quot; — because people would need to be authorised to see stuff in that system. Also, because of the nature of my public work, it made sense to label the public one 201 — meaning &quot;Created&quot;.
I know that Jonny says that systems work best as letter-number-number, but the whole point is to keep things memorable, and I have a mnemonic for this, so I'm just rolling with it.</p>
<p>Now with my System IDs in place, I was ready to roll out the next levels of ids, to fit with the Johnny Decimal <code>SYS.AC.ID</code> format
When creating numbers for my areas, I stuck to Johnny's template of using <code>00-09</code> for stuff to do with the system. I also found a couple of categories I couldn't seem to fit into a particular area, so I created <code>90-99 Miscellany</code> as an area to catch all that stuff. For example, <code>L42.91</code> is a place to archive some old journal stuff; <code>201.91</code> is for a LEGO keyboard side-project which doesn't really fit into any of my other areas or categories.
Following on from that idea of using <code>9</code> for miscellany, I set up <code>201.99</code> as a place to hold random ideas about things to work on in the future.</p>
<p>Also, I decided to follow the <code>A0</code> categories (<code>10</code>, <code>20</code>, etc) as being reserved for system information about the area (not that I'm really using it yet). But I also took this one step further and reserved the <code>AC.0x</code> IDs for special standards within a particular category. I plan to use <code>AC.01</code> for notes about a particular category. Especially if I'm eventually planning to publish stuff under this format, it makes sense to have at least a note explaining the context of a category. Using my LEGO Keyboard example, <code>201.91.01</code> would be the ID for a note explaining the general idea and reasoning around the project.
Similarly, I'm using <code>L42.00.01</code> to store a note <em>about</em> the system, documenting non-Johnny standards like this. while keeping <code>00.00</code> free as the index.
I'm also planning on using <code>AC.10</code> as a category inbox, for stuff that I still have to sort and/or assign an ID to. This means all my IDs start at 11, which is a little odd, but it just means every digit in the id starts with 1 and goes up from there when giving a thing an ID. if there's a zero in it, it's some sort of meta thing, or information about the thing. (I mean, that's not strictly true because 20, 30, etc exist, but it works in my head, okay?)</p>
<p>As I went along like this, I also noticed that in a few of the areas, I had created a folder for general resources and/or documentation that would assist me in that area. For example, in <code>201.20-29 Game Dev</code> I had a category for documentation of the game engines I was using.
Even in my life system, I had a couple of categories for things like the ringtones I keep around to use on my phone. And so, just as <code>9</code> was my standard number for miscellany, so to <code>8</code> has become my number for resources and documentation.
That game engine documentation is all going to go in <code>201.28</code>. Plus I also have an entire area (<code>201.80-89</code>) for resources and docs on various technologies, tools and development resources (all the music, SFX and resource packs I've purchased from Humble over the years are going to live in <code>201.88</code>). My ringtones are in <code>L42.87</code>.
I'm also planning on using <code>AC.08</code> as a sort of container for &quot;category bookmarks&quot;. Links to a GitHub repository, or a ClickUp task list, that kind of thing.</p>
<p>So we have some standards going! <code>0</code> means meta, <code>9</code> means misc and <code>8</code> means some sort of resource. It won't always work that way, but it's enough to be able to look at the system as a whole (i.e. all three systems) and see consistency and patterns. And that's what we want, right?
If I want the DragonRuby documentation, it's a resource, so will have an <code>8</code> in it, and it's to do with my game dev work, which is public, so it's probably in <code>201.28</code> somewhere. There's probably even a direct link in my bookmarks in <code>201.28.08</code>. If it's not, for whatever reason, there are at most two or three other locations it could be (somewhere in the <code>201.80</code>s, perhaps?) and even if it is, I should probably also duplicate it where I looked first.</p>
<p>The only thing I was having trouble categorising so far was my blog posts. Looking at the Johnny Decimal website for inspiration, it seems the blog posts are identified with an example of: <code>D01.22.00.0031</code> (if I include the system identifier, which isn't done on the website). It's a lot. It kinda has to be, really. Theoretically <code>22.00</code> is an index, but it hasn't been built yet, and all the posts are identified by four-digit numbers under that.
So for me, <code>201</code> is my public dev, <code>10-19</code> is general stuff, <code>14</code> is my website. So, like <code>201.14.11.0001</code> or even <code>201.14.00.0001</code>? It just feels like a lot. Plus, I kind of have another level in there because I've had a couple of different blogs over the years, and I feel like consolidating them. But keeping them as sequential doesn't make sense to me, as each blog had its own context.
So, I'm gonna cheat. I'm making a new area just for my blog: <code>201.B0-B9</code>. Yep, I'm putting letters in the categories; time to call the men in the white coats to come and take me away.
But seriously, the reason I'm doing this is because the letter makes it unique, and while it will live in my <code>201</code> system, none of the resulting IDs need to reference that to be unique. <code>B1</code> is the category for my oldest blog (at least the oldest one I can find), with <code>B1.01</code> being the content of the &quot;About&quot; page for that old blog, providing the context for the rest of the posts in that category. Right now, this post is <code>B4.12</code>, which is much better than <code>201.14.14.0002</code> (though I am considering using the 4 digits like JD does, so <code>B4.0012</code>).
It's going to be so great when I fix it all up on my website and can link directly to <code>https://death.id.au/B4.0012</code> — but that's still way off in the future.</p>
<p>For now, most of my folders are empty and still need to be populated with stuff. I also need to build an index that I can easily update and add IDs to. And I need to start using the IDs in other contexts; like e-mail, ClickUp, code repositories, etc. Plus, you know, there is <em>actual</em> work to be done, aside from organising all my work.</p>
<iframe src="https://coub.com/embed/3e6wp1" allowfullscreen="true" frameborder="0" width="640" height="360" allow="autoplay"></iframe><script async="true" src="https://c-cdn.coub.com/embed-runner.js"></script>
<p class="caption">Just replace the word &quot;children&quot; with &quot;work&quot; in this clip.</p>
<p>But I'm going to try moving my stuff over as I go, tagging things with JD.IDs as I need them.
I'm trying to embrace &quot;<a href="https://notes.andymatuschak.org/zCMhncA1iSE74MKKYQS5PBZ">Working with the garage door open</a>&quot;, at least in part so I stick with it.
I'll try to keep updated.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b4.0012/" />
    <summary type="html">I&#39;ve taken a leap and have started organising all the things with a Johnny Decimal system.
It&#39;s been something I&#39;ve been mulling over for some time, and with the Johnny Decimal workshop coming soon and an attempt to switch to a new OS, it seems like a perfect time to get organised.</summary>
  </entry>
  
  <entry>
    <title>OBTF, Subtext, ELF... Oh my!</title>
    <id>https://death.id.au/b4.0011/</id>
    <updated>2024-02-20T00:00:00Z</updated>
    <published>2024-02-20T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>OBTF, Subtext, ELF... Oh my!</h1>
<p>Some random ideas that have popped up are sort of coalescing. Into what, I'm not sure yet. But I fell down a rabbit hole, and this article represents my climb back up. As such, I wouldn't consider this a proper, professional think piece. Just some notes, thoughts and scratchings that I decided to share.</p>
<hr />
<h4>OBTF</h4>
<p>@<a href="https://pkm.social/users/ellane" title="Ellane W">Ellane W</a> has been documenting her experiments with OBTF (One Big Text File) (<a href="https://www.blog.plaintextpaperless.com/i/141545358/the-one-big-text-file-experiment-has-begun">link</a>). She links off to others who are doing this, and it's something I've seen before and not paid too much attention to. But now I'm seriously considering it.</p>
<p>One thing she's doing that stuck out to me, is following bullet-journaling principals and marking each entry with a single letter followed by a period (e.g. <code>N.</code> for note, <code>T.</code> for task, <code>E.</code> for event...). She chose letters rather than symbols so they could be quickly and easily entered on a phone without too much hunting. Double-tapping space will enter the period for her. Plus it's simple to search write queries for.</p>
<h4>Subtext</h4>
<p>Entirely separate to that whole thing, I stumbled across <a href="https://github.com/subconsciousnetwork/subtext">Subtext</a>, which bills itself as &quot;markup for note taking&quot;. The general idea is to treat each line as its own block with a &quot;sigil&quot; at the start indicating the block's purpose (e.g. <code>#</code> for heading, <code>-</code> for list item, <code>&gt;</code> for quote). It bears a resemblance to markdown, except for that focus on blocks, and a complete lack of formatting (it's an index card, not a page).</p>
<p>It also supports linking, through simple URLs (or surrounding unusual URLS in <code>&lt;&gt;</code>), as well as shorthand &quot;slashlinks&quot; to local files. The intention is for links to be transcluded. This allows things like tables to be included by linking to a CSV file. Or if you really need presentation formatting, you can link to a markdown file or PDF or something.</p>
<h4>ELF</h4>
<p>Subtext is conceptually similar to <a href="http://www.thetednelson.com/">Ted Nelson</a>'s idea of an ELF (Evolutionary List File). An ELF has three elements:</p>
<ul>
<li>Entries: A discrete unit of information designated by the user. Text (long or short), symbols, pictures, anything. <em>I could see these as &quot;files&quot; as they exist today. Remember, Ted was living in the 60s and 70s and imagining the future of computers at this point.</em></li>
<li>Lists: An ordered set of entries as designated by the user. An entry can exist in any number of lists. <em>#<a href="/tag/MOC">MOC</a>s?</em></li>
<li>Links: A connector, designated by the user, between two entries in different lists. An entry in one list may be linked to only one entry in another list. <em>Of course, entries in lists can be linked <strong>to</strong> from multiple places</em><br />
I think of Notion. Every Notion &quot;document&quot; is actually a list of blocks that can be of different types. Blocks can be linked to specifically.<br />
Backlinks are a big thing, too. Seeing everything that links to the entry you're looking at is important. Of course, Ted is all about that transclusion, too. So this is where the &quot;slashlinks&quot; of Subtext come into it. A Subtext file is a <strong>list</strong> of <strong>entries</strong>. There is one entry per line - mostly plain-text, with specified &quot;sigils&quot; to indicate different types of meanings, or URLs/slashlinks, which can transclude files as different kinds of entries. As a plain-text file, it can't really block-reference entries in other list files, so it doesn't quite fit the ELF 100%, but it's close.</li>
</ul>
<h4>Putting it all together</h4>
<p>Can we use the Subtext and ELF principals in OBTF? Subtext &quot;sigils&quot; are basically just the bullet types Ellaine was using for her bullet-journal-style approach to the OBTF. Also keep in mind that the OBTF <em>isn't</em> the entire knowledge base - it's just an inbox; a staging area; a replacement for your daily notes; an ever-evolving #<a href="/tag/MOC">MOC</a> of your day-to-day life.<br />
It's a highly personal thing. You could make a note in your OBTF, then later refactor it out into its own note file (I would leave the OBTF entry as-is and just link it to the new note). You can quick-capture tasks, which you keep track of through other means (by Dataview queries in Obsidian, or moving them over to a task management app as part of a daily process). It's all up to you.</p>
<p>I think I'm inspired to start my own OBTF experiment. I've been off-and-on looking for a new journaling practice since I fell off that bandwagon many years ago, but nothing has quite stuck. Maybe I could try some form of interstitial journaling, along with capturing tasks, ideas, meeting notes, etc in a OBTF list, using bullet journaling principals for each entry. As I intend to keep this file in Obsidian, I intend to use markdown-compatible &quot;sigils&quot; from Subtext (e.g. <code>#</code> for heading, <code>-</code> for list item, <code>&gt;</code> for quote), as well as others like <code>- [ ]</code> for tasks.<br />
There are some plugins/styles for rendering different kinds of &quot;task statuses&quot;, too. I could render <code>- [i]</code> as a lightbulb (💡) for example. Simple to search for and query, as well. A bit cumbersome to type on mobile, but I could put in some work to customize the toolbar in obsidian mobile...</p>
<p>Alternately, I could just keep things like &quot;ideas&quot; as basic text blocks with a hashtag (e.g. <code>#idea</code>) at the end of the line if it's something I'll want to search for later. It might resemble a more free-form journal that way.</p>
<hr />
<p>As I stated at the start, this was a collection of ideas I needed to braindump and decided to share. So I don't really have a proper conclusion for you.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b4.0011/" />
    <summary type="html">Some random ideas that have popped up are sort of coalescing. Into what, I&#39;m not sure yet. But I fell down a rabbit hole, and this article represents my climb back up. As such, I wouldn&#39;t consider this a proper, professional think piece. Just some notes, thoughts and scratchings that I decided to share.</summary>
  </entry>
  
  <entry>
    <title>Procrastinations issues</title>
    <id>https://death.id.au/b3.13/</id>
    <updated>2020-02-03T00:00:00Z</updated>
    <published>2020-02-03T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Procrastinations issues</h1>
<p><em>I barely managed to get this written at all...</em></p>
<p>I'm a natural born procrastinator. I can't help it - I just keep finding ways to avoid the work I know I have to do. This newsletter is an obvious example. I somehow made sure never to commit myself to a schedule, and now that I've hit the date for sending out my third issue, I haven't even written so much as a draft. So keep in mind - I haven't even proofread this one properly.</p>
<p>It's an issue that is seeping into all aspects of my life, and it comes and goes in cycles. A month ago I was motivated to build and stick to a weekly plan. I was going to collect articles I read so I could reference them in this newsletter. I was going to do a bunch of writing for DevReady, and post links to that in this newsletter as a way to get more content going.</p>
<p>My weekly plan lasted about a fortnight, I haven't retained much of what I've read for it to be worth posting and I've done exactly zero writing for DevReady.</p>
<p>The thing is, I know what needs to be done - it's now about finding the motivation to do it. And I know enough to know that &quot;finding motivation&quot; is a myth. An excuse to further put off the work. What I really need to do is dig in and figure out what it is that is holding me back. Figure out <em>why</em> I &quot;lack motivation&quot; and how I can break down those barriers to get back to work.</p>
<p>Last week I mentioned the concept of the sculpture/sculptor, and I'm reminded of a direct quote from the r!Animorphs fic I referenced which kinda sums up how I'm feeling at the moment:</p>
<blockquote>
<p>I don’t know. I didn’t have a conclusion. I didn’t even really have a <em>question</em>.</p>
<p>But it seemed like something I really ought to think about.</p>
</blockquote>
<h1>Stuff I've found interesting recently:</h1>
<h2><a href="https://medium.com/@dsilvestre?source=post_page-----76b54838a877----------------------">Dan Silvestre</a> - <a href="https://medium.com/swlh/digital-minimalism-how-to-simplify-your-online-life-76b54838a877">Digital Minimalism: How to Simplify Your Online Life</a></h2>
<p>This is a list of things you can do to &quot;clean up&quot; your digital life. Some of the things suggested here I've already done, but it feels like it's time for another purge of a whole bunch of digital noise in my life that I really don't need... Apps I've downloaded and don't use, files I'm hoarding in my Downloads folder, bringing back some semblance of a schedule that makes sense.</p>
<p>For example, I've started using <a href="https://wavebox.io/">Wavebox</a> as a browser for work. I've been using it for ages as a home for my digital apps (email, Slack, Jira, etc) and now that it's built on Chromium and has proper tab support and a neat profile system, it's basically become my default browser at work. The one thing it seems to be crucially missing is saving my open tabs for when I next start my browser</p>
<p>But I'm coming to see that as a <em>good</em> thing. It means I don't keep a bunch of stuff open because I know it will disappear, and at the end of my day I <em>have to</em> sort out what any open tabs mean to me and set up reminders or whatever for later, leaving me with a clean slate each morning.</p>
<h2><a href="https://haacked.com/about/">Phil Haack</a> - <a href="https://haacked.com/archive/2020/01/23/burnout/">Recovering from Burnout</a></h2>
<p>This is a good article from a man formerly in denial about his own burnout, and his plan to turn it around. He doesn't really offer a lot in the way of actionable advice - he admits he's in a privileged position where he can afford to just take time out and have a break, which not everyone can do (myself included).</p>
<p>But it's pushed me into facing some thoughts I've been having about how I might be burnt out, and encouraging me to find a way to address it. I'm not there yet, and the digital noise purge I mentioned earlier may be a baby step towards getting the space I need. I'm not sure that a break will help at this stage anyway, it's more about getting the load under control to move forward.</p>
<h2><a href="https://ryanandrewlangdon.wordpress.com/author/rlangdon9/">InsideMyMind</a> - <a href="https://ryanandrewlangdon.wordpress.com/2020/01/28/today-i-learned-that-not-everyone-has-an-internal-monologue-and-it-has-ruined-my-day/">Today I Learned That Not Everyone Has An Internal Monologue And It Has Ruined My Day.</a></h2>
<p>Okay, so this blew my mind too, but after the initial reaction, I think I've learned to be more careful with this information.</p>
<p>So, it turns out that some people run with a constant internal monologue, and some do not. I have a &quot;voice&quot; in my head, thinking in words (and sometimes just abstract concepts, but mostly words). This voice is me - it is the thing that is piloting my body, voicing my words, etc.</p>
<p>I'd just assumed up until this point that this was the default mode of operation for people in general. Popular fiction seems to support this. Most fiction from a point-of-view will have characters thinking in sentences, characters in movies will have echo-y representations of their voice reflecting their thoughts, etc. But this is not necessarily experienced by everyone.</p>
<p>It's come to help me make sense of the personalities of some others. How they seem to just say whatever is on their mind, or their need to talk out problems with others out loud. To some degree or another we all need these things, but some of us can act this out internally, while for others it's much more difficult.</p>
<p>Do you constantly think to yourself in words? Do you play conversations in your head before committing to the words out loud? Or is this whole concept completely alien to you and now you are discovering that other people in your life live with little voices in their heads and now <em>your</em> day is ruined?</p>
<p>The biggest takeaway here is that everyone's mind works differently. There's probably no one out there who thinks exactly the way you do.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b3.13/" />
    <summary type="html">I barely managed to get this written at all...</summary>
  </entry>
  
  <entry>
    <title>Sculpting myself</title>
    <id>https://death.id.au/b3.12/</id>
    <updated>2020-01-20T00:00:00Z</updated>
    <published>2020-01-20T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Sculpting myself</h1>
<p><em>Oh, that's right, I never introduced myself...</em></p>
<h1>Who Am I?</h1>
<p>My first issue established my purpose for starting up this newsletter, but I never actually introduced who I was.</p>
<p>So who am I?</p>
<p>Turns out, I'm not sure.</p>
<p>My name is Gordon Pedersen, and I'm a software developer and designer for <a href="https://aerion.com.au/">Aerion Technologies</a>. I have various interests in software development, consumer tech, gaming and productivity/PKM (Personal Knowledge Management).</p>
<p>But lately, I've been thinking about the topic of who I am, internally, and come up mostly empty. I've never really been good at looking to the future, and the very typical question of &quot;Where do you see yourself in x years?&quot; usually stumps me. Even when x = 1.</p>
<p>But I've been reading a <a href="https://archiveofourown.org/works/5627803/">rational fanfiction based on the Animorphs books</a> I loved so much when I was younger, and one of the main themes that is hitting me is the various characters' explorations of their sense of self.</p>
<p>The main antagonist has the very villain-trope-y end goal of immortality, but in his exploration of how to attain it, he's very careful to make sure any future version of him is still <em>him</em>. The story has just hit a point where he's noticed something different in his own thought processes, which starts him questioning which <em>parts</em> of those thoughts are him and which are not.</p>
<p>Completely separate to that, a different character starts questioning who she wants to be and noticing how she's changing. How she felt like some of the changes were good and some bad, like she was &quot;a sculptor comparing each new chisel mark against the grand vision&quot;.</p>
<p>But what is this &quot;grand vision&quot;? Who is the sculptor that gets to decide what to keep and what to chip away?</p>
<p>The answer is both incredibly complex, perhaps even unknowable - but simultaneously incredibly simple: You.</p>
<p>You are the sculptor of your own self. You get to decide who you are and who you are not. The trick is recognising what is happening and intentionally making the decisions.</p>
<p>So who am I? How do I know what parts of me to chip away at? I got in touch with the author of the story and he gave me some sound advice:</p>
<blockquote>
<p>Practice building up the <em>yes, that's it</em> skill within yourself. Like, the skill of noticing when a phrase perfectly matches an emotion, or when a proposed meal perfectly matches the hunger you're feeling.</p>
<p>I think you're most likely to find fulfillment (rather than disappointment or failure or discontent) along a path where you recognize what you &quot;should be trying to shape yourself into&quot; rather than generating it from whole cloth.</p>
<p>I bet the &quot;optimal path&quot; for you will feel familiar, at least a little bit. It'll be like putting on an old shoe that still fits perfectly. And the skill of recognizing your own psyche's response of &quot;yes that's it&quot; is one that a lot of people are rusty with, and can improve by trying.</p>
</blockquote>
<p>This advice actually resonated with me. It actually did feel like &quot;yes, that's it&quot;.</p>
<p>So, who am I? I'm not sure yet, but I'm on the look out, and hopeful that I can figure it out as I go.</p>
<h1>Stuff I've found interesting recently:</h1>
<h2><a href="https://www.notoverthinking.com/">Not Overthinking</a> Podcast</h2>
<p>I've been listening to Ali and Taimur Abdaal's podcast for a while now and if you like my ruminations above, you'll love the kinds of conversations these brothers have about &quot;happiness, creativity, and the human condition&quot;.</p>
<p>They most recently discussed how specific words or phrases have changed their thought processes, and discuss anything from social interactions to how a good kitchen bin can measurably improve your life.</p>
<p>Definitely worth listening to for a different perspective on life advice.</p>
<h2>Zbigniew Gecis - <a href="https://uxdesign.cc/8-things-to-use-in-jobs-to-be-done-framework-for-product-development-4ae7c6f3c30b">8 things to use in “Jobs-To-Be-Done” framework for product development</a></h2>
<p>The &quot;Jobs-To-Be-Done&quot; framework is an oddly named framing for a way to develop a product. Rather than focus on the features or functionality of a product, focus on the job the customer is trying to do and - more importantly - why they are trying to do it.</p>
<p>The general idea is that a customer is &quot;hiring&quot; your product to do a &quot;job&quot;, but that job is in service of a particular goal. The key is to figure out what the goals are, and design and sell your product in terms of that goal.</p>
<p>It's a different perspective on product development and can even be useful in framing your own purchasing decision: &quot;For what 'job' am I 'hiring' this product to do?&quot;</p>
<h2>Nat Eliason - <a href="https://www.nateliason.com/blog/roam">Roam: Why I Love It and How I Use It</a></h2>
<p>A great primer on what Roam is, with some great productivity tips.</p>
<p>I've always seen the need for something like a weekly review, but never quite got the hang of doing it. But I'm borrowing some of the tips from this article and working them into my own Roam workflow, and so far it seems to be working for me.</p>
<p>I might write more about this in the next issue.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b3.12/" />
    <summary type="html">Oh, that&#39;s right, I never introduced myself...</summary>
  </entry>
  
  <entry>
    <title>An outlet for my writing</title>
    <id>https://death.id.au/b3.11/</id>
    <updated>2020-01-06T00:00:00Z</updated>
    <published>2020-01-06T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>An outlet for my writing</h1>
<p><em>It's the first issue. Do you really expect a witty subtitle?</em></p>
<h1>What is this?</h1>
<p>I've been thinking for a long time how I want to write more. I've made many attempts to start journals and blogs, and have just never quite stuck to any of them. There are probably many reasons behind this, but a major one is that I'm a procrastinator. I just sit on things and never finish them.</p>
<p>A little while ago, I read an article by Khe Hy of RadReads (<a href="https://radreads.co/">go subscribe to his newsletter</a>)) about <a href="https://radreads.co/start-email-newsletter/">how starting an email newsletter will change your life</a> and it started my gears turning. I came to the conclusion that if I do start an email newsletter, that gives me a regular interval to stick to, instead of trying to write stuff every day or sporadically when I remember an old blog exists.</p>
<p>This comes at the same time I have discovered <a href="https://roamresearch.com/">Roam Research</a> which is a great note-taking tool which excels at linking ideas (something I've really struggled with in other platforms). So this year, I've resolved to take more notes on the things I read, and having an outlet for those ideas in the form of a recurring newsletter seems like great motivation to stick to that.</p>
<p>The best way I've found in my own experience to reinforce my knowledge on any topic is to share it with others. Writing it down is all well and good, but I rarely go back and look at old notes (I'm hoping Roam will help with that). Sharing the knowledge with someone else and then having them provide feedback or ask questions makes me think about the topic in a new way and helps embed the knowledge permanently into my brain.</p>
<p>I haven't yet fully committed to a schedule or format for this newsletter yet (I've already procrastinated past the initial deadline I set for myself), but hopefully you can still learn something. Go read some RadReads articles, play around with Roam, or have a look at some of this other stuff I'm linking off to:</p>
<h1>Stuff I've found interesting recently:</h1>
<h2><a href="https://youtu.be/hMKy52Intac">Ted Nelson - Xanadu Basics</a></h2>
<p>I recently discovered a <a href="https://youtu.be/hMKy52Intac">video series</a> by Ted Nelson explaining the concepts behind his Xanadu system, which seems to be a kind of alternative to HTML and the Internet before it became a big thing. Kinda like a VHS vs Betamax thing, except I'd never heard of Xanadu before.</p>
<p>Its big differentiator with HTML is the way things are linked. Where HTML uses one-way &quot;jump links&quot;, Xanadu links are two-way and sources for the links are loaded in advance and visible in the periphery.</p>
<p>This means quotes are always linked to their original, in-context source, marginalia and footnotes hover around their original context, and it's simple to branch off topics and tangents without breaking the flow of the text.</p>
<p>I sort of wish I lived in the alternate universe where Xanadu beat out HTML and became the way the Internet works. It works a lot more like my brain works. It's just unfortunate that there's no widespread software utilising its principals fully.</p>
<p>Go watch the <a href="https://youtu.be/hMKy52Intac">video series</a>. Ted has a bit of a &quot;grumpy old man&quot; vibe about him, but he has good reason - his ideas are solid and worth pursuing.</p>
<h2><a href="https://www.huffpost.com/entry/the-art-of-finding_b_3982289">Huffington Post - Meet Thad Starner</a></h2>
<p><a href="https://www.huffpost.com/entry/the-art-of-finding_b_3982289">This is an old article</a> (from 2013) about a man named Thad Starner who ended up as one of the main designers behind Google Glass because of his experience in living with a computer strapped to his face since 1993.</p>
<p>I first head of him in an episode of the <a href="https://xd.adobe.com/ideas/perspectives/wireframe-podcast/good-design-is-why-not-wearing-ar-glasses-episode-5/">Wireframe podcast</a> by Adobe and I remember thinking at the time that his setup sounded awesome, but there was no way I'd be able to concentrate on having a conversation as well as taking notes and retrieving notes from previous conversations. It sounded like a lot of work.</p>
<p>But now that I've started using Roam and discovered the power of bi-directional linking, I've realised how possible a system like this really is. It's just a pity Google Glass never went mainstream...</p>
<h2><a href="https://writingcooperative.com/zettelkasten-its-like-gtd-for-writing-and-here-s-why-you-should-consider-it-7dddf02be394">Phil Houtz - Zettelkasten</a></h2>
<p>In looking into Roam, I've head about this thing called Zettelkasten, which is supposed to be a framework for taking and linking notes.</p>
<p>I found <a href="https://writingcooperative.com/zettelkasten-its-like-gtd-for-writing-and-here-s-why-you-should-consider-it-7dddf02be394">this article</a> by Phil Houtz explaining the concept. Each note consists of a single thought consisting of a single paragraph or a couple of sentences. Each note has its own home in a filing system so it can be found again, along with a unique identifier for linking related thoughts and ideas.</p>
<p>It seems like a lot of work to manage a system like this using physical cards as Zettelkasten's inventor, Niklas Luhmann, did but in this age of digital tools, variations on this practice make perfect sense.</p>
<p>The aim is to build a system of knowledge that can be queried and communicated with. Like having your own personal external intelligence. The idea is intriguing, and I really hope I can build something like that for myself with Roam</p>
<h1>DevReady writing</h1>
<p>I work for a software development company called <a href="https://aerion.com.au/">Aerion Technologies</a>, and we've built ourselves a process for designing and managing software development we call <a href="https://devready.design/">DevReady</a>. We're now aiming to educate people on not just this process, but how to actually go about turning an idea into software.</p>
<p>There's a lot that goes into the process of building software. A lot of people start off by documenting their idea, handing it over to developers and expecting their dreams to come true in 6-12 months. In reality, there's a lot of work that goes into the idea before it's dev ready (get it?) and that's what we aim to educate people about. Everything from refining the idea, to defining an MVP, to finding a team and the nitty-gritty of managing that team to deliver on your outcomes.</p>
<p>Over the coming months we plan to write a lot more, and I want to use this newsletter as an opportunity to share some of the writing I am doing on these topics. I haven't written a lot myself yet, but I plan to change that soon and hopefully this newsletter can help motivate me to continue writing more articles</p>
<h2><a href="https://aerion.com.au/2019/08/13/just-give-us-the-throttles-we-can-use/">Just Give Us the Throttles We Can Use</a></h2>
<p>[
<img src="https://cdn.some.pics/deathau/68cb5acfe9f09.jpg" alt="Navy personnel looking at a screen with data and guages. Below the sceen are some hardware controls" /></p>
<p>Modern touchscreen applications leave old mechanical controls behind. And it's not always for the better. In this case, lives were lost...</p>
<h2><a href="https://aerion.com.au/2019/12/10/5-types-of-wireframes-prototypes/">5 Types Of Wireframes &amp; Prototypes</a></h2>
<p><img src="https://cdn.some.pics/deathau/68cb5b61dccf6.jpg" alt="Various app wireframes, printed and stuck to a wall. There are a lot of pins with black string connecting the various wireframes" /></p>
<p>So, what even is wireframing and how do you do it? What tools do you use? How does it tie in with the eventual &quot;complete&quot; design? Why is it even necessary?</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b3.11/" />
    <summary type="html">It&#39;s the first issue. Do you really expect a witty subtitle?</summary>
  </entry>
  
  <entry>
    <title>5 Types of Wireframes &amp; Prototypes</title>
    <id>https://death.id.au/b2.13/</id>
    <updated>2019-12-10T00:00:00Z</updated>
    <published>2019-12-10T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>5 Types of Wireframes &amp; Prototypes</h1>
<p><em>So, what even is wireframing and how do you do it? What tools do you use? How does it tie in with the eventual &quot;complete&quot; design? Why is it even necessary?</em></p>
<p>Wireframes are a crucial tool in the early stages of any project – the clarity they provide to your conceptualisation, refinement and features is unparalleled. They can come in all shapes and sizes (depending on your type of app) as well as a varying level of detail included in them.</p>
<p>One of the main benefits of wireframing is that they allow for clarification of terms that are thrown around in the early stage. When someone says &quot;Google Map&quot; or &quot;pop-up&quot;, etc you can actually visualise how that feature should be developed and used – it's no longer left to be ambiguous.</p>
<p>As a visual tool, they approach the design of your app from a graphic and user-centred approach rather than a technical perspective that developers may focus on. The user experience should be first and foremost when designing wireframes – after all, who wants to continue using a difficult and clumsy app?</p>
<p>They are a useful tool in the design process as they allow decisions to be quickly evaluated for the usability and flow of processes as well as for the look and feel prior to development. They are also a quick way to iterate through features and thought processes.</p>
<p><img src="https://cdn.some.pics/deathau/68cb5bb4152cb.jpg" alt="A blueprint of a cross-section of a house with two floors and a basement. It is labelled &quot;Section looking east&quot;." /></p>
<p>Wireframes can be thought of as providing the &quot;blueprint&quot; for software developers on what the app should do and helps them identify what needs to be prototyped and what to experiment with at the early stages to help reduce risk and unknowns when moving into development.</p>
<p>There are many ways your designers can create wireframes. Each level requires more effort but also provides more detail and clarity to the design. Some things that go hand-in-hand with the extra detail at each level are the decisions and items that clients will fixate on. Its best to move through each level sequentially otherwise they may be focusing on the font-type or colours rather than the structure or elements at the early stages.</p>
<p>The wireframing process should follow a bottom-up approach and start with the least amount of detail to allow structural elements to be decided and then provide more detail as decisions are made and the direction of the app becomes more clear.</p>
<p>The 5 different wireframe approaches that we follow are:</p>
<ul>
<li>Hand-Drawn</li>
<li>Lo-Fi</li>
<li>Hi-Fi</li>
<li>Clickable Prototypes</li>
<li>Interactive Prototypes &amp; Simulations</li>
</ul>
<h2>Hand-Drawn</h2>
<p><img src="https://cdn.some.pics/deathau/68cb5bfb972c1.jpg" alt="A notebook on a wooden desk with a keyboard pen and phone slightly out-of-focus. The notebook is showing a hand-drawn sketch of a website titled &quot;FJMAG&quot; with many boxes presumably representing images and articles, as well as ones with &quot;play&quot; buttons representing videos." /></p>
<p>This is the simplest method of creating wireframes and getting your idea down. All you need is some paper and a Pen or Pencil and start sketching out your ideas for screens. It's rough, it's crude, but it puts your ideas down on paper and starts to solidify your ideas and concepts. Just through this simple process you might even revise and refine certain aspects as once you can actually see it you might start getting new ideas or figure out how to improve it.</p>
<p>An easy way to step up the quality from a rough sketch on pen and paper is download and print out some phone or screen templates and use those with a ruler to draw your designs in a frame that looks like where it will end up.</p>
<h3>Tools</h3>
<p>Obviously, hand-drawing a wireframe requires only a pencil and paper (or whiteboard and markers) and your imagination, however, there are some tools that can help frame your wireframes better</p>
<p>For example, here are some printable templates that you can download for free:</p>
<ul>
<li><a href="https://sketchize.com/">https://sketchize.com/</a></li>
<li><a href="https://sneakpeekit.com/">https://sneakpeekit.com/</a></li>
<li><a href="https://sketchsheets.com/">https://sketchsheets.com</a></li>
<li><a href="http://www.raincreativelab.com/paperbrowser/">http://www.raincreativelab.com/paperbrowser/</a></li>
</ul>
<p>There are also products specifically designed for helping you with hand-drawn wireframes that you can purchase online. For example, the website <a href="https://www.uistencils.com/">UI Stencils</a> provides a range of stencils with common UI elements from iOS, Android and Web applications. They also provide a range of sketch pads and sticky notes with phone and web browser borders to get you in the right frame of mind for sketching UIs.</p>
<p>There are also tools like <a href="https://marvelapp.com/pop">Marvel's POP app</a> which help you bridge the gap between paper prototypes and further stages of wireframing by digitizing your paper sketches and allowing you to string them together in the form of a clickable prototype, as talked about later in this article.</p>
<h2>Lo-Fi</h2>
<p><img src="https://cdn.some.pics/deathau/68cb5c313c053.png" alt="An image titled &quot;Components for Web, Application and Mobile Interface Design&quot; which contains a lot of UI components in a hand-drawn style, including text notes using the Comic Sans font. There are a lot of different components." /></p>
<p>Lo-Fi wireframes are a large step up from hand-drawn, even though you can use a visual style that looks like it's sketched. This is where you begin using a software tool to start visualising your designs. Moving to this process allows for editing and grouping and be able to arrange your screens. Some platforms provide component packs that allow you to easily add a button or a popup to a screen to get your point across easier. There's no point in creating everything yourself when you can leverage the components available.</p>
<p>These are a great starting point for developers – who aren't designers – to get a point and idea across and provide a general feel and direction for the application's functionality.</p>
<h3>Tools</h3>
<p>There are many tools for creating lo-fi wireframes, ranging all the way from drawing something from scratch in Microsoft Paint, all the way through to tools designed to create clickable wireframes with large libraries of (lo-fi) pre-made UI components.</p>
<p>One great tool we've used is <a href="https://www.draw.io/">Draw.io</a>. It can easily integrate into Google Drive or Microsoft OneDrive, and is great for making flowcharts, process diagrams and a whole bunch more. It also has a library of components and shapes to quickly and easily get started making lo-fi wireframes for iOS &amp; Android, or mockup components for any kind of wireframe you want to create.</p>
<p>We've also used some other great wireframing-specific tools like <a href="https://proto.io/">Proto.io</a> and <a href="https://balsamiq.com/">Balsamiq</a> that make wireframing easy and can easily progress from lo-fi wireframing onto more well-defined hi-fi wireframes through to clickable prototypes.</p>
<h2>Hi-Fi</h2>
<p><img src="https://cdn.some.pics/deathau/68cb5c6c6bcc8.png" alt="A screenshot of Figma (or a similar design tool) showing a canvas with many app wireframes related to ordering, notifications, calendar stuff and more. It shows the sidebar on the right with attributes of the currently selected window." /></p>
<p>Hi-Fi wireframes are a term we use to describe UI wireframes that are completely designed, include your branding, and are pretty close to the final versions of your screens. They are handed over to the developer prior to implementation and are the plan for how the UI should look and feel.</p>
<p>This is the point where the customers start to feel like they own the designs. Once their branding and logo are used, it begins to feel like their app. It can take quite a while to get to this point as a lot more things have to be considered to add enough detail e.g.</p>
<ul>
<li>transitions</li>
<li>animations</li>
<li>hover colours</li>
<li>hover interactions</li>
<li>font-sizes</li>
<li>font-types etc.</li>
</ul>
<h3>Tools</h3>
<p>Sometimes when you get to the hi-fi wireframing stage, you will have engaged a graphic design agency, and they may provide wireframes and designs in whichever tool they use for designing. These tools are usually used for their flexibility and power when it comes to graphical design.</p>
<p>Adobe's ubiquitous <a href="https://photoshop.com/">Photoshop</a> is a common tool for making high-quality designs and often used to make hi-fi wireframes. It's complex to try and make storyboards, and exact positioning can be tricky, but it's a tool a lot of designers know and love. Photoshop is a powerful tool for any high fidelity design work.</p>
<p>A great tool for hi-fi wireframing is <a href="https://www.sketch.com/">Sketch</a> which allows the creation of reusable components and allows you to divide up your wireframes into pages and artboards. It also has the ability to make clickable prototypes (see below) and has advanced options for layouts, effects, vector editing and more. It's a preferred tool for many designers. Also, take a look at Figma for a web-based tool that provides similar functionality</p>
<h2>Clickable Prototypes</h2>
<p><img src="https://cdn.some.pics/deathau/68cb5ca4efd9e.gif" alt="An animation of creating a clickable prototype in Figma. A mouse cursor drags a line from a button to a popup screen, then  clicks a play button which shows the interactive mode. In this mode the cursor clicks the button and the popup screen appears." /></p>
<p>The next step from Hi-Fi wireframes would be to produce clickable prototypes. This can be done for Lo-Fi as well but it seems to have more impact when you begin clicking through wireframes that look like the final polished app. The clickable prototypes allow users, designers and developers to understand how the app will flow between screens, which buttons do what, etc. It's a great way to get further insights and opinions from users as they can step-through the app without you having to invest a large amount of time and resources in creating a functioning prototype to do the same thing.</p>
<h3>Tools</h3>
<p><a href="https://www.adobe.com/au/products/xd.html">Adobe XD</a> is a powerful, collaborative platform for creating designs and clickable prototypes. It's designed to help create everything from basic wireframes up to beautiful designs including re-usable components and design systems. Beyond simple clicking, XD also supports triggers such as time-based events and keyboard input, through to voice commands and game controllers. Designs and prototypes can be shared online for review and collaboration, as well as Design Specs for developers to work from.</p>
<p>We're also big fans of <a href="https://www.figma.com/">Figma</a>, which has a lot of the same features as XD, with a greater emphasis on collaboration. It works online through a website or desktop app and allows real-time collaboration - showing the cursors of other users as they work. It's quick and easy to create clickable prototypes to link together different screens, dynamic overlays and more. Previews you share for review update in real-time as you make edits, so everything's always up-to-date (and you can utilize versioning so as not to lose previous work).</p>
<h2>Interactive Prototypes &amp; Simulations</h2>
<p><img src="https://cdn.some.pics/deathau/68cb5cd467ebb.jpg" alt="Stock image of a corner of a macbook with some code displayed. The code appears to be CSS, in case that matters." /></p>
<p>The last possible step prior to the development of your complete app would be to develop a prototype app. This means you can follow the same process for your clickable prototype but extend it into an actual application that is the basis for your app.</p>
<p>This involves the development of all your UI's as an actual mobile app, website or front-end. This is actual development and the process shifts from designer to the developer. However, everything that is made at this stage can be used moving forward. Anything that is developed for the prototype can and should be leveraged when you proceed with final development.</p>
<p>Some developers might do a &quot;quick and nasty&quot; build to get this step done but they should really proceed as if they were starting the proper development so that the time and effort is not wasted and needed to be done again.</p>
<p>As these prototypes are coded into actual apps, you also have the ability to extend the prototype into a simulation – based on data – which can show users how things will look over time.</p>
<p>As an example, imagine you have a real-time dashboard in your app. The basic prototype will show static data on that page. A simulation will load in a data set and feed it into the dashboard to show the dashboard changing over time, based on the data it is provided with. When following this method you also have the option of changing the speed of the data retrieval and, for example, showing a whole day's worth of data in one minute.</p>
<h2>There's No One-Size-Fits-All solution</h2>
<p><img src="https://cdn.some.pics/deathau/68cb5d0146e39.jpg" alt="A stock image of two wooden artist dummies. One has a hand on his head and is gesturing in dismay at a small pile of tetromino-style wooden blocks. The other is hunched over holding one of these block, presumably attempting to construct something out of the mess." /></p>
<p>We've presented a rough guide on the different wireframing and prototyping approaches we use, but this doesn't mean you have to follow the process through all of these stages for every project you develop. Sometimes if you're building a small feature onto an existing system with a polished design, Lo-Fi wireframes are all you need to get to development. Other times, you may end up building an interactive prototype or simulation before the graphic design is finalized to prove that the concept will work before pouring resources into the design.</p>
<p>Nor is this an exhaustive guide to every approach to visually designing an application or system. Flow charts, process diagrams and site maps can also be useful. In their <em>Shape Up</em> book, Basecamp presents the idea of &quot;<a href="https://basecamp.com/shapeup/1.3-chapter-04#breadboarding">breadboarding</a>&quot; - an approach for defining the elements required for a process or a feature to work, without getting into design decisions like layout that factor even into basic hand-drawn wireframes. This can also be a useful tool to provide clarity to a project that could be expanded into Lo-Fi or Hi-Fi wireframes when the time comes.</p>
<p>The key here is to introduce clarity about what it is you are trying to develop. It pays to start with rough sketches and concepts in order to clarify the needs and functionality of the idea. From here you can progress into clarifying UX decisions – about <em>how</em> the end-users will be using this functionality. Finer details about font size and colours also need to be clarified, but these can be established much later in the process.</p>
<p>The clearer you are at each step of the process, the fewer decisions need to be made as you get into development – and the better the result becomes.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b2.13/" />
    <summary type="html">So, what even is wireframing and how do you do it? What tools do you use? How does it tie in with the eventual &quot;complete&quot; design? Why is it even necessary? Wireframes are a crucial tool in the early stages of any project – the clarity they provide to your conceptualisation, refinement and features is unparalleled. They can come in all shapes and sizes (depending on your type of app) as well as a varying level of detail included in them.</summary>
  </entry>
  
  <entry>
    <title>How Slack Can Help Drive Customer Engagement</title>
    <id>https://death.id.au/b2.12/</id>
    <updated>2019-11-14T00:00:00Z</updated>
    <published>2019-11-14T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>How Slack Can Help Drive Customer Engagement</h1>
<p><em>You may already know Slack as the team communication platform it was designed to be. But did you know you can also use it as a customer engagement tool?</em></p>
<p>You may already know Slack as the team communication platform it was designed to be. Conceived as a way to facilitate team communication and increase workplace productivity, Slack allows teams to collaborate and be more productive through the use of bots. But did you know you can also use it as a customer engagement tool?</p>
<h3>Integrate with your existing tools</h3>
<p><img src="https://cdn.some.pics/deathau/68cb5dca12080.png" alt="Clip art of slack with icons for calendar, mail and folders each separately plugging into the slack window" /></p>
<p>You may be using an existing CRM, help desk or other tools to drive your customer engagement currently. If so, you can probably find an integration in the Slack app directory to connect to your existing tools. In this way, you can interact with the tool, and perhaps your customers and leads directly from within Slack.</p>
<p>For example, you may have something like Intercom set up on your website and you can use the integration to communicate directly with customers and leads through the same tool you use to communicate with your colleagues. Or you could get notifications from helpdesk systems like Freshdesk or Zendesk directly in Slack.</p>
<h3>Use Slack-based tools</h3>
<p><img src="https://cdn.some.pics/deathau/68cb5e024b524.png" alt="A chat popup on awebsite selling heaphones and shirts. There is also a slack window showing a similar chat." /></p>
<p>The Slack apps directory also includes many customer engagement tools you can use from directly within Slack. For example, tools like Slaask or Chatlio let you embed a chat window on your website which will create channels in Slack for you to talk directly to your customers and leads. Talkus allows you to receive customer emails in Slack and even track support tickets.</p>
<h3>Get your users in on Slack</h3>
<p><img src="https://cdn.some.pics/deathau/68cb5e2d4c53e.png" alt="The Slack logo (a hash symbol). Eeach end of all lines end with a coloured circle with someone's face in it. The lines are coloured to represent connections between the people depicted." /></p>
<p>Slack itself can be used as a community for your customers, like your own private social network. You can create some open channels, then share a join link with those you want to join your community, or publish it publicly for anyone to join. Private channels can be created for your team, or community members you choose. You can also use the power of Slack's apps and bots to engage directly with the members of your community.</p>
<h3>Share and collaborate with others on Slack</h3>
<p><img src="https://cdn.some.pics/deathau/68cb5e5a7a8a6.png" alt="A slack window for an &quot;Acme Creamery&quot; organization with a lot of different channels. The &quot;campaign-icecream&quot; channel is selected and the chat is generically discussing an advertising campaign." /></p>
<p>If you're a business that serves other businesses on ongoing projects, then oftentimes they will also use Slack. But rather than have individuals join your slack channels (or you join theirs) and have to juggle multiple workspaces, Slack has a new feature in beta called Shared Channels, where one channel can exist across multiple workspaces and have members from both. This allows for better communication and collaboration. And if they don't use Slack, there are other options to help connect other chat programs with Slack, such as the Sameroom bot, or integrations with other tools such as the Field Trip app for integration with Basecamp.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b2.12/" />
    <summary type="html">You may already know Slack as the team communication platform it was designed to be. But did you know you can also use it as a customer engagement tool?</summary>
  </entry>
  
  <entry>
    <title>Just Give Us the Throttles We Can Use</title>
    <id>https://death.id.au/b2.11/</id>
    <updated>2019-08-13T00:00:00Z</updated>
    <published>2019-08-13T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Just Give Us the Throttles We Can Use</h1>
<p><em>Modern touchscreen applications leave old mechanical controls behind. And it's not always for the better. In this case, lives were lost...</em></p>
<p>In the early morning on August 21, 2017, the US Navy destroyer <em>John S McCain</em> was in the Singapore Strait – one of the busiest waterways in the world – inbound for a port call in Singapore. Through a lot of confusion about the ship's controls, the ship started veering port. Within about three minutes, another ship had struck the starboard side of the <em>John S McCain</em>. The impact breached the hull in a berthing compartment, and 10 sailors were killed.</p>
<p>Recently, the National Transportation Safety Board released an <a href="https://www.ntsb.gov/investigations/AccidentReports/Reports/MAR1901.pdf">accident report</a> for the incident. There were many factors that led to this unfortunate circumstance, from fatigue to a lack of training, but one thing that stood out was the touch-screen controls for thrust and steering.</p>
<p>The NTSB report suggests that had mechanical throttles been present, the helmsmen would have likely been able to feel the problems in steering and correct for them much more quickly. They note that mechanical throttles are preferred because “they provide both immediate and tactile feedback to the operator.”</p>
<p>During a keynote speech at the American Society of Naval Engineers’ annual Fleet Maintenance and Modernization Symposium, Program Executive Officer for Ships Rear Adm. Bill Galinis said as a result of innovation and a desire to incorporate new technology, “we got away from the physical throttles, and that was probably the number-one feedback from the fleet – they said, just give us the throttles that we can use.”</p>
<p>“When we started getting the feedback from the fleet from the Comprehensive Review effort – it was SEA 21 (NAVSEA’s surface ship lifecycle management organization) that kind of took the lead on doing some fleet surveys and whatnot – it was really eye-opening. And it goes into the, in my mind, ‘just because you can doesn’t mean you should’ category.&quot;</p>
<p>In our modern world, there's a constant push towards new technology and innovations. This is often a good thing, but there are also times when new technology is deployed because that's what everyone else is doing, and not enough thought is put into the 'why'. Just because you can, doesn't mean you should.</p>
<p>Take the time to better understand what the underlying issues are. Work out the weaknesses of the current systems and find ways to modernise and improve them. But also pay attention to the stuff that works well in the current system and try to factor that into the new design. Just give us the throttles that we can use.</p>
<p><strong>References:</strong></p>
<p><a href="https://www.ntsb.gov/investigations/AccidentReports/Reports/MAR1901.pdf">Marine Accident Report NTSB/MAR-19/01</a></p>
<p><a href="https://news.usni.org/2019/08/09/navy-reverting-ddgs-back-to-physical-throttles-after-fleet-rejects-touchscreen-controls">USNI News</a>, via <a href="https://www.theverge.com/2019/8/11/20800111/us-navy-uss-john-s-mccain-crash-ntsb-report-touchscreen-mechanical-controls">The Verge</a></p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b2.11/" />
    <summary type="html">Modern touchscreen applications leave old mechanical controls behind. And it&#39;s not always for the better. In this case, lives were lost...</summary>
  </entry>
  
  <entry>
    <title>Game Idea — The Humble Indie Rumble</title>
    <id>https://death.id.au/b1.22/</id>
    <updated>2012-06-09T00:00:00Z</updated>
    <published>2012-06-09T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Game Idea — The Humble Indie Rumble</h1>
<p>Well, this blog has well and truly fallen by the wayside...</p>
<p>This is an idea that's been kicking around in my head for a long, long time. But it has been brought to the forefront recently for various reasons. The basis of the idea is simple enough: A Super Smash Bros. clone with Indie game characters. Super Meat Boy vs The Sythian, Josef (the robot from Machinarium) vs ... that guy from Braid, Commander Video vs Captain Viridian, you get the idea. Of course, There's a little bit more meat to the idea than simply cloning Smash Bros, but I hope to get into that in another post. In this post I wanted to talk more about where the idea has come from; and even though it's most likely I'll never make a game like this, I really want this idea above all else to exist.</p>
<p>The idea of a mash-up fighting game such as this has been around in my head almost as long as Super Smash Bros. itself, but the roots of the idea go back further than that. In fact, they probably go back to the classic movie &quot;Who Framed Roger Rabbit?&quot; As a child, there was something amazing about having characters from all corners of cartoon land together in one film. Even then I knew that Warner Bros and Disney were rivals, but yet their characters came together for one amazing experience that as far as I know has never been repeated.</p>
<p>There's something amazing to see multiple things you're a fan of come together. Especially if they are rivals, but also if they are unrelated.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b1.22/" />
    <summary type="html">Well, this blog has well and truly fallen by the wayside...</summary>
  </entry>
  
  <entry>
    <title>The FoFiX Days</title>
    <id>https://death.id.au/b1.21/</id>
    <updated>2011-06-24T00:00:00Z</updated>
    <published>2011-06-24T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>The FoFiX Days</h1>
<p>I started this blog in response to the frustration I feel in things I start never getting finished. Things I’ve had ideas about or started, but never got around to completing. Nowhere is this laid bare more than my involvement with FoFiX.</p>
<p>For those who are unaware, Frets on Fire X (FoFiX) is the evolution of the open-source guitar hero-like rhythm game Frets on Fire. I used to be involved with it’s development. I’ve written code, built graphical themes, and was a prominent member of the <a href="http://fretsonfire.net">fretsonfire.net</a> forums, where I am still a forum moderator. I helped people with any issues, documented a large portions of theming capabilities, maintained a list of FoFiX themes and attempted to keep the peace. I was also prominent in a lot of development discussions, and presented ideas that helped shape some of the features of FoFiX itself. As those closest to me can attest, I practically lived on those forums.</p>
<p>But the real world started to catch up with me. I found I had less and less time for FoFiX, and one day I simply stopped visiting the forums. I just disappeared without a word, with the intention to come back when time permitted. I feel guilty about it. I’ve visited the forums a couple of times (generally I hide my online status out of guilt), and my theme list has gone completely stagnant. My themes need updating. Development on FoFiX appears to be slow, but exciting.</p>
<p>I want to go back. I feel like I have friends there. But there’s a guilt and fear holding me back. I feel guilty for leaving things unfinished, and I fear that a lack of adequate free time will have me not particularly active. And I may stop again. People come and go from the FoFiX forum all the time. I’m certainly not the first to fade out when Real Life catches up, and I probably won’t be the last. But I don’t want to be the guy who comes back every few months, makes promises, and then leaves again with a bunch of loose ends.</p>
<p>I need to find some extra time in my life somewhere to work on all my current loose ends. Figure out if/where/how I can still be useful in the community in a diminished (and easily abandoned, if necessary) capacity. If I am able to adequately tie up all those loose ends, perhaps I could go out with a bang and get some closure. Or maybe I always want to be involved in some way or another.</p>
<p>Either way, at the moment I miss FoFiX and I want a way back in…</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b1.21/" />
    <summary type="html">I started this blog in response to the frustration I feel in things I start never getting finished. Things I’ve had ideas about or started, but never got around to completing. Nowhere is this laid bare more than my involvement with FoFiX.</summary>
  </entry>
  
  <entry>
    <title>Martyrdom sucks: why it&#39;s so wonderful</title>
    <id>https://death.id.au/b1.20/</id>
    <updated>2011-06-23T00:00:00Z</updated>
    <published>2011-06-23T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Martyrdom sucks: why it's so wonderful</h1>
<p>I just finished my second play-through of what has quickly become one of my favorite games of all time. An iOS-only game called Superbrothers: Sword &amp; Sworcery EP. That's &quot;EP&quot; in the same way that a musician would release an EP. And while there's no rhythm component to this, the music does play a big part. The music is from a fella named Jim Guthrie, who I'd never heard of until this game, but I might look up. I love the sworcery soundtrack.</p>
<p>But music is only part of the charm. Some of it is the blocky pixel graphics, that are nostalgic for games past. The characters remind me of old Sierra adventure games. The colors are generally of a darker nature and really help to set the mood of the scenes. Zooming out and panning around reveal beautiful, surprisingly detailed and beautiful environments.</p>
<p>So it looks good and sounds good, but that's not enough to make it a good game. It's hard to pin it down exactly. There's an element of emotional involvement in the story, which in itself is sort of vague and generic, but at the same time wonderful, with an emotional ending.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b1.20/" />
    <summary type="html">I just finished my second play-through of what has quickly become one of my favorite games of all time. An iOS-only game called Superbrothers: Sword &amp;amp; Sworcery EP. That&#39;s &amp;quot;EP&amp;quot; in the same way that a musician would release an EP. And while there&#39;s no rhythm component to this, the music does play a big part. The music is from a fella named Jim Guthrie, who I&#39;d never heard of until this game, but I might look up. I love the sworcery soundtrack.</summary>
  </entry>
  
  <entry>
    <title>iOS IM With Quick Reply</title>
    <id>https://death.id.au/b1.19/</id>
    <updated>2011-06-14T00:00:00Z</updated>
    <published>2011-06-14T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>iOS IM With Quick Reply</h1>
<p>I’d love to see an IM program for jailbroken iPhones with QuickReply support like BiteSMS or MobileNotifier.</p>
<p>On my PC, I use digsby for IM, and it’s great for the different protocols I’m signed up for (WLM, Facebook, etc). And one thing I really like is the unobtrusive pop ups when you get a message. In the popup itself there’s a text box so you can reply to the message without bringing up the full conversation window and disrupting your workflow. I think that the iOS one-full-screen-app-at-a-time environment could benefit from a similar approach.</p>
<p>I think I read something about MobileNotifier’s Quick Reply being open to developers? It would be awesome.<br />
Just open up your IM app and sign in, then go play games or do anything else on your phone and when someone says something, the unobtrusive bar appears at the top. For me, it would be great if in that box was the sender’s name and display pic, and the first X number of characters of their message. Tapping the bar brings up the quick reply box with the full message text (scrollable if really necessary) and a text box to input your reply. To make it even more awesome, borrow some ideas from biteSMS like the pulldown with conversation history and templates/smileys available right from the quick reply box.<br />
Better still if it could integrate with existing IM apps, like imo.</p>
<p>If I had a Mac, I’d try playing with the idea, but I’d probably get stuck at the IM bit, without even touching an OS-integrates quick reply component…<br />
Alas, another dream…</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b1.19/" />
    <summary type="html">I’d love to see an IM program for jailbroken iPhones with QuickReply support like BiteSMS or MobileNotifier.</summary>
  </entry>
  
  <entry>
    <title>Narbacular Science – A Portal Map Pack Idea</title>
    <id>https://death.id.au/b1.18/</id>
    <updated>2011-05-13T00:00:00Z</updated>
    <published>2011-05-13T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Narbacular Science – A Portal Map Pack Idea</h1>
<p>An idea for a Portal or Portal 2 map pack. On the surface, it is simply porting all the Narbacular Drop levels into portal, set in the Aperture Science Enrichment Center testing facility. Although it would definitely add an extra something to it to include a story of sorts.<br />
Basically the player will be a small child, a young girl, which ambiguously may or may not be a younger Chell. All that is ever made clear about her story is that she is a daughter of an employee, perhaps somehow caught up in the unknown events of the infamous Bring Your Daughter to Work Day.<br />
The (admittedly little) story of Narbacular Drop would have been invented by an employee as the premise for a test sequence involving a child. His theory being that children would be much more suited to thinking with portals due to their lack of knowledge about the laws of physics. They have not yet learned what is impossible, as it were. As you progress through the test, nooks, dens and secret areas not present in the original Narbacular Drop can contain papers and possibly security clips detailing the inception of the current test, codenamed “Narbacular” and its subsequent dismissal due to putting children in danger. I’m thinking the person in charge would have been passionately against it, stating words to the effect of “We will not be testing children while I’m still alive”. In this way, GLaDOS can deem it acceptable to test children because that person is no longer alive.<br />
Perhaps after the Narbacular Drop levels are over there can be an extension with more levels of an escape sequence (now typical of Portal) that could explore some currently unexplained aspect of the story, like the post-activation attachment of the morality core on GLaDOS. The final boss battle could involve attaching cores to GLaDOS in a similar manner to the final battle of Portal 2.</p>
<p>In the Narbacular Drop levels alone, there may be some new technologies to develop and explain in the context of Aperture Science. For example, the turtles that swim trough lava (or the green Aperture Science gunk that kills you in portal), and the perpetually rolling boulders. Also, in the very first chamber of Narbacular Drop there is a cube-and-button puzzle, and there is nothing stopping you taking the cube with you. In fact people made it a challenge to do “cube runs”: speed runs where you take the cube with you all the way to the end. I’d like to keep that, and for this reason that first cube should be a companion cube. It also means no Emancipation Grills.<br />
I wouldn’t even know where to start making a Portal map, and one like this involves some pretty advanced stuff, plus proper writing and voice acting.</p>
<p>A thought that just occurred to me, perhaps the story could be explored by an ever present and always out of reach survivor scientist. The one who designed the test, but now sees the error of his ways and attempts to help you through the tests in order to help you escape. He can explain his reasons for creating the test, his feelings at the rejection of his idea, his escape from GLaDOS and the deadly neurotoxin and his fear and guilt now that his test is actually being used with a real child. He also tries to help you escape and through lack of other options gets you to attempt to attach the cores to GLaDOS. He may be blocked from her chamber, but is able to provide you cores to use in a mirror of the Portal 2 final boss.</p>
<p>Ass always, feedback of my ideas is appreciated.</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b1.18/" />
    <summary type="html">An idea for a Portal or Portal 2 map pack. On the surface, it is simply porting all the Narbacular Drop levels into portal, set in the Aperture Science Enrichment Center testing facility. Although it would definitely add an extra something to it to include a story of sorts.
Basically the player will be a small child, a young girl, which ambiguously may or may not be a younger Chell. All that is ever made clear about her story is that she is a daughter of an employee, perhaps somehow caught up in the unknown events of the infamous Bring Your Daughter to Work Day.
The (admittedly little) story of Narbacular Drop would have been invented by an employee as the premise for a test sequence involving a child. His theory being that children would be much more suited to thinking with portals due to their lack of knowledge about the laws of physics. They have not yet learned what is impossible, as it were. As you progress through the test, nooks, dens and secret areas not present in the original Narbacular Drop can contain papers and possibly security clips detailing the inception of the current test, codenamed “Narbacular” and its subsequent dismissal due to putting children in danger. I’m thinking the person in charge would have been passionately against it, stating words to the effect of “We will not be testing children while I’m still alive”. In this way, GLaDOS can deem it acceptable to test children because that person is no longer alive.
Perhaps after the Narbacular Drop levels are over there can be an extension with more levels of an escape sequence (now typical of Portal) that could explore some currently unexplained aspect of the story, like the post-activation attachment of the morality core on GLaDOS. The final boss battle could involve attaching cores to GLaDOS in a similar manner to the final battle of Portal 2.</summary>
  </entry>
  
  <entry>
    <title>Wikinauts — Multiplatform Scribblenauts</title>
    <id>https://death.id.au/b1.17/</id>
    <updated>2011-04-27T00:00:00Z</updated>
    <published>2011-04-27T00:00:00Z</published>
    <content type="xhtml"><div xmlns="http://www.w3.org/1999/xhtml"><h1>Wikinauts — Multiplatform Scribblenauts</h1>
<p>This is an idea I've had since the first Scribblenauts game was released: A community-driven version. The basic premise is identical to that of Scribblenauts: solving puzzle-based levels using just about anything you can think of by summoning items, NPCs, etc by writing the name down. For example, the objective is to get a star out of a tree. You could summon a lumberjack to cut the tree down, or a saw/chainsaw/axe/etc to cut it down yourself. Or you could summon a ladder to climb up into the tree. Or a jetpack, helicopter, wings... The possibilities are endless. The point of difference between Scribblenauts and my idea of Wikinauts is the social, community-driven aspect.</p>
<p>The general idea is to build a robust engine and data structure, and let everyone add to and modify the database of items to be summoned. In this way, a creative mind might decide to summon something that doesn't exist in the database, let's say &quot;Batman&quot; for instance. They can then go to the Wikinauts database and submit graphics and behaviors for Batman, who can then be summoned in the game. The problem is, Batman won't really be well defined, as he's just been created for someone's purposes in one particular level. This is where the advantages of the community-based wiki approach really start to show. Others can see that Batman has been created and decide to modify his behaviours. For example, tell him to fight evil, drive the Batmobile, hate the Joker, things like that. And as soon as someone notices that Batman does not behave the way you might expect in a certain situation, then his behaviour can be modified again to move towards the expected.</p>
<p>According to Wikipedia: Maged N. Kamel Boulos, Cito Maramba and Steve Wheeler write that it is the &quot;openness of wikis that gives rise to the concept of 'Darwikinism', which is a concept that describes the socially Darwinian process' that wiki pages are subject to. Basically, because of the openness and rapidity that wiki pages can be edited, the pages undergo a <a href="http://en.wikipedia.org/wiki/Natural_selection">natural selection</a> process like that which nature subjects to living organisms. 'Unfit' sentences and sections are ruthlessly culled, edited and replaced if they are not considered 'fit', which hopefully results in the evolution of a higher quality and more relevant page. Whilst such openness may invite 'vandalism' and the posting of untrue information, this same openness also makes it possible to rapidly correct or restore a 'quality' wiki page.&quot;<br />
Given the right community mindset, Wikinauts has potential to outshine Scribblenauts both in number of summoned items and quality of item interactions. As the items and interactions are modified over time by many different people, they will <em>evolve</em> to be of higher quality. Graphics will be touched up, behaviours will be modified, and a community concencus will be formed on how a certain object or behaviour should look or behave in the game world.</p>
<p>A game such as this will have to have an exceptionally robust and flexible engine. People will want to throw everything they have at it, and the gameplay will evolve along with the items. It seems a bit difficult to have gameplay evolve with a static game engine, but it shouldn't be impossible. I'm thinking of involving some sort of low-level scripting language, so actions and behaviours can be modified at <em>almost</em> the engine level for the most flexibility. Items can inherit these actions and behaviours from generalizations. For example, a &quot;vehicle&quot; may contain all the necessary code and behaviours to allow movement, and things like cars and aeroplanes will not need to be told <em>how to</em> move, but simply impose restrictions or add actions to the basic movement actions (cars stick to the ground, etc).</p>
<p>Development of such an application will have to be split into phases:</p>
<ol>
<li>An initial engine and data structure</li>
<li>A public database backend application for development of objects</li>
<li>Development of objects using the backend application, while testing items with the engine</li>
<li>Polished front end game client<br />
5+) Clients for multiple platforms</li>
</ol>
<p>Once phase 4 is completed, and provided there is an active community producing polished objects for the database, a database snapshot could be created for shipping with standalone versions of the game, for example on a console or mobile version. It's a large and ambitious project, with a great many factors needing to be taken into consideration. And I've already found one (somewhat poor, unfortunately) attempt to fulfill this idea called Project Everything. I couldn't figure out how to work the game itself, and the object back end seems to consist of adding an image and an animation, with not much regard to behavior. And any image can be used with no scaling, leading to overly large, white-boxed, useless items. Looking at this, it's clear that there needs to be more freedom and more direction when building objects and interactions. I'm considering a library of bits, with the ability to draw your own bits where necessary. So for the Batman example, you could build a person from predefined Man bits, with predefined behaviors, and simply draw the cape and cowl, and perhaps add a few small animations and behaviors to differentiate Batman from Man. The cape and cowl bits can then be used for other things, like Batgirl, or the cape for Superman (re-textured).</p>
</div></content>
    <link rel="alternate" href="https://death.id.au/b1.17/" />
    <summary type="html">This is an idea I&#39;ve had since the first Scribblenauts game was released: A community-driven version. The basic premise is identical to that of Scribblenauts: solving puzzle-based levels using just about anything you can think of by summoning items, NPCs, etc by writing the name down. For example, the objective is to get a star out of a tree. You could summon a lumberjack to cut the tree down, or a saw/chainsaw/axe/etc to cut it down yourself. Or you could summon a ladder to climb up into the tree. Or a jetpack, helicopter, wings... The possibilities are endless. The point of difference between Scribblenauts and my idea of Wikinauts is the social, community-driven aspect.</summary>
  </entry>

</feed>