Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,6 @@ Thumbs.db
# Vite
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

# prisma
/generated/
3 changes: 3 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ yarn.lock
bun.lock
bun.lockb

# generated files
/generated

# Miscellaneous
/static/
91 changes: 91 additions & 0 deletions bun.lock

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite dev",
"dev": "bun --env-file=.env vite dev",
"build": "vite build",
"preview": "vite preview",
"prepare": "svelte-kit sync; lefthook install; true",
"prepare": "svelte-kit sync; prisma generate; lefthook install; true",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"format": "prettier --write .",
Expand All @@ -29,11 +29,16 @@
"lefthook": "^1.13.6",
"prettier": "^3.6.2",
"prettier-plugin-svelte": "^3.4.0",
"prisma": "^6.19.0",
"svelte": "^5.39.5",
"svelte-check": "^4.3.2",
"tailwindcss": "^4.1.14",
"typescript": "^5.9.2",
"typescript-eslint": "^8.44.1",
"vite": "^7.1.7"
},
"dependencies": {
"@prisma/adapter-pg": "^6.19.0",
"@prisma/client": "^6.19.0"
}
}
3 changes: 3 additions & 0 deletions prisma/migrations/migration_lock.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Please do not edit this file manually
# It should be added in your version-control system (e.g., Git)
provider = "postgresql"
22 changes: 22 additions & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// This is your Prisma schema file,
// learn more about it in the docs: https://pris.ly/d/prisma-schema

// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions?
// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init

generator client {
provider = "prisma-client-js"
output = "../generated/prisma"
engineType = "client" // no Rust engine
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model BoardState {
id Int @id @default(autoincrement())
createdAt DateTime @default(now())
boardData Json
}
4 changes: 3 additions & 1 deletion src/iframe/life-game.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ <h1>
<button id="sizeChangeButton">サイズ変更</button>

<table id="game-board" style="border-collapse: collapse"></table>
<button id="randombutton">RANDOM</button>      
<button id="randombutton">RANDOM</button>
<button id="resetbutton">RESET</button>
<button id="saveButton">DBに保存</button>
<button id="loadButton">DBから読込</button>
<div id="pattern-button-container"></div>
<script src="./life-game.js"></script>
</body>
Expand Down
30 changes: 30 additions & 0 deletions src/iframe/life-game.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ const generation = document.getElementById("generation"); //世代を表す文
const randomButton = document.getElementById("randombutton");
const resetButton = document.getElementById("resetbutton");
const sizeChangeButton = document.getElementById("sizeChangeButton");
const saveButton = document.getElementById("saveButton");
const loadButton = document.getElementById("loadButton");
//サイズ入力欄
const sizeInput = document.getElementById("sizeInput");
const sizeLabel = document.getElementById("sizeLabel");
Expand Down Expand Up @@ -187,3 +189,31 @@ sizeChangeButton.onclick = () => {
stop();
updatePatternButtons();
};

saveButton.onclick = async () => {
console.log("保存ボタンが押されました");
window.parent.postMessage({ type: "save_board", data: board }, "*");
};

/**
* 「DBから読込」ボタンが押された時の処理
*/
loadButton.onclick = async () => {
console.log("読込ボタンが押されました");
window.parent.postMessage({ type: "request:load_board" }, "*");
};

on.load_board = (loadedBoard) => {
console.log("on.load_board");
//
// 取得したデータで、現在の盤面(board)を上書きする
//
board = loadedBoard;

//
// 画面を更新し、ゲームの状態をリセットする
//
renderBoard(); // 新しい盤面を画面に描画
generationChange(0); // 世代カウントをリセット
stop(); //
};
8 changes: 8 additions & 0 deletions src/lib/prisma.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { PrismaPg } from "@prisma/adapter-pg";
import { PrismaClient } from "../../generated/prisma/client.ts";

export const prisma = new PrismaClient({
adapter: new PrismaPg({
connectionString: process.env.DATABASE_URL,
}),
});
29 changes: 29 additions & 0 deletions src/routes/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import * as icons from "$lib/icons/index.ts";
import patterns from "$lib/board-templates";
import { onMount } from "svelte";
import { loadBoard, saveBoard } from "./api.ts";

let code = $state(lifeGameJS);

Expand Down Expand Up @@ -40,6 +41,34 @@
function sendEvent(event: string, message?: unknown) {
preview_iframe?.contentWindow?.postMessage({ type: event, data: message }, "*");
}

onMount(() => {
const handler = async (event: MessageEvent<unknown>) => {
console.log("handler call");
const data = event.data as
| { type: "unknown event" }
| { type: "save_board"; data: boolean[][] }
| { type: "request:load_board" };
if (data.type === "save_board") {
console.log("board saved!");
await saveBoard(data.data);
return;
}

if (data.type === "request:load_board") {
console.log("loaded board");
const board = await loadBoard();
if (board) {
sendEvent("load_board", board);
alert("盤面を読み込みました!");
}
return;
}
};

window.addEventListener("message", handler);
return () => window.removeEventListener("message", handler);
});
</script>

<div class="navbar bg-[#E0E0E0] shadow-sm">
Expand Down
49 changes: 49 additions & 0 deletions src/routes/api.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
export async function saveBoard(board: boolean[][]) {
try {
// Phase 1 で作ったAPI(/api/board)に、
// 'POST' メソッドで現在の盤面(board)をJSON形式で送信
const response = await fetch("/api/board", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(board), //
});

if (!response.ok) {
// サーバーがエラーを返した場合
throw new Error("サーバーとの通信に失敗しました。");
}

alert("盤面を保存しました!");
} catch (err) {
console.error("保存エラー:", err);
alert("保存に失敗しました。");
}
}

export async function loadBoard(): Promise<boolean[][] | undefined> {
try {
// Phase 1 で作ったAPI(/api/board)に、
// 'GET' メソッド(デフォルト)でデータを要求
const response = await fetch("/api/board");

if (!response.ok) {
if (response.status === 404) {
// Phase 1 のAPIで、データが1件もない場合に404を返すようにしたため
throw new Error("保存されているデータがありません。");
} else {
throw new Error("サーバーとの通信に失敗しました。");
}
}

// サーバーから返ってきたJSONデータを取得
const loadedBoard = await response.json();

console.log("fetched board:", loadedBoard);
return loadedBoard as boolean[][]; // TODO: add proper types
} catch (err) {
console.error("読込エラー:", err);
alert("読み込みに失敗しました。");
}
}
34 changes: 34 additions & 0 deletions src/routes/api/board/+server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { json } from "@sveltejs/kit";
import { prisma } from "@/lib/prisma.server.ts";

// POSTリクエスト(=保存)の処理
export async function POST({ request }) {
const boardData = await request.json(); // 送られてきた盤面データを取得

// データベースに新しいレコードとして保存
const newState = await prisma.boardState.create({
data: {
boardData: boardData, // boardData カラムに JSON データを保存
},
});

return json(newState, { status: 201 }); // 保存成功を通知
}

// GETリクエスト(=読み込み)の処理
export async function GET() {
// データベースから一番「最後」に保存されたデータを1件だけ探す
const latestState = await prisma.boardState.findFirst({
orderBy: {
createdAt: "desc", // 作成日時(createdAt)の降順(desc)で並び替え
},
});

if (!latestState) {
// もしデータがなければ、エラーではなく「何もない」ことを通知
return json({ message: "No state found" }, { status: 404 });
}

// 見つかった盤面データを返す
return json(latestState.boardData);
}
Loading