From Alfred to Raycast
#post
This post originally appeared on Blog 2.0
I recently moved from Alfred to Raycast. Overall, I quite like the approach that Raycast takes.
H2 Why the Switch
Raycast just feels more modern than Alfred. It comes with a nice-looking interface out of the box that blends in well with macOS aesthetics. Alfred’s is extensible by all means, but Workflows feels way too overcomplicated for me. With Raycast, however, I can just throw in a single Python script and forget about passing variables between every block. Building a Raycast extension looks reasonably easy too. It’s all familiar technologies — TypeScript and React, plus its easy-to-use API. Overall, Raycast makes a great environment for tinkering.
Not to say that Raycast is perfect. There are frustrating things like… having to hit esc
a million times to get out of nested searches.
(The Raycast team also takes some really interesting development approaches)
H2 Updated Obsidian Scripts
It was quite necessary that I upgrade my Obsidian Alfred Workflow for Raycast since my brain already relies on it. Turned out it makes a pretty chill coding assignment. Here’s the code.
(See GitHub repo for newest code)
H3 Natural Language Date for Task Capture
Right, this is a new feature. With the ability to pass on multiple arguments, we can capture a task and give it a due date at the same time like this:
I resorted to the dateparser python package. It doesn’t behave exactly the same as Obsidian Tasks but it was close enough.
#!/usr/bin/env python3
# NOTE: dateparser package required!!!
# Required parameters: text
# @raycast.schemaVersion 1
# @raycast.title Obsidian Capture Task
# @raycast.mode silent
# Optional parameters:
# @raycast.icon ./assets/obsidian.png
# @raycast.argument1 { "type": "text", "placeholder": "task" }
# @raycast.argument2 { "type": "text", "placeholder": "when", "optional": true }
# Documentation:
# @raycast.author chaosarium
# @raycast.authorURL https://github.com/chaosarium
import sys, os, datetime, dateparser
from lib.insert_to_md_heading import insert_to_heading
query = sys.argv[1]
when = sys.argv[2]
# set vault path
OBSIDIAN_VAULT_PATH = "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Zettelkasten"
# file to manipulate within vault
FILE_PATH = f"050 Periodic/051 Daily/{datetime.datetime.now().strftime('%Y/%m-%B/%Y-%m-%d')}.md"
# format appended text
heading = '### Inbox'
if len(when):
if when == 'td' or when == 't':
when = 'today'
if when == 'tm':
when = 'tomorrow'
if when == 'tw':
when = 'saturday'
parsed_date = dateparser.parse(when, settings={'PREFER_DATES_FROM': 'future', 'PREFER_DAY_OF_MONTH': 'first'})
if parsed_date:
insertion = f'''- [ ] #t #inbox {query} 📅 {parsed_date.strftime('%Y-%m-%d')}
'''
else:
insertion = f'''- [ ] #t #inbox {query}
'''
else:
insertion = f'''- [ ] #t #inbox {query}
'''
print(insertion)
abs_path = os.path.expanduser(os.path.join(OBSIDIAN_VAULT_PATH, FILE_PATH))
try:
file = open(abs_path, "r")
original_text = file.read()
file.close()
processed_text = insert_to_heading(original_text, heading, insertion)
file = open(abs_path, "w")
file.write(processed_text)
file.close()
if when and not parsed_date:
print('failed to parse date; due date ignored')
else:
print('success')
sys.exit(0)
except Exception as e:
print(e)
sys.exit(1)
H3 Idea Capture
#!/usr/bin/env python3
# Required parameters: text
# @raycast.schemaVersion 1
# @raycast.title Obsidian Capture Idea
# @raycast.mode silent
# Optional parameters:
# @raycast.packageName obsidian-capture-idea
# @raycast.icon ./assets/obsidian.png
# @raycast.argument1 { "type": "text", "placeholder": "entry" }
# Documentation:
# @raycast.author chaosarium
# @raycast.authorURL https://github.com/chaosarium
import sys, os, datetime
query = sys.argv[1]
# set vault path
OBSIDIAN_VAULT_PATH = "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Zettelkasten"
# file to manipulate within vault
FILE_PATH = "000 System/080 Inboxes/Ideas Inbox.md"
# format appended text
insertion = f"""
%%captured {datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}%%
{query}
---"""
abs_path = os.path.expanduser(os.path.join(OBSIDIAN_VAULT_PATH, FILE_PATH))
try:
file = open(abs_path, "a")
file.write(insertion)
file.close()
print('success')
sys.exit(0)
except Exception as e:
print(e)
sys.exit(1)
H3 Replace in Heading
import re, sys, os, unicodedata
def insert_to_heading(original_text, heading, insertion):
find = re.compile(r'(?<=' + heading + r'\n)((\n.*?)+)(?=#+ |$)', flags = re.MULTILINE|re.DOTALL)
found = re.findall(find, original_text)
replace = f'\\1{insertion}'
if re.findall(find, original_text) == []:
new_content = original_text + heading + '\n\n' + insertion
else:
new_content = re.sub(find, replace, original_text)
# print(unicodedata.normalize("NFC",new_content))
return unicodedata.normalize("NFC",new_content)
H3 Example: Capture Journal
#!/usr/bin/env python3
# Required parameters: text
# @raycast.schemaVersion 1
# @raycast.title Obsidian Capture Journal
# @raycast.mode silent
# Optional parameters:
# @raycast.icon ./assets/obsidian.png
# @raycast.argument1 { "type": "text", "placeholder": "entry" }
# Documentation:
# @raycast.author chaosarium
# @raycast.authorURL https://github.com/chaosarium
import sys, os, datetime
from lib.insert_to_md_heading import insert_to_heading
query = sys.argv[1]
# set vault path
OBSIDIAN_VAULT_PATH = "~/Library/Mobile Documents/iCloud~md~obsidian/Documents/Zettelkasten"
# file to manipulate within vault
FILE_PATH = f"050 Periodic/051 Daily/{datetime.datetime.now().strftime('%Y/%m-%B/%Y-%m-%d')}.md"
# format appended text
heading = '### Journal'
insertion = f'''> **{datetime.datetime.now().strftime('%Y-%m-%d %H:%M')}**
> {query}
'''
abs_path = os.path.expanduser(os.path.join(OBSIDIAN_VAULT_PATH, FILE_PATH))
try:
file = open(abs_path, "r")
original_text = file.read()
file.close()
processed_text = insert_to_heading(original_text, heading, insertion)
file = open(abs_path, "w")
file.write(processed_text)
file.close()
print('success')
sys.exit(0)
except Exception as e:
print(e)
sys.exit(1)
H2 Moving Forward
There are a few things that I find quite frustrated, such as the inability to batch edit snippets. I guess some follow-up projects will be on the way… maybe an extension.