<template>
	<MkStickyContainer>
		<template #header>
			<MkPageHeader :actions="[]" :tabs="[]" :display-back-button="true" />
		</template>
		<MkSpacer :content-max="940">
			<div class="spec">
				<section class="panel hero">
					<div>
						<h1>Lua4Frozen 仕様書</h1>
						<p>
							Lua4Frozen は、FrozenFriendsYume の投稿やブラウザ上のテストランナーで
							Lua プログラムを実行するためのサンドボックス実行環境です。
						</p>
					</div>
					<MkA to="/lua4frozen/test" class="testLink">
						<i class="ph-play ph-bold"></i>
						<span>ブラウザで試す</span>
					</MkA>
				</section>

				<section class="panel">
					<h2>実行方法</h2>
					<ul>
						<li>投稿で公開またはホーム公開にし、本文に <code>#Lua4Frozen</code> を含めます。</li>
						<li><code>.lua</code> ファイルを 1 つ以上添付すると Lua4Frozen トップに掲載されます。</li>
						<li>投稿本文の 1 行目はタイトル、2 行目以降は説明として表示されます。</li>
						<li>アップロードなしで試す場合は <MkA to="/lua4frozen/test">テストランナー</MkA> を使います。</li>
					</ul>
				</section>

				<section class="panel">
					<h2>サンドボックス</h2>
					<ul>
						<li><code>io</code>, <code>os</code>, <code>package</code>, <code>require</code>, <code>debug</code>, <code>dofile</code>, <code>loadfile</code> は使えません。</li>
						<li>Lua から外部ネットワークへ直接通信する API は公開されません。</li>
						<li>起動時と各フレームのコールバックには命令数制限があります。</li>
						<li>HTML 出力は危険なタグやイベント属性、外部 URL を除去してから表示します。</li>
					</ul>
				</section>

				<section class="panel">
					<h2>基本構造</h2>
					<p>
						初期化処理はファイル読み込み時に実行されます。継続的な処理は
						<code>ffy.on_update</code> と <code>ffy.on_draw</code> に関数を登録します。
					</p>
					<pre><code>x = 100

function update(dt)
  x = x + 60 * dt
end

function draw()
  ffy.gfx.clear("#111827")
  ffy.gfx.color("#7dd3fc")
  ffy.gfx.circle(x, 120, 24)
end

ffy.on_update(update)
ffy.on_draw(draw)</code></pre>
				</section>

				<section class="panel">
					<h2>Canvas API</h2>
					<ul>
						<li><code>ffy.gfx.size()</code>: Canvas の幅と高さを返します。</li>
						<li><code>ffy.gfx.clear(color)</code>: 画面を消去し、色が指定されていれば塗ります。</li>
						<li><code>ffy.gfx.color(color)</code>: 塗りと線の色を設定します。</li>
						<li><code>ffy.gfx.rect(x, y, w, h, fill)</code>: 矩形を描画します。<code>fill</code> が false なら線のみです。</li>
						<li><code>ffy.gfx.circle(x, y, r, fill)</code>: 円を描画します。</li>
						<li><code>ffy.gfx.line(x1, y1, x2, y2)</code>: 線を描画します。</li>
						<li><code>ffy.gfx.text(text, x, y, size)</code>: テキストを描画します。</li>
					</ul>
					<pre><code>w, h = ffy.gfx.size()
ffy.gfx.clear("#20242a")
ffy.gfx.color("#8bd5ff")
ffy.gfx.rect(40, 40, 160, 80)
ffy.gfx.circle(w - 100, 90, 40, false)
ffy.gfx.line(10, 10, w - 10, h - 10)
ffy.gfx.text("Hello Frozen", 40, 180, 24)</code></pre>
				</section>

				<section class="panel">
					<h2>SVG API</h2>
					<p>
						<code>ffy.svg</code> は Canvas の 1 つ上のレイヤーに SVG を表示します。
						描画済み Canvas はそのまま残り、SVG は <code>ffy.svg.clear()</code> で消去できます。
					</p>
					<ul>
						<li><code>ffy.svg.set(markup)</code>: SVG 全体、または SVG 要素断片を表示します。</li>
						<li><code>ffy.svg.clear()</code>: SVG レイヤーを消去します。</li>
					</ul>
					<pre><code>function draw()
  ffy.gfx.clear("#111827")
  ffy.svg.set('&lt;circle cx="400" cy="225" r="80" fill="#f472b6" opacity="0.75" /&gt;')
end

ffy.on_draw(draw)</code></pre>
				</section>

				<section class="panel">
					<h2>入力 API</h2>
					<p>
						Canvas にフォーカスがある間、キーボードとマウスの状態を取得できます。
						キー名は小文字で比較されます。
					</p>
					<pre><code>if ffy.input.key("arrowleft") then
  x = x - 2
end

mouse = ffy.input.mouse()
if mouse.down then
  ffy.gfx.circle(mouse.x, mouse.y, 8)
end</code></pre>
				</section>

				<section class="panel">
					<h2>HTML と音声</h2>
					<p>
						Canvas の下に簡単な HTML を出力できます。音声はユーザー操作で実行された場合に
						ブラウザの許可範囲内で鳴ります。
					</p>
					<pre><code>ffy.html.set("&lt;b&gt;Status&lt;/b&gt; Ready")
ffy.audio.beep(440, 0.12)</code></pre>
				</section>

				<section class="panel">
					<h2>ファイル I/O</h2>
					<p>
						添付ファイルとテストランナーの仮想ファイルは読み取り専用です。
						実行ユーザーの Drive の小さなテキスト系ファイルを読み込む場合と、
						<code>ffy.fs.write</code> で Drive にコピーを保存する場合は、実行ユーザーの確認が必要です。
					</p>
					<pre><code>names = ffy.fs.list()
for i, name in ipairs(names) do
  print(name)
end

score = tonumber(ffy.fs.read("score.txt")) or 0
score = score + 10
ffy.fs.write("score.txt", tostring(score))</code></pre>
				</section>

				<section class="panel">
					<h2>例: クリックで音を鳴らす</h2>
					<pre><code>was_down = false

function draw()
  mouse = ffy.input.mouse()
  ffy.gfx.clear("#0f172a")
  ffy.gfx.color(mouse.down and "#fda4af" or "#93c5fd")
  ffy.gfx.circle(mouse.x, mouse.y, mouse.down and 30 or 14)

  if mouse.down and not was_down then
    ffy.audio.beep(660, 0.08)
  end
  was_down = mouse.down
end

ffy.on_draw(draw)</code></pre>
				</section>

				<section class="panel">
					<h2>例: 仮想ファイルを使うメモ</h2>
					<pre><code>memo = ffy.fs.read("memo.txt")
if memo == "" then
  memo = "初回起動"
  ffy.fs.write("memo.txt", memo)
end

function draw()
  ffy.gfx.clear("#111827")
  ffy.gfx.color("#fef3c7")
  ffy.gfx.text("memo.txt: " .. memo, 40, 80, 24)
  ffy.html.set("テストランナーでは左下の Virtual files に保存されます。")
end

ffy.on_draw(draw)</code></pre>
				</section>
			</div>
		</MkSpacer>
	</MkStickyContainer>
</template>

<script lang="ts" setup>
import { computed } from "vue";
import MkA from "@/components/global/MkA.vue";
import { definePageMetadata } from "@/scripts/page-metadata";

definePageMetadata(
	computed(() => ({
		title: "Lua4Frozen 仕様書",
		icon: "ph-book-open ph-bold ph-lg",
	})),
);
</script>

<style lang="scss" scoped>
.spec {
	display: grid;
	gap: 16px;
	max-width: 100%;
	min-width: 0;
	overflow-x: hidden;
	box-sizing: border-box;
}

.panel {
	min-width: 0;
	max-width: 100%;
	box-sizing: border-box;
	padding: 18px;
	border-radius: 8px;
	background: var(--panel);
	border: solid 1px var(--divider);
}

.hero {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 16px;
	flex-wrap: wrap;
	min-width: 0;
}

.hero > div {
	min-width: 0;
	flex: 1 1 260px;
}

.testLink {
	display: inline-flex;
	align-items: center;
	gap: 8px;
	padding: 8px 12px;
	border-radius: 8px;
	color: var(--accent);
	background: var(--accentedBg);
	max-width: 100%;
	box-sizing: border-box;
}

h1,
h2 {
	margin: 0 0 10px;
	line-height: 1.3;
}

h1 {
	font-size: 1.45rem;
}

h2 {
	font-size: 1.1rem;
}

p,
ul {
	margin: 0;
	color: var(--fg);
	min-width: 0;
	overflow-wrap: anywhere;
}

p + pre,
ul + pre {
	margin-top: 12px;
}

ul {
	padding-left: 1.4em;
}

li + li {
	margin-top: 4px;
}

code,
pre {
	font-family: monospace;
	max-width: 100%;
	box-sizing: border-box;
}

code {
	padding: 0.1em 0.35em;
	border-radius: 4px;
	background: var(--bg);
	white-space: normal;
	overflow-wrap: anywhere;
}

pre {
	overflow: auto;
	margin: 0;
	padding: 12px;
	border-radius: 8px;
	background: var(--bg);
	border: solid 1px var(--divider);
	-webkit-overflow-scrolling: touch;
}

pre code {
	padding: 0;
	background: transparent;
	white-space: pre;
	overflow-wrap: normal;
	display: block;
	min-width: 0;
}

@media (max-width: 640px) {
	.panel {
		padding: 12px;
	}

	h1 {
		font-size: 1.25rem;
	}

	h2 {
		font-size: 1rem;
	}

	ul {
		padding-left: 1.1em;
	}

	pre {
		padding: 10px;
		font-size: 0.85rem;
	}
}
</style>
