macOS のサービスを使った Notion code block 挿入 : Notion 解説(41)

はじめに

以前、VSCode の選択部分を codeblock として挿入する記事を書きました。

こちらから入手できますので、使いたい方はどうぞ。

github.com

これはこれで便利なのですが、RubyMine、MATLAB など他のアプリでも利用したいですし、ターミナルの出力なども codeblock にできればいいなと思いました。昨日作成したタスク登録 (macOS のサービスを用いた Notion タスク登録 : Notion 解説(40) - hkob’s blog)と同様に選択部分を codeblock として登録する macOS サービスを作りました。

作成したスクリプト

作成方法はタスク登録と同じなので省略して、スクリプトだけ示します。今回、プログラムコードを貼り付けることもあり「'」が文字列の中に入り込むことがあります。今回は、内部で shell スクリプトの呼び出しをしているため、「'」をエスケープする必要がありました。この部分でちょっと時間がかかりました。

昨日のタスク登録も同じ問題があったので、こちらも Ver 1.1 としたサービスをリリースし直しました。

// ########## Personal settings ##########
// Set your Notion API Token (strings that starts with `secret_`)
const NOTION_API_TOKEN = "secret_XXXXXXXXXXX"
// Set your database ID (32-digits hex number)
const DATABASE_ID = "YYYYYYYYYYYYYYYYYYYYYYYYYYYY"
// Set default language
const DEFAULT_LANGUAGE = "shell"
// If you want to open a new task by Notion.app, set true.  If you want to open it by your default browser, set false.
const OPEN_BY_APP = true
// Available languages
const languages = [
      "abap", "arduino", "bash", "basic", "c", "clojure", "coffeescript", "c++", "c#", "css", "dart", "diff", "docker",
      "elixir", "elm", "erlang", "flow", "fortran", "f#", "gherkin", "glsl", "go", "graphql", "groovy", "haskell", "html",
      "java", "javascript", "json", "julia", "kotlin", "latex", "less", "lisp", "livescript", "lua", "makefile", "markdown",
      "markup", "matlab", "mermaid", "nix", "objective-c", "ocaml", "pascal", "perl", "php", "plain text", "powershell",
      "prolog", "protobuf", "python", "r", "reason", "ruby", "rust", "sass", "scala", "scheme", "scss", "shell", "sql",
      "swift", "typescript", "vb.net", "verilog", "vhdl", "visual basic", "webassembly", "xml", "yaml", "java/c/c++/c#"
    ]

// show a dialog to register dateTimeStr
function dialogText(app, input) {
  const message = "Input language \n" +
    " Example: (no input) -> " + DEFAULT_LANGUAGE + "\n Available: " +
    languages.join(", ") + "\n for Message : \n" + input
  return app.displayDialog(message, {
    defaultAnswer: "",
    withIcon: "note",
    buttons: ["Cancel", "Continue"],
    defaultButton: "Continue"
  })
}

// Call Notion API
function sendNotion(app, url, payload, method) {
  const header = " --header 'Authorization: Bearer '" + NOTION_API_TOKEN + "'' "  +
    "--header 'Content-Type: application/json' " +
    "--header 'Notion-Version: 2021-08-16' " +
    "--data '"
  const script = "curl -X " + method + " " + url + header + JSON.stringify(payload).replaceAll("'", '\'"\'"\'') + "'"
  return JSON.parse(app.doShellScript(script))
}

function getNotionPages(app, payload, databaseId) {
  const url = "https://api.notion.com/v1/databases/" + databaseId + "/query"
  return sendNotion(app, url, payload, "POST")
}

function getLastEditedPage(app, databaseId) {
  const payload = {
    sorts: [
      {
        timestamp: "last_edited_time",
        direction: "descending"
      }
    ],
    page_size: 1
  }
  return getNotionPages(app, payload, databaseId)
}

function appendBlockChildren(app, pageId, payload) {
  const url = "https://api.notion.com/v1/blocks/" + pageId + "/children"
  return sendNotion(app, url, payload, "PATCH")
}

function appendCodeBlock(app, page, code, language) {
  if (page) {
    const pageId = page.id
    const payload = {
      children: [
        {
          type: "code",
          object: "block",
          code: {
            text: [
              {
                type: "text",
                text: {
                  content: code
                }
              }
            ],
            language: language
          }
        }
      ]
    }
    const result = appendBlockChildren(app, pageId, payload)
    return result.url
  } else {
    return null
  }
}

function run(input, parameters) {
  const app = Application.currentApplication()
  app.includeStandardAdditions = true

  if (input.length > 0) {
    const inputStr = String(input)
    three_lines = inputStr.match("^[^\n]+\n[^\n]+\n[^\n]+") || inputStr
    const response = dialogText(app, three_lines)
    if (response.buttonReturned == "Continue") {
      var language = response.textReturned
      if (languages.indexOf(language) == -1) {
        language = "shell"
      }
      const lastEditedPage = getLastEditedPage(app, DATABASE_ID).results[0]
      appendCodeBlock(app, lastEditedPage, inputStr, language)
      var url = lastEditedPage.url
      if (OPEN_BY_APP == true) {
        url = url.replace("https", "notion")
        let notion = Application("Notion")
        notion.activate()
      }
    }
    return url
  } else {
    return false
  }
}

利用方法

保存したら、キーボード - ショートカットのタグでショートカットを設定します。今回は、「Opt-Cmd-P」にしました。

f:id:hkob:20220110133027p:plain
ショートカットの登録

コードブロックにしたいテキストを選択した状態で、設定したショートカットをタイプします。言語設定のダイアログが出るので、タイプします。最後に編集したデータベースページの最後の部分にcode block が追加されます。

おわりに

macOS サービスはアプリケーションを問わないので、かなり使いやすいと思います。これから Github にリリースするので、後でリポジトリを張っておきます。

(1/21追記) リポジトリリンクを貼るのを忘れていました。申し訳ありません。

github.com


hkob.notion.site