From 721195bd2e6741a1b39d45b2e01f33b86c8b6594 Mon Sep 17 00:00:00 2001
From: Your Name
Date: Fri, 20 Mar 2026 12:17:03 +0900
Subject: [PATCH 01/21] =?UTF-8?q?#001=20=E3=83=93=E3=83=AB=E3=83=89?=
=?UTF-8?q?=E8=A8=AD=E5=AE=9A=E3=81=AE=E6=9C=AC=E7=95=AA=E5=8C=96=20+=20?=
=?UTF-8?q?=E3=83=9D=E3=83=AA=E3=83=95=E3=82=A3=E3=83=AB=E9=99=A4=E5=8E=BB?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- webpack: mode production, minify, tree shaking 有効化, ソースマップ除去
- babel: IE11→モダンブラウザ, ESモジュール化, React production
- entry: 不要なポリフィル除去 (core-js, regenerator-runtime, jquery-binarytransport)
- EnvironmentPlugin NODE_ENV を production に修正
main.js: 108MB → 31MB / Score: 2 → 4
---
application/client/babel.config.js | 7 +++----
application/client/package.json | 2 +-
application/client/webpack.config.js | 23 +++++++++--------------
3 files changed, 13 insertions(+), 19 deletions(-)
diff --git a/application/client/babel.config.js b/application/client/babel.config.js
index c3c574591a..502104384a 100644
--- a/application/client/babel.config.js
+++ b/application/client/babel.config.js
@@ -4,16 +4,15 @@ module.exports = {
[
"@babel/preset-env",
{
- targets: "ie 11",
- corejs: "3",
- modules: "commonjs",
+ targets: "defaults",
+ modules: false,
useBuiltIns: false,
},
],
[
"@babel/preset-react",
{
- development: true,
+ development: false,
runtime: "automatic",
},
],
diff --git a/application/client/package.json b/application/client/package.json
index 9f8e80a6a8..af2d3a5156 100644
--- a/application/client/package.json
+++ b/application/client/package.json
@@ -5,7 +5,7 @@
"license": "MPL-2.0",
"author": "CyberAgent, Inc.",
"scripts": {
- "build": "NODE_ENV=development webpack",
+ "build": "NODE_ENV=production webpack",
"typecheck": "tsc"
},
"dependencies": {
diff --git a/application/client/webpack.config.js b/application/client/webpack.config.js
index 9fae72647f..0ee7565611 100644
--- a/application/client/webpack.config.js
+++ b/application/client/webpack.config.js
@@ -25,18 +25,15 @@ const config = {
],
static: [PUBLIC_PATH, UPLOAD_PATH],
},
- devtool: "inline-source-map",
+ devtool: false,
entry: {
main: [
- "core-js",
- "regenerator-runtime/runtime",
- "jquery-binarytransport",
path.resolve(SRC_PATH, "./index.css"),
path.resolve(SRC_PATH, "./buildinfo.ts"),
path.resolve(SRC_PATH, "./index.tsx"),
],
},
- mode: "none",
+ mode: "production",
module: {
rules: [
{
@@ -60,10 +57,9 @@ const config = {
},
output: {
chunkFilename: "scripts/chunk-[contenthash].js",
- chunkFormat: false,
filename: "scripts/[name].js",
path: DIST_PATH,
- publicPath: "auto",
+ publicPath: "/",
clean: true,
},
plugins: [
@@ -77,7 +73,7 @@ const config = {
BUILD_DATE: new Date().toISOString(),
// Heroku では SOURCE_VERSION 環境変数から commit hash を参照できます
COMMIT_HASH: process.env.SOURCE_VERSION || "",
- NODE_ENV: "development",
+ NODE_ENV: "production",
}),
new MiniCssExtractPlugin({
filename: "styles/[name].css",
@@ -128,14 +124,13 @@ const config = {
},
},
optimization: {
- minimize: false,
+ minimize: true,
splitChunks: false,
- concatenateModules: false,
- usedExports: false,
- providedExports: false,
- sideEffects: false,
+ concatenateModules: true,
+ usedExports: true,
+ providedExports: true,
+ sideEffects: true,
},
- cache: false,
ignoreWarnings: [
{
module: /@ffmpeg/,
From f744b7784a1f8cf4735e328224d7ae5d0e2f995d Mon Sep 17 00:00:00 2001
From: Your Name
Date: Fri, 20 Mar 2026 17:46:42 +0900
Subject: [PATCH 02/21] =?UTF-8?q?#005-008=20=E3=83=91=E3=83=95=E3=82=A9?=
=?UTF-8?q?=E3=83=BC=E3=83=9E=E3=83=B3=E3=82=B9=E7=BD=A0=E4=BF=AE=E6=AD=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- fetchers.ts: async:false 除去(メインスレッドブロック解消)
- index.tsx: window.load → DOMContentLoaded(描画開始を早期化)
- InfiniteScroll.tsx: 2^18回ループ削除 + passive:true
- AspectRatioBox.tsx: 500ms遅延削除 + passive:true
Score: 4 → 44 / TBT: 7,530ms → 430ms / CLS: 0.484 → 0
---
.../components/foundation/AspectRatioBox.tsx | 4 ++--
.../components/foundation/InfiniteScroll.tsx | 18 ++++--------------
application/client/src/index.tsx | 2 +-
application/client/src/utils/fetchers.ts | 4 ----
4 files changed, 7 insertions(+), 21 deletions(-)
diff --git a/application/client/src/components/foundation/AspectRatioBox.tsx b/application/client/src/components/foundation/AspectRatioBox.tsx
index 0ae891963c..83b67f7fe0 100644
--- a/application/client/src/components/foundation/AspectRatioBox.tsx
+++ b/application/client/src/components/foundation/AspectRatioBox.tsx
@@ -19,10 +19,10 @@ export const AspectRatioBox = ({ aspectHeight, aspectWidth, children }: Props) =
const clientWidth = ref.current?.clientWidth ?? 0;
setClientHeight((clientWidth / aspectWidth) * aspectHeight);
}
- setTimeout(() => calcStyle(), 500);
+ calcStyle();
// ウィンドウサイズが変わるたびに計算する
- window.addEventListener("resize", calcStyle, { passive: false });
+ window.addEventListener("resize", calcStyle, { passive: true });
return () => {
window.removeEventListener("resize", calcStyle);
};
diff --git a/application/client/src/components/foundation/InfiniteScroll.tsx b/application/client/src/components/foundation/InfiniteScroll.tsx
index 408f24c107..0221fde640 100644
--- a/application/client/src/components/foundation/InfiniteScroll.tsx
+++ b/application/client/src/components/foundation/InfiniteScroll.tsx
@@ -13,14 +13,9 @@ export const InfiniteScroll = ({ children, fetchMore, items }: Props) => {
useEffect(() => {
const handler = () => {
- // 念の為 2の18乗 回、最下部かどうかを確認する
- const hasReached = Array.from(Array(2 ** 18), () => {
- return window.innerHeight + Math.ceil(window.scrollY) >= document.body.offsetHeight;
- }).every(Boolean);
+ const hasReached = window.innerHeight + Math.ceil(window.scrollY) >= document.body.offsetHeight;
- // 画面最下部にスクロールしたタイミングで、登録したハンドラを呼び出す
if (hasReached && !prevReachedRef.current) {
- // アイテムがないときは追加で読み込まない
if (latestItem !== undefined) {
fetchMore();
}
@@ -29,19 +24,14 @@ export const InfiniteScroll = ({ children, fetchMore, items }: Props) => {
prevReachedRef.current = hasReached;
};
- // 最初は実行されないので手動で呼び出す
prevReachedRef.current = false;
handler();
- document.addEventListener("wheel", handler, { passive: false });
- document.addEventListener("touchmove", handler, { passive: false });
- document.addEventListener("resize", handler, { passive: false });
- document.addEventListener("scroll", handler, { passive: false });
+ document.addEventListener("scroll", handler, { passive: true });
+ window.addEventListener("resize", handler, { passive: true });
return () => {
- document.removeEventListener("wheel", handler);
- document.removeEventListener("touchmove", handler);
- document.removeEventListener("resize", handler);
document.removeEventListener("scroll", handler);
+ window.removeEventListener("resize", handler);
};
}, [latestItem, fetchMore]);
diff --git a/application/client/src/index.tsx b/application/client/src/index.tsx
index b1833b0af3..871e45920e 100644
--- a/application/client/src/index.tsx
+++ b/application/client/src/index.tsx
@@ -5,7 +5,7 @@ import { BrowserRouter } from "react-router";
import { AppContainer } from "@web-speed-hackathon-2026/client/src/containers/AppContainer";
import { store } from "@web-speed-hackathon-2026/client/src/store";
-window.addEventListener("load", () => {
+document.addEventListener("DOMContentLoaded", () => {
createRoot(document.getElementById("app")!).render(
diff --git a/application/client/src/utils/fetchers.ts b/application/client/src/utils/fetchers.ts
index 92a14f408f..c4615071b6 100644
--- a/application/client/src/utils/fetchers.ts
+++ b/application/client/src/utils/fetchers.ts
@@ -3,7 +3,6 @@ import { gzip } from "pako";
export async function fetchBinary(url: string): Promise {
const result = await $.ajax({
- async: false,
dataType: "binary",
method: "GET",
responseType: "arraybuffer",
@@ -14,7 +13,6 @@ export async function fetchBinary(url: string): Promise {
export async function fetchJSON(url: string): Promise {
const result = await $.ajax({
- async: false,
dataType: "json",
method: "GET",
url,
@@ -24,7 +22,6 @@ export async function fetchJSON(url: string): Promise {
export async function sendFile(url: string, file: File): Promise {
const result = await $.ajax({
- async: false,
data: file,
dataType: "json",
headers: {
@@ -43,7 +40,6 @@ export async function sendJSON(url: string, data: object): Promise {
const compressed = gzip(uint8Array);
const result = await $.ajax({
- async: false,
data: compressed,
dataType: "json",
headers: {
From dce842a2aa46f53519185da2c878667f78df688f Mon Sep 17 00:00:00 2001
From: Your Name
Date: Fri, 20 Mar 2026 18:03:23 +0900
Subject: [PATCH 03/21] =?UTF-8?q?#002-003=20gzip=E5=9C=A7=E7=B8=AE=20+=20W?=
=?UTF-8?q?ASM/FFmpeg=E5=A4=96=E9=83=A8=E3=83=95=E3=82=A1=E3=82=A4?=
=?UTF-8?q?=E3=83=AB=E5=8C=96?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- サーバーにcompression middlewareを追加(gzip圧縮)
- Cache-Control: max-age=0, no-transform / Connection: close ヘッダーを除去
- webpack: asset/bytes → asset/resource でWASM/FFmpegを別ファイルに分離
- convert_image.ts: WASM URLをfetchしてUint8Arrayで渡す方式に変更
- load_ffmpeg.ts: Blob URL生成を廃止し直接URLを渡す方式に変更
- main.js 31MB → 12.3MB、FCP 160.6s → 66.0s (-59%)
---
application/client/src/utils/convert_image.ts | 5 +++--
application/client/src/utils/load_ffmpeg.ts | 8 ++-----
application/client/webpack.config.js | 5 ++++-
application/pnpm-lock.yaml | 22 +++++++++++++++----
application/server/package.json | 2 ++
application/server/src/app.ts | 11 +++-------
6 files changed, 32 insertions(+), 21 deletions(-)
diff --git a/application/client/src/utils/convert_image.ts b/application/client/src/utils/convert_image.ts
index 9fce086d9c..014fd31681 100644
--- a/application/client/src/utils/convert_image.ts
+++ b/application/client/src/utils/convert_image.ts
@@ -1,5 +1,5 @@
import { initializeImageMagick, ImageMagick, MagickFormat } from "@imagemagick/magick-wasm";
-import magickWasm from "@imagemagick/magick-wasm/magick.wasm?binary";
+import magickWasmUrl from "@imagemagick/magick-wasm/magick.wasm?binary";
import { dump, insert, ImageIFD } from "piexifjs";
interface Options {
@@ -7,7 +7,8 @@ interface Options {
}
export async function convertImage(file: File, options: Options): Promise {
- await initializeImageMagick(magickWasm);
+ const wasmBinary = await fetch(magickWasmUrl).then((r) => r.arrayBuffer());
+ await initializeImageMagick(new Uint8Array(wasmBinary));
const byteArray = new Uint8Array(await file.arrayBuffer());
diff --git a/application/client/src/utils/load_ffmpeg.ts b/application/client/src/utils/load_ffmpeg.ts
index f923a3d5a4..e40c942c07 100644
--- a/application/client/src/utils/load_ffmpeg.ts
+++ b/application/client/src/utils/load_ffmpeg.ts
@@ -4,12 +4,8 @@ export async function loadFFmpeg(): Promise {
const ffmpeg = new FFmpeg();
await ffmpeg.load({
- coreURL: await import("@ffmpeg/core?binary").then(({ default: b }) => {
- return URL.createObjectURL(new Blob([b], { type: "text/javascript" }));
- }),
- wasmURL: await import("@ffmpeg/core/wasm?binary").then(({ default: b }) => {
- return URL.createObjectURL(new Blob([b], { type: "application/wasm" }));
- }),
+ coreURL: (await import("@ffmpeg/core?binary")).default,
+ wasmURL: (await import("@ffmpeg/core/wasm?binary")).default,
});
return ffmpeg;
diff --git a/application/client/webpack.config.js b/application/client/webpack.config.js
index 0ee7565611..8c1157d93a 100644
--- a/application/client/webpack.config.js
+++ b/application/client/webpack.config.js
@@ -51,7 +51,10 @@ const config = {
},
{
resourceQuery: /binary/,
- type: "asset/bytes",
+ type: "asset/resource",
+ generator: {
+ filename: "assets/[hash][ext]",
+ },
},
],
},
diff --git a/application/pnpm-lock.yaml b/application/pnpm-lock.yaml
index 510570f5c9..142746e58d 100644
--- a/application/pnpm-lock.yaml
+++ b/application/pnpm-lock.yaml
@@ -62,7 +62,7 @@ importers:
version: 9.5.0
gifler:
specifier: github:themadcreator/gifler#v0.3.0
- version: https://codeload.github.com/themadcreator/gifler/tar.gz/c3259b071c7782f85d4928a5f03d0b378ed003b5
+ version: https://codeload.github.com/themadcreator/gifler/tar.gz/89484cb3db174c584a3138e89664f0167a7760c1
image-size:
specifier: 2.0.2
version: 2.0.2
@@ -268,6 +268,9 @@ importers:
'@tsconfig/strictest':
specifier: 2.0.8
version: 2.0.8
+ '@types/compression':
+ specifier: 1.8.1
+ version: 1.8.1
'@web-speed-hackathon-2026/server':
specifier: workspace:*
version: 'link:'
@@ -277,6 +280,9 @@ importers:
body-parser:
specifier: 2.2.0
version: 2.2.0
+ compression:
+ specifier: 1.8.1
+ version: 1.8.1
connect-history-api-fallback:
specifier: 2.0.0
version: 2.0.0
@@ -1637,6 +1643,9 @@ packages:
'@types/common-tags@1.8.4':
resolution: {integrity: sha512-S+1hLDJPjWNDhcGxsxEbepzaxWqURP/o+3cP4aa2w7yBXgdcmKGQtZzP8JbyfOd0m+33nh+8+kvxYE2UJtBDkg==}
+ '@types/compression@1.8.1':
+ resolution: {integrity: sha512-kCFuWS0ebDbmxs0AXYn6e2r2nrGAb5KwQhknjSPSPgJcGd8+HVSILlUyFhGqML2gk39HcG7D1ydW9/qpYkN00Q==}
+
'@types/connect-history-api-fallback@1.5.4':
resolution: {integrity: sha512-n6Cr2xS1h4uAulPRdlw6Jl6s1oG8KrVilPN2yUITEs+K48EzMJJ3W1xy8K5eWuFvjp3R74AOIGSmp2UfBJ8HFw==}
@@ -2667,8 +2676,8 @@ packages:
get-tsconfig@4.13.0:
resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==}
- gifler@https://codeload.github.com/themadcreator/gifler/tar.gz/c3259b071c7782f85d4928a5f03d0b378ed003b5:
- resolution: {tarball: https://codeload.github.com/themadcreator/gifler/tar.gz/c3259b071c7782f85d4928a5f03d0b378ed003b5}
+ gifler@https://codeload.github.com/themadcreator/gifler/tar.gz/89484cb3db174c584a3138e89664f0167a7760c1:
+ resolution: {tarball: https://codeload.github.com/themadcreator/gifler/tar.gz/89484cb3db174c584a3138e89664f0167a7760c1}
version: 0.3.0
github-from-package@0.0.0:
@@ -6003,6 +6012,11 @@ snapshots:
'@types/common-tags@1.8.4': {}
+ '@types/compression@1.8.1':
+ dependencies:
+ '@types/express': 5.0.3
+ '@types/node': 22.18.8
+
'@types/connect-history-api-fallback@1.5.4':
dependencies:
'@types/express-serve-static-core': 5.1.0
@@ -7161,7 +7175,7 @@ snapshots:
dependencies:
resolve-pkg-maps: 1.0.0
- gifler@https://codeload.github.com/themadcreator/gifler/tar.gz/c3259b071c7782f85d4928a5f03d0b378ed003b5:
+ gifler@https://codeload.github.com/themadcreator/gifler/tar.gz/89484cb3db174c584a3138e89664f0167a7760c1:
dependencies:
bluebird: 3.7.2
omggif: 1.0.10
diff --git a/application/server/package.json b/application/server/package.json
index 9482575df7..a5467c16ab 100644
--- a/application/server/package.json
+++ b/application/server/package.json
@@ -14,9 +14,11 @@
},
"dependencies": {
"@tsconfig/strictest": "2.0.8",
+ "@types/compression": "1.8.1",
"@web-speed-hackathon-2026/server": "workspace:*",
"bcrypt": "6.0.0",
"body-parser": "2.2.0",
+ "compression": "1.8.1",
"connect-history-api-fallback": "2.0.0",
"express": "5.1.0",
"express-session": "1.18.2",
diff --git a/application/server/src/app.ts b/application/server/src/app.ts
index 671fb424cc..cce288ae73 100644
--- a/application/server/src/app.ts
+++ b/application/server/src/app.ts
@@ -1,4 +1,5 @@
import bodyParser from "body-parser";
+import compression from "compression";
import Express from "express";
import { apiRouter } from "@web-speed-hackathon-2026/server/src/routes/api";
@@ -9,17 +10,11 @@ export const app = Express();
app.set("trust proxy", true);
+app.use(compression());
+
app.use(sessionMiddleware);
app.use(bodyParser.json());
app.use(bodyParser.raw({ limit: "10mb" }));
-app.use((_req, res, next) => {
- res.header({
- "Cache-Control": "max-age=0, no-transform",
- Connection: "close",
- });
- return next();
-});
-
app.use("/api/v1", apiRouter);
app.use(staticRouter);
From 6e8f76ecc26bd748ce6f64cf93f721335bde7eac Mon Sep 17 00:00:00 2001
From: Your Name
Date: Fri, 20 Mar 2026 22:45:06 +0900
Subject: [PATCH 04/21] =?UTF-8?q?#004=20=E9=87=8D=E9=87=8F=E3=83=A9?=
=?UTF-8?q?=E3=82=A4=E3=83=96=E3=83=A9=E3=83=AA=E9=99=A4=E5=8E=BB=20+=20?=
=?UTF-8?q?=E3=82=B3=E3=83=BC=E3=83=89=E5=88=86=E5=89=B2?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- moment.js → Intl.DateTimeFormat / Intl.RelativeTimeFormat に置換 (-0.67MB)
- lodash → ネイティブJS に置換 (-0.53MB)
- standardized-audio-context → ネイティブAudioContext に置換 (-0.52MB)
- CrokContainer / SearchContainer / NewPostModalContainer を React.lazy化
- web-llm / negaposi-analyzer-ja を dynamic import化
- main.js 11.89MB → 700KB (94%削減)、FCP 63.6s → 5.2s
---
.../direct_message/DirectMessageListPage.tsx | 14 ++++-
.../direct_message/DirectMessagePage.tsx | 3 +-
.../components/foundation/SoundWaveSVG.tsx | 35 ++++++-----
.../src/components/post/CommentItem.tsx | 6 +-
.../client/src/components/post/PostItem.tsx | 6 +-
.../src/components/timeline/TimelineItem.tsx | 5 +-
.../user_profile/UserProfileHeader.tsx | 5 +-
.../client/src/containers/AppContainer.tsx | 59 ++++++++++---------
application/client/src/utils/bm25_search.ts | 14 ++---
.../client/src/utils/create_translator.ts | 2 +-
.../client/src/utils/negaposi_analyzer.ts | 18 +++---
application/client/webpack.config.js | 1 -
12 files changed, 88 insertions(+), 80 deletions(-)
diff --git a/application/client/src/components/direct_message/DirectMessageListPage.tsx b/application/client/src/components/direct_message/DirectMessageListPage.tsx
index 5a373e918e..e5164c8318 100644
--- a/application/client/src/components/direct_message/DirectMessageListPage.tsx
+++ b/application/client/src/components/direct_message/DirectMessageListPage.tsx
@@ -1,4 +1,3 @@
-import moment from "moment";
import { useCallback, useEffect, useState } from "react";
import { Button } from "@web-speed-hackathon-2026/client/src/components/foundation/Button";
@@ -100,7 +99,18 @@ export const DirectMessageListPage = ({ activeUser, newDmModalId }: Props) => {
className="text-cax-text-subtle text-xs"
dateTime={lastMessage.createdAt}
>
- {moment(lastMessage.createdAt).locale("ja").fromNow()}
+ {(() => {
+ const diff = Date.now() - new Date(lastMessage.createdAt).getTime();
+ const rtf = new Intl.RelativeTimeFormat("ja", { numeric: "auto" });
+ const sec = Math.round(diff / 1000);
+ if (sec < 60) return rtf.format(-sec, "second");
+ const min = Math.round(diff / 60000);
+ if (min < 60) return rtf.format(-min, "minute");
+ const hr = Math.round(diff / 3600000);
+ if (hr < 24) return rtf.format(-hr, "hour");
+ const day = Math.round(diff / 86400000);
+ return rtf.format(-day, "day");
+ })()}
)}
diff --git a/application/client/src/components/direct_message/DirectMessagePage.tsx b/application/client/src/components/direct_message/DirectMessagePage.tsx
index 098c7d2894..4d896b43fb 100644
--- a/application/client/src/components/direct_message/DirectMessagePage.tsx
+++ b/application/client/src/components/direct_message/DirectMessagePage.tsx
@@ -1,5 +1,4 @@
import classNames from "classnames";
-import moment from "moment";
import {
ChangeEvent,
useCallback,
@@ -141,7 +140,7 @@ export const DirectMessagePage = ({
{isActiveUserSend && message.isRead && (
既読
diff --git a/application/client/src/components/foundation/SoundWaveSVG.tsx b/application/client/src/components/foundation/SoundWaveSVG.tsx
index d95e63164c..9ab8edea30 100644
--- a/application/client/src/components/foundation/SoundWaveSVG.tsx
+++ b/application/client/src/components/foundation/SoundWaveSVG.tsx
@@ -1,4 +1,3 @@
-import _ from "lodash";
import { useEffect, useRef, useState } from "react";
interface ParsedData {
@@ -9,21 +8,29 @@ interface ParsedData {
async function calculate(data: ArrayBuffer): Promise
{
const audioCtx = new AudioContext();
- // 音声をデコードする
const buffer = await audioCtx.decodeAudioData(data.slice(0));
- // 左の音声データの絶対値を取る
- const leftData = _.map(buffer.getChannelData(0), Math.abs);
- // 右の音声データの絶対値を取る
- const rightData = _.map(buffer.getChannelData(1), Math.abs);
+ const leftData = buffer.getChannelData(0);
+ const rightData = buffer.getChannelData(1);
- // 左右の音声データの平均を取る
- const normalized = _.map(_.zip(leftData, rightData), _.mean);
- // 100 個の chunk に分ける
- const chunks = _.chunk(normalized, Math.ceil(normalized.length / 100));
- // chunk ごとに平均を取る
- const peaks = _.map(chunks, _.mean);
- // chunk の平均の中から最大値を取る
- const max = _.max(peaks) ?? 0;
+ const len = leftData.length;
+ const normalized = new Float32Array(len);
+ for (let i = 0; i < len; i++) {
+ normalized[i] = (Math.abs(leftData[i]) + Math.abs(rightData[i])) / 2;
+ }
+
+ const chunkSize = Math.ceil(len / 100);
+ const peaks: number[] = [];
+ let max = 0;
+ for (let i = 0; i < len; i += chunkSize) {
+ const end = Math.min(i + chunkSize, len);
+ let sum = 0;
+ for (let j = i; j < end; j++) {
+ sum += normalized[j];
+ }
+ const mean = sum / (end - i);
+ peaks.push(mean);
+ if (mean > max) max = mean;
+ }
return { max, peaks };
}
diff --git a/application/client/src/components/post/CommentItem.tsx b/application/client/src/components/post/CommentItem.tsx
index cb5bd38bda..804bb53b2b 100644
--- a/application/client/src/components/post/CommentItem.tsx
+++ b/application/client/src/components/post/CommentItem.tsx
@@ -1,5 +1,3 @@
-import moment from "moment";
-
import { Link } from "@web-speed-hackathon-2026/client/src/components/foundation/Link";
import { TranslatableText } from "@web-speed-hackathon-2026/client/src/components/post/TranslatableText";
import { getProfileImagePath } from "@web-speed-hackathon-2026/client/src/utils/get_path";
@@ -42,8 +40,8 @@ export const CommentItem = ({ comment }: Props) => {
-
diff --git a/application/client/src/components/post/PostItem.tsx b/application/client/src/components/post/PostItem.tsx
index 5fa904c91a..f15b35078d 100644
--- a/application/client/src/components/post/PostItem.tsx
+++ b/application/client/src/components/post/PostItem.tsx
@@ -1,5 +1,3 @@
-import moment from "moment";
-
import { Link } from "@web-speed-hackathon-2026/client/src/components/foundation/Link";
import { ImageArea } from "@web-speed-hackathon-2026/client/src/components/post/ImageArea";
import { MovieArea } from "@web-speed-hackathon-2026/client/src/components/post/MovieArea";
@@ -67,8 +65,8 @@ export const PostItem = ({ post }: Props) => {
) : null}
-
- {moment(post.createdAt).locale("ja").format("LL")}
+
+ {new Intl.DateTimeFormat("ja", { year: "numeric", month: "long", day: "numeric" }).format(new Date(post.createdAt))}
diff --git a/application/client/src/components/timeline/TimelineItem.tsx b/application/client/src/components/timeline/TimelineItem.tsx
index 21b88980f8..a30418a335 100644
--- a/application/client/src/components/timeline/TimelineItem.tsx
+++ b/application/client/src/components/timeline/TimelineItem.tsx
@@ -1,4 +1,3 @@
-import moment from "moment";
import { MouseEventHandler, useCallback } from "react";
import { Link, useNavigate } from "react-router";
@@ -76,8 +75,8 @@ export const TimelineItem = ({ post }: Props) => {
-
-
- {moment(post.createdAt).locale("ja").format("LL")}
+
+ {new Intl.DateTimeFormat("ja", { year: "numeric", month: "long", day: "numeric" }).format(new Date(post.createdAt))}
diff --git a/application/client/src/components/user_profile/UserProfileHeader.tsx b/application/client/src/components/user_profile/UserProfileHeader.tsx
index c1c3355e19..c897a3a14a 100644
--- a/application/client/src/components/user_profile/UserProfileHeader.tsx
+++ b/application/client/src/components/user_profile/UserProfileHeader.tsx
@@ -1,5 +1,4 @@
import { FastAverageColor } from "fast-average-color";
-import moment from "moment";
import { ReactEventHandler, useCallback, useState } from "react";
import { FontAwesomeIcon } from "@web-speed-hackathon-2026/client/src/components/foundation/FontAwesomeIcon";
@@ -43,8 +42,8 @@ export const UserProfileHeader = ({ user }: Props) => {
-
- {moment(user.createdAt).locale("ja").format("LL")}
+
+ {new Intl.DateTimeFormat("ja", { year: "numeric", month: "long", day: "numeric" }).format(new Date(user.createdAt))}
からサービスを利用しています
diff --git a/application/client/src/containers/AppContainer.tsx b/application/client/src/containers/AppContainer.tsx
index d66858a949..409f8a45d7 100644
--- a/application/client/src/containers/AppContainer.tsx
+++ b/application/client/src/containers/AppContainer.tsx
@@ -1,21 +1,22 @@
-import { useCallback, useEffect, useId, useState } from "react";
+import { lazy, Suspense, useCallback, useEffect, useId, useState } from "react";
import { Helmet, HelmetProvider } from "react-helmet";
import { Route, Routes, useLocation, useNavigate } from "react-router";
import { AppPage } from "@web-speed-hackathon-2026/client/src/components/application/AppPage";
import { AuthModalContainer } from "@web-speed-hackathon-2026/client/src/containers/AuthModalContainer";
-import { CrokContainer } from "@web-speed-hackathon-2026/client/src/containers/CrokContainer";
import { DirectMessageContainer } from "@web-speed-hackathon-2026/client/src/containers/DirectMessageContainer";
import { DirectMessageListContainer } from "@web-speed-hackathon-2026/client/src/containers/DirectMessageListContainer";
-import { NewPostModalContainer } from "@web-speed-hackathon-2026/client/src/containers/NewPostModalContainer";
import { NotFoundContainer } from "@web-speed-hackathon-2026/client/src/containers/NotFoundContainer";
import { PostContainer } from "@web-speed-hackathon-2026/client/src/containers/PostContainer";
-import { SearchContainer } from "@web-speed-hackathon-2026/client/src/containers/SearchContainer";
import { TermContainer } from "@web-speed-hackathon-2026/client/src/containers/TermContainer";
import { TimelineContainer } from "@web-speed-hackathon-2026/client/src/containers/TimelineContainer";
import { UserProfileContainer } from "@web-speed-hackathon-2026/client/src/containers/UserProfileContainer";
import { fetchJSON, sendJSON } from "@web-speed-hackathon-2026/client/src/utils/fetchers";
+const CrokContainer = lazy(() => import("@web-speed-hackathon-2026/client/src/containers/CrokContainer").then((m) => ({ default: m.CrokContainer })));
+const SearchContainer = lazy(() => import("@web-speed-hackathon-2026/client/src/containers/SearchContainer").then((m) => ({ default: m.SearchContainer })));
+const NewPostModalContainer = lazy(() => import("@web-speed-hackathon-2026/client/src/containers/NewPostModalContainer").then((m) => ({ default: m.NewPostModalContainer })));
+
export const AppContainer = () => {
const { pathname } = useLocation();
const navigate = useNavigate();
@@ -61,32 +62,36 @@ export const AppContainer = () => {
newPostModalId={newPostModalId}
onLogout={handleLogout}
>
-
- } path="/" />
-
- }
- path="/dm"
- />
- }
- path="/dm/:conversationId"
- />
- } path="/search" />
- } path="/users/:username" />
- } path="/posts/:postId" />
- } path="/terms" />
- }
- path="/crok"
- />
- } path="*" />
-
+
+
+ } path="/" />
+
+ }
+ path="/dm"
+ />
+ }
+ path="/dm/:conversationId"
+ />
+ } path="/search" />
+ } path="/users/:username" />
+ } path="/posts/:postId" />
+ } path="/terms" />
+ }
+ path="/crok"
+ />
+ } path="*" />
+
+
-
+
+
+
);
};
diff --git a/application/client/src/utils/bm25_search.ts b/application/client/src/utils/bm25_search.ts
index c590d12c09..6bd95c5980 100644
--- a/application/client/src/utils/bm25_search.ts
+++ b/application/client/src/utils/bm25_search.ts
@@ -1,6 +1,5 @@
import { BM25 } from "bayesian-bm25";
import type { Tokenizer, IpadicFeatures } from "kuromoji";
-import _ from "lodash";
const STOP_POS = new Set(["助詞", "助動詞", "記号"]);
@@ -28,15 +27,12 @@ export function filterSuggestionsBM25(
const tokenizedCandidates = candidates.map((c) => extractTokens(tokenizer.tokenize(c)));
bm25.index(tokenizedCandidates);
- const results = _.zipWith(candidates, bm25.getScores(queryTokens), (text, score) => {
- return { text, score };
- });
+ const scores = bm25.getScores(queryTokens);
+ const results = candidates.map((text, i) => ({ text, score: scores[i] }));
- // スコアが高い(=類似度が高い)ものが下に来るように、上位10件を取得する
- return _(results)
+ return results
.filter((s) => s.score > 0)
- .sortBy(["score"])
+ .sort((a, b) => a.score - b.score)
.slice(-10)
- .map((s) => s.text)
- .value();
+ .map((s) => s.text);
}
diff --git a/application/client/src/utils/create_translator.ts b/application/client/src/utils/create_translator.ts
index ad1dabad22..1dbae08948 100644
--- a/application/client/src/utils/create_translator.ts
+++ b/application/client/src/utils/create_translator.ts
@@ -1,4 +1,3 @@
-import { CreateMLCEngine } from "@mlc-ai/web-llm";
import { stripIndents } from "common-tags";
import * as JSONRepairJS from "json-repair-js";
import langs from "langs";
@@ -21,6 +20,7 @@ export async function createTranslator(params: Params): Promise {
const targetLang = langs.where("1", params.targetLanguage);
invariant(targetLang, `Unsupported target language code: ${params.targetLanguage}`);
+ const { CreateMLCEngine } = await import("@mlc-ai/web-llm");
const engine = await CreateMLCEngine("gemma-2-2b-jpn-it-q4f16_1-MLC");
return {
diff --git a/application/client/src/utils/negaposi_analyzer.ts b/application/client/src/utils/negaposi_analyzer.ts
index f81ed5f4ea..248161aa4b 100644
--- a/application/client/src/utils/negaposi_analyzer.ts
+++ b/application/client/src/utils/negaposi_analyzer.ts
@@ -1,19 +1,17 @@
-import Bluebird from "bluebird";
-import kuromoji, { type Tokenizer, type IpadicFeatures } from "kuromoji";
-import analyze from "negaposi-analyzer-ja";
-
-async function getTokenizer(): Promise> {
- const builder = Bluebird.promisifyAll(kuromoji.builder({ dicPath: "/dicts" }));
- return await builder.buildAsync();
-}
-
type SentimentResult = {
score: number;
label: "positive" | "negative" | "neutral";
};
export async function analyzeSentiment(text: string): Promise {
- const tokenizer = await getTokenizer();
+ const [Bluebird, kuromoji, { default: analyze }] = await Promise.all([
+ import("bluebird"),
+ import("kuromoji"),
+ import("negaposi-analyzer-ja"),
+ ]);
+
+ const builder = Bluebird.default.promisifyAll(kuromoji.default.builder({ dicPath: "/dicts" }));
+ const tokenizer = await builder.buildAsync();
const tokens = tokenizer.tokenize(text);
const score = analyze(tokens);
diff --git a/application/client/webpack.config.js b/application/client/webpack.config.js
index 8c1157d93a..2023887bd2 100644
--- a/application/client/webpack.config.js
+++ b/application/client/webpack.config.js
@@ -68,7 +68,6 @@ const config = {
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
- AudioContext: ["standardized-audio-context", "AudioContext"],
Buffer: ["buffer", "Buffer"],
"window.jQuery": "jquery",
}),
From 8d0844b375a1035c586fcc6fe94a3d2ed6fea1b8 Mon Sep 17 00:00:00 2001
From: Your Name
Date: Sat, 21 Mar 2026 00:44:17 +0900
Subject: [PATCH 05/21] =?UTF-8?q?#004a=20SearchContainer=E3=81=AElazy?=
=?UTF-8?q?=E5=8C=96=E3=82=92=E6=88=BB=E3=81=99=20+=20fetchBinary=E3=82=92?=
=?UTF-8?q?fetch=20API=E3=81=AB=E4=BF=AE=E6=AD=A3?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- SearchContainerをReact.lazyから通常importに戻す(スコアリングテスト対応)
- fetchBinaryをjQuery.ajaxからネイティブfetch()に変更(画像表示修正)
---
application/client/src/containers/AppContainer.tsx | 2 +-
application/client/src/utils/fetchers.ts | 9 ++-------
2 files changed, 3 insertions(+), 8 deletions(-)
diff --git a/application/client/src/containers/AppContainer.tsx b/application/client/src/containers/AppContainer.tsx
index 409f8a45d7..7a1b4aace6 100644
--- a/application/client/src/containers/AppContainer.tsx
+++ b/application/client/src/containers/AppContainer.tsx
@@ -8,13 +8,13 @@ import { DirectMessageContainer } from "@web-speed-hackathon-2026/client/src/con
import { DirectMessageListContainer } from "@web-speed-hackathon-2026/client/src/containers/DirectMessageListContainer";
import { NotFoundContainer } from "@web-speed-hackathon-2026/client/src/containers/NotFoundContainer";
import { PostContainer } from "@web-speed-hackathon-2026/client/src/containers/PostContainer";
+import { SearchContainer } from "@web-speed-hackathon-2026/client/src/containers/SearchContainer";
import { TermContainer } from "@web-speed-hackathon-2026/client/src/containers/TermContainer";
import { TimelineContainer } from "@web-speed-hackathon-2026/client/src/containers/TimelineContainer";
import { UserProfileContainer } from "@web-speed-hackathon-2026/client/src/containers/UserProfileContainer";
import { fetchJSON, sendJSON } from "@web-speed-hackathon-2026/client/src/utils/fetchers";
const CrokContainer = lazy(() => import("@web-speed-hackathon-2026/client/src/containers/CrokContainer").then((m) => ({ default: m.CrokContainer })));
-const SearchContainer = lazy(() => import("@web-speed-hackathon-2026/client/src/containers/SearchContainer").then((m) => ({ default: m.SearchContainer })));
const NewPostModalContainer = lazy(() => import("@web-speed-hackathon-2026/client/src/containers/NewPostModalContainer").then((m) => ({ default: m.NewPostModalContainer })));
export const AppContainer = () => {
diff --git a/application/client/src/utils/fetchers.ts b/application/client/src/utils/fetchers.ts
index c4615071b6..4f278202f5 100644
--- a/application/client/src/utils/fetchers.ts
+++ b/application/client/src/utils/fetchers.ts
@@ -2,13 +2,8 @@ import $ from "jquery";
import { gzip } from "pako";
export async function fetchBinary(url: string): Promise {
- const result = await $.ajax({
- dataType: "binary",
- method: "GET",
- responseType: "arraybuffer",
- url,
- });
- return result;
+ const response = await fetch(url);
+ return response.arrayBuffer();
}
export async function fetchJSON(url: string): Promise {
From db2e9a9f14acc954205855adfb7f82cd9da4ad4a Mon Sep 17 00:00:00 2001
From: Your Name
Date: Sat, 21 Mar 2026 00:47:46 +0900
Subject: [PATCH 06/21] =?UTF-8?q?#009-015=20=E5=A4=A7=E9=87=8F=E6=94=B9?=
=?UTF-8?q?=E5=96=84:=20jQuery=E9=99=A4=E5=8E=BB/DM=E7=BD=A0=E4=BF=AE?=
=?UTF-8?q?=E6=AD=A3/=E3=83=95=E3=82=A9=E3=83=B3=E3=83=88/=E3=82=AD?=
=?UTF-8?q?=E3=83=A3=E3=83=83=E3=82=B7=E3=83=A5/defer?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- jQuery + pako を完全除去、全API呼び出しをネイティブfetch()に置換 (-308KB)
- DM詳細のsetInterval(1ms)をMutationObserverに置換 (TBT改善)
- font-display: block → swap (FCP/LCP改善)
- script defer属性追加 (レンダリングブロック解消)
- 静的ファイルにCache-Control追加 (etag:false → maxAge+immutable)
- ProvidePluginからjQuery除去
- main.js 702KB → 573KB
---
.../direct_message/DirectMessagePage.tsx | 12 ++---
.../src/containers/AuthModalContainer.tsx | 28 ++++++------
application/client/src/index.css | 4 +-
application/client/src/index.html | 2 +-
application/client/src/utils/fetchers.ts | 45 ++++++-------------
application/client/webpack.config.js | 2 -
application/server/src/routes/static.ts | 12 ++---
7 files changed, 42 insertions(+), 63 deletions(-)
diff --git a/application/client/src/components/direct_message/DirectMessagePage.tsx b/application/client/src/components/direct_message/DirectMessagePage.tsx
index 4d896b43fb..cc2b0866b4 100644
--- a/application/client/src/components/direct_message/DirectMessagePage.tsx
+++ b/application/client/src/components/direct_message/DirectMessagePage.tsx
@@ -73,15 +73,17 @@ export const DirectMessagePage = ({
);
useEffect(() => {
- const id = setInterval(() => {
- const height = Number(window.getComputedStyle(document.body).height.replace("px", ""));
+ const scrollToBottom = () => {
+ const height = document.body.scrollHeight;
if (height !== scrollHeightRef.current) {
scrollHeightRef.current = height;
window.scrollTo(0, height);
}
- }, 1);
-
- return () => clearInterval(id);
+ };
+ scrollToBottom();
+ const observer = new MutationObserver(scrollToBottom);
+ observer.observe(document.body, { childList: true, subtree: true });
+ return () => observer.disconnect();
}, []);
if (conversationError != null) {
diff --git a/application/client/src/containers/AuthModalContainer.tsx b/application/client/src/containers/AuthModalContainer.tsx
index 8d159f3528..0b7a9358fd 100644
--- a/application/client/src/containers/AuthModalContainer.tsx
+++ b/application/client/src/containers/AuthModalContainer.tsx
@@ -16,23 +16,21 @@ const ERROR_MESSAGES: Record = {
USERNAME_TAKEN: "ユーザー名が使われています",
};
-function getErrorCode(err: JQuery.jqXHR, type: "signin" | "signup"): string {
- const responseJSON = err.responseJSON;
+function getErrorCode(err: unknown, type: "signin" | "signup"): string {
if (
- typeof responseJSON !== "object" ||
- responseJSON === null ||
- !("code" in responseJSON) ||
- typeof responseJSON.code !== "string" ||
- !Object.keys(ERROR_MESSAGES).includes(responseJSON.code)
+ typeof err === "object" &&
+ err !== null &&
+ "code" in err &&
+ typeof (err as Record).code === "string" &&
+ Object.keys(ERROR_MESSAGES).includes((err as Record).code)
) {
- if (type === "signup") {
- return "登録に失敗しました";
- } else {
- return "パスワードが異なります";
- }
+ return ERROR_MESSAGES[(err as Record).code]!;
+ }
+ if (type === "signup") {
+ return "登録に失敗しました";
+ } else {
+ return "パスワードが異なります";
}
-
- return ERROR_MESSAGES[responseJSON.code]!;
}
export const AuthModalContainer = ({ id, onUpdateActiveUser }: Props) => {
@@ -68,7 +66,7 @@ export const AuthModalContainer = ({ id, onUpdateActiveUser }: Props) => {
}
handleRequestCloseModal();
} catch (err: unknown) {
- const error = getErrorCode(err as JQuery.jqXHR, values.type);
+ const error = getErrorCode(err, values.type);
throw new SubmissionError({
_error: error,
});
diff --git a/application/client/src/index.css b/application/client/src/index.css
index 8612ebcdd2..9222352f47 100644
--- a/application/client/src/index.css
+++ b/application/client/src/index.css
@@ -5,7 +5,7 @@
@font-face {
/* Source Han Serif JP Regular の Y 軸を 1/1.43 に縮小した改変フォント */
font-family: "Rei no Are Mincho";
- font-display: block;
+ font-display: swap;
src: url(/fonts/ReiNoAreMincho-Regular.otf) format("opentype");
font-weight: normal;
}
@@ -13,7 +13,7 @@
@font-face {
/* Source Han Serif JP Heavy の Y 軸を 1/1.43 に縮小した改変フォント */
font-family: "Rei no Are Mincho";
- font-display: block;
+ font-display: swap;
src: url(/fonts/ReiNoAreMincho-Heavy.otf) format("opentype");
font-weight: bold;
}
diff --git a/application/client/src/index.html b/application/client/src/index.html
index 3d949e7473..b15fc9388e 100644
--- a/application/client/src/index.html
+++ b/application/client/src/index.html
@@ -4,7 +4,7 @@
CaX
-
+
+
diff --git a/application/pnpm-lock.yaml b/application/pnpm-lock.yaml
index 142746e58d..4d9d56849e 100644
--- a/application/pnpm-lock.yaml
+++ b/application/pnpm-lock.yaml
@@ -160,6 +160,9 @@ importers:
'@babel/preset-typescript':
specifier: 7.27.1
version: 7.27.1(@babel/core@7.28.4)
+ '@tailwindcss/postcss':
+ specifier: 4.2.2
+ version: 4.2.2
'@tsconfig/strictest':
specifier: 2.0.8
version: 2.0.8
@@ -238,6 +241,9 @@ importers:
react-markdown:
specifier: 10.1.0
version: 10.1.0(@types/react@19.2.2)(react@19.2.0)
+ tailwindcss:
+ specifier: 4.2.2
+ version: 4.2.2
typescript:
specifier: 5.9.3
version: 5.9.3
@@ -356,6 +362,10 @@ importers:
packages:
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
'@babel/code-frame@7.27.1':
resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
engines: {node: '>=6.9.0'}
@@ -1614,6 +1624,98 @@ packages:
engines: {node: '>=18'}
hasBin: true
+ '@tailwindcss/node@4.2.2':
+ resolution: {integrity: sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==}
+
+ '@tailwindcss/oxide-android-arm64@4.2.2':
+ resolution: {integrity: sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.2.2':
+ resolution: {integrity: sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.2.2':
+ resolution: {integrity: sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.2.2':
+ resolution: {integrity: sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2':
+ resolution: {integrity: sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.2.2':
+ resolution: {integrity: sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.2.2':
+ resolution: {integrity: sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.2.2':
+ resolution: {integrity: sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.2.2':
+ resolution: {integrity: sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.2.2':
+ resolution: {integrity: sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.2.2':
+ resolution: {integrity: sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.2.2':
+ resolution: {integrity: sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.2.2':
+ resolution: {integrity: sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==}
+ engines: {node: '>= 20'}
+
+ '@tailwindcss/postcss@4.2.2':
+ resolution: {integrity: sha512-n4goKQbW8RVXIbNKRB/45LzyUqN451deQK0nzIeauVEqjlI49slUlgKYJM2QyUzap/PcpnS7kzSUmPb1sCRvYQ==}
+
'@tokenizer/inflate@0.4.1':
resolution: {integrity: sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==}
engines: {node: '>=18'}
@@ -2435,6 +2537,10 @@ packages:
resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
engines: {node: '>=10.13.0'}
+ enhanced-resolve@5.20.1:
+ resolution: {integrity: sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==}
+ engines: {node: '>=10.13.0'}
+
entities@2.2.0:
resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==}
@@ -3084,6 +3190,80 @@ packages:
launch-editor@2.11.1:
resolution: {integrity: sha512-SEET7oNfgSaB6Ym0jufAdCeo3meJVeCaaDyzRygy0xsp2BFKCprcfHljTq4QkzTLUxEKkFK6OK4811YM2oSrRg==}
+ lightningcss-android-arm64@1.32.0:
+ resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.32.0:
+ resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.32.0:
+ resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.32.0:
+ resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.32.0:
+ resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.32.0:
+ resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
+ engines: {node: '>= 12.0.0'}
+
lines-and-columns@1.2.4:
resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
@@ -3129,6 +3309,9 @@ packages:
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
engines: {node: '>=10'}
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
make-fetch-happen@9.1.0:
resolution: {integrity: sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==}
engines: {node: '>= 10'}
@@ -4352,6 +4535,9 @@ packages:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
engines: {node: '>= 0.4'}
+ tailwindcss@4.2.2:
+ resolution: {integrity: sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==}
+
tapable@2.3.0:
resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
engines: {node: '>=6'}
@@ -4700,6 +4886,8 @@ packages:
snapshots:
+ '@alloc/quick-lru@5.2.0': {}
+
'@babel/code-frame@7.27.1':
dependencies:
'@babel/helper-validator-identifier': 7.27.1
@@ -5981,6 +6169,75 @@ snapshots:
dependencies:
playwright: 1.50.1
+ '@tailwindcss/node@4.2.2':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.20.1
+ jiti: 2.6.1
+ lightningcss: 1.32.0
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.2.2
+
+ '@tailwindcss/oxide-android-arm64@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.2.2':
+ optional: true
+
+ '@tailwindcss/oxide@4.2.2':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.2.2
+ '@tailwindcss/oxide-darwin-arm64': 4.2.2
+ '@tailwindcss/oxide-darwin-x64': 4.2.2
+ '@tailwindcss/oxide-freebsd-x64': 4.2.2
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.2.2
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.2.2
+ '@tailwindcss/oxide-linux-arm64-musl': 4.2.2
+ '@tailwindcss/oxide-linux-x64-gnu': 4.2.2
+ '@tailwindcss/oxide-linux-x64-musl': 4.2.2
+ '@tailwindcss/oxide-wasm32-wasi': 4.2.2
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.2.2
+ '@tailwindcss/oxide-win32-x64-msvc': 4.2.2
+
+ '@tailwindcss/postcss@4.2.2':
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ '@tailwindcss/node': 4.2.2
+ '@tailwindcss/oxide': 4.2.2
+ postcss: 8.5.6
+ tailwindcss: 4.2.2
+
'@tokenizer/inflate@0.4.1':
dependencies:
debug: 4.4.3
@@ -6870,6 +7127,11 @@ snapshots:
graceful-fs: 4.2.11
tapable: 2.3.0
+ enhanced-resolve@5.20.1:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.0
+
entities@2.2.0: {}
entities@6.0.1: {}
@@ -7607,6 +7869,55 @@ snapshots:
picocolors: 1.1.1
shell-quote: 1.8.3
+ lightningcss-android-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.32.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.32.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ optional: true
+
+ lightningcss@1.32.0:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.32.0
+ lightningcss-darwin-arm64: 1.32.0
+ lightningcss-darwin-x64: 1.32.0
+ lightningcss-freebsd-x64: 1.32.0
+ lightningcss-linux-arm-gnueabihf: 1.32.0
+ lightningcss-linux-arm64-gnu: 1.32.0
+ lightningcss-linux-arm64-musl: 1.32.0
+ lightningcss-linux-x64-gnu: 1.32.0
+ lightningcss-linux-x64-musl: 1.32.0
+ lightningcss-win32-arm64-msvc: 1.32.0
+ lightningcss-win32-x64-msvc: 1.32.0
+
lines-and-columns@1.2.4: {}
loader-runner@4.3.1: {}
@@ -7649,6 +7960,10 @@ snapshots:
yallist: 4.0.0
optional: true
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
make-fetch-happen@9.1.0:
dependencies:
agentkeepalive: 4.6.0
@@ -9334,6 +9649,8 @@ snapshots:
supports-preserve-symlinks-flag@1.0.0: {}
+ tailwindcss@4.2.2: {}
+
tapable@2.3.0: {}
tar-fs@2.1.4:
diff --git a/application/public/fonts/ReiNoAreMincho-Heavy.woff2 b/application/public/fonts/ReiNoAreMincho-Heavy.woff2
new file mode 100644
index 0000000000000000000000000000000000000000..c0881f2904b2e4814e084a904baaf801d17e9a61
GIT binary patch
literal 3877024
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