From 9fa72d8c0fb5f1fe5ce507465cc7246a4a6b9a3b Mon Sep 17 00:00:00 2001 From: reodesureodesu Date: Tue, 3 Mar 2026 10:17:24 +0900 Subject: [PATCH 1/5] =?UTF-8?q?URL=E6=8B=9B=E5=BE=85=E3=81=A8=E3=82=AB?= =?UTF-8?q?=E3=82=B9=E3=82=BF=E3=83=A0=E3=82=B9=E3=82=BF=E3=83=B3=E3=83=97?= =?UTF-8?q?=E6=A9=9F=E8=83=BD=E3=82=92=E8=BF=BD=E5=8A=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.html | 1460 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 1267 insertions(+), 193 deletions(-) diff --git a/index.html b/index.html index fa8bab5..008eb01 100644 --- a/index.html +++ b/index.html @@ -7,7 +7,6 @@
-
LINE Ultimate
- -
-

ログインしてチャットを始めましょう

-

Google またはメールアドレスでサインインできます。

- - - - - -
+
+ + -
-
-
- -
- - +
+ + LINE Ultimate +
+ + avatar + +
+
+ +
通知ボックス

送信済み申請
+ +
+

ログインしてチャットを始めましょう

+

Google またはメールアドレスでサインインできます。

+
+ + + + + + +
+ +
+
+ + + 全体 +
+
+
+
+ +
+ +
+ + + +
+ +
+
-
- -
From 960342614e1a90b2ff5a837ccb74def6ca1ae787 Mon Sep 17 00:00:00 2001 From: reodesureodesu Date: Tue, 3 Mar 2026 16:16:30 +0900 Subject: [PATCH 2/5] Fix invite copy, retry queue UX, custom stamps, and screen size presets --- index.html | 165 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 148 insertions(+), 17 deletions(-) diff --git a/index.html b/index.html index 008eb01..6e4bca0 100644 --- a/index.html +++ b/index.html @@ -32,9 +32,11 @@ } #app { + --app-width: min(100%, 420px); + --app-height: min(92vh, 760px); position: relative; - width: min(100%, 420px); - height: min(92vh, 760px); + width: var(--app-width); + height: var(--app-height); background: var(--surface); border-radius: 28px; box-shadow: var(--shadow); @@ -70,8 +72,8 @@ transform: translateX(-100%); transition: transform 0.2s ease; z-index: 9; - display: grid; - grid-template-rows: auto auto auto minmax(170px, 1fr) auto auto; + display: flex; + flex-direction: column; gap: 14px; padding: 16px; overflow-y: auto; @@ -379,6 +381,21 @@ padding-top: 10px; } +.inline-btn-row { + display: flex; + gap: 8px; +} + +.inline-btn-row button, +.inline-btn-row input { + min-width: 0; +} + +#retryQueueBtn[disabled] { + opacity: .6; + cursor: not-allowed; +} + body.dark { --bg-gradient: linear-gradient(160deg, #0f172a 0%, #111827 60%, #1f2937 100%); --surface: #111827; @@ -423,6 +440,20 @@

フレンド

+
@@ -452,6 +483,7 @@

ログインしてチャットを始めましょう

+ 全体
@@ -522,10 +554,16 @@

ログインしてチャットを始めましょう

sentRequestList: document.getElementById("sentRequestList"), replyPreview: document.getElementById("replyPreview"), copyInviteLink: document.getElementById("copyInviteLink"), + copyInviteQuick: document.getElementById("copyInviteQuick"), acceptInviteLink: document.getElementById("acceptInviteLink"), customStampInput: document.getElementById("customStampInput"), addCustomStamp: document.getElementById("addCustomStamp"), - fontSizeSlider: document.getElementById("fontSizeSlider") + fontSizeSlider: document.getElementById("fontSizeSlider"), + retryQueueBtn: document.getElementById("retryQueueBtn"), + screenPreset: document.getElementById("screenPreset"), + customWidth: document.getElementById("customWidth"), + customHeight: document.getElementById("customHeight"), + applyScreenSize: document.getElementById("applyScreenSize") }; let allMessages = []; @@ -541,6 +579,12 @@

ログインしてチャットを始めましょう

let failedQueue = JSON.parse(localStorage.getItem("line_failed_queue") || "[]"); let customStamps = []; const inviteParams = new URLSearchParams(window.location.search); +const screenSizePresets = { + smartphone: { width: "min(100%, 420px)", height: "min(92vh, 760px)" }, + tablet: { width: "min(100%, 768px)", height: "min(92vh, 980px)" }, + pc: { width: "min(100%, 1100px)", height: "min(92vh, 900px)" } +}; +let screenSettings = JSON.parse(localStorage.getItem("line_screen_settings") || '{"preset":"smartphone","customWidth":420,"customHeight":760}'); const authErrorMessages = { "auth/email-already-in-use": "このメールアドレスはすでに登録されています。ログインをお試しください。", @@ -563,6 +607,48 @@

ログインしてチャットを始めましょう

const normalizeEmail = (email = "") => email.trim().toLowerCase(); const buildConversationId = (a, b) => [normalizeEmail(a), normalizeEmail(b)].sort().join("__"); +const updateRetryButton = () => { + ui.retryQueueBtn.textContent = `再送 (${failedQueue.length})`; + ui.retryQueueBtn.disabled = failedQueue.length === 0; +}; + +const copyText = async (text) => { + try { + await navigator.clipboard.writeText(text); + return true; + } catch (_error) { + const fallback = window.prompt("コピーできなかったため、下記URLを手動でコピーしてください", text); + return fallback !== null; + } +}; + +const applyScreenSize = (settings) => { + const preset = settings?.preset || "smartphone"; + if (preset === "custom") { + const width = Math.max(320, Math.min(1200, Number(settings.customWidth) || 420)); + const height = Math.max(480, Math.min(1200, Number(settings.customHeight) || 760)); + document.getElementById("app").style.setProperty("--app-width", `min(100%, ${width}px)`); + document.getElementById("app").style.setProperty("--app-height", `min(92vh, ${height}px)`); + } else { + const target = screenSizePresets[preset] || screenSizePresets.smartphone; + document.getElementById("app").style.setProperty("--app-width", target.width); + document.getElementById("app").style.setProperty("--app-height", target.height); + } +}; + +const syncScreenControls = () => { + const preset = screenSettings?.preset || "smartphone"; + ui.screenPreset.value = preset; + ui.customWidth.value = String(screenSettings?.customWidth || 420); + ui.customHeight.value = String(screenSettings?.customHeight || 760); + const enabled = preset === "custom"; + ui.customWidth.disabled = !enabled; + ui.customHeight.disabled = !enabled; +}; + +const persistScreenSettings = () => { + localStorage.setItem("line_screen_settings", JSON.stringify(screenSettings)); +}; const setStatus = (message, type="info") => { if (!message) { @@ -650,6 +736,7 @@

ログインしてチャットを始めましょう

const persistFailedQueue = () => { localStorage.setItem("line_failed_queue", JSON.stringify(failedQueue.slice(-50))); + updateRetryButton(); }; const enqueueFailed = (payload) => { @@ -683,8 +770,11 @@

ログインしてチャットを始めましょう

const retryFailedQueue = async () => { if (!failedQueue.length) { setStatus("再送キューは空です。", "info"); + updateRetryButton(); return; } + ui.retryQueueBtn.disabled = true; + ui.retryQueueBtn.textContent = "再送中..."; const remaining = []; for (const item of failedQueue) { const ok = await postMessage(item.payload, true); @@ -1093,9 +1183,12 @@

ログインしてチャットを始めましょう

ui.notifyPanel.style.display = ui.notifyPanel.style.display === "block" ? "none" : "block"; }; document.getElementById("closeNotify").onclick = () => { ui.notifyPanel.style.display = "none"; }; +applyScreenSize(screenSettings); +syncScreenControls(); +updateRetryButton(); renderStampBar(); ui.friendSearchInput.oninput = renderFriends; -document.getElementById("retryQueueBtn").onclick = retryFailedQueue; +ui.retryQueueBtn.onclick = retryFailedQueue; const processInviteFromUrl = async () => { if (!auth.currentUser) return; @@ -1126,30 +1219,68 @@

ログインしてチャットを始めましょう

ui.acceptInviteLink.style.display = "none"; }; -ui.copyInviteLink.onclick = async () => { - if (!auth.currentUser) return; +const copyInviteUrl = async () => { + if (!auth.currentUser) { + setStatus("URLコピーはログイン後に利用できます。", "error"); + return; + } const name = encodeURIComponent(userSettings.fixedName || auth.currentUser.displayName || "friend"); const url = `${location.origin}${location.pathname}?inviteUid=${auth.currentUser.uid}&inviteEmail=${encodeURIComponent(normalizeEmail(auth.currentUser.email || ""))}&inviteName=${name}`; - await navigator.clipboard.writeText(url); - setStatus("招待URLをコピーしました。", "success"); + const copied = await copyText(url); + setStatus(copied ? "招待URLをコピーしました。" : "招待URLのコピーに失敗しました。", copied ? "success" : "error"); }; +ui.copyInviteLink.onclick = copyInviteUrl; +ui.copyInviteQuick.onclick = copyInviteUrl; + ui.acceptInviteLink.onclick = processInviteFromUrl; ui.addCustomStamp.onclick = async () => { - if (!auth.currentUser || !ui.customStampInput.files[0]) return; - const dataUrl = await fileToDataUrl(ui.customStampInput.files[0]); - customStamps = [...customStamps, dataUrl].slice(-24); - await setDoc(doc(db, "users", auth.currentUser.uid), { customStamps }, { merge: true }); - ui.customStampInput.value = ""; - renderStampBar(); - setStatus("カスタムスタンプを追加しました。", "success"); + if (!auth.currentUser || !ui.customStampInput.files[0]) { + setStatus("追加するスタンプ画像を選択してください。", "error"); + return; + } + try { + const compressed = await compressImageFile(ui.customStampInput.files[0], 512, 0.76); + const dataUrl = await fileToDataUrl(compressed); + customStamps = [...customStamps, dataUrl].slice(-24); + await setDoc(doc(db, "users", auth.currentUser.uid), { customStamps }, { merge: true }); + ui.customStampInput.value = ""; + renderStampBar(); + setStatus("カスタムスタンプを追加しました。", "success"); + } catch (error) { + console.error("custom stamp failed", error); + setStatus("カスタムスタンプの追加に失敗しました。画像サイズを小さくして再試行してください。", "error"); + } }; ui.fontSizeSlider.oninput = (event) => { document.documentElement.style.setProperty("--msg-font-size", `${event.target.value}px`); }; +ui.screenPreset.onchange = () => { + const isCustom = ui.screenPreset.value === "custom"; + ui.customWidth.disabled = !isCustom; + ui.customHeight.disabled = !isCustom; +}; + +ui.applyScreenSize.onclick = () => { + const preset = ui.screenPreset.value; + const next = { + preset, + customWidth: Number(ui.customWidth.value) || 420, + customHeight: Number(ui.customHeight.value) || 760 + }; + if (preset === "custom" && (!ui.customWidth.value || !ui.customHeight.value)) { + setStatus("カスタムサイズの幅と高さを入力してください。", "error"); + return; + } + screenSettings = next; + applyScreenSize(screenSettings); + persistScreenSettings(); + setStatus(`画面サイズを${preset === "custom" ? "カスタム" : ui.screenPreset.options[ui.screenPreset.selectedIndex].text}に変更しました。`, "success"); +}; + document.getElementById("googleLogin").onclick = () => signInWithPopup(auth, googleProvider) .then(() => setStatus("Googleでログインしました。", "success")) .catch(showAuthError); From c24be41cc2dc5153c459c84e264764856c09aa1b Mon Sep 17 00:00:00 2001 From: reodesureodesu Date: Tue, 3 Mar 2026 18:13:22 +0900 Subject: [PATCH 3/5] Add mini game UI and PWA install support with editable app name --- icons/app-icon.svg | 12 +++++ index.html | 124 ++++++++++++++++++++++++++++++++++++++++++++- manifest.json | 18 +++++++ sw.js | 30 +++++++++++ 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 icons/app-icon.svg create mode 100644 manifest.json create mode 100644 sw.js diff --git a/icons/app-icon.svg b/icons/app-icon.svg new file mode 100644 index 0000000..6aa37ea --- /dev/null +++ b/icons/app-icon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/index.html b/index.html index 6e4bca0..36bb3cf 100644 --- a/index.html +++ b/index.html @@ -3,6 +3,10 @@ + + + + LINE Ultimate Stable