Chrome(Canary) の Native File System API で ローカルファイルの読み書きをする

ブラウザ上でローカルファイルの読み書きができる Native File System API が ChromeCanary で実装された。

前々から欲しかった機能なので、自分が作ってる markdown preview ツールに実装してみた。

動かすとこんな感じ

https://mdbuf.netlify.com/ で Meta+O/Meta+S のキーバインドを振ってる。

有効化

できること

  • Cmd+O でファイルを開く
  • Cmd+S で保存できる
    • 一度保存したら Cmd+S で保存し続けられる
  • Cmd+Shift+S で別名で保存

よくあるデスクトップアプリに寄せてる。GUI メニューは作ってない(いるか?)

できないこと

  • Read & Write な API がないので、読みこんだコンテキストで書き込む、みたいなものができない
  • リロード時にパーミッションを持ち越せないので、開き直したら Cmd+S で再度パーミッションを取得し直す必要がある
  • セキュリティ上の理由でフルパスが取れない

簡単なコードの例

const tx = document.createElement("textarea") as HTMLTextAreaElement;

async function writeFile(handler: any, text: string): Promise<void> {
  const writer = await handler.createWriter();
  await writer.truncate(0);
  await writer.write(0, text);
  await writer.close();
}

// mount open button
const btn = document.createElement("button");
btn.textContent = "Open file and edit!";
document.body.appendChild(btn);
btn.addEventListener("click", async (ev: any) => {
  let handler: any = null;
  try {
    handler = await window.chooseFileSystemEntries({
      type: "saveFile",
      accepts: [
        {
          description: "Text file",
          extensions: ["txt"],
          mimeTypes: ["text/plain"]
        }
      ]
    });
  } catch (err) {
    alert("Please select file");
    return;
  }

  tx.style = "width: 80vw; height: 80vh";
  document.body.appendChild(tx);

  tx.addEventListener("change", async (ev: any) => {
    await writeFile(handler, ev.target.value);
  });

  btn.remove();
});

window.chooseFileSystemEntries(...) を呼ぶとモーダルが出る。これはクリックのようなユーザーのアクション起点でないと起動できない。(フルスクリーンAPIなどと同じ)

感想

在りし日の ActiveX を彷彿とさせる…(さすがにあれほど自由なパーミッションではないが)

単機能なエディタにはいいけど、複数ファイルを読み書きするにはまだ貧弱。まずは単機能なエディタを作っていくと良さそう。たとえば React のプレビューツールとか。

ディレクトリのパーミッションが取れる機能があって、その場合ファイルブラウザが作れる?んだろうか。あとで試す。

ただ、最近の safari はこういう PWA 系の機能を最近実装しないのが気がかりで、 しばらくは PC/Android Chrome で動くものと割り切ったほうがいいような気もしてる。