最近の開発の記録(20210802)

最近の開発のきろくをばをば。みんなでつくるダンジョン のマップエディタ機能がほぼほぼ固まってきたのでした。こんな感じのマップエディタができつつあります。 (ちなみにこのマップエディタは pico2map として公開しています。まだ安定版ではありませんが、そのうちいろいろちゃんとします)

※記事中のマップに含まれる素材画像は、墨cmさまから提供いただいたものと、ぴぽや様(https://pipoya.net)からお借りしたものを使っています。

f:id:piyorinpa:20210802204815p:plain
マップ編集のようす

これまでのマップエディタは、このようにパーツを配置して並べていくようにマップを作り上げる方式でした。

f:id:piyorinpa:20210802205719g:plain
いままでのマップエディタ

さらに、昨年には 見下ろしビュー のマップをつくれるようになったため、これまでのアクションゲーム形式の横スクロールマップのほか、RPGのようなマップも作れるようになりました(こんなマップがつくれます)。しかしながら、「パーツを配置して並べていく」形式のマップエディタだと、RPG向け素材画像などが活用しづらいなどの問題がありました。

ということで以下のように、マップチップを並べてマップをつくっていくタイプのマップエディタを開発しているのでした。マップチップをぺたぺたと貼り付けていくようにマップをつくれます。また、いわゆるオートタイルをサポートし、道や川などをつくるときに使われるマップチップを自動生成/配置するので、便利になっています。

f:id:piyorinpa:20210802220430g:plain
マップチップを配置していくタイプのマップエディタ

レイヤーも4層つかうことができます。4枚目のレイヤーが「キャラクターより前側」に表示されるようになっています。

f:id:piyorinpa:20210802222732g:plain
レイヤーの切り替え

もちろん今まで通りの「パーツを並べる」方式でのマップ編集もサポートしており、マップチップとパーツを共存することができます。マップチップ編集ボタンを押すと、マップチップを配置できるようになります。

f:id:piyorinpa:20210802224306g:plain
パーツ配置とマップチップの切り替え

マップチップは(いまのところ)アニメーションをサポートしていませんが、これまでの「パーツ」はアニメーションや「プラグインの適用」ができるので、このようにねこや人を右往左往させたりできます。

f:id:piyorinpa:20210802222019g:plain
うごくひととねこ

マップチップはあくまでも「画像を並べる」だけなので、動きのあるものを作るときは引き続き「パーツを配置」していく形になります。


こんなかんじのマップエディタができつつあります。まだ最低限というかんじの機能ですが、遊んでいて楽しい感じになってきました。

いまはひたすら私自身が遊んでバグを見つけて直して...という作業を行って動作を安定させる方向で開発作業をしています。渋いバグなどは取れつつあるので、あともうちょっと~というかんじです。ではでは~

Amazon S3+CloudFront環境へGitHub Actionsをつかってデプロイする

静的サイトの配信のために、Amazon S3にファイルを設置してCloudFrontで配信する方法はよく行われていると思いますが、私の管理するアプリケーションでもこのような構成のものがあります。

というわけで、もっとかんたんにデプロイできるようにしたいな~ということで、GitHub Actionsでデプロイできるようにしたので、備忘録としてまとめました。

成果物のWorkflow

こんなかんじのWorkflowを定義します。私のアプリケーションの場合は事前にnode環境のセットアップ(actions/setup-node@v1)をしたり、ビルド( npm run build )してからS3にファイルを設置していますが、このへんはそれぞれのケースに応じて調整します。 Workflowのトリガは、今回は workflow_dispatch として任意のタイミングで実行することとしましたが、このへんもそれぞれのケースに応じて調整するとよさそうです。

name: Deploy to s3
on: workflow_dispatch

jobs:
  upload:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2

      # 成果物を生成する
      - uses: actions/setup-node@v1
        with:
          node-version: '16.x'
      - name: Cache dependencies
        uses: actions/cache@v2
        with:
          path: |
            **/node_modules
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
      - run: npm install
      - run: npm run build

      # S3にアップロードしてInvalidationリクエストを投げる
      - uses: aws-actions/configure-aws-credentials@v1
        with:
          aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
          aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
          aws-region: (region)
      - run: aws s3 sync ./build s3://example-bucket
      - run: aws cloudfront create-invalidation --distribution-id (distribution-id) --paths "/*"

やっていることは概ねこんな感じです。

  • aws-actions/configure-aws-credentials@v1 をつかって認証情報をセットアップする
    • これを使うとawsコマンドを認証が済んだ状態+Regionをセットした状態で使うことができます。
    • (region) を任意のRegionに設定します。
  • aws s3 sync コマンドでビルドした成果物(など)をS3 Bucketに送信する
    • example-bucket の部分は任意のBucket名に置き換えます。
    • S3への各種リクエストにはお金がかかります。頻繁にこのActionsを実行する場合はこのことも考慮される必要がありそうです。
  • aws cloudfront create-invalidation でファイルの無効化を行い、CDNのサーバのコンテンツを差し替える( --paths "/*" とすることですべてのファイルを無効化する)
    • (distribution-id) の部分は任意のDistribution IDに置き換えます。
    • Invalidationにはお金がかかります。 CloudFrontのドキュメント によると、1000件までの無効化パスまでは無料のようです。頻繁にこのActionsを実行する場合はこのことも考慮される必要がありそうです。

Workflowを動かすためのAWSアクセスキーの準備

各種awsコマンドが使えるように、アクセスキーを準備します。上記Actionsで用いるコマンド( aws s3 sync , aws cloudfront create-invalidation)には以下の権限が必要です。 (s3 sync に必要な権限は、 こちらの記事 を参照しました)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::(bucket-name)/*"
            ]
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "s3:ListBucket"
            ],
            "Resource": [
                "arn:aws:s3:::(bucket-name)"
            ]
        },
        {
            "Sid": "VisualEditor2",
            "Effect": "Allow",
            "Action": [
                "cloudfront:CreateInvalidation"
            ],
            "Resource": [
                "arn:aws:cloudfront::(aws-account-id):distribution/(distribution-id)"
            ]
        }
    ]
}
  • (bucket-name) はアップロード先のBucket名を設定します。
  • (aws-account-id) は、アカウントIDを設定します。
  • (distribution-id) は、CloudFrontのDistribution ID を設定します。

これらのアクセス権限を持つIAM Userを作成し、アクセスキーとシークレットアクセスキーを取得します。これらを AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY として、それぞれGitHubリポジトリのSecretsに保存します

これで、さきほど定義したWorkflowが動作するようになります。

そのほか参考にしたもの

aws.amazon.com


ビルドやアップロード周りを自動化することで、うっかりアップロードしちゃいけないファイルをアップロードするなどのミスが減るので、気軽にデプロイができるようになりますね。ではでは~

JSON Schemaでお手軽アンケートフォーム

こんにちは。お手軽に自分のサイトに埋め込むアンケート的なやつを作りたいけど、Google Formsなどでは若干要件を満たさないというお悩みがありました。

  • ログインしているときだけアンケートに答えられるようにしたい
  • 入力フォームに隠しパラメータを埋め込みたい(<input type="hidden"> みたいなものを入れたい)
  • データの後加工をしたいので任意の場所にアンケート結果を保存したい

これらを解決するあれこれを作っていました(作っています)。

github.com

だいたいこんな感じのアプリケーションです。

  • JSON Schema に基づきアンケートフォームが表示される
  • アンケートを送信すると、サーバ側でJSON Schemaの内容に基づきバリデーションを行う
  • 適切な場所にアンケート結果を保存する

f:id:piyorinpa:20210608224119p:plain
こんなイメージで構成されるアプリケーション

保存したアンケートを集計したり、分析したりすることはこのアプリケーションではしないものとします。

このリポジトリはmonorepo構成で、いくつかのアプリケーションで構成されています。

アンケートフォーム

JSON Schemaに基づき、アンケートフォームを生成して表示する、Reactベースのアプリケーションです。

アンケートフォームの生成には react-jsonschema-form を使っています。JSON Schemaを与えることで、 適したInput要素を表示してくれます。このライブラリをベースに、アンケートフォームとして使いやすい形のデザインに調整しました。

このアプリケーションを起動し、 /surveys/:schemaId にアクセスすると、指定したJSON Schemaを指定したアンケート定義JSON Schema配信サーバから取得してフォームを表示します。

f:id:piyorinpa:20210608232110p:plain
アンケートフォームのルーティング

たとえば、このアプリケーションをビルドする際の環境変数 REACT_APP_SCHEMA_BASE_URL="https://example.com"として、以下のようなJSON Schemaを https://example.com/test-survey に設置するとします。

{
  "title": "アンケート",
  "description": "サンプルアンケートです",
  "type": "object",
  "required": ["useful"],
  "properties": {
    "useful": {
      "type": "string",
      "title": "満足度",
      "description": "このウェブサイトににどれくらい満足していますか?",
      "enum": [
        "とても満足",
        "まあまあ満足",
        "あまり満足じゃない",
        "不満"
      ]
    },
    "comment": {
      "title": "ご意見やご感想",
      "description": "ご意見やご感想、機能追加や改善のご要望などご自由にお書きください",
      "type": "string",
      "maxLength": 1000
    }
  }
}

/surveys/test-survey にアクセスしたら GET https://example.com/test-surveyJSON Schemaを取得し、以下のようなアンケートフォームを表示します。

f:id:piyorinpa:20210607235615p:plain
表示されるアンケート

送信ボタンを押すと、環境変数 REACT_APP_BASE_URL に指定したアンケート受付APIにアンケートをPOSTします。

埋め込みアンケートフォームと署名付きパラメータ

f:id:piyorinpa:20210608224217p:plain
埋め込みアンケートフォーム

このアンケートフォームをiframeを使ってサイトに埋め込んだとき、サイト側で生成した署名付きパラメータを受け取ることができます。アンケートと一緒に任意のパラメータを埋め込みたいときなどに使います。

アンケートを埋め込みたいページに以下のようにiframeを設置します。(アンケートフォームが http://localhost:3000 、埋め込み先ウェブサイトが http://localhost:3001 で起動しているとします。)

<!-- アンケートフォームが localhost:3000 で起動しているとする -->
<!-- 埋め込んでいるウェブサイトは localhost:3001 で起動しているとする -->
<iframe id="survey-form" src="http://localhost:3000/surveys/test-survey"></iframe>

たとえば、先ほどのアンケート test-surveyJSON Schemaに以下のようなキー reportBoxOptions を追加します。signedParameters には署名付きパラメータの定義を、embedded には埋め込み先ページのOriginを記述します。

{
 "title": "アンケート",
  "description": "サンプルアンケートです",
  "type": "object",
  "properties": { ... }
  "reportBoxOptions": {
    "signedParameters": {
      "type": "object",
      "required": ["loggedIn"],
      "properties": {
        "loggedIn": {
          "type": "boolean"
        }
      },
      "embedded": {
        "parentOrigin": "http://localhost:3001"
      }
    }
  }
}

この signedParameters に記載された形式のJSONをJWTに変換してアンケートフォームに渡します。

アンケートを埋め込みたいページのサーバサイド側でJWTを生成します(secret秘密鍵

const jwt = require('jsonwebtoken');
const secret = 'test-secret';

// 今のところパラメータの暗号化を考慮していないので、秘密の情報は入れないこと
const signedParameters= jwt.sign({ params: { loggedIn: true } }, secret);

アンケートを埋め込んだページに以下のような処理を追加します。

<script type="text/javascript">
  const iframe = document.getElementById('survey-form');
  const formOrigin = 'http://localhost:3000';

  window.addEventListener('message', e => {
    // アンケートフォームから送られたメッセージでなければ受け取らない
    if (e.origin !== formOrigin) return;

    // アンケートフォームからSignedParameters受け取り準備完了のメッセージを受け取ったら
    // アンケートフォームへSignedParameters(をJWTエンコードした値)を送信する
    if (e.data.event === 'readyToReceiveSignedParameters') {
      // (signedParameters は埋め込み先のサーバ側で生成したJWT)
      iframe.contentWindow.postMessage(signedParameters, formOrigin);
    }
  })
</script>

アンケートフォーム側から signedParameters の受け取り準備が完了したタイミングでメッセージが送出されるので、確認の上アンケートフォームへ signedParameters の JWT を渡します。

アンケート送信時にこのJWTも一緒に送信され、アンケート受付APIでデコードされます。署名検証に成功したら、データストアに保存します。

JSON Schemaに signedParameters の設定を記述した場合は、署名付きパラメータがアンケート受付APIへのリクエストボディに含まれないと保存されないので、たとえばログインしていない人にはアンケートを 送信してほしくないなどの用途にもつかえます。

アンケート回答後の遷移先の指定

JSON SchemaにreportBoxOptions.callbackUrl パラメータを指定することで、アンケートを回答した後に任意のページに遷移させることができます。 完了画面を表示したり、アンケート回答後に何らかのアクションをしたいときに使います。

{
 "title": "アンケート",
  "description": "サンプルアンケートです",
  "type": "object",
  "properties": { ... }
  "reportBoxOptions": { 
    ... ,
   "callbackUrl": "https://example.com/callback"

  }
}

アンケート受付API

アンケートを受け取り、アンケート定義JSON Schemaサーバからアンケートに対応したJSON Schemaを取得したうえで、バリデーションをしてデータストアに保存します。 サーバ側のバリデーションには ajv をつかっています。

今回は、実装のひとつの例として、AWS環境(API Gateway と Lambda)にデプロイできるSAM TemplateとLambda Functionのサンプルを用意しました。 提出されたアンケートを検証(JSON Schemaを用いた入力値バリデーションと署名付きパラメータの署名検証)し、成功した場合はreportBoxMeta というメタデータを付与したうえで、指定したS3 Bucketにデータを保存します。

例えば先ほどの test-survey フォームを送信して検証が成功したとき、こんなデータが保存されます。

{
  "useful":"まあまあ満足",
  "comment":"ここをあれこれするともっといいと思います。",
  "loggedIn":true,
  "reportBoxMeta":{
    "metaDataVersion":1,
    "schemaId":"test-survey",
    "createdAtUtc":"2021-06-07T16:01:12.672Z"
  }
}

まとめ

JSON Schemaでアンケートが作れるそれっぽいアプリケーション群をお試しで実装してみました。好きな場所にアンケート結果を収集できるようになったので、集計やデータの加工がしやすくなったかなと思っています。 私の自作アプリケーションに組み込むなどしながら引き続き手を加えていきたいなと思っています。あと、せっかく作ったのでちゃんと使い方とかをまとめなければ...。

ではでは~。

最近の開発の記録(2021/5/24)

こんにちは、最近の開発の記録をばをば

みんなでつくるダンジョンのマップエディタのあたり判定

先日の記事で、みんなでつくるダンジョンに「マップチップを並べてマップをつくれるモード」を作っていることに触れましたが、あたり判定編集・判定まわりもこれに合わせて作っていました。

f:id:piyorinpa:20210524214844g:plain
当たり判定を設定しているところ

このように、マップチップを塗りつぶすと、その箇所が通行不可になるようになります。あたり判定処理を久々に書いたので、なかなかうまくいかずに時間がかかってしまいました。(ソースコードはこちら

f:id:piyorinpa:20210524223649p:plain
当たり判定処理のながれ

  • キャラクタの4隅のマップ座標(マップ座標 = マップチップの大きさ / ピクセル座標)を取得して、キャラクタにかかるマップチップを取得(画像1)
  • キャラクタにかかっているマップ座標から、あたり判定情報を確認する(画像の赤い部分があたり判定するマップチップとする)(画像2)
  • マップチップ(0, 0)とのめり込み量  d_{x1} , d_{y1} を計算(画像3)
    • めり込み量は、(キャラクターの大きさ+マップチップの大きさ)- キャラクタとマップチップの中心間距離 で求められる
    •  d_{x1}=w_c+w_p-d_{cx1}
    •  d_{y1}=h_c+h_p-d_{cy1}
  • マップチップ(0, 1) とのめり込み量  d_{x2} , d_{y2} を計算(画像4)
  • すべてのマップチップとの比較においてめり込み量が大きいほうの座標軸を確認
    • その座標軸ではないほうの座標軸のむきにキャラクタを移動する
    • 例の場合は  d_{xmax} = d_{x1} = d_{x2} <  d_{ymax} = d_{y2} となり、y方向のめり込み量が大きいので、x方向にキャラクタを移動する
    • キャラクタを d_{x} の分だけずらす(画像5)

これで「マップチップの配置」と「当たり判定処理」がそろい、いよいよマップチップなマップを構成できるようになりかけていますが、まだまだなんだか動作が不安定です。引き続き調整をがんばらねば...という雰囲気です。

あともうちょっとなので、もうちょっとだけおまちください~!

おてがるに質問フォームを設置できる仕組みづくり

おてがるに質問フォームを作りたいけど、Googleフォームなどだとやや要件を満たせない...という需要があり、これを解決する仕組みをつくったりしていました。

https://github.com/piyoppi/report-box

以下の要件を満たすように開発中です。

  • スキーマを記述するとそれっぽい質問フォームができて、回答をサーバに保存できる
  • ウェブサイトに埋め込めるようにする
  • 埋め込み元のウェブサイトが署名した任意のデータを回答に紐づけられる(たとえばどのログインユーザーが回答したか、などを記録できる)
  • 埋め込みたいウェブサイトなどから独立したシステムとして提供できる

これを自作ウェブアプリに組み込めるようにしたく、ちまちまとつくっているのでした。

最近の活動はこんなかんじでした。はやくマップエディタまわりを完成させたい~。ではでは。

最近の開発の記録(2021/4/30)

最近の開発のようすをまとめてないじゃん、という気持ちになったのでまとめます。

みんなでつくるダンジョンにマップエディタを組み込んでいる

マップをつくりやすくするぞ企画の第一弾ということで、いわゆる「マップチップを並べてマップをつくる機能」をつくっています。こんなかんじ。(画像中のマップチップは「ドット絵世界 さんからお借りしています)

f:id:piyorinpa:20210430184206p:plain
マップエディタでマップをつくっている様子

これまでは「マップにアイテムを設置して壁や床をつくる」ことでマップを構成していましたが、これに加えて以下のようにマップチップを並べてマップを構成できるようにします。

f:id:piyorinpa:20210430184429g:plain
マップチップをならべている様子(開発中)

もともとマップチップを並べて構成するマップのようなデータ形式を想定していないつくりになっているので、埋め込みにとても苦戦中(はちゃめちゃにたいへん)ですが、なんとか動いている雰囲気を感じられる程度には動くようになってきました。

ウディタやRPGツクールなどがメジャーなゲーム制作ツールであるということもあり、マップチップ画像が素材として充実しているという背景があるので、この機能が完成したらたくさんの素材をつかってマップをつくることができるようになるのではと期待しています。

このマップエディタは「pico2map(読み方は「ぴこぴこマップ」)」として、以下のリポジトリで管理しています(絶賛開発中)。ウェブブラウザでつかえる汎用マップエディタです。みんなでつくるダンジョンは以下のマップエディタを埋め込むように使っている格好になります。なので、これをつかえばお好きなウェブサイトにマップエディタを埋め込めるようになります。

github.com

ぴこぴこマップのサンプルのデモはこちら

マップエディタUIやマップチップ選択UIなどで構成されるWebComponentモジュール(@piyoppi/pico2map-editor, @piyoppi/pico2map-ui-components)や、マップデータに関するモジュール(@piyoppi/pico2map-tiled)で構成されています。もうちょっと形になったら、そのうちぴこぴこマップについての解説記事でも書いてみようかなと思います。自由に使っていただけるようなライセンスで提供予定です。

残された課題はこんなかんじです。改めて書き出すとそれなりにあるなぁ。。。

  • このマップエディタでつくったマップの当たり判定どうしよう
  • 描画が安定しないので安定させる
    • というか全体的に安定していないので安定させる
  • オートタイルの取り扱いはどうしようか....(ぴこぴこマップ上には実装されているので、これをどうやって埋め込むか)

めげずに開発をがんばろうとおもいます。最近いろいろ新たに作りたいものを思いつくようになったので、早く終わらせてしまいたい~。ではでは。

WebFont(アイコンフォント)をつくってみた

自作アプリケーションのあれやこれやのために描きためたアイコンがお手元にそこそこたまってきました。たとえばこんなかんじのもの。

f:id:piyorinpa:20210411164455p:plain
お手製のアイコンたち

これらのアイコンは主にウェブページで使用され、SVGデータとして管理しているのですが、かねてからWebFontにしたいなと思っていました。理由は以下の通り。

  • 色や大きさを簡単に変えられるから(CSScolor font-size などで簡単に変えられる)
  • 画像として管理するよりも利用が楽だから(フォントファイルを読めばすぐ使える)

というわけで、WebFontにしてみたのでした。利用できるアイコン一覧はこちら

github.com

このフォントにはまだライセンスを付けていないのですが、基本的には自由に使っていただけるようにするつもりです。

使い方の雰囲気

こんな感じで使うことができます。フォントファイルたち を設置することで、たとえば以下の表記で、ピンアイコン(f:id:piyorinpa:20210411165433p:plain )を設置することができます。アイコン一覧に掲載されているアイコンは、同様に設置できます。

<!-- @font-face の設定などを行うCSSを読み込む -->
<!-- (/fonts ディレクトリにフォントファイルなどの生成物を設置する) -->
<link rel="stylesheet" href="fonts/garakuta-font.css">

<!-- アイコンフォントを利用する -->
<span class="garakuta-font">pin</span>

どうやってつくったの?

webfont を利用させていただきました。指定したSVGからフォントファイルを作成してくれます。assets/svg 配下にフォントにしたいSVGファイルを入れておくことにした場合、以下のようなコマンドでフォントを作成できます。

npm i --save-dev webfont

npx webfont assets/svg/**/*.svg

.webfontrc ファイルを設置することで、設定を書くこともできます。今回はこんな感じに設定してみました。

{
  "fontName": "garakuta-font",
  "dest": "fonts/",
  "template": "css"
}
  • フォント名は garakuta-font
  • 出力先は ./fonts/
  • フォント読み込みの各種設定をCSSファイルとして書き出し

最近ligatureにも対応した ようで、HTML上のコーディングがしやすくなりました。ligatureは複数文字を合わせて1つの文字にすることですが、先ほどの例のように、 pin と書けば、これを一文字にしたf:id:piyorinpa:20210411165433p:plainが表示されます。pin という文字は、SVGのファイル名から決定されています。

<span class="garakuta-font">pin</span>

今後の予定

まだまだフォント化していないSVGがあるので、コツコツと調整しながら追加していくつもりです。また、そのうちライセンスをちゃんとつける予定です。ではでは~。

公開しているnpmパッケージをGitHub Packagesから取得できるようにしました

じぶんのプロジェクトでつかうために、いくつかのnpmパッケージ を公開しています。これらのパッケージをGitHub Packagesから取得できるようにしました。

以下の設定を .npmrc に追加すると、 @piyoppi スコープのパッケージについては GitHub Packages(npm.pkg.github.com) から取得します。

//npm.pkg.github.com/:_authToken=${NPM_TOKEN}
@piyoppi:registry=https://npm.pkg.github.com

インストールするときは、NPM_TOKEN環境変数を設定してふつうに npm install します。たとえばこんなかんじ。

export NPM_TOKEN=xxxxxxxx
npm install --save @piypppi/pico2map-editor

パッケージの取得にはGitHubのアクセストークンが必要です(ref)。NPM_TOKENには、read:packages スコープの付いたアクセストークンを発行し、設定します。

たとえばHerokuにデプロイする過程で npm install をするときは、上記 .npmrc をコミットしておき、別途 NPM_TOKEN 環境変数を設定することで同様にパッケージを参照できます。

https://devcenter.heroku.com/articles/nodejs-support#private-dependencies

最近は自作ライブラリの公開に GitHub Actions を活用していますが、GitHub Packagesなら別途アクセストークンを用意しなくていい(GITHUB_TOKEN がつかえる)ので、 お手軽だな~というかんじです。

また、開発中のパッケージをちょっと公開して自分のプロジェクトで参照したいときに、なんとなくnpmに公開するよりはお手軽感を感じているのでした。(たとえば、目下開発中の @piyoppi/pico2map-editor はいまのところGitHub Packagesでのみパッケージを公開しています。それ以外のパッケージはnpmでも公開しています。)

ではでは