Categories: sharing » writing » blogging

RSS - Atom - Subscribe via email

Tweaking my writing workflow using SuperNote's new handwriting recognition

| blogging, supernote

Both Google Cloud Vision and SuperNote's new handwriting recognition handle my print fine. Neither handle columns the way I'd like, but to be fair, I'm not really sure how I want columns and wrapping handled anyway. I can always experiment with the standard use-case: one column of text, to export as text (with perhaps the occasional sketch, which I can crop and include).

If I can get the hang of writing my thoughts, then it turns some of those bedtime hours into writing hours. Writing by hand feels slow and linear, but it's better than nothing, and thinking takes most of the time anyway. While speech recognition feels like it might be faster in short bursts, I don't have a lot of "talking to myself" time (aside from sleepy brain dumps), and my workflow for processing audio is still slow and disjointed. I can't type on my phone because then A- will want to be on a screen too. I'm glad e-ink devices are different enough not to trigger her sense of unfairness, although sometimes she does ask if she can do mazes or connect-the-dots. Then I switch to knitting until it's really really time to go to bed.

I'm slowly figuring out my workflows for experimenting with and writing about code. Naturally, that's a little more challenging to write about by hand, but I could draft the context. I can think through life stuff too, and maybe look into saving more notes in my Org files.

I've experimented with handwritten blog posts before. Now that I have a little more time to tweak my workflow and think thoughts, maybe I'll get the hang of them!


It looks like the Supernote's real-time recognition is pretty accurate for my handwriting, getting the text out of multiple pages is pretty straightforward.

Here's the raw TXT output from the Supernote.

Here's what it took to edit it into the first part of this post - just adding line-breaks and fixing up some words:

"A screen recording showing editing"
Figure 1: My editing process - just added line breaks and fixed some words
Source images

[["The first page of my handwritten post"

"The second page of my handwritten post"
Figure 2: Second page

If I add more lines between paragraphs when writing, I might be able to skip adding them in the text export.

For comparison, here's the text output from Google Cloud Vision.

Tweaking my handwriting workflow
Both Google Cloud Vision and Super Note's new
handwriting recognition handle my print fine. Neither
handle columns the way I'd like, but to be fair,
I'm not really sure how I want columns and wrapping
handled anyway I can always experiment with the
standard use-case
use-case: One column of text, to export
as Text (with perhaps the occasional sketch, which
can crop and include).
If I can get the hang of writing my thoughts,
then it turns some of those bedtime hours into writi
writing
hours. Writing by hand feels slow and linear, but it's
better than nothing, and thinking takes most of the time
anyway while speech recognition feels like it might be
faster in short bursts, don't have a lot of "talking to
myself" time (aside from sleepy braindumps), and my workflow
for processing audio is still slow and disjointed. I can't
type on my phone because then A- will want to be on

I'm glad e-ink devices are different enough
not to trigger her sense of unfairness, although sometimes
she does ask if she can do mazes or connect-the-dots
a screen too
Then I switch to Knitting until it's really really time to
go to bed.
I'm slowly figuring out my workflows for experimenting
with and writing about code. Naturally, that's a little
more challenging to write about by hand, but I could
draft the context. I can think through life stuff too, and
maybe look into saving more notes in my org files
I've experimented with handwritten blog posts before
Now that I have a little more time to tweak my workflow
and think thoughts, maybe I'll get the hang of them!

I'm leaning towards SuperNote's recognition results for long text, although I don't get access to the confidence data so I'll probably just have to delete the misrecognized text if I include sketches.

Using Javascript to add a "Copy code" link to source code blocks in my blog posts

| css, js, blogging

I'd like to write about code more often. It's easier for people to try out ideas if they can copy the code without fiddling with selecting the text, especially on mobile browsers, so "Copy code" buttons on source code blocks would be nice. I used this tutorial for adding code buttons as a basis for the following CSS and JS code.

First, let's add the buttons with Javascript. I want the buttons to be visible in the summary line if I'm using the <details /> element. If not, they can go in the div with the org-src-container class.

/* Start of copy code */
// based on https://www.roboleary.net/2022/01/13/copy-code-to-clipboard-blog.html
const copyLabel = 'Copy code';

async function copyCode(block, button) {
  let code = block.querySelector('pre.src');
  let text = code.innerText;
  await navigator.clipboard.writeText(text);
  button.innerText = 'Copied';
  setTimeout(() => {
    button.innerText = copyLabel;
  }, 500);
}

function addCopyCodeButtons() {
  if (!navigator.clipboard) return;
  let blocks = document.querySelectorAll('.org-src-container');
  blocks.forEach((block) => {
    let button = document.createElement('button');
    button.innerText = copyLabel;
    button.classList.add('copy-code');
    let details = block.closest('details');
    let summary = details && details.querySelector('summary');
    if (summary) {
      summary.appendChild(button);
    } else {
      block.appendChild(button);
    }
    button.addEventListener('click', async() => {
      await copyCode(block, button);
    });
    block.setAttribute('tabindex', 0);
  });
}
document.addEventListener("DOMContentLoaded", function(event) { 
  addCopyCodeButtons();
});
/* End of copy code */

Then we style it:

/* Start of copy code */
pre.src { margin: 0 }
.org-src-container {
    position: relative;
    margin: 0 0;
    padding: 1.75rem 0 1.75rem 1rem;
}
summary { position: relative; }
summary .org-src-container { padding: 0 }
summary .org-src-container pre.src { margin: 0 }
.org-src-container button.copy-code, summary button.copy-code {
    position: absolute;
    top: 0px;
    right: 0px;
}
/* End of copy code */

Someday I'll figure out how to make it easier to tangle things to the post's directory and make the file available for download. In the meantime, this might be a good start.

Compiling selected blog posts into HTML and EPUB so I can annotate them

| blogging, 11ty, nodejs, supernote

[2023-01-04 Wed] Added a screenshot showing annotation.

I was thinking about how to prepare for my next 10-year review, since I'll turn 40 this year. I've been writing yearly reviews with some regularity and monthly reviews sporadically, and I figured it would be nice to have those posts in an EPUB so that I can read them on my e-reader and annotate them as I do my review.

I use the 11ty static site generator to publish my blog as HTML files, since I currently can't keep more than Emacs Lisp, Javascript, and Python in my brain. (No Hugo or Jekyll for me at the moment.) I briefly thought about getting 11ty to create that archive for me, but I realized it might be easier to just write it as an external script instead of trying to figure out how to get 11ty to export one thing conditionally.

One of the things I've configured 11ty to make is a JSON file that includes all of my posts with dates, titles, permalinks, and categories. It was easy to then parse this list and filter it to get the posts I wanted. I parsed the HTML out of the _site directory that 11ty produces instead of fetching the pages from my webserver. I got the images from my webserver, though, and I made a local cache and rewrote the URLs. That way, the EPUB conversion could include the images.

Download blog.js

blog.js
const blog = require('/home/sacha/proj/static-blog/_site/blog/all/index.json');
const cheerio = require('cheerio');
const base = '/home/sacha/proj/static-blog/_site';
const fs = require('fs');
const path = require('path');

function slugify(p) {
  return p.permalink.replace('/blog', 'post-').replace(/\//g, '-');
}

async function processPost(p) {
  console.log('Processing '+ p.permalink);
  let $ = cheerio.load(fs.readFileSync(base + p.permalink + 'index.html'));
  $('#comment').remove();
  let images = $('article img');
  await Promise.all(images.map((i, e) => {
    let url = $(e).attr('src');
    const outputFileName = 'images/' + path.basename(url).replace(/ |%20|%23/g, '-');
    $(e).attr('src', outputFileName);
    $(e).attr('style', 'max-height: 100%; max-width: 100%; ' + ($(e).attr('style') || ''));
    $(e).attr('srcset', null);
    $(e).attr('sizes', null);
    $(e).attr('width', null);
    $(e).attr('height', null);
    if (!fs.existsSync(outputFileName)) {
      console.log('fetch', outputFileName);
      return fetch(url).then(res => res.arrayBuffer()).then(data => {
        const buffer = Buffer.from(data);
        return fs.createWriteStream(outputFileName).write(buffer);
      });
    } else {
      console.log(outputFileName, 'exists');
      return null;
    }
  }));
  console.log('Done ' + p.permalink);
  let slug = slugify(p);
  $('article h2').attr('id', slug);
  let header = $('article header').html();
  let entry = $('article .entry').html();
  return `<article>${header}${entry}</article>`;
}

let last10 = blog.filter((p) => p.date >= '2013-08-01');
let posts = last10.filter((p) => p.categories.indexOf('yearly') >= 0)
    .concat(blog.filter((p) => p.title == 'Turning 30: A review of the last decade'))
    .concat(last10.filter((p) => p.categories.indexOf('monthly') >= 0));

let toc = '<h1>Table of Contents</h1><ul>' + posts.map((p) => {
  return `<li><a href="#${slugify(p)}">${p.title}</a></li>\n`;
}).join('') + '</ul>';

let content = posts.reduce(async (prev, val) => {
    return await prev + await processPost(val);
  }, '');
content.then((data) => {
  fs.writeFileSync('archive.html',
                   `<html><body>${toc}${data}</body></html>`);

});

This created an archive.html with my posts, using the images/ directory for the images. Then I used my shell script for converting and copying files to convert it to EPUB and copy it over.

On the SuperNote, I can highlight text by drawing square brackets around it. If I tap that text, I can write or draw underneath it. Here's what that looks like:

20230104_090739.png
Figure 1: Writing an annotation

These notes are collected into a "Digest" view, and I can export things from there. (Example: archive.pdf)

2023-01-04_09-23-57.png
Figure 2: Here's what that digest is like when exported.

(Hmm, maybe I should ask them about hiding the pencil icon…)

Anyway, I think that might be a good starting point for my review.

Writing my blog posts by hand

| blogging, supernote

A- complains if I get screentime when she doesn't get screentime, so it's hard to find time to write on my laptop or on my phone. I've experimented with dictation before, since Google Recorder can make a half-decent transcript. I'm not used to talking things out, though. I keep correcting false starts, stutters, and mis-recognized words.

Fortunately, I can write on my A5X while waiting for A-. I get more space than I do when writing on my phone, so it's easier for me to think. I can export pages as PNGs, Dropbox, share each page, sync with with Google Photos, and then use Lens to copy the text. I can then paste it into Orgzly, which automatically syncs with Syncthing so that I can edit it on my laptop with Emacs. It needs a little cleanup (capitalization, stray punctuation, missed words, things in the wrong order), but editing it feels easier than dealing with the output of speech recognition, so it seems to be worth the extra time and effort. Besides, it feels less embarrassing to write at the sandbox than it is to talk to myself.

I can edit the text directly on my phone, but I still need my laptop to publish my blog because I haven't set up my static site generator on my server. Some day! In the meantime, this might be a good workflow for getting thoughts out there.

What if I want to refer to sketches while I write? Flipping between pages on the A5X can be challenging if they're not next to each other, but I can keep my current writing page next to my sketch. I could also view the sketch on my phone and balance it on the A5X, or use layers to keep a small version of the sketch as a handy reference. Lots of ideas to play around with…

Statically generating my blog with Eleventy

| blogging

Things will probably be a little strange on my blog for the next few days, as I've decided to experiment with statically generating my blog with Eleventy. It's a little complicated because I wanted to keep as many of my posts and category/tag feeds as possible.

To speed things up, I usually work with a subset of my posts. Generating a partial copy of my site results in 557 files and takes 3.73 seconds. When I generate the full copy of my site, it writes 13358 files in 96.73 seconds.

With any luck, I'll be able to get most of the things working before the next Emacs News post. Let's see!

Daily, weekly, and monthly journals: my Memento + Google Sheets + Tasks Free + Google Tasks + WordPress workflow

Posted: - Modified: | android, blogging, writing

Journaling considerations:

  • A- nurses a lot in bed. I keep my phone handy and I write when she doesn’t want to let me go.
  • I also jot quick notes throughout the day so that I don’t have to keep them in my head. These go into the nearest synchronized device.
  • It’s hard to remember the context for those notes if too much time passes. A daily verbal recap for W- and a weekly summary for my blog seem to be just the right balance. Anything older than a week gets too fuzzy, while writing detailed notes every day takes too much time away from other things I’d like to do.
  • Monthly reviews give me a better perspective on big changes. It’s hard to keep enough in my head when I’m reading or writing on my phone, so I need help summarizing a month’s worth of highlights.

Here are the technical details:

I set up Memento Database on my phone and on a backup Android phone. I picked it because it can synchronize between phones in the background, and it can also sync with Google Sheets so that I can process things further.

My journal database has the following fields:

  • Date: defaults to current date
  • Note
  • Category: single value from a list. Most of my entries go into Gross Motor, Fine Motor, Language, Self-care, Other, or Us, and I add other categories as needed.
  • Highlight: a number indicating the level of review this should be included in: 1 – weekly, 2 – monthly, 3 – yearly. I display this field as the status, so that it shows up on the right side.

I have a shortcut on my home screen so that I can quickly add a journal entry.

I normally sort the list by date, with recent entries on top.

As part of my weekly review, I look at recent entries, fill in any categories I skipped, and choose a few to highlight. For example, last week, I wrote 17 entries and I chose 13 to include in the weekly review.

I configured Memento’s default export formatting to include only the Note field and to export that without the field label.

I filtered the database to show only the entries within a given date range where the highlight value was greater than 0.5.

I grouped it by category so that similar entries were together. This was better than fiddling with the sorting, since this takes fewer taps to set back to my default view.

After filtering and grouping the entries, I used the “Send all > Send as text” command to send it to Tasks Free, which is a task manager that synchronizes with Google Tasks. I like the way I can drag-and-drop tasks to reorder them, which makes prioritizing so much easier on my phone. I edit the text in Tasks Free, turning the keywords into paragraphs and moving things around for better flow.

After drafting the body of the post (and possibly switching between phones, if my battery ran low), I select all the text, copy it into the WordPress app, set the categories and the title, and post the entry.

The monthly review process is quite similar. I start with a filtered view that shows all entries for last month (133 entries in November), and I group it by category. I skim all the entries, not just the ones included in the weekly review, because sometimes little moments turn out to be significant or part of a bigger pattern. After setting the highlight values for the things I’d like to include in my monthly review, I switch to another filter that shows me last month’s entries with a highlight value greater than 1.5 (28 entries in November). I send it all to Tasks Free, edit the post, copy it into WordPress, and publish.

If I manage to squeeze in some computer time, I use Google Tasks to copy the text into Emacs and then use my regular Org Mode review/publish processes.

I’ve been thinking about how I can improve this workflow. Sending text to the WordPress app doesn’t seem to work (the text disappears after I save or publish), and it’s kinda nice being able to move my weekly review task around on my task list in order to accommodate other priorities. I also like the way Google Tasks keeps the data from completed tasks, which has come in handy a few times. Tasks Free editing is more responsive, too. Synchronizing with Tasks Free seems to be more robust than synchronizing with Orgzly, since I only have to watch out for editing the same task on two devices instead of watching out for the whole file.

I’d like to get back to drawing the weekly and monthly reviews, but maybe that can wait until A-‘s sleep is more settled and my discretionary time is more consolidated. The visual journals are more fun to flip through, but the bulk and chronological views I hacked into my WordPress theme are reasonable workarounds.

What do I want from an annual review?

Posted: - Modified: | blogging

I’ve got most of the pieces for an annual review: monthly reviews in visual and text form, my time records, and a recent flip-through of all of my sketches. I’d like to bring my ledger of income and expenses up to date, finish reading all of my blog posts, and draw a couple of yearly summaries (monthly events, differences between 2015 and 2016, analyses). I want to make the most of my computer time, so I should think about what I want from my annual review and how I can get that more efficiently.

Highlights of the year
A month-by-month list of highlights is good for reminding me of events and getting around the fogginess of memory. There’s so much to celebrate and appreciate. This also simplifies longer-term reviews, like the 10-year review I did when I turned 30.
Differences
What did I learn? What did I forget? It can be easier to see the differences when you compare across a longer time period. This can help me solidify growth, revisit things I’ve left behind, watch out for drifting, and choose what to focus on next year.
Patterns and trends
Taking a look at the data can sometimes turn up things I wouldn’t have guessed. Time, finances, and A-‘s data too – so much to explore! This might take a little longer, since it involves code.
Decision review
This is probably better broken up into separate posts, maybe even decoupled from my annual review.
What worked well? Why? How can we make things even better?
Good for continuous improvement. Might not go into as much depth as the decision reviews.

My overall goals are to:

  • remember and celebrate the journey
  • keep improving; remember what I’ve learned and revisit what I might have shelved
  • make it easier for my future self (or other people reading my archive) to get an overview of the year
  • maybe have conversations that grow out of the updates (notes on things I’ve tried, ideas for stuff that might help)

I’ll probably end up doing my annual review in chunks instead of waiting until it’s all done, since otherwise it might take me a few months.