Compare commits

13 Commits
11 changed files with 338 additions and 1 deletions
+11 -1
View File
@@ -10,7 +10,7 @@ working across different projects via [VisualMode](https://www.visualmode.dev/).
For a steady stream of TILs, [sign up for my newsletter](https://visualmode.kit.com/newsletter).
_1884 TILs and counting..._
_1894 TILs and counting..._
See some of the other learning resources I work on:
@@ -169,9 +169,11 @@ If you've learned something here, support my efforts writing daily TILs by
- [Allow Edits From The Start](claude-code/allow-edits-from-the-start.md)
- [Ask An Agent To Fix Merge Conflicts](claude-code/ask-an-agent-to-fix-merge-conflicts.md)
- [Build From My Phone In Cloud Environments](claude-code/build-from-my-phone-in-cloud-environments.md)
- [Distinguish Sessions With Different Colors](claude-code/distinguish-sessions-with-different-colors.md)
- [Monitor Usage Limits From CLI](claude-code/monitor-usage-limits-from-cli.md)
- [Open Current Prompt In Default Editor](claude-code/open-current-prompt-in-default-editor.md)
- [Pick Up Session Locally That Started In Cloud](claude-code/pick-up-session-locally-that-started-in-cloud.md)
- [Resume Specific Session](claude-code/resume-specific-session.md)
- [Set Permission Mode When Starting Session](claude-code/set-permission-mode-when-starting-session.md)
- [Stash The Current Prompt To Send Another First](claude-code/stash-the-current-prompt-to-send-another-first.md)
@@ -227,6 +229,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Parameterized SCSS Mixins](css/parameterized-scss-mixins.md)
- [Prevent Invisible Elements From Being Clicked](css/prevent-invisible-elements-from-being-clicked.md)
- [:root Has Higher Specificity Than html](css/root-has-higher-specificity-than-html.md)
- [Show Full URLs With Print Styles](css/show-full-urls-with-print-styles.md)
- [Style A Background With A Linear Gradient](css/style-a-background-with-a-linear-gradient.md)
- [Using Maps In SCSS](css/using-maps-in-scss.md)
@@ -479,11 +482,13 @@ If you've learned something here, support my efforts writing daily TILs by
### GitHub
- [Access Your GitHub Profile Photo](github/access-your-github-profile-photo.md)
- [Create A Private Repo From The CLI](github/create-a-private-repo-from-the-cli.md)
- [Create And Push To New Repo From CLI](github/create-and-push-to-new-repo-from-cli.md)
- [List PRs Awaiting Your Review](github/list-prs-awaiting-your-review.md)
- [Open A PR To An Unforked Repo](github/open-a-pr-to-an-unforked-repo.md)
- [Open File To Specific Line In Browser](github/open-file-to-specific-line-in-browser.md)
- [Process JSON Output From gh With jq](github/process-json-output-from-gh-with-jq.md)
- [Rerun Failed Jobs For Current Branch](github/rerun-failed-jobs-for-current-branch.md)
- [Target Another Repo When Creating A PR](github/target-another-repo-when-creating-a-pr.md)
- [Tell gh What The Default Repo Is](github/tell-gh-what-the-default-repo-is.md)
@@ -494,6 +499,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Disable A Workflow With The gh CLI](github-actions/disable-a-workflow-with-the-gh-cli.md)
- [Reference An Encrypted Secret In An Action](github-actions/reference-an-encrypted-secret-in-an-action.md)
- [Run Schedule Action To Commit Regular Updates](github-actions/run-scheduled-action-to-commit-regular-updates.md)
- [Set Repository Secret From CLI](github-actions/set-repository-secret-from-cli.md)
- [Trigger A Workflow Via An API Call](github-actions/trigger-a-workflow-via-an-api-call.md)
- [Use Labels To Block PR Merge](github-actions/use-labels-to-block-pr-merge.md)
@@ -1872,6 +1878,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Make Neovim The Default Way To View Man Pages](unix/make-neovim-the-default-way-to-view-man-pages.md)
- [Manually Pass Two Git Files To Delta](unix/manually-pass-two-git-files-to-delta.md)
- [Map A Domain To localhost](unix/map-a-domain-to-localhost.md)
- [Merge A Sequence Of Lines Of Output](unix/merge-a-sequence-of-lines-of-output.md)
- [Move A List Of Files To Another Directory](unix/move-a-list-of-files-to-another-directory.md)
- [Negative Look-Ahead Search With ripgrep](unix/negative-look-ahead-search-with-ripgrep.md)
- [Occupy A Local Port With Netcat](unix/occupy-a-local-port-with-netcat.md)
@@ -1892,6 +1899,7 @@ If you've learned something here, support my efforts writing daily TILs by
- [Rename A Bunch Of Files By Constructing mv Commands](unix/rename-a-bunch-of-files-by-constructing-mv-commands.md)
- [Repeat Yourself](unix/repeat-yourself.md)
- [Replace Pattern Across Many Files In A Project](unix/replace-pattern-across-many-files-in-a-project.md)
- [Restart Process Managed By `process-compose`](unix/restart-process-managed-by-process-compose.md)
- [Restart Specific Overmind Process](unix/restart-specific-overmind-process.md)
- [Reverse Each Line Of A File](unix/reverse-each-line-of-a-file.md)
- [Run A Command Repeatedly Several Times](unix/run-a-command-repeatedly-several-times.md)
@@ -2143,12 +2151,14 @@ If you've learned something here, support my efforts writing daily TILs by
- [Change Window Name In iTerm](workflow/change-window-name-in-iterm.md)
- [Configure Email Redirect With Cloudflare](workflow/configure-email-redirect-with-cloudflare.md)
- [Control Media With Drop Keyboard](workflow/control-media-with-drop-keyboard.md)
- [Create 1Password Login Item From The CLI](workflow/create-1password-login-item-from-the-cli.md)
- [Convert An ePub Document To PDF On Mac](workflow/convert-an-epub-document-to-pdf-on-mac.md)
- [Create A Local Sanity Dataset Backup](workflow/create-a-local-sanity-dataset-backup.md)
- [Create A Public URL For A Local Server](workflow/create-a-public-url-for-a-local-server.md)
- [Create Todo Items In Logseq](workflow/create-todo-items-in-logseq.md)
- [Do Project Time Tracking From The CLI](workflow/do-project-time-tracking-from-the-cli.md)
- [Enable Dev Tools For Safari](workflow/enable-dev-tools-for-safari.md)
- [Ensure Tooltips Appear In Screenshots](workflow/ensure-tooltips-appear-in-screenshots.md)
- [Forward Stripe Events To Local Server](workflow/forward-stripe-events-to-local-server.md)
- [Get URL For GitHub User Profile Photo](workflow/get-url-for-github-user-profile-photo.md)
- [Get Your Public IP Address](workflow/get-your-public-ip-address.md)
@@ -0,0 +1,27 @@
# Build From My Phone In Cloud Environments
I mostly use Claude Code from a terminal with the `claude` CLI. Everything is
running on my machine (except the inference). The codebase is already there, the
feature branch is already in flight, all my tools and dotfiles are installed,
the dev server or container VMs are running, etc. It's the closest to how I
already worked before coding harnesses and allows me to seamlessly flow between
agentic code, review, making my own edits, and so forth.
However, I've been finding it pretty satisfying to open _Claude Code_ either in
the MacOS app or iOS app, connect a GitHub repo, and then ask for a quick
feature landed as a PR.
This flow uses [Claude in the Cloud](https://code.claude.com/docs/en/claude-code-on-the-web) to boot up a
development environment, pull down your codebase, create a branch, build a
feature, run tests, boot the app, do all the different explorations and checks
that `claude` typically does, and eventually land a PR back on GitHub.
What I find particularly remarkable about this flow is that this _cloud session_
is effectively synced across any device where I'm using Claude. So while I'm on
the train home from the gym, I open the Claude iOS app, pick my repo, and
describe a feature. By the time I get home, I open Claude.app on my computer
where I can skim through the chat summary, view the diff, and then suggest next
steps or jump to the PR on GitHub.
I'm finding this workflow particularly exciting for side projects because I can
kick off feature experiments from anywhere when the idea comes to me.
@@ -0,0 +1,33 @@
# Pick Up Session Locally That Started In Cloud
I've been enjoying using _Claude Code_ from the iOS and MacOS apps for various
side tasks recently. For smaller things I can usually get most of the way there
from the app. And eventually I pull down the branch or have Claude create a PR
that I can review.
Sometimes I want to transfer the session onto my computer to pick up where it
left off for various reasons (e.g. I want the session to have access to some
local data or specific dependencies).
The `claude` CLI has a flag for that — `--teleport`.
> Resume a teleport session, optionally specify session ID
```
❯ claude --teleport
Select a session to resume (jbranchaud/still):
Updated Session Title
❯ 1. 21m ago Blogmark link search modal
2. 11h ago Mobile feed reader margins
3. 11h ago Feed pagination with Pagy
4. 1d ago RSS feed highlights and annotations
5. 1d ago RSS feed categories
6. 3d ago Blogmarks image drag-and-drop upload
↑/↓ to select · Enter to confirm · Esc to cancel
```
This is similar to how `--resume` works, but it is for a session that was
previously running in the cloud.
+46
View File
@@ -0,0 +1,46 @@
# Show Full URLs With Print Styles
Web pages are best experienced in a browser where you can fully interact with
them. Links, for instance, can be hovered, inspected, and clicked on to navigate
to the target location -- that's a core part of the web browser experience and
what makes the internet.
Some people have printers though and they want to be able to print off web
pages. Any web page can be printed off as is, but if your site or app is
anticipating certain pages being printed, then you may add some
`@print`-specific CSS styling to improve how it looks in print format.
Links are a prime example of lost interactivity. When printed, links render as
blue underlined text with no way to know what they are linking to. One trick
that I recently picked up from [this lobste.rs thread](https://lobste.rs/s/olnqh2/websites_should_be_printable#c_wigxfy) is to
add _after content_ that renders the actual, full URL next to the link.
```css
@media print
{
a[href]::after
{
content: " [" attr(href) "]";
}
}
```
This means that when a page has `<a href="https://still.visualmode.dev">still</a>`, the printed version will not
just render the word "still", but will be styled to render `still [https://still.visualmode.dev]`.
This will add content after links for both absolute URLs and relative paths (and
anchor tags). If I wanted to limit the after content to only show absolute URLs,
I could add a bit more specificity to the matching tag.
```css
@media print
{
a[href^="http"]::after
{
content: " [" attr(href) "]";
}
}
```
The `^=` matches the pattern of `http` on the start of the `href` string which
means it will only display absolute URLs.
@@ -0,0 +1,31 @@
# Set Repository Secret From CLI
I set up a GitHub Action that will run daily and potentially push data up to a
3rd-party API. To be able to post to that API, the action must include a secret
as the bearer token in the requests. I need to securely include that secret in
the GitHub Actions environment for this repo.
I can do that from the CLI with `gh` like so:
```bash
❯ gh secret set TIL_SYNC_TOKEN
? Paste your secret: ****************************************************************
```
Notice that it prompts me for the secret and stars out what I paste in. That way
the secret doesn't end up in my bash history and isn't visible on my screen.
After I've run this, I can see that the secret is set by opening up the repo in
the GitHub web UI, clicking on _Settings_ > _Secrets and Variables_ > _Actions_.
I should see `TIL_SYNC_TOKEN` listed under the _Repository Secrets_ section.
Here is an excerpt from my workflow action sync script where that secret is
referenced -- notice the `${{ secrets.TIL_SYNC_TOKEN }}`.
```yaml
- name: Sync posts
env:
TIL_SYNC_URL: ${{ vars.TIL_SYNC_URL }}
TIL_SYNC_TOKEN: ${{ secrets.TIL_SYNC_TOKEN }}
run: ruby script/til_sync/til_sync.rb --repo til ${{ inputs.all && '--all' || '--days 3' }}
```
@@ -0,0 +1,34 @@
# Create A Private Repo From The CLI
The vast majority of the GitHub repos that I've created over my career have been
done from the web UI. It was only recently that I found out that [I can create a
new repo from the CLI with `gh`](create-and-push-to-new-repo-from-cli.md).
A repo can be created for the current directory (where a local git repo already
exists) using the `gh repo create` command. I'm wanting this to be a private
repo so I can include the `--private` flag instead of the `--public` flag.
```bash
❯ gh repo create marathon-training --private --source=. --remote=origin --push
✓ Created repository jbranchaud/marathon-training on github.com
https://github.com/jbranchaud/marathon-training
✓ Added remote https://github.com/jbranchaud/marathon-training.git
Enumerating objects: 125, done.
Counting objects: 100% (125/125), done.
Delta compression using up to 16 threads
Compressing objects: 100% (114/114), done.
Writing objects: 100% (125/125), 56.69 KiB | 14.17 MiB/s, done.
Total 125 (delta 52), reused 0 (delta 0), pack-reused 0 (from 0)
remote: Resolving deltas: 100% (52/52), done.
To https://github.com/jbranchaud/marathon-training.git
* [new branch] HEAD -> main
branch 'main' set up to track 'origin/main' by rebasing.
✓ Pushed commits to https://github.com/jbranchaud/marathon-training.git
```
Since I am running this command from the root directory of the git-backed
project, I specify the `--source` as `.` (mean _here_, the current directory).
The remaining flags tell it what the remote should be set up as and that I want
to push this local repo up to the GitHub remote once it is created.
See `gh repo create --help` for more details.
@@ -0,0 +1,30 @@
# Rerun Failed Jobs For Current Branch
I have a branch that I've already opened a PR for. I've made more changes, run
the tests, and pushed up the latest to the remote. I know the tests should have
been green, but I'm seeing a flickering test fail in CI. I want to retrigger the
relevant failed jobs to see if I can get the flickers to pass.
First, I need to figure out the workflow ID with any failed jobs.
```bash
❯ gh run list --branch "$(git branch --show-current)" --status failure
STATUS TITLE WORKFLOW BRANCH EVENT ID ELAPSED AGE
X Better comp... Ruby on R... jbranchau... pull_request 38778306948 10m54s about 25 ...
```
This one-liner does that by referencing the current branch (via `git`) and then
asking `gh` to list failed workflows associated with that branch.
Grabbing the `ID` value, I can then ask `gh` to rerun failed jobs for that
workflow.
```bash
❯ gh run rerun 38778306948 --failed
✓ Requested rerun (failed jobs) of run 38778306948
```
Opening that PR up in the browser, I can see that the specific workflow has been
rerun and is currently _in progress_.
See `gh run list --help` and `gh run rerun --help` for more details.
@@ -0,0 +1,42 @@
# Merge A Sequence Of Lines Of Output
More often than not when I'm chaining a sequence of Unix commands together, I'm
eventually trying to fan them out or execute each argument separately. I
recently had the need for a bunch of separate lines of output to be fanned back
in to a single line.
The tool I found for this job is `paste` (not the most intuitive name).
> merge corresponding or subsequent lines of files
I was listing out all the categories that my active [TIL posts](https://github.com/jbranchaud/til) full under. I used the following
command to do that.
```bash
❯ git ls-files '*/*.md' | cut -d/ -f1 | sort -u
ack
ansible
astro
aws
bash
brew
chrome
...
```
Those are all my categories, but then the format I really wanted was for them to
be all on the same line with a clear delimiter so that I could paste them for
[use in another script](/llm/translate-to-canonical-tags-with-small-model.md).
The trick is to tack on one more pipe to `paste`. The `-s` tells `paste` to join
every line from a given input file (e.g. stdin) onto a single line. The `-d`
allows for overriding the default delimiter (tab) with something like a `,`.
Lastly, the trailing `-` tells `paste` to read from stdin rather than some other
named file(s).
```bash
❯ git ls-files '*/*.md' | cut -d/ -f1 | sort -u | paste -sd, -
ack,ansible,astro,aws,bash,brew,chrome,claude-code,clojure,css,cursor,deno,devops,docker,drizzle,elixir,gatsby,git,github,github-actions,go,groq,heroku,html,http,inngest,internet,java,javascript,jj,jq,kitty,linux,llm,mac,math,mise,mongodb,mysql,neovim,netlify,next-auth,nextjs,phoenix,planetscale,pnpm,postgres,prisma,python,rails,react,react_native,react-testing-library,reason,remix,rspec,ruby,sed,shell,sqlite,streaming,tailwind,taskfile,tmux,typescript,unix,vercel,vim,vscode,webpack,workflow,xstate,yaml,zed,zod,zsh
```
See `man paste` for more details.
@@ -0,0 +1,28 @@
# Restart Process Managed By `process-compose`
I'm working on a project where we are experimenting with
[`process-compose`](https://github.com/F1bonacc1/process-compose) as a better
way orchestrate a set of processes that all need to be running for a web
development server (Rails) to work. This includes the Rails server itself, a
JavaScript build process, PostgreSQL, a background worker process, etc.
In the past [I've used `overmind` with
Procfiles](/rails/run-dev-processes-with-overmind-instead-of-foreman.md) to do a
similar sort of thing. No matter what tooling is in play, from time to time, I
will eventually need to restart one of the processes that has become stale.
The `vite` (JavaScript build) process is a good example of this. Configuration
changes, new `npm` dependencies, and tailwind classes on new files can all lead
to the build being stale.
From the `process-compose` TUI, I can navigate the cursor over the `vite`
process and hit `Ctrl-r` to restart the process.
I can also issue a restart via the CLI like so:
```
❯ mise exec -- process-compose process restart vite
Process vite restarted
```
[source](https://f1bonacc1.github.io/process-compose/cli/process-compose_process_restart/)
@@ -0,0 +1,38 @@
# Create 1Password Login Item From The CLI
The default shape of a login item in 1Password is _Username_ and _Password_.
Whenever I'm trying to save something in 1Password, like an AWS Access Key for
example, I find myself manually adjusting the fields to get it into the right
shape for that item.
What I realized would be nicer is using [`op` (the 1Password
CLI)](https://www.1password.dev/cli) to create a login item with the shape I
want (which I can later reference in my shell history). Then I can pop open the
desktop app and update the latest entry with the actually secret values that I
copy from, in this case, the AWS Console.
```bash
❯ op item create --vault "Personal" --category login --title "AWS - jbranchaud-lastword-cli" \
'Access Key ID[text]=TODO' \
'Secret Access Key[concealed]=TODO' \
'Default region[text]=us-east-1'
ID: abc123
Title: AWS - jbranchaud-lastword-cli
Vault: Personal (efg456)
Created: now
Updated: now
Favorite: false
Version: 1
Category: LOGIN
Fields:
Access Key ID: TODO
Secret Access Key: [use 'op item get abc123 --reveal' to reveal]
Default region: us-east-1
```
In this command I point to my `Personal` vault, I update the title of the login
item inline, and then I have some custom fields on the item that match the shape
of this scenario.
When I hit enter, a listing of fields is output. I can then hop over to the
desktop app to set the actually ID and secret for the access key.
@@ -0,0 +1,18 @@
# Ensure Tooltips Appear In Screenshots
I use [CleanShot X](https://cleanshot.com/) for all my screenshot needs. It's by
far the best tool for the job.
I'm a big proponent of taking high-context screenshots. That is, including
everything in the screenshot that another person viewing it would need to know
so that they don't have to answer follow up questions. This usually means other
parts of the window besides the focal point or including the URL bar.
Another, less common, aspect of a shot I sometimes need, especially in a
browser, are hover-based visual elements. That could be a tooltip or the option
in a dropdown the cursor is over. Sometimes it is even a visual element that
goes away after a beat.
To capture these with CleanShot X, I need to turn on the "Freeze screen when
taking a screenshot" setting which can be found in _Settings_ under the
_Screenshots_ tab in the _Capture_ section.