EMC2NARY

#開発👨‍💻

今まで開発したものやtipsなど


資産運用を「UI/UXの問題」として解く:自分をだますHCIとホーミーズ

我が家には、ちょっと変わった文化があります。モノに名前をつけて、人格のあるものとして扱うんです。おかげで家のあちこちに〝ホーミーズ〟がいます。 先に種明かしをしておくと、この記事は、その名前をつける発想を、とうとう無形のもの——資産運用のポートフォリオ——にまで広げてしまった話です。そして我が家の資産運用は、「入口で自分を騙し、出口でも自分を騙す」という、二段構えのマインドハックになっています。 冗談みたいですが、やってみると地味に理にかなっている気がしているので、順にお話しします。

思想編:なぜ私は、ブログシステムを自分で作り続けるのか

前回、ブログシステムを6年ぶりに作り直した話を書きました。技術編です。何をどう作り替えたのか、という話でした。 今回は、そもそもなぜnoteもZennもある時代に、わざわざ自分でブログシステムを作っているのか、という話を書きます。たぶん、こっちが本題です。

技術編:ブログシステムを6年ぶりに作り直しました

このブログのシステムを、全面的に作り直しました。いま読んでいるこのページは、数日前までとは中身がまるごと別物になっています。 最初にお断りしておくと、この話は二部構成です。今回は技術の話をします。何がどう変わったのかを、実際に動くデモを交えて紹介していきます。「そもそもなぜ、noteもある時代に自分でブログを作り続けるのか」という思想の話は、次回にとっておきます。

1つのD、2つのD、4つのD

テック系のタイムラインには、毎日のように「作ってみた」が流れてくる。新しい技術に飛びつき、形にして、イベントで展示して、大盛況で終わる。私はあの熱量が好きだし、自分自身、ずっとそっち側の人間だった。作っては公開し、展示し、ありがたいことに賞をもらったこともある。

FDEとSESは何が違うのか——その正体は、モンスターハンターである

いま、FDE(Forward Deployed Engineer)という職種がにわかに盛り上がっている。 PalantirやOpenAI、Anthropicが採用を拡大し、VCのa16zは「いまテック業界で最もホットな職種」と呼び、日本でもLayerXやログラスなどのSaaS企業を中心に採用が立ち上がってきた。 一方で、この説明を聞いた多くの日本のエンジニアがこう思ったはずである。 「それ、客先常駐のSESと何が違うの?」 この記事を読み終わる頃には、この問いに一言で答えられるようになっているはずである。しかもその答えは、FDEの解説記事によくある「上流から関われる」「自社プロダクトがある」といった差分の列挙ではない。なぜその差が生まれるのかという構造のほうである。 種明かしを先に言ってしまうと、こうだ。 SESとFDEの違いは、ドラクエとモンハンの違いである。

どうしてもローカルでPDFを1枚ずつJPEG化したい

iLovePDFなど、便利なツールがありますがどうしてもオンラインではなくローカルだけでPDFをそれぞれ画像化したいときってありますよね。例えば個人情報とか、アップロードしたくはないのでローカルでできればベストです。 chatGPTに聞いたところpdftkとimagemagickを組み合わせればできるとのことだったのですが、現行のImageMagick 7.1.1-36ではconvertコマンドに変わりmagickコマンドを利用せよとのことでした WARNING: The convert command is deprecated in IMv7, use "magick" instead of "convert" or "magick convert" ただドキュメントを見る限りmagickコマンドにはこれまでconvertコマンドで利用できていたようなpdfからjpg変換するようなものが見つからず困っていました 最終的にcubepdfを使い位置枚ずつjpeg化して対応しました。これならpdftkを利用する必要もなくGUIだけでできるのでベストかもしれません

markdownエディタ「Tale」をリリースしました。

久しぶりのリリースです。markdownがスラスラ書けるブラウザエディタ「Tale」をリリースしました。 OSSとして公開しています 背景 OSSで公開されているNovelというプロジェクトをフォークして開発しています NovelはNotionライクな WYSIWYG editorでSteven Teyらによって開発されています。 NovelはNotionのような書き心地で非常に良いエディタではあるのですが、基本的に開発者が独自の機能を追加して個別で利用することを前提としたプロジェクトであり、保存やロード、markdownエクスポートなどはNovelではサポートしない方針でした こうした背景から自前で拡張コマンドを実装しローカルで使っていましたが、そちらをVercelでデプロイしリリースしたものになります。 拡張コマンド /clear : 執筆中のドキュメントを全削除するコマンドです。このコマンドがない場合、全てドラッグで選択して削除する必要がありました。内部的にはeditor.commands.clearContent()を利用しているため間違って実行してもCtrl + Zで戻ることが可能です。 /export : .md形式でダウンロードします。アップロードした画像ファイルは.png形式で同時にダウンロードされ、markdownからは名前で参照される形となります。 /save : .tale形式で保存します。画像ファイルはbase64形式でファイル内に埋め込まれるため、単一ファイルとして保存されます。 /load : .tale形式で保存されたファイルを開きます /fullscreen : エディタをフルスクリーン化します ?load= : ホストされた.taleファイルを開きます 機能リクエスト・問い合わせ なにかありましたらDMにてお問い合わせください

novelで書いたmarkdownを保存できるようにする

novel markdownエディタにnovelを採用し、ローカルに構築してみました ※テキストは Good Morning #01 inowayさんの章を拝借しています リポジトリ デプロイしてみた novel novelはvercelの方たちが中心となって開発したオープンソースのnotionライクなmarkdownエディタです。ライブデモで動作を確認できます。 notionライクな書き味とlocalStorageを利用したリアルタイムセーブなんかが良いです。 現在ライブデモとVSCode拡張で提供されていますが、ライブデモはmarkdownエクスポートができなかったり、VSCodeは見た目が小さかったり画像のアップができなかったりと、色々残念なポイントがあります。 今回はnovelのブランチをフォークして自前の環境を作り、markdownエクスポートを実現したので共有します。 markdown export \novel\packages\core\src\ui\editor\index.tsxに機能的な処理が記載されています onUpdate()がアップデートされたときに呼ばれるコールバックとなっていて、そちらにエクスポート機能を追加します。 エクスポートのトリガは /saveと入力した際にされるようにします。 const lastFive = getPrevText(e.editor, { chars: 5 }) if (lastFive === "/save" && !isLoading ){ const id = Math.random().toString(32).substring(2); e.editor.commands.deleteRange({ from: selection.from - 5, to: selection.from, }); let editorContent = e.editor.storage.markdown.getMarkdown(); const blob = new Blob([editorContent], {type: 'text/plain'}); // Blob オブジェクトの作成 const link = document.createElement('a'); link.download = 'novel-'+id+'.md'; // ダウンロードファイル名称 link.href = URL.createObjectURL(blob); // オブジェクト URL を生成 link.click(); // クリックイベントを発生させる } これで/saveコマンドを打つとmarkdownがダウンロードされるようになります。 base64化解除 ただ上記の状態だと、埋め込まれた画像が全てbase64化され本文に記述されているため、容量が非常に多くなっています。 画像ファイルはbase64からpngデータに変更して単一のファイルとして保存されるように以下の処理を追加します。 let req = /\(data:image.*?\)/g //base64化された画像のみ()ごと抜き出す let imgs = editorContent.match(req); for(let i in imgs){ var a = document.createElement("a"); //Create <a> let base64 = imgs[i].slice(1); base64 = base64.slice(0,-1) a.href = base64 //Image Base64 Goes here a.download = i + "-" + id +".png"; //File name Here a.click(); //Downloaded file editorContent = editorContent.replace(base64,i+ "-"+id+".png") } これでエクスポート時に画像ファイルがダウンロードされるようになり、markdownには画像ファイル名が記述されるようになりました。 課題 本当はJSZipなんかでzip化したほうがいいんだと思います。めんどくさくてやってません localstorageに残っているデータは現状デベロッパーツールで消すかすべて選択して消すかしかないので/deleteなんかを定義してもいいのかもしれません。事故が怖いですが。 novelのせいなのか私の書いたコードのせいなのかたまにオートセーブが効かなくなります。エラーを見る限りローカルストレージに保存する際のパースエラーっぽいんですが、続けて書いていれば消えて問題なく保存再開されるので現状無視してます update()の返り値のeから最終的にmarkdownが平文で取得できるので、頑張ればchrome拡張なんかでも提供できそうです そもそも書こうと思っていた記事があったのに環境構築から始めたせいで結局執筆は一ミリも進んでおらずこんな時間です 続報 スラッシュメニュー化&json形式でのセーブ・ロードを実装しました。 公式ではフルスタックアプリ化を避けるためこういった機能はマージしないそうなのでissueにて展開済み 参考

Nuxt3+@nuxt/contentでブログを作ってcontentをgit submoduleで別リポジトリ管理にする

なるべくお金をかけずに、複数人でコンテンツ管理が可能な静的Webサイトが作りたくなったのでNuxt3の勉強がてら作ってみます。 システム コンテンツ 利用イメージ nuxt3-blogtest-systemの方でNuxt3製Webシステムを構築、contentディレクトリをnuxt3-blogtest-contentからgit submoduleで引っ張ってくる、著者はnuxt3-blogtest-contentだけを操作するイメージ。 手順 Nuxt3の導入 > npx nuxi init nuxt3-blogtest-system > cd nuxt3-blogtest-system > npm install @nuxt/contentの導入 > npm install --save-dev @nuxt/contentnuxt.config.tsを編集 export default defineNuxtConfig({ modules: [ '@nuxt/content' ], dir :{ public: "content/public" } }) pagesフォルダを作成 > mkdir pages > cd pages > touch index.vue > mkdir posts > cd posts > touch [...slug].vue index.vue / [...slug].vueをそれぞれ作成 index.vue <template> <div> <p> here is index.vue</p> </div> </template> [...slug].vue <template> <main> <ContentDoc /> </main> </template> content用ディレクトリを作成 // systemとは別階層で作成する > mkdir nuxt3-blogtest-content > cd nuxt3-blogtest-content > mkdir public > mkdir content > cd content > mkdir posts nuxt3-blogtest-content/content/postsディレクトリにmarkdownファイルを追加 > touch hello.md hello.md # here is hello.md 画像を利用する ![myimg](/img/img.png) //画像を使う場合はpublic/img フォルダにimg.pngという名前で画像を保存 nuxt3-blogtest-system側でnuxt3-blogtest-contentをsubmodule化 > git submodule add content コンテンツを更新した場合は、submoduleをupdate > git submodule update --remote これでnpm run dev もしくは npm run generateするとコンテンツがレンダリングされます。

PlayCanvasを使用してLooking Glassアプリを作成する:Looking Glass WebXR SDKの利用ガイド

demo 実施手順 Looking Glass Portraitの準備 Looking Glass Portraitを用意し、電源とHDMIを接続します。電源をONにすると、LookingGlassのLEDが青色に点滅します。 Looking Glass Bridgeのインストール Looking Glass BridgeはLooking Glass Factoryのウェブサイトからダウンロードしてインストールします。インストール後、タスクバーのLooking Glass Bridge iconをクリックしてLookingGlassをリセットし、デスクトップを表示します。 LookingGlass用のPlayCanvasプロジェクトの作成 PlayCanvasで新しいプロジェクトを作成します。プロジェクトのテンプレートとして、Model Viewer Starter Kitを推奨します。 プロジェクト内でのスクリプトとHTMLの作成 プロジェクト内で、スクリプト(LG.js)とHTML(LG.html)を作成します。 LG.htmlの作成 LG.htmlを以下のように記述します。 import { LookingGlassWebXRPolyfill, LookingGlassConfig } from " const config = LookingGlassConfig config.tileHeight = 256 config.numViews = 20 config.targetY = 0 config.targetZ = 0 config.targetDiam = 3 config.fovy = (14 * Math.PI) / 180 new LookingGlassWebXRPolyfill()LG.jsの作成 LG.jsを以下のように書き換えます。 var Lg = pc.createScript('lg'); Lg.prototype.initialize = function() { pc.app.xr._available["immersive-vr"] = true; var htmlAsset = this.app.assets.find('LG.html'); var div = document.createElement('script'); div.type = "module" div.innerHTML = htmlAsset.resource; document.body.appendChild(div); htmlAsset.on('load', function () { div.innerHTML = htmlAsset.resource; }); }; Lg.prototype.update = function(dt) { if(this.app.keyboard.wasPressed(pc.KEY_ENTER)){ this.camera.camera.startXr(pc.XRTYPE_VR, pc.XRSPACE_LOCALFLOOR); } };アプリケーションの実行 アプリケーションを実行し、エンターキーを押すとLookingGlass用のコンテンツが別ウィンドウで再生されます。そのウィンドウをLookingGlassに移動させ、F11キーを押します。 以上で、LookingGlass用のPlayCanvasアプリケーションが実行されます。 動作原理 webxr.jsはWeb標準のWebXR APIをラップし、オープンソースで提供されています。このライブラリは3Dエンジンに依存せず、カメラの位置を取得し、複数のレンダリングを立体視可能な画像に変換して出力します。 LookingGlass用のウィンドウはマウスイベントの受け取りと最終レンダリングを行います。実質的には、このウィンドウはPlayCanvasのLaunchタブそのものです。そのため、タブを閉じたりバックグラウンドに移動すると、動作が停止します。また、複数のレンダリングを行うため、描画負荷が増大する点には注意が必要です。 問題点 PlayCanvasでアプリケーションを実行すると、webxr.jsにより定義されたcanvasのサイズ変更アルゴリズムがBufferの問題で誤った数値を出力する可能性があります。その結果、画像が意図せずにトリミングされる可能性があります。立体視画像の生成には複数の画像が必要なので、一部の画像が欠落したり黒くなったりすると、立体視が一部見えなくなる欠点があります。この問題は、canvasのサイズをオーバーライドすることで一時的に対処可能です。 まとめ 裸眼立体視技術は以前から存在していましたが、その技術を5万円程度の手頃な価格で利用できるポータブルデバイスに組み込み、しかもWeb技術のみで実行可能という点で、その革新性を感じます。Bridgeの導入などまだ一部制限はありますが、Webとの接続性により、環境構築の手間が減少し、さまざまなAPIとの接続によるコンテンツ開発が進むでしょう。

PlayCanvasのOrbit-cameraを制御して指定位置にカメラを移動させる

PlayCanvasのStarter Kitで用意されている「Model Viewer Starter Kit」では360°ビューワーを簡単に作る事ができますが、常にインプット情報からカメラ位置を制御しているため、スクリプトやボタンで指定位置にカメラを移動する方法にやや癖があります。 この記事ではスクリプトからorbit-camera.jsを利用した状態で指定位置にカメラを移動する方法を説明します。 これを使うとコンフィギュレーターなどを開発する際に、基本は自由移動でボタンを押したときだけ特定の部品に寄るなどの処理ができるようになります。

11/28を過ぎて停止したHerokuアプリを課金して復旧した方法

11/28でHerokuの無料プランがなくなりました。 いままで動かしていたHerokuアプリが動かなくなったので自分の運営しているアプリも影響を受けていました。 本来であれば11/28までにやらなければいけなかったのですが、少し遅れてしまったので急ぎ対応した記録です。 Step1.Ecoプランに加入する 月額5USDです account > Billingからいけます Step2.ダッシュボードからDynoタイプを変更する 有償にした時点でEcoが選択されていると思います。 Step3. 停止を解除してアプリを再開する こちらのトグルをクリックして再開できます。 これでアプリを復旧できました。

ncmbのREST APIでエラー{"error":"12.16.1 is invalid version","status":400,"code":"E400001"}が出る

ncmbに対してRESTでAPIを投げたときにエラー {"error":"12.16.1 is invalid version","status":400,"code":"E400001"} が出る エラー一覧 にそのようなエラーはない 結論: RESTではなくREST先で読んでいるスクリプト実行時のエラー 対応:バージョンを12.16.1からlatestに変更してあげればOK

反Apple信者の私が移動用のメインマシンをiPad Pro(第5世代) 12.9インチに変えてからしばらく経ったので現在の構成を紹介

PCはWindows、スマホはAndroid(Pixel)と、脱Appleを過去掲げていた自分ですが、移動用のメインマシンをiPad Proに変えてからしばらく経ちました。 気づいたらかなりやりやすい環境まで整えて来ていたのでWindows中心で貼るけどiPad Proを移動用のメインマシンとして考えている方向けに現在の構成について紹介します。

Thunderbirdのトップに格言を表示してメーラーを開くたびにテンションが上がるようにした

背景 ずっと利用していたWindows標準のメーラーの調子が悪くなっていたので、心機一転懐かしいメーラーであるThunderbirdに乗り換えました。 Thunderbirdはmozilla製で、ブラウザ技術を使って作られています。 設定を見てみると、このウェルカムページを変更する項目を発見しました。 このURLを書き換えると好きなWebページをThunderbird起動時に表示することが出来ます。 ずっとウェルカムページが表示されているのもつまらないので、ちょっと面白いページを作って設定してみました。 作ったもの アクセスするたびに格言を表示するWebアプリを作りました 格言データベースはスプレッドシートで管理しています。好きなものを追加するだけで、ランダムに表示される仕様になっています。 作り方 スプレッドシート + GASで構築しています。 GASでスプレッドシートからランダムに一つ格言を取得して、doGet()で表示します。 function getQuotesAtRandom() { const ss = SpreadsheetApp.getActiveSpreadsheet(); let sheet = ss.getSheetByName('Quotes'); let lastrow = sheet.getLastRow(); let id = getRandomInt(2,lastrow); let word = sheet.getRange(id,1).getValue(); let author = sheet.getRange(id,2).getValue(); return [word,author] } function getRandomInt(min, max) { max = max + 1; min = Math.ceil(min); max = Math.floor(max); return Math.floor(Math.random() * (max - min) + min); //The maximum is exclusive and the minimum is inclusive } function doGet(e){ var output = HtmlService.createTemplateFromFile('page'); let wa = getQuotesAtRandom(); output.word = wa[0]; output.author = wa[1]; return output.evaluate().setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL); } 表示するWebページを適当に用意して表示します。 <!DOCTYPE html> <html> <head> <base target="_top"> <link rel="preconnect" href=" <link rel="preconnect" href=" crossorigin> <link href=" rel="stylesheet"> </head> <body> <h1><?= word ?></h1> <h2><?= author ?></h2> <style> .warning-bar-content{ display:none; } h1{ font-family: 'Noto Serif JP', serif; letter-spacing: 2px; color:white; } h2{ text-align: right; padding-right: 10%; font-weight: 300; color: gray; font-size: 1.3rem; } body{ text-align:center; margin-top:150px; background-color:rgb(46,46,46); } </style> </body> </html> GASが生成するWebアプリのURLに直接アクセスすると、 このアプリケーションは、Google ではなく、別のユーザーによって作成されたものです。 という表示が出てしまいます。 これを表示させないために、一枚適当なhtmlを用意し、iframeでGASのWebアプリを呼び出すようにする必要があります。 今回は自前のサーバーを使いましたがGithub Pagesとか使えば完全サーバーレスでも全然行けると思います。 最後にThunderbirdに設定すると、開くたびにランダムで格言が表示されるようになりました。 これでメールをチェックするたびに気が引き締まるような環境ができあがりました。 他にもいいアイデアがあれば実装したいと思います。 参考

Power Automate Desktopで無理やり時間指定実行を実装したときは気をつけよう

Power Automate Desktopが最高に便利です 無料版でだいたいのことができるので重宝しています。 ただ無料版だと時間指定実行が出来ないのですが、力技で時間指定実行を実装することが出来ます。 ですがその方法を取ったときに問題が起きたので記事にします

JamStackでもアクセスカウンターを実装したい

この記事はmicroCMS Advent Calendar 2021 17日目の記事です。 皆さんの記事を見ていて自分も知らなかったMicroCMSの機能や使い方をしれたので、Advent Calendarはとても好きです。 個人的には@Ryusouさんの1日目の記事で、MicroCMSダッシュボードが知らない間にアップデートされていたことを知れたのが良かったです。 後はPOST入稿の際にリッチテキストをもうちょっとなんとかできると個人的に嬉しいなあ。。。

LIFF(LINE)ブラウザでwindow.prompt()が期待する動作をしない

運用しているLIFFアプリでwindow.prompt()を使っているのだけれど、ある時から、promptのデフォルトメッセージそのままだと処理が進行しないというバグ報告をもらった。 該当の部分はこちら if (comment = window.prompt("[ユーザー名:" + displayname + "]で申込します。\n\nコメントをどうぞ!","よろしくおねがいします")) { こちらがデフォルトの"よろしくおねがいします"のまま確認ないしはOKを押すとcommentには"よろしくおねがいします"が入力されるはずだが、commentは""のままらしくif文が通らないらしい。prompt画面でメッセージを修正すると、問題なく修正されたメッセージが入る。 検証してみた。 以下のようなhtmlを作成 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>LIFF window.confirm test</title> </head> <body> <input type="button" onClick="buttonclick()" value="button"></input> <p>output : <span id="output"></span></p> <script> function buttonclick(){ let comment = window.prompt("LIFFブラウザでwindow.prompt()でdefaultメッセージから変更しない場合、OKを押しても値が渡りません","default message"); if(comment){ document.getElementById("output").innerHTML = comment; } } </script> </body> </html>スマホブラウザから実行すると問題なくpromptの返り値がデフォルトでも表示される {{spb}} だがLIFFで実行した場合、デフォルトのままだと返り値はemptyで、変更して初めて戻り地がメッセージになった {{liffb}} ぜひ手元の端末で以下のURLから試して欲しい LIFFで実行する場合は以下URLをコピーしてLINE上にペースト、初回起動でアクセスを許可して実行可能。 URL: これは通常のWebブラウザで期待する動きと異なった動作なのでLINE側の修正が入ってくれないと困るバグ、、、 ご対応よろしくお願いいたします。 追記 2021/10/17 ここで報告してみた

microCMSにプリセットで入っているimgixを利用してお手軽に動的OGPを設定する

Qiitaとかである記事のタイトルなんかをOGPに入れ込んでシェアした段階でわかりやすくなるあれです 動的OGPを作成する手続きとしてはいくつかあるんですが、大きく分けでサーバーサイドで画像を作成するもの、image CDNを利用していい感じに作ってもらうものの2種類があります。 VOXELCANVASなんかはモデル作成後にphpで画像化してimage CDNに保存して、最後にimage CDNでロゴとblendするというハイブリッドな方法をとっています。 このブログも今まで記事ページは動的OGPを噛ませていなかったので、そういえばimgixとmicroCMSがパートナーとなったことだし、もしかしたら超簡単にできるんじゃね?ってことで挑戦してみました 手順を解説します。 まず最初に動的OGPのベースとなる画像を作成してmicroCMSにアップロードします アップロードした時点でimgixの機能が使えるようになるので、テキストなんかを自由に入力できます {{image}} あとはこれをOGPとしてNuxtで設定してあげるだけです。 head (){ return { title : this.title + " | ", meta: [ { hid: 'description', name: 'description', content: this.body.replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,'').substr(0, 99)+"..." }, { hid: 'og:title', property: 'og:title', content: this.title + ' | 空想は世界を包み込む' }, { hid : 'og:image', property: 'og:image', content: " { hid: 'og:url', property: 'og:url', content: ' + this.$route.path + "/" }, { hid: 'og:description', property: 'og:description', content: this.body.replace(/<("[^"]*"|'[^']*'|[^'">])*>/g,'').substr(0, 99)+"..." }, ] } } 以上で動的OGPが設定できます。 imgix周りの環境構築をすべてmicroCMSがやってくれるのでとても楽でした。 今回は1行で実現したので楽でしたが複数行にする場合は少しややこしくなるようです。 →対応したので追記しました また、このブログでも何回か紹介しているimagekit.ioでは選べるフォントが少なめなのでまだimgixのほうが良さそうです。 2022/08/10追記:複数行に対応しました blendで複数行もワンラインでできることに気づき、対応しました。 {{imagemul}} og:imageを以下のように変更しています。 { hid : 'og:image', property: 'og:image', content: "

actions-js/push@masterでのデフォルトブランチがmasterからmainに変わっていてCIが動かなくなっていた

ある日突然CIが動かなくなった このCI自体はこのブログシステムのJAMStackアーキテクチャ内で利用していて、ブログシステムのアップデートと記事の更新によるトリガでCIが動くような仕組みになっている。 triggerと書かれたworkflow runが記事の更新によるものでソースコードについては編集を加えていないため、CIが動作しなくなるのはなにかのライブラリのはず ということで調査を行った 失敗したCIのログがこちら エラーが出ているActionはこちら エラー箇所のログはこちら Run actions-js/push@master with: github_token: *** author_email: github-actions[bot]@users.noreply.github.com author_name: github-actions[bot] branch: main directory: . Started: bash /home/runner/work/_actions/actions-js/push/master/start.sh Push to branch main [master 856abcb] chore: autopublish 2021-08-08T14:39:20Z (中略) To ! [rejected] HEAD -> main (fetch first) error: failed to push some refs to ' 原因はbranch: mainの箇所 ドキュメントを読んでみると、Inputsの中のbranchはデフォルトで'master'を参照していると書いてあったのだが、Destination branch to push changes とあるので、デフォルトブランチを参照するという意味とも取れる。ややこしい。 まだ自分の管理しているリポジトリではmasterからmainへの移行がすすんでいなかったため起きたエラーだった。以下のように修正し、問題なく動作することが確認できた。 - name: Commit & Push changes uses: actions-js/push@master with: github_token: ${{ secrets.GITHUB_TOKEN }} branch: 'master' #追加

Webサイトの変更を監視してLINEに即通知を行うシステムを開発・提供しています

Webの変更を監視して、変更があった場合即LINEへ通知する仕組みを開発・提供しています。 ECサイトでの商品の確認や、トレードサイトなどでのレートの変更確認、ブログやメディアなどで最新記事が公開された瞬間にすぐ情報を得ることができます。 現在coconalaで提供させていただいております。ご興味がある方はこちらからご連絡ください。 技術的にはGoogle スプレッドシート + Google Apps Script + LINE notifyで開発しています。 Webサイトの確認したい箇所をGASのトリガー機能を利用して定期的に監視、スプレッドシートに保存します。 監視内容とスプレッドシートに記録された内容が違った場合、LINE notify経由でLINEに送信するシンプルな設計です。 納品はGoogleスプレッドシートの管理者切り替えで行いますので、Googleアカウントがあれば簡単に受け取り、利用開始可能です。 手動でのスクレイピングサービスは多くありますが、自分の代わりに定期的に確認し、変更があるときだけ送ってくれるような自動化ツールとしては 面白いと思います。ぜひ低コストでWeb監視したい場合はお声がけください。

nuxt.jsでテキストボックスクリック時にテキストを全選択する

URLを発行したりしたときにコピーさせやすくするためのアレです こんなふうにテキストボックスを用意します <b-input ref="selectablebox" readonly id="inline-form-input-name" value="コピーしたいテキスト" @click="select($refs.selectablebox)" ></b-input> methodsにselect()を作成します select : function(target){ if(target.$el){ target.$el.select(); } } 以上。 注意点: refを利用してその後に$elで参照しているため Vueコンポーネント化されたDOMに対してのみしか動きません。 なのでデモではbootstrapのb-inputを利用しています。

私的Nuxt.jsでどのアーキテクチャを選ぶかフロー

ユーザージェネレーテッドかどうか YES → SSR NO ↓ ページ数が多くそれぞれのページのSEOを気にするか YES → SSG NO ↓ SPA SSR 動作が早いがデプロイ環境がnode.jsサーバーがホスティングされている必要がある。firebase hostingを利用すれば無料で環境を構築できなくもないがやや面倒。私はロリポップマネージドクラウドを利用している。 ユーザージェネレーテッドなWebアプリをJAMStack構成でやる場合、ページ数合計100ページくらいだと、Github Actionの無料枠2000分のビルドから逆算すると多くても150回程度しかCIを回せない。 そんなにページ数がない、そんなにCIを回さないのであれば可能だが、ユーザー投稿後ページとして出来上がるまでのダウンタイムはやはり無視できない SSG 基本はSSG+github actionでJAMStack構成でやっていきたい。SSGであればhosting先はgithub pagesでもいいので、ドメインさえ気にしなければ無料でできてしまう SSGを諦める理由としては、上記ユーザージェネレーテッドコンテンツの存在が大きい。 投稿者がコントロール可能なWebメディアなんかはJAMStackが最適だと思う。出力された静的ファイルをCDNでホストすれば爆速になると思う。 ただ課題もあって、1000件をこすようなコンテンツ量であればCIのコストがかかるだろうし、都度デプロイで静的サイトを足していく方法では、CIによっては過去ビルドはリフレッシュされてしまう使用のものも少なくないので気をつけたい。またJAMStackの特性として特定時間に公開といった処理は難しいのでそういった用途では選択を避けたい SPA これは正直選びどころがわからない。サーバーで動作させたくないライブラリを利用している場合(そんなときSSGでもprocess.clientで絞ればいいが。。。)とかはSPAにしとくと楽かもしれない。 やったことはないが、限られたページかつ、流入がトップからがほとんどなネイティブアプリに近いような作りで、PWAやelectronでネイティブで動作するようにし、ばらまく前提で作っているようなアプリはSPAで開発してPWAでストアにアップすることがおすすめかも知れない。

Among us向けヘルパーツールを作りました「Among us ビジュアルヘルパー」

ツールはこちら Web版: デスクトップ版:ダウンロード ダウンロードして解凍した後、amongus-visual-helper.exeを実行してください Among usビジュアルヘルパーはAmong us初心者プレイヤー向けのゲームプレイ支援ツールです。アイコンを配置したりルートを描画したりして行動を可視化できます。配信用途でも利用可能です。 ツールは以下機能を搭載しています アイコン 自由移動配置 アイコン 生死切り替え アイコン 名前設定(色変更可能) アイコン 除外 フリーハンド ライン描画(色変更可能) 背景色変更 背景色を変更することで、OBSでカラーキー設定することで配信に使うこともできます 活動中はマップとして、議論中はメモとして使えます。 カラーキーはマゼンタがおすすめです。 日本語版も3/5のアップデートで正式にリリースしたのでもっと盛り上がったらいいですね。 私もハマっているので人数合わせにお困りでしたらぜひリプでお声がけください。

会社のテックブログを勝手に爆速にしてみた

本記事はOne Tech Blogに寄稿した記事を転載したものです こんにちは GMOグローバルサイン・ホールディングス株式会社 GSP事業部 PlayCanvas推進室室長の津田@utautattaroです。 普段はPlayCanvasの事業責任者として普及促進活動をしたりWebGLのサンプルを作ったりしています。 会社では珍しく(?)フロント系のエンジニアなので、今回はフロントエンドな話題を取り上げてみました。

【Web表現チェンジャー】PlayCanvasに追加された3Diframeサンプルが控えめに言ってヤバい

最近PlayCanvasで3Diframeのデモが@yausterによって追加されました これがなかなかに面白い機能でPlayCanvasの3D空間内に好きなだけiframeでWebページを埋め込めてしまうのです。 バーチャルゲームセンターも実装できた これをもう少し工夫すればバーチャル美術館とか、展示場とか、何でもできそう。 ポートフォリオもこれで構築すればすごい差別化できそう。 しかも実装はスクリプトを板ポリにアタッチしてattributeでURLを指定するだけ。すごい。 実装自体はCSS3Dでやっていて、PlayCanvasの3D座標系とCSS3Dの3D座標系を完璧に正しくマッピングしきっている。美しい。。 また3D空間でCSS3Dの前にEntityが映り込んだ際は正しくCSS3Dがエンティティ背景に回り込むようになっていて、より没入感を高めている。これはすごすぎる。。。 なおこちらの3Dモデルは全部VOXELCANVASでちゃっちゃっと作成。リソース作成からアプリ開発、デプロイまで全部Webでできてしまった。。。 VOXELCANVASではvirtual-deskkitというタグでパブリック公開中。ぜひダウンロードして使ってみてね。 これで好きなYouTube動画に囲まれるWebサービスとか作ったらおもしろそう。 YouTubeで検索ワード入れたら候補の動画が10個くらい帰ってくるようなAPIないかな 試してみたい。

健康生活

送り迎え 今日試しに保育園の送り迎えをどちらも歩いて行ったところ、合計13000歩、9.5km踏破していた。このうち半分は息子を担いで歩いている。 今日の体感としては、慣れれば楽になりそうだと思った。だが、早めにいい靴がほしい。 仕事 仕事の方は、3回目のPlayCanvas Japan TVを配信した。今日のテーマは「PlayCanvas 日本の歩み」と言う事で、色々振り返ってみた twitter埋め込み実装 ブログについてTwitter埋め込みがかんたんに実装できた。 だがwiget.jsの読み込みが走らないと処理が開始されないためF5を押さないと表示されない。 なんとか外部からメソッドを叩けるといいのだが… 次回実装予定 [2020/11/06 追記] twttr.widgets.load()で実装完了 twtterはwidgets.jsを読み込んでいればグローバルに存在している まとめ 今日はようやくルーチン化してきて早めに終わったのでこの時間に就寝。 おやすみなさい。

次はURLのogp出力に挑戦

やりたいこと noteみたいにURLを貼ったらogpを読み取ってリッチに表示するようにする 検討している実装手段 ビルドのタイミングでbodyを直接書き換える方法を取る。 具体的に URLを検出したら以下のライブラリを利用してogp情報を取得する Nuxt.jsでのプライグインの利用方法も忘れない 課題 microCMSではaタグが使えるのだけど、aタグで貼ったときはむりやりリッチリンク化したくないので、そこは無視するようにする。 具体的に これはリッチリンク化したいURL これはしたくないURL とりあえずやってみよう

目次をデフォルトで開いたり、開いたり閉じたりを検出したい

やりたいこと ↑この上の目次をデフォルトで開いてる状態にしたい 現状想定しているやり方 まずはDOMにアクセスしないといけないので、これを試す Bootstrapのイベントで取れるようなら撮ってみる だめならJavaScript書く できた bootstrap-vueのcollapseの詳細を見て解決。 toggleの項ばっかみてた

目次を作りたい→作った

目次を作りたい 目次とは hタグをまとめて上部にアンカーリンクを置く 要件 完成物 とりあえずhタグが全部リンク化されている状態にしたい どうやって作るか? とりあえずフルスクラッチで作ろう 具体的に bodyをデータで持ってるからそれからhタグを検索。 idはmicroCMSがどうしてるのか調査したい 見出し5 見出し5って使うの?パラグラフより小さいけど まとめ とりあえず頑張って作る 作った 作り方 正規表現でタグ切り取り <h1>-<h5>までのタグを正規表現で切り取り let reg = /<h[1-5].*?<\/h[1-5]>/g; let tables = payload.post.body.match(reg); 配列に入れる microCMSだとh1-h5は自動でidを振ってくれるのでひと手間減って便利 if(tables){ for(let i=0;i<tables.length;i++){ let obj = { "tag" : tables[i].slice(1,3), "id" : '#' + tables[i].match(/".*"/)[0].slice(1).slice(0,-1), "text" : tables[i].match(/>.*</)[0].slice(1).slice(0,-1), } t.push(obj); } } 表示 横全面だとダサいのでgridでmd=4へ いい感じ <b-container class="p-0 m-0"> <b-row> <b-col md="4"> <div class="tableofcontent bg-light p-2 mb-2" v-if="t[0]"> <b-list-group> <p class="p-1 m-0 text-center"><strong>もくじ</strong></p> <b-list-group-item class="py-1" v-bind:class="'t'+table.tag" v-bind:href="table.id" v-for="table in t">{{table.text}}</b-list-group-item> </b-list-group> </div> </b-col> <b-col> </b-col> </b-row> </b-container> h1-h5でインデントをCSSでつけている .th1{ padding-left: 0.5rem !important; } .th2{ padding-left: 1.0rem !important; } .th3{ padding-left: 1.5rem !important; } .th4{ padding-left: 2.0rem !important; } .th5{ padding-left: 2.5rem !important; }結果 やったぜ

highlight.jsを入れた

vue-highlight.jsもmarkdownitも試したがうまくいかなかったので最終手段CDNで入れたら秒でできた。 2時間返してくれ。 C++ #include <iostream> int main(int argc, char *argv[]) { /* An annoying "Hello World" example */ for (auto i = 0; i < 0xFFFF; i++) cout << "Hello, World!" << endl; char c = '\n'; unordered_map <string, vector<string> > m; m["key"] = "\\\\"; // this is an error return -2e3 + 12l; } HTML <!DOCTYPE html> <title>Title</title> <style>body {width: 500px;}</style> <script type="application/javascript"> function $init() {return true;} </script> <body> <p checked class="title" id='title'>Title</p> <!-- here goes the rest of the page --> </body> C# using System.IO.Compression; #pragma warning disable 414, 3021 namespace MyApplication { [Obsolete("...")] class Program : IInterface { public static List<int> JustDoIt(int count) { Console.WriteLine($"Hello {Name}!"); return new List<int>(new int[] { 1, 2, 3 }) } } } CSS @font-face { font-family: Chunkfive; src: url('Chunkfive.otf'); } body, .usertext { color: #F0F0F0; background: #600; font-family: Chunkfive, sans; --heading-1: 30px/32px Helvetica, sans-serif; } @import url(print.css); @media print { a[href^=http]::after { content: attr(href) } } JavaScript function $initHighlight(block, cls) { try { if (cls.search(/\bno\-highlight\b/) != -1) return process(block, true, 0x0F) + ` class="${cls}"`; } catch (e) { /* handle exception */ } for (var i = 0 / 2; i < classes.length; i++) { if (checkCondition(classes[i]) === undefined) console.log('undefined'); } return ( <div> <web-component>{block}</web-component> </div> ) } export $initHighlight; PHP require_once 'Zend/Uri/Http.php'; namespace Location\Web; interface Factory { static function _factory(); } abstract class URI extends BaseURI implements Factory { abstract function test(); public static $st1 = 1; const ME = "Yo"; var $list = NULL; private $var; /** * Returns a URI * * @return URI */ static public function _factory($stats = array(), $uri = 'http') { echo __METHOD__; $uri = explode(':', $uri, 0b10); $schemeSpecific = isset($uri[1]) ? $uri[1] : ''; $desc = 'Multi line description'; // Security check if (!ctype_alnum($scheme)) { throw new Zend_Uri_Exception('Illegal scheme'); } $this->var = 0 - self::$st; $this->list = list(Array("1"=> 2, 2=>self::ME, 3 => \Location\Web\URI::class)); return [ 'uri' => $uri, 'value' => null, ]; } } echo URI::ME . URI::$st1; __halt_compiler () ; datahere datahere datahere */ datahere CDN最高

テキストから一意の値を算出する

タグに自動で色がつくようにしました。なお、同一のタグは同一の色がつくようになっています タグのコンポーネントはこんな感じ <template> <b-badge pill v-bind:style="'background-color : #'+ string_to_utf8_hex_string(tag).substr(1,6) + ';'" class="mr-2" v-bind:to="to">{{tag}}</b-badge> </template> <script> export default { props:["tag","to"], methods : { string_to_utf8_hex_string : function(text){ var bytes1 = this.string_to_utf8_bytes(text); var hex_str1 = this.bytes_to_hex_string(bytes1); return hex_str1; }, string_to_utf8_bytes : function(text){ var result = []; if (text == null) return result; for (var i = 0; i < text.length; i++) { var c = text.charCodeAt(i); if (c <= 0x7f) { result.push(c); } else if (c <= 0x07ff) { result.push(((c >> 6) & 0x1F) | 0xC0); result.push((c & 0x3F) | 0x80); } else { result.push(((c >> 12) & 0x0F) | 0xE0); result.push(((c >> 6) & 0x3F) | 0x80); result.push((c & 0x3F) | 0x80); } } return result; }, bytes_to_hex_string : function(bytes){ var result = ""; for (var i = 0; i < bytes.length; i++) { result += this.byte_to_hex(bytes[i]); } return result; }, byte_to_hex : function(byte_num){ var digits = (byte_num).toString(16); if (byte_num < 16) return '0' + digits; return digits; } } } </script> この後はワードクラウドとか作りたい

Webの変更をLINEに通知する仕組みを提供するサービスをココナラに出品しました

息子のお守りをしながら文章書いた 自分で使っていた仕組みが意外といい感じのフレームだなーと思ったので、提供することにした ちなみにWebサイトのスクレイピングはサーバーにそこそこの負荷をかけるため規約で禁止されているサイトは割と多い。 基本方針としては当然怒られない範囲内での提供とする。価格設定はとりあえずつけてみたけど運用し続けていくようなサービスでこの金額は妥当なのかは正直よくわからない。まあでも需要はありそうなので一旦これでリリースすることにする。後で良さげなサムネイル作ろう。

Github Codespaces試してみた

今のところめっちゃいい感じ 基本的にVSCode こまったらCtrl + Shift + Pで何とかなるところとか普通にVSCode npm run devとかでlocalhost:3000とかでデバック環境立ち上がると、URLが作成されてどの端末からでもそのURLでアクセス可能 スマホからデバッグできるの良いね 初動が遅い、修正の反映も遅いのがちょっと微妙かも、、、 と思ったけど反映早いわ ちょっとエラーがうざい VSCodeでは見逃されていたようなエラー(v-forのv-bind:keyないとかそのレベル)が結構怒られる。 オートセーブ 知らなくて焦った

backnumberを実装した

こんな感じ microCMSのcreatedAtで、年月だけを抽出して記事を作成する仕組みを作った これで、タグ機能、バックナンバー機能とブログとしての体裁は整ったかな 最近は、Github Actionsのビルド時間が長いことが悩み。一回大体6分くらい ほぼFTPでの時間がかかっているようで、悩み中 現在29記事でそのレベルなら、増えてきたら大変そう どうしようかなあ、、、まあまだGithub Actionsの無料2000分/月には遠く及ばないので、いったんは無視できるかな とりあえず記事を書き溜めていく

tweetlogを作った

自分のツイートを収集してブログに残す仕組みを作りました IFTTT GAS microCMS で構成しています。 まず、Twitter APIがめんどくさそうむずかしそうだっため、IFTTTでスプレッドシートにツイート取得し続ける仕組みを構築しました。 IFTTTも最近有料になってしまい、ちょっと辛いです。50ほどあったレシピは2個まで絞りました。Google asistant系だけはどうしようもない で、週に一回蓄積されたスプレッドシートの内容をmicroCMSにPOSTして、tweetlogとして出力するような形にしました。 microCMS, POSTでアップするときは\nみたいな改行コードが効くのですが、HTMLを直接流し込むとすべてがエスケープシーケンスにされてしまい困りました。解決策あれば教えてください。 ひとまず週一回のロギングで運用開始。問題なければ毎週日曜のお昼にツイートログが出力されます。 こうご期待!