InkdropのノートをAstroサイトに簡単に同期する方法

InkdropのノートをAstroサイトに簡単に同期する方法

InkdropのノートをAstroサイトに簡単に同期する方法

live-exportというノートをProgrammaticallyに出力するツールを作りました

こんにちは、Inkdrop作者のTAKUYAです。

僕はMarkdownのシンタックス拡張を必要とするような機能要望にノーと言い続けてきました。なぜならInkdropは ‘Markdown’のノートアプリであり、その普及率の高さによる利用可能性を最大限に活かせるように作っているからです。例えば、Markdownは多くの静的サイトジェネレータで広く採用されています。Astro、Gatsby、Jekyll、Hugoなどなど。御存知の通り、既に多くの人がMarkdownでブログ記事を書いています(より厳密にはGitHub-flavored Markdown)。つまり、それは技術ノートだけでなく技術記事の投稿にも適しているのです。

Inkdropはプラットフォームとデバイスをまたいでシームレスな体験を提供します。それによりどこでもMarkdownを閲覧・執筆できます。しかしながら、その同期機能のためにファイルシステムとの親和性を犠牲にしました。それがInkdropからブログ記事を投稿する妨げになっていました — — これまでは。ここで、live-exportをご紹介します。これは、ローカルのファイルシステムにProgrammaticallyにノートをエクスポートできるツールです。以下、詳しく説明していきます。

柔軟にファイルシステムで作業するための方法

なぜlive-exportが必要なのでしょうか?Inkdropはノートをメニューからエクスポートする機能を既に持っています。しかし、メタデータに従ってファイル名のフォーマットやディレクトリ構造、どのノートを出力するか否かなどを自由に決められません。live-exportは柔軟なAPIを提供することでその問題を解決します。更に、名前の通り、これはInkdrop内で変更がある度に ‘continuously(継続的)’ に出力します。それは、結果をブラウザで確認しながら記事が書けるということです。このように:

Live export demo

なかなか便利ではないでしょうか。今回に向けて、デモも用意しました。こちらは、Astroで構築した僕のブログです:

What I use — Takuya Matsuyama
As an indie developer, I’ve been spending hours and hours at my desk every day. So, I’ve been continuously improving my…

ソースコードはこちらにあります:

GitHub — craftzdog/craftzdog-uses: A curated list of the tech I use, built with Astro and Tailwind…
A curated list of the tech I use, built with Astro and Tailwind CSS https://uses.craftz.dog/ Watch how I built this…

いい感じでしょう :) 英語ですがYouTubeにチュートリアルもあります:

データやデザイン、デプロイ先まで全てコントロール可能

どのように動作するのでしょうか。live-exportはInkdrop内部で動くプラグインではありません。ターミナル上で別個に動作し、ローカルInkdropサーバ経由でノートのデータにアクセスします。

こちらがノートブックからノートをインポートするコードの例です:

import { LiveExporter, toKebabCase } from '@inkdropapp/live-export'
const liveExport = new LiveExporter({  username: 'foo',  password: 'bar',  port: 19840})
const sub = await liveExport.start({  live: true,  bookId: '<YOUR_BOOK_ID>',  // Pre-process the specified note. It is useful to update the frontmatter information based on the note metadata  preProcessNote: ({ note, frontmatter, tags }) => {    frontmatter.title = note.title    // Convert note title to kebab case (eg. "kebab-case-note-title")    frontmatter.slug = toKebabCase(note.title)    frontmatter.tags = tags.map(t => t.name)  },  // Generate a path to export the specified note  pathForNote: ({ /* note, */ frontmatter }) => {    // export only if it's public    if (frontmatter.public) {      return `./<PATH_TO_EXPORT_NOTES>/${frontmatter.slug}.md`    } else return false  },  // Generate a URL for the specified note. It is necessary to link from the note to another note  urlForNote: ({ frontmatter }) => {    if (frontmatter.public) {      return `/<URL_TO_LINK_NOTES>/${frontmatter.slug}`    } else return false  },  // Generate a path and URL to export the specified image file  pathForFile: ({ mdastNode, /* note, file, */ extension, frontmatter }) => {    if (frontmatter.slug && mdastNode.alt) {      const fn = `${frontmatter.slug}_${toKebabCase(        mdastNode.alt      )}${extension}`      const res = {        filePath: `./<PATH_TO_EXPORT_IMAGES>/${fn}`,        url: `./<URL_TO_LINK_IMAGES>/${fn}`      }      // If the `alt` attribute of the image is 'thumbnail', use it as a hero image      if (mdastNode.alt === 'thumbnail') {        frontmatter.heroImage = res.filePath      }      return res    } else return false  },  // Post-process the specified note right before writing the note to a file. It is useful to tweak the Markdown data  postProcessNote: ({ md }) => {    // Remove the thumbnail image from the Markdown body    const md2 = md.replace(/\!\[thumbnail\]\(.*\)\n/, '')    return md2  }})

ご覧の通り、live-exportはツール非依存であり、基本的にどの静的サイトジェネレータとも動作します。データからディレクトリ構造、サイトのデザイン、どこにデプロイするかも自由自在です。仮にInkdropを使うのをやめてしまっても、ブログ記事はgitリポジトリの中に存在し続けます。ノー・ロックインです。

テンプレートからブログを作成する

使用方法について学んでいきましょう。先述の通り、live-exportはローカルのInkdropサーバ経由でデータにアクセスします。まずはそれを有効化します。ユーザデータディレクトリにある config.cson を編集します。

  • macOS: ~/Library/Application Support/inkdrop/config.cson
  • Windows: %APPDATA%\inkdrop\config.cson
  • Linux(deb/rpm): ~/.config/inkdrop/config.cson
  • Linux(Snap): ~/snap/inkdrop/current/.config/inkdrop/config.cson

Inkdropを終了し、以下の行を追記します:

"*":  core:    server:      enabled: true      port: 19840      bindAddress: "127.0.0.1"      auth:        username: "foo"        password: "bar"

そしてアプリを再起動します。以下のコマンドを実行すれば、サーバが正しく動いていることを確認できます:

curl http://foo:bar@localhost:19840/# => {"version":"5.5.1","ok":true}

これでlive-exportを使う準備が整いました!ブログをすぐに作れるようにテンプレートを用意しました:

GitHub — inkdropapp/inkdrop-blog-template: A template for creating simple blogs with Inkdrop
A template for creating simple blogs using live-export.

このテンプレートはAstroで組まれています。Astroは静的サイトジェネレータの一つで、スピードに特化しています:

Astro | Build faster websites
Trusted by over 30,000 developers and world-class teams Your content, your way. Astro works with your favorite content…

テンプレートをあなたのディレクトリにcloneします:

git clone git@github.com:inkdropapp/inkdrop-blog-template.git ./your-blogcd ./your-blog

次に依存モジュールをインストールします:

npm install# Or, yarn install

そして、以下のような.env ファイルをプロジェクトのルートに作成してください:

DEBUG='inkdrop:export:info,inkdrop:export:error'INKDROP_USERNAME='foo'INKDROP_PASSWORD='bar'INKDROP_PORT=19840INKDROP_BOOKID='<BOOKID>'

ここで、出力したいノートブックのIDが必要になります。このIDを簡単に取得するには、dev-toolsというプラグインをインストールします。Preferences → Plugins/Installの画面からインストールしてください。

Install dev-tools

インストールできたら、早速目的のノートブックを右クリックして「Copy Notebook ID」を選択してください:

Copy a notebook ID

.env ファイルに戻り、コピーしたノートブックIDを貼り付けます。

DEBUG='inkdrop:export:info,inkdrop:export:error'INKDROP_USERNAME='foo'INKDROP_PASSWORD='bar'INKDROP_PORT=19840INKDROP_BOOKID='book:r8KUw7SBx'

よし、ではAstroのdev serverを立てましょう。

npm run start

ブログがhttp://localhost:3000/ で走っているはずです:

Dev server

ではブログ用ノートブックに新規ノートを作成しましょう。

Example post

ここで、ノートがYAML frontmatterという —--ブロックで始まっている事に注目してください:

---public: true---

public: true をそこで指定することで、live-exportはそのノートがエクスポート対象であると認識します。ではlive-exportを別のターミナルで実行しましょう:

npm run live-import
> inkdrop-blog-template@0.0.1 live-import> node --experimental-vm-modules tools/import.mjs
(node:59628) ExperimentalWarning: The Fetch API is an experimental feature. This feature could change at any time(Use `node --trace-warnings ...` to show where the warning was created)  inkdrop:export:info Exporting note: note:_F3VsSEhJ Convert mp4 to webm +0ms  inkdrop:export:info Watching changes.. +6ms

するとノートがファイルにインポートされ、以下のように新しい記事が見えるようになったはずです:

記事をクリックします:

live-exportが走っている間は、ほぼリアルタイムにInkdrop側の変更がブログ側に反映され続けます。

添付画像にも対応しています。一つ画像ファイルをノートに追加してみましょう。

---public: true---
![rocket](inkdrop://file:3Z5LUBZk5)
```shffmpeg -i masthead-bg.mp4 -c:v libvpx-vp9 -crf 30 -b:v 0 -b:a 128k -c:a libopus masthead-bg.webm```

すると、画像ファイルは <PROJECT_ROOT>/public/posts/配下に出力され、ブログ側にも画像が現れたはずです。しかし記事一覧にはまだ出てきません。この画像を記事のサムネイルとして使用したいですね。その場合は、画像のalt部分を “thumbnail” に変更します:

![thumbnail](inkdrop://file:3Z5LUBZk5)

すると、記事一覧にも画像が現れました!

次にノートにタグを追加してみましょう。ブログはノートに付与されたタグを自動で検出してページを生成してくれます。

ノートにタグを追加すると、タグ一覧ページ http://localhost:3000/tags にそれが現れたはずです。

もしタグが見えない場合は、live-exportを再実行してみてください。これであなたのブログを執筆する準備ができました。もちろん、ナビゲーションバーのロゴやタイトル、配色など全てあなたの好きにカスタマイズ可能です。エンジョーイ!

以下はいくつか想定されるFAQです。

どこに私のブログをデプロイすれば良いですか?

Astroのドキュメンテーションに詳しいガイドが記載されています。

ローカルサーバのアクセスログはどうやったら見れますか?

Inkdropを --enable-logging フラグを付加して実行してください。詳しくはドキュメンテーションを参照ください。

ノートをInkdropにインポートして戻せますか?

いいえ。live-exportはあなたのプロジェクトに合わせてノートを変形させます。よってInkdropとの互換性が失われます。

Read more

なぜ体を壊してまで個人開発を頑張るのか?自尊心の欠如や過集中癖と向き合う

なぜ体を壊してまで個人開発を頑張るのか?自尊心の欠如や過集中癖と向き合う

どうもTAKUYAです。最近、個人開発を頑張りすぎて体調を崩してしまいました。アトピーが猛烈に悪化して、QoLが著しく下がってしまいました。まだ療養中ですが、毎日1万歩以上歩いて、徐々に回復しつつあります。 この過ちを繰り返さないためにも、自分は一体何が原因で頑張りすぎてしまうのか?という事について深堀りして考えてみたいと思います。また、個人開発におけるメンタルヘルスはあまり語られていないトピックだと思います。本記事が、同じように仕事を頑張りすぎてしまう人の助けになれば幸いです。 TL;DR * なんとなく続けていたソフト開発が自分を救った * 原体験が歪んだモチベーションを生んでしまった * 親が引くほどの過集中癖がある * 生得的な直せないバグと考えることにする * アプリの成功に関係なく、自分をあるがままに受け入れる * 挫折しないのは、なんだかんだで前向きだから * ユーザさんから「休め!」と叱咤された * 人生は長い。個人開発なんかで死ぬな 自己の原体験について振り返ってみる 個人開発だけで生活するようになって、かれこれ8年ぐらいが経ちます。こう

By Takuya Matsuyama
ユーザサポートの問い合わせを装った攻撃が怖すぎた

ユーザサポートの問い合わせを装った攻撃が怖すぎた

どうもTAKUYAです。個人開発をしていてアプリの知名度が上がってくると、作者個人(あるいはサイト管理人)を狙った攻撃というのをたまに受けます。つい先日も、怖すぎるメールを受け取ったのでシェアします。 件名: Cookie consent prevents platform access Hello, I cannot access use the store. The cookie consent notice keeps appearing and nothing happens once I approve or try to close it, so I’m unable to interact with the website. Please provide guidance on

By Takuya Matsuyama
万年ペーパーの自分が車の運転を楽しめるようになった理由

万年ペーパーの自分が車の運転を楽しめるようになった理由

どうもTAKUYAです。大学の入学前に免許を取って以来ずっとペーパードライバーで、都市生活では出来る限り運転は避ける生活を送っていた。事故を起こせば人を◯してしまう可能性もある代物を日常的に運転するなんて考えられなかった。 そんな自分に転機が訪れたのは、結婚して大阪に戻った事と、子供ができた事、そしてアウトドアに興味を持った事だ。大阪近辺だと箕面とか野勢、神戸、丹波篠山などが日帰りでドライブしやすい距離だ。それで、恐る恐るタイムズのカーシェアで時々ではあるが運転するようになった。 他の車も生きた人間が運転しているという驚き まず運転していて気づいたのは、他の車にも生きた人間が運転していると言う点だ。そんなのは当たり前だろと思うかもしれないが、結構新鮮な発見だった。Grand Theft Autoなどの現代をモチーフにしたゲームをプレイすれば分かるが、NPCの車の動きは鈍臭いのでガンガンぶつかる。プレイヤーの進行を予測した動きなどしないからだ。 しかし現実では相手も事故りたくないので、お互いに動きを読み合い、譲り合って運転する。ルードな運転手もたまにいるものの、どちらかがよっぽ

By Takuya Matsuyama
禅的思考: なぜInkdropはMarkdown独自拡張をしないのか

禅的思考: なぜInkdropはMarkdown独自拡張をしないのか

InkdropはMarkdownのノートアプリですが、Markdownの独自拡張は「絶対にやらない」と決めていて、それがアプリの哲学になっています。 Markdown(厳密にはGitHub-flavored Markdown)の強みは、ソフトウェア業界標準で広く使われてい緩い文書フォーマットという所です。 アプリの独自記法を加えてしまったら、あなたの書いたノートはたちまちそれらと互換性がなくなります。 「独自記法を加えた方が便利な機能が付けられるだろう」と思うかもしれません。もちろん実際Markdownは完璧な書式ではないため、必要な場面はいくつかあります。例えば画像のサイズ指定方法が定まっていない、など。それでも自分は、ノートの可搬性を第一にしてきました。その裏には禅にまつわる哲学があります。 日本の文化は周りの環境と対立するのではなく、溶け込もう、馴染ませよう、共生しようとする傾向があります。窓の借景、枯山水、建築の非対称性、茶室のシンプルさ、侘び寂びなどあらゆるところで見られます。 絵画における「減筆」の手法を例にとって説明します。 これは、描線を最小限に抑えながら絹や紙の

By Takuya Matsuyama