https://forest.watch.impress.co.jp/docs/news/1621008.html
Release v0.84.0
https://github.com/microsoft/PowerToys/releases
Workspaces utility
https://learn.microsoft.com/ja-jp/windows/powertoys/workspaces
日本語で頼む。
デスクトップ、モバイル、エンタープライズ用 Firefox をダウンロード – Mozilla
https://www.mozilla.org/ja/firefox
昔はすごかった?
現在のブラウザのシェアを見るとChrome一択だけど、最近複数画面/複数タブでピクチャーインピクチャー機能を使用するためにFirefoxをインストールした。
Chromeでも拡張機能をインストールすればピクチャーインピクチャー機能を使用できるけれどマルチウインドウには対応していない。
https://chromewebstore.google.com/detail/picture-in-picture-extens/hkgfoiooedgoejojocmhlaklaeopbecg
https://github.com/GoogleChromeLabs/picture-in-picture-chrome-extension/issues/19
マルチウインドウに対応するには大きな改修が必要らしい。データフォルダを用意して起動時に指定してやれば複数画面で使用できるみたいなことが書かれている?
Firefox ならマルチウインドウで使用できる。
https://support.mozilla.org/ja/kb/about-picture-picture-firefox
動画にレイヤーが重ねられていてアクセサリーなどが配置されている場合は、ピクチャーインピクチャーのボタンが押せないので、Ctrl + Shift + ] キーのキーボードショートカットを使用している。
追記:アドレスバーにもアイコンが用意されている。
アドレスバーでのサイト内検索機能が使いづらい。ブックマークでタグを設定してURLに「%s」を埋め込んでやれば検索できそうではあるけどChromeのアドレスバー/オムニバーの方がいい感じ。
Firefoxでも過去の機能を復活させればショートカット+tab/spaceでサイト内検索できる模様。
https://stackoverflow.com/questions/77219507/how-do-i-add-a-custom-search-engine-to-firefox-desktop
Re-enable a feature from the past
検索エンジン名 | 別名 | URL |
Amazon Prime Video | pv | https://www.amazon.co.jp/s?k=%s&i=instant-video |
Netflix | nf | https://www.netflix.com/search?q=%s |
YouTube | yt | https://www.youtube.com/results?search_query=%s |
Wikipedia | wp | https://ja.wikipedia.org/w/index.php?search=%s |
とりあえず4つ登録した。
あとは、Firefoxは標準機能でフォントの指定を強制できるのはいいけどレンダリング結果が汚い。フォントがにじむの何とかならないのか調査中…
Chromeの画面なのかFirefoxの画面なのかぱっと見で区別できないので、ブラウザの「テーマ」を初めて設定してみた。
しばらくオレンジ色で使ってみる。
https://ja.wikipedia.org/wiki/フォクすけ
フォクすけ、ぐぐっても古い情報しかヒットしないですね… 終わったんですね。
https://ja.wikipedia.org/wiki/ブラウザ戦争
ブラウザ戦争は死語ですか?そうですかー
https://www.stefanjudis.com/snippets/how-to-unregister-all-installed-service-workers-in-chrome
サービスワーカーは、基本的にウェブアプリケーション、ブラウザー、そして(もし繋がっていれば)ネットワークの間に介在するプロキシサーバーのように振る舞います。これは、よりよいオフラインの操作性を可能にするように意図されており、ネットワークのリクエストに介在してネットワークの使用可否の状況に基づいて適切な対応を取ったり、サーバー上にある資産を更新したりします。また、プッシュ通知やバックグラウンド同期の API 群へのアクセスもできるようになります。
https://developer.mozilla.org/ja/docs/Web/API/Service_Worker_API
よりよいオフラインの操作性?そんなのはいらん。
chrome://serviceworker-internals/→登録されているサービスワーカーが表示される
$$('.unregister').forEach(b => b.click())→サービスワーカーが削除される
他のタブで開いているサイトや使っている拡張機能のサービスワーカーは削除されない。
たまにゴミ掃除したくなるけどサービスワーカーを削除しても体感できるようなパフォーマンス改善が見込めるわけではないので悪しからずご了承くださいませ…
https://learn.microsoft.com/ja-jp/azure/spring-apps/quickstart-deploy-restful-api-app
実際にデプロイしたURLがこれ。
https://deploy-test-simple-todo-api.azuremicroservices.io/swagger-ui/index.html (※削除済)
便利なようで便利じゃないやつ多すぎて草。
Raspberry Pi がいいね。
https://learn.microsoft.com/ja-jp/azure/spring-apps/quickstart-deploy-web-app
実際にデプロイしたURLがこれ。
https://deploy-test-simple-todo-web.azuremicroservices.io/ (※削除済)
サンプルアプリひどい。リロードすると画面がw
Azure Spring Apps と Azure Database for PostgreSQL が高すぎて草。期間限定の無料クレジット200ドルがなければやってみることもしないだろうなぁ…
Raspberry Pi がいいね。
ひげおやじ物語
https://static-web.kozawa.tokyo/ ※削除済
無料枠でAzure DevOpsのパイプラインからAzure Static Web Appsにデプロイしようとすると以下のエラーとなる。
[error]No hosted parallelism has been purchased or granted. To request a free parallelism grant, please fill out the following form https://aka.ms/azpipelines-parallelism-request
エラーメッセージのURLからリクエストを投げても音沙汰なし。
「Please consider that it could take 2-3 business days to proceed the request. We are working on improving this process at the moment. Sorry for the inconvenience.」
と書かれているけれど1週間経過しても権限付与してもらえない。
Azure DevOpsは諦めてGitHubのリポジトリからデプロイした。SSHキーの生成や設定が必要になった。
ひげおやじ物語
https://blob-storage.kozawa.tokyo/ ※削除済
Azure Blob Storageの静的Webサイトでカスタムドメインを使用する場合は、「Front Door と CDN」でドメインを認証してプライベートエンドポイント接続してやらないとhttpsでアクセスができない。めんどくさい作業が発生した。
カスタム ドメインを Azure Blob Storage エンドポイントにマップする
https://learn.microsoft.com/ja-jp/azure/storage/blobs/storage-custom-domain-name
Static Web Apps、Blob Storage、どちらも画面ポチポチでサーバ証明書を生成してくれるのは便利だけど、初めてやってみると何かしら引っかかるやん。しかも、Front Door、Storage、Networkで課金が発生している。Front Door高いな。結局、無料枠だけでは難しい気がする。
Raspberry Pi がいいね。
備忘:
SVGファイルでテキスト1文字を綺麗に回転させることはできない。Androidで描画が欠落することを確認した。Windowsは知らん。テキストだけで完結したいけど無理そう…
<svg version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
height="50px"
width="50px"
viewBox="0 0 50 50">
<style>
#typhoon {
dominant-baseline: text-before-edge;
font-size: xxx-large;
}
</style>
<animateTransform
xlink:href="#typhoon"
attributeName="transform"
type="rotate"
from="0 25 25"
to="360 25 25"
dur="5s"
repeatCount="indefinite"
/>
<text id="typhoon">🌀</text>
</svg>
「🌀」という1文字の回転中心座標を正確に指定することは、環境やフォントによって異なるため、困難です。JSを使用するとSVGファイルをimgタグで埋め込むことができなくなります。さらに、SVGファイル内ではCSS/@keyframesが正常に動作しないことがあります。
この問題に対する直接的な解決策は限られているようですが、一般的なアプローチとしては、素直に画像を回転させる方法が考えられます。回転したいテキストやシンボルを画像として作成し、必要な角度で回転させることで、一貫した表示が可能です。
また、回転が必要なテキストやシンボルを固定された角度で使用する場合、事前に回転済みの画像を用意しておくことも考えられます。
状況によって最適な方法が異なるかもしれませんが、回転の正確さやクロスブラウザの互換性を考慮しながら、適切な方法を選択することが重要です。
<text
id="typhoon"
x="50%"
y="50%"
text-anchor="middle"
dominant-baseline="central"
>🌀</text>
テキストの中心座標を求めるのではなく、テキストを上下左右中央に揃えても、結局固定値のハードコーディングが残存してしまい、フォントサイズが変わると描画が欠落する可能性が考えられます。
むずかしいなあ…
常識の範囲内で、自由につかってください(2回目)
備忘:
常識の範囲内で、自由につかってください①
https://twitter.com/JP_GHIBLI/status/1685606160580198400
常識の範囲内で、自由につかってください②
https://twitter.com/JP_GHIBLI/status/1685606411752108033
常識の範囲内で、自由につかってください③
https://twitter.com/JP_GHIBLI/status/1685606915164749824
Aは、「常識の範囲内で、自由につかってください③」ツイートに添付されていたオリジナルファイル。Bは、Aをファイル変換サイトで変換したファイル。Cは、「常識の範囲内で、自由につかってください①②」に添付されている画像ファイルから作成したファイル。背景透過済み。クリックイベントを実装済み。マウスポインターが矢印のままだ今気づいた。軽微な不備1。
SVGファイルにはCSS/JSを内包できる。ページ内に同じSVGファイルを複数配置してもidの重複などは発生しない。JSのグローバル領域が汚染されることもない。画像として表示する場合は、普通に<img>タグが使えるが、JSを実行する場合は、<object>タグで埋め込む必要がある。
時を戻そう。日本がんばれ〜
ツール:Flourish https://flourish.studio/
データ:Bing AI
データ加工:Excel ピボットテーブル + XLOOKUP関数
新しいBingでデータを集めた。一括でデータ収集させてCSV形式でダウンロードしたいのだけど現状では無理っぽい。1年ごとに検索してExcelにコピペする手作業が発生してる。しかも、チャットのレスが遅い… 持ってないデータを検索して解析してゴニョゴニョしてて時間がかかるとは思うもののまだまだ使いづらい。
1990年と1991年はデータが見つからない。データも怪しいしランキング順位が大きく変わる年はグラフが正しく表示されない。逆再生したかっただけだからいいけど…
時間は戻らないらしいな。時間とは何か?それが問題だ?
正しそうなやつも貼っとく
ChatGPTで生成したバグってるソースを手直した。変数の宣言がなかったり関数ごと欠損しているソースをデバッグするところからスタートして何となく動くようになったので投稿。初めテトリスを作ろうとしたけど、まともに動かない状態から抜け出せずに挫折… オセロは、テトリスの半分以下で300行程度。
備忘:CSSに一番苦戦した。borderとborder-radiusが同時に指定できない。border-radiusでコマを描くとborderで描いた枠線が欠落してしまう。最終的にはコマの白黒画像を用意してbackground-imageで読み込むように修正した。
ご参考:
プロンプト:オセロゲームのサンプルをHTML/CSS/JavaScriptで作成してください。
ChatGPT:はい、以下にHTML、CSS、JavaScriptで書かれたオセロゲームのサンプルを用意しました。
・・・
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>オセロゲーム</title>
<style>
.container {
display: flex;
flex-wrap: wrap;
width: 320px;
height: 320px;
margin: 0 auto;
}
.cell {
width: 40px;
height: 40px;
background-color: green;
border: 1px solid black;
box-sizing: border-box;
cursor: pointer;
}
.cell.black {
background-color: black;
}
.cell.white {
background-color: white;
}
.turn {
margin-top: 10px;
text-align: center;
font-size: 20px;
}
.win {
margin-top: 10px;
text-align: center;
font-size: 30px;
font-weight: bold;
}
</style>
</head>
<body>
<div class="container"></div>
<div class="turn">黒の手番です</div>
<div class="win"></div>
<script>
const WHITE = 1;
const BLACK = -1;
const EMPTY = 0;
// ゲームボードのサイズ
const BOARD_SIZE = 8;
// ゲームボードの初期化
let board = [];
for (let i = 0; i < BOARD_SIZE; i++) {
board[i] = [];
for (let j = 0; j < BOARD_SIZE; j++) {
board[i][j] = 0;
}
}
board[3][3] = board[4][4] = 1;
board[3][4] = board[4][3] = -1;
// 現在のプレイヤー(1:黒, -1:白)
let currentPlayer = 1;
// 盤面を描画する関数
function drawBoard() {
const container = document.querySelector('.container');
container.innerHTML = '';
for (let i = 0; i < BOARD_SIZE; i++) {
for (let j = 0; j < BOARD_SIZE; j++) {
const cell = document.createElement('div');
cell.className = 'cell';
if (board[i][j] === 1) {
cell.classList.add('black');
} else if (board[i][j] === -1) {
cell.classList.add('white');
}
cell.dataset.row = i;
cell.dataset.col = j;
cell.addEventListener('click', handleClick);
container.appendChild(cell);
}
}
}
drawBoard();
// クリックイベントのハンドラー関数
function handleClick(event) {
const row = parseInt(event.target.dataset.row);
const col = parseInt(event.target.dataset.col);
if (board[row][col] !== 0) {
return;
}
const directions = [
{ x: 0, y: -1 }, // 上
{
x: 0, y
1 }, // 下
{ x: -1, y: 0 }, // 左
{ x: 1, y: 0 }, // 右
{ x: -1, y: -1 }, // 左上
{ x: 1, y: -1 }, // 右上
{ x: -1, y: 1 }, // 左下
{ x: 1, y: 1 } // 右下
];
let canPlace = false;
for (let d = 0; d < directions.length; d++) {
const dx = directions[d].x;
const dy = directions[d].y;
let x = row + dx;
let y = col + dy;
let flipCount = 0;
while (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE) {
if (board[x][y] === -currentPlayer) {
flipCount++;
x += dx;
y += dy;
} else if (board[x][y] === currentPlayer) {
if (flipCount > 0) {
canPlace = true;
}
break;
} else {
break;
}
}
if (canPlace) {
break;
}
}
if (!canPlace) {
return;
}
board[row][col] = currentPlayer;
event.target.classList.add(currentPlayer === 1 ? 'black' : 'white');
event.target.removeEventListener('click', handleClick);
flipStones(row, col);
currentPlayer = -currentPlayer;
const turnEl = document.querySelector('.turn');
turnEl.textContent = ${ currentPlayer === 1 ? '黒' : '白' } の手番です;
if (!hasPlaceableCell()) {
currentPlayer = -currentPlayer;
if (!hasPlaceableCell()) {
const winEl = document.querySelector('.win');
const blackCount = countStones(1);
const whiteCount = countStones(-1);
if (blackCount > whiteCount) {
winEl.textContent = '黒の勝ち!';
} else if (blackCount < whiteCount) {
winEl.textContent = '白の勝ち!';
} else {
winEl.textContent = '引き分け!';
}
} else {
turnEl.textContent = ${ currentPlayer === 1 ? '黒' : '白' } はパスです;
}
}
}
// 石を返す関数
function flipStones(row, col) {
const directions = [
{ x: 0, y: -1 }, // 上
{ x: 0, y: 1 }, // 下
{ x: -1, y: 0 }, // 左
{ x: 1, y: 0 }, // 右
{ x: -1, y: -1 }, // 左上
{ x: 1, y: -1 }, // 右上
{ x: -1, y: 1 }, // 左下
{ x: 1, y: 1 } // 右下
];
for (let d = 0; d < directions.length; d++) {
const dx = directions[d].x;
const dy = directions[d].y;
let x = row + dx;
let y = col + dy;
let flipCount = 0;
while (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE) {
if (board[x][y] === -currentPlayer) {
flipCount++;
x += dx;
y += dy;
} else if (board[x][y] === currentPlayer) {
if (flipCount > 0) {
for (let i = 1; i <= flipCount; i++) {
const flipX = row + i * dx;
const flipY = col + i * dy;
board[flipX][flipY] = currentPlayer;
const flipEl = document.querySelector(`[data-row="${flipX}"][data-col="${flipY}"]`);
flipEl.classList.remove(currentPlayer === 1 ? 'white' : 'black');
flipEl.classList.add(currentPlayer === 1 ? 'black' : 'white');
}
}
break;
} else {
break;
}
}
}
}
// 置けるセルがあるかどうかを判定する関数
function hasPlaceableCell() {
for (let i = 0; i < BOARD_SIZE; i++) {
for (let j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] === 0 && canPlaceStone(i, j)) {
return true;
}
}
}
return false;
}
// 石の数を数える関数
function countStones(player) {
let count = 0;
for (let i = 0; i < BOARD_SIZE; i++) {
for (let j = 0; j < BOARD_SIZE; j++) {
if (board[i][j] === player) {
count++;
}
}
}
return count;
}
// ゲームをリセットする関数
function resetGame() {
board = initializeBoard();
currentPlayer = 1;
const cells = document.querySelectorAll('.cell');
for (let i = 0; i < cells.length; i++) {
cells[i].classList.remove('black', 'white');
cells[i].addEventListener('click', handleClick);
}
const turnEl = document.querySelector('.turn');
turnEl.textContent = '黒の手番です';
const winEl = document.querySelector('.win');
winEl.textContent = '';
}
function canPlaceStone(row, col, color) {
if (board[row][col] !== EMPTY) {
return false;
}
for (let drow = -1; drow <= 1; drow++) {
for (let dcol = -1; dcol <= 1; dcol++) {
if (drow === 0 && dcol === 0) {
continue;
}
let r = row + drow;
let c = col + dcol;
let flipped = false;
while (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE) {
if (board[r][c] === EMPTY) {
break;
}
if (board[r][c] === color) {
if (flipped) {
return true;
} else {
break;
}
}
flipped = true;
r += drow;
c += dcol;
}
}
}
return false;
}
function initializeBoard() {
for (let row = 0; row < BOARD_SIZE; row++) {
for (let col = 0; col < BOARD_SIZE; col++) {
const cell = document.querySelector(`.cell[data-row="${row}"][data-col="${col}"]`);
cell.classList.remove('black', 'white');
if ((row === 3 && col === 3) || (row === 4 && col === 4)) {
cell.classList.add('white');
board[row][col] = WHITE;
} else if ((row === 3 && col === 4) || (row === 4 && col === 3)) {
cell.classList.add('black');
board[row][col] = BLACK;
} else {
board[row][col] = EMPTY;
}
}
}
}
// 初期化
let board = initializeBoard();
let currentPlayer = 1;
const cells = document.querySelectorAll('.cell');
for (let i = 0; i < cells.length; i++) {
cells[i].addEventListener('click', handleClick);
}
const resetBtn = document.querySelector('.reset');
resetBtn.addEventListener('click', resetGame);
const turnEl = document.querySelector('.turn');
turnEl.textContent = '黒の手番です';
}) ();
</script>
</body>
</html>
※インデントなど整形済み