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

Inkdrop v6 Canary版リリースしました — 新Markdownエディタやその他新機能盛り沢山

Inkdrop v6 Canary版リリースしました — 新Markdownエディタやその他新機能盛り沢山

Inkdrop v6.0.0 Canary版リリースしました — 新Markdownエディタやその他新機能盛り沢山 こんにちはTAKUYAです。 v6.0.0 の最初の Canary バージョンをリリースしました 😆✨ v6では、アプリのコア機能の改善がたくさん盛り込まれています! * リリースノート(英語): https://forum.inkdrop.app/t/inkdrop-desktop-v6-0-0-canary-1/5339 CodeMirror 6 ベースの新しいエディタ フローティングツールバー v5ではツールバーがエディタの上部に固定されており、使っていないときもスペースを占有していました。 v6では、テキストを選択したときだけ表示されるフローティングツールバーに変わりました。 GitHub Alerts 構文のサポート Alerts の構文が正しい色と左ボーダーでハイライトされるようになりました。 ネストされたアラートや引用にも対応しています。 また、アラートタイプの入力を支援する補完機能も追加されました。 スラッシュコマンド 空行で /

By Takuya Matsuyama
AIのお陰で最近辛かった個人開発がまた楽しくなった

AIのお陰で最近辛かった個人開発がまた楽しくなった

AIのお陰で最近辛かった個人開発がまた楽しくなった こんにちは、TAKUYAです。日本語ではお久しぶりです。僕はInkdropというプレーンテキストのMarkdownノートアプリを、デスクトップとモバイル向けにマルチプラットフォームで提供するSaaSとして、かれこれ9年にわたり開発運営しています。 最近、その開発にClaude Codeを導入しました。エージェンティックコーディングを可能にするCLIのAIツールです。 最初の試行は失敗に終わったものの、徐々に自分のワークフローに馴染ませることができました。そして先日、アプリ開発がまた「楽しい」と感じられるようになったのです。これは予想外でした。 本稿では、自分がエージェンティック・コーディングをワークフローに取り入れた方法と、それが個人開発への視点をどう変えたかを共有します。 * 翻訳元記事(英語): Agentic coding made programming fun again 自分のアプリに技術的負債が山ほどあった ご想像のとおり、9年も続くサービスをメンテするのは本当に大変です。 初期の頃は新機能の追加も簡単で

By Takuya Matsuyama
個人開発を7年以上続けて分かった技術選択のコツ

個人開発を7年以上続けて分かった技術選択のコツ

個人開発を7年以上続けて分かった技術選択のコツ InkdropというMarkdownノートアプリを作り続けて7年になる。 お陰さまでその売上でずっと生活できている。 これまで個人開発でどう継続していくかについて「ユーザの退会理由をあれこれ考えない」とか「アプリの売上目標を立てるのをやめました」とか、ビジネス面あるいはメンタル面からいろいろ書いてきた。 今回は、技術面にフォーカスして、どう継続して開発していくかについてシェアしたい。 TL;DR * 最初はとにかく最速でリリースする事を最優先する * 迷ったら「ときめく方」を選べ * 程よいところで切り上げて開発を進める * 使っているモジュールがdeprecatedされるなんてザラだと覚悟する * 古いから悪いとは限らない * シンプルにしていく * 老舗から継続の秘訣を学ぶ * 運ゲー要素は排除しきれない 最初はとにかく最速でリリースする事を目標に技術選定する 開発計画とビジネス計画は切っても切り離せない。 コーディングに傾倒するあまり完璧主義に陥って結局リリース出来ないまま頓挫してしまう個人開発者は多い

By Takuya Matsuyama
子育て中の個人開発者の一日

子育て中の個人開発者の一日

子育て中の個人開発者の一日 どうもTAKUYAです。 久しぶりに生活まわりの事を書きたい。自分はInkdropというMarkdownノートアプリを売って生きている。 子供も無事順調に成長しており、あと数ヶ月で3歳になるというところで、イヤイヤ期もやっと終わりが見えてきた。 生活パターンもなんとなく定着しつつあるので、ここで一旦どんなルーティンなのか書き出してみる。ちなみに当方今年で40歳。 平日の1日の流れ * 06:30 妻と子供起床、朝食 * 07:10–30 俺起床、朝食 * 07:40 布団を畳んで子供を着替えさせる。妻はその間に化粧や通勤の準備 * 08:00 ストレッチと軽い筋トレ(腕立て50回、スクワット100回) * 08:10 妻と子供を見送る。15分前後瞑想 * 08:30 散歩 * 09:00 作業開始(カフェまたは家) * 11:00 昼飯 * 12:00 ダラダラする * 12:30 作業再開(だいたい家)

By Takuya Matsuyama