summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.env.sample6
-rw-r--r--.github/workflows/nextjs.yml91
-rw-r--r--.gitignore38
-rw-r--r--LICENSE121
-rw-r--r--next.config.mjs9
-rw-r--r--package-lock.json1979
-rw-r--r--package.json27
-rw-r--r--postcss.config.mjs8
-rw-r--r--readme.dev.md36
-rw-r--r--readme.md5
-rw-r--r--src/app/_components/navigationHeader.tsx35
-rw-r--r--src/app/anime/[animeId]/page.tsx231
-rw-r--r--src/app/anime/[animeId]/style.css3
-rw-r--r--src/app/anime/[animeId]/style.module.css3
-rw-r--r--src/app/anime/all/page.tsx64
-rw-r--r--src/app/anime/all/style.module.css3
-rw-r--r--src/app/article/_components/articleListElement.tsx123
-rw-r--r--src/app/article/all/[pageId]/page.tsx165
-rw-r--r--src/app/article/category/[categoryName]/page.tsx44
-rw-r--r--src/app/article/category/page.tsx29
-rw-r--r--src/app/article/category/style.css3
-rw-r--r--src/app/article/tag/[tagName]/page.tsx40
-rw-r--r--src/app/article/tag/page.tsx30
-rw-r--r--src/app/article/tag/style.css3
-rw-r--r--src/app/globals.css31
-rw-r--r--src/app/iframe/page.tsx52
-rw-r--r--src/app/layout.tsx28
-rw-r--r--src/app/page.tsx30
-rw-r--r--src/app/style.css3
-rw-r--r--src/app/style.module.css3
-rw-r--r--src/util/animeLoader.ts93
-rw-r--r--src/util/articleLoader.ts88
-rw-r--r--src/util/pagenation.ts91
-rw-r--r--tailwind.config.ts19
-rw-r--r--tsconfig.json40
35 files changed, 3574 insertions, 0 deletions
diff --git a/.env.sample b/.env.sample
new file mode 100644
index 0000000..ccf99f7
--- /dev/null
+++ b/.env.sample
@@ -0,0 +1,6 @@
+AKIBA_SOUKEN_ARTICLE_JSON=.articles.json
+AKIBA_SOUKEN_ANIME_JSON=.anime.json
+# 記事一覧で1ページあたりの表示記事数
+AKIBA_SOUKEN_AR_ITEM_PPV=1000
+
+
diff --git a/.github/workflows/nextjs.yml b/.github/workflows/nextjs.yml
new file mode 100644
index 0000000..3282d64
--- /dev/null
+++ b/.github/workflows/nextjs.yml
@@ -0,0 +1,91 @@
+# このテンプレートはGithubのレポジトリ設定画面のGithubPagesのページ
+# Sample workflow for building and deploying a Next.js site to GitHub Pages
+#
+# To get started with Next.js see: https://nextjs.org/docs/getting-started
+#
+name: Deploy Next.js site to Pages
+
+on:
+ # Runs on pushes targeting the default branch
+ push:
+ branches: ["main"]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages
+permissions:
+ contents: read
+ pages: write
+ id-token: write
+
+# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.
+# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.
+concurrency:
+ group: "pages"
+ cancel-in-progress: false
+
+jobs:
+ # Build job
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+ - name: .envを設定
+ run: |
+ cp .env.sample .env
+ - name: ドキュメントファイルをDLする
+ run: |
+ curl -o "./.anime.json" "${{ secrets.ANIME_FILE_PATH }}"
+ curl -o "./.articles.json" "${{ secrets.ARTICLE_FILE_PATH }}"
+ - name: Detect package manager
+ id: detect-package-manager
+ run: |
+ if [ -f "${{ github.workspace }}/yarn.lock" ]; then
+ echo "manager=yarn" >> $GITHUB_OUTPUT
+ echo "command=install" >> $GITHUB_OUTPUT
+ echo "runner=yarn" >> $GITHUB_OUTPUT
+ exit 0
+ elif [ -f "${{ github.workspace }}/package.json" ]; then
+ echo "manager=npm" >> $GITHUB_OUTPUT
+ echo "command=ci" >> $GITHUB_OUTPUT
+ echo "runner=npx --no-install" >> $GITHUB_OUTPUT
+ exit 0
+ else
+ echo "Unable to determine package manager"
+ exit 1
+ fi
+ - name: Setup Node
+ uses: actions/setup-node@v4
+ with:
+ node-version: "20"
+ cache: ${{ steps.detect-package-manager.outputs.manager }}
+ - name: Setup Pages
+ uses: actions/configure-pages@v5
+ with:
+ # Automatically inject basePath in your Next.js configuration file and disable
+ # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized).
+ #
+ # You may remove this line if you want to manage the configuration yourself.
+ static_site_generator: next
+ - name: Install dependencies
+ run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }}
+ - name: Build with Next.js
+ run: ${{ steps.detect-package-manager.outputs.runner }} next build
+ - name: Upload artifact
+ uses: actions/upload-pages-artifact@v3
+ with:
+ path: ./.ssg-output/akiba-souken-archive
+
+ # Deployment job
+ deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
+ runs-on: ubuntu-latest
+ needs: build
+ steps:
+ - name: Deploy to GitHub Pages
+ id: deployment
+ uses: actions/deploy-pages@v4
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..63f3e7c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,38 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.js
+.yarn/install-state.gz
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# local env files
+.env*.local
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+.env
+.ssg-output/
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..0e259d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,121 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+ CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+ LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+ ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+ INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+ REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+ PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+ THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+ HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+ i. the right to reproduce, adapt, distribute, perform, display,
+ communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+ likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+ subject to the limitations in paragraph 4(a), below;
+ v. rights protecting the extraction, dissemination, use and reuse of data
+ in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+ European Parliament and of the Council of 11 March 1996 on the legal
+ protection of databases, and under any national implementation
+ thereof, including any amended or successor version of such
+ directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+ world based on applicable law or treaty, and any national
+ implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+ surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+ warranties of any kind concerning the Work, express, implied,
+ statutory or otherwise, including without limitation warranties of
+ title, merchantability, fitness for a particular purpose, non
+ infringement, or the absence of latent or other defects, accuracy, or
+ the present or absence of errors, whether or not discoverable, all to
+ the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+ that may apply to the Work or any use thereof, including without
+ limitation any person's Copyright and Related Rights in the Work.
+ Further, Affirmer disclaims responsibility for obtaining any necessary
+ consents, permissions or other rights required for any use of the
+ Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+ party to this document and has no duty or obligation with respect to
+ this CC0 or use of the Work.
diff --git a/next.config.mjs b/next.config.mjs
new file mode 100644
index 0000000..1913058
--- /dev/null
+++ b/next.config.mjs
@@ -0,0 +1,9 @@
+/** @type {import('next').NextConfig} */
+const nextConfig = {
+ output: "export",
+ basePath: "/akiba-souken-archive",
+ distDir: ".ssg-output/akiba-souken-archive",
+ trailingSlash: true,
+};
+
+export default nextConfig;
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..bc5a328
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,1979 @@
+{
+ "name": "2.viewer",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "2.viewer",
+ "version": "0.1.0",
+ "dependencies": {
+ "@types/dateformat": "3.0.1",
+ "dateformat": "4.5.1",
+ "next": "14.2.13",
+ "react": "^18",
+ "react-dom": "^18",
+ "zod": "3.23.8"
+ },
+ "devDependencies": {
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "postcss": "^8",
+ "tailwindcss": "^3.4.1",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^5.1.2",
+ "string-width-cjs": "npm:string-width@^4.2.0",
+ "strip-ansi": "^7.0.1",
+ "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+ "wrap-ansi": "^8.1.0",
+ "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz",
+ "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-14.2.13.tgz",
+ "integrity": "sha512-s3lh6K8cbW1h5Nga7NNeXrbe0+2jIIYK9YaA9T7IufDWnZpozdFUp6Hf0d5rNWUKu4fEuSX2rCKlGjCrtylfDw==",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.2.13.tgz",
+ "integrity": "sha512-IkAmQEa2Htq+wHACBxOsslt+jMoV3msvxCn0WFSfJSkv/scy+i/EukBKNad36grRxywaXUYJc9mxEGkeIs8Bzg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.2.13.tgz",
+ "integrity": "sha512-Dv1RBGs2TTjkwEnFMVL5XIfJEavnLqqwYSD6LXgTPdEy/u6FlSrLBSSfe1pcfqhFEXRAgVL3Wpjibe5wXJzWog==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.2.13.tgz",
+ "integrity": "sha512-yB1tYEFFqo4ZNWkwrJultbsw7NPAAxlPXURXioRl9SdW6aIefOLS+0TEsKrWBtbJ9moTDgU3HRILL6QBQnMevg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.2.13.tgz",
+ "integrity": "sha512-v5jZ/FV/eHGoWhMKYrsAweQ7CWb8xsWGM/8m1mwwZQ/sutJjoFaXchwK4pX8NqwImILEvQmZWyb8pPTcP7htWg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.2.13.tgz",
+ "integrity": "sha512-aVc7m4YL7ViiRv7SOXK3RplXzOEe/qQzRA5R2vpXboHABs3w8vtFslGTz+5tKiQzWUmTmBNVW0UQdhkKRORmGA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.2.13.tgz",
+ "integrity": "sha512-4wWY7/OsSaJOOKvMsu1Teylku7vKyTuocvDLTZQq0TYv9OjiYYWt63PiE1nTuZnqQ4RPvME7Xai+9enoiN0Wrg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.2.13.tgz",
+ "integrity": "sha512-uP1XkqCqV2NVH9+g2sC7qIw+w2tRbcMiXFEbMihkQ8B1+V6m28sshBwAB0SDmOe0u44ne1vFU66+gx/28RsBVQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-ia32-msvc": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.2.13.tgz",
+ "integrity": "sha512-V26ezyjPqQpDBV4lcWIh8B/QICQ4v+M5Bo9ykLN+sqeKKBxJVDpEc6biDVyluTXTC40f5IqCU0ttth7Es2ZuMw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.2.13.tgz",
+ "integrity": "sha512-WwzOEAFBGhlDHE5Z73mNU8CO8mqMNLqaG+AO9ETmzdCQlJhVtWZnOl2+rqgVQS+YHunjOWptdFmNfbpwcUuEsw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.5",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.5.tgz",
+ "integrity": "sha512-KGYxvIOXcceOAbEk4bi/dVLEK9z8sZ0uBB3Il5b1rhfClSpcX0yfRO0KmTkqR2cnQDymwLB+25ZyMzICg/cm/A==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@swc/counter": "^0.1.3",
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/dateformat": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/@types/dateformat/-/dateformat-3.0.1.tgz",
+ "integrity": "sha512-KlPPdikagvL6ELjWsljbyDIPzNCeliYkqRpI+zea99vBBbCIA5JNshZAwQKTON139c87y9qvTFVgkFd14rtS4g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "20.16.5",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.16.5.tgz",
+ "integrity": "sha512-VwYCweNo3ERajwy0IUlqqcyZ8/A7Zwa9ZP3MnENWcB11AejO+tLy3pu850goUW2FC/IJMdZUfKpX/yxL1gymCA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.19.2"
+ }
+ },
+ "node_modules/@types/prop-types": {
+ "version": "15.7.13",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz",
+ "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/react": {
+ "version": "18.3.8",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.8.tgz",
+ "integrity": "sha512-syBUrW3/XpnW4WJ41Pft+I+aPoDVbrBVQGEnbD7NijDGlVC+8gV/XKRY+7vMDlfPpbwYt0l1vd/Sj8bJGMbs9Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/prop-types": "*",
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "18.3.0",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.0.tgz",
+ "integrity": "sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/react": "*"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
+ "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+ "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/anymatch": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+ "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "normalize-path": "^3.0.0",
+ "picomatch": "^2.0.4"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/arg": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
+ "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/binary-extensions": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+ "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+ "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/braces": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "fill-range": "^7.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/camelcase-css": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
+ "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001662",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001662.tgz",
+ "integrity": "sha512-sgMUVwLmGseH8ZIrm1d51UbrhqMCH3jvS7gF/M6byuHOnKyLOBL7W8yz5V02OHwgLGA36o/AFhWzzh4uc5aqTA==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/chokidar/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/commander": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
+ "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+ "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/cssesc": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
+ "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "cssesc": "bin/cssesc"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/dateformat": {
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.5.1.tgz",
+ "integrity": "sha512-OD0TZ+B7yP7ZgpJf5K2DIbj3FZvFvxgFUuaqA/V5zTjAtAAXZ1E8bktHxmAGs4x5b7PflqA9LeQ84Og7wYtF7Q==",
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/didyoumean": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
+ "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/dlv": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
+ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-glob": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+ "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@nodelib/fs.stat": "^2.0.2",
+ "@nodelib/fs.walk": "^1.2.3",
+ "glob-parent": "^5.1.2",
+ "merge2": "^1.3.0",
+ "micromatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=8.6.0"
+ }
+ },
+ "node_modules/fast-glob/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fastq": {
+ "version": "1.17.1",
+ "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+ "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "reusify": "^1.0.4"
+ }
+ },
+ "node_modules/fill-range": {
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "to-regex-range": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz",
+ "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.0",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/glob": {
+ "version": "10.4.5",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
+ "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
+ },
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob-parent": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+ "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.3"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "license": "ISC"
+ },
+ "node_modules/hasown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
+ "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/is-binary-path": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+ "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "binary-extensions": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-core-module": {
+ "version": "2.15.1",
+ "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.15.1.tgz",
+ "integrity": "sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/is-extglob": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+ "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-glob": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+ "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-extglob": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/is-number": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "1.21.6",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.6.tgz",
+ "integrity": "sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "bin/jiti.js"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/lilconfig": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+ "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/lines-and-columns": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
+ "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/loose-envify": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
+ "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "js-tokens": "^3.0.0 || ^4.0.0"
+ },
+ "bin": {
+ "loose-envify": "cli.js"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/merge2": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+ "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/micromatch": {
+ "version": "4.0.8",
+ "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
+ "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "braces": "^3.0.3",
+ "picomatch": "^2.3.1"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/mz": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
+ "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0",
+ "object-assign": "^4.0.1",
+ "thenify-all": "^1.0.0"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.7",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz",
+ "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "14.2.13",
+ "resolved": "https://registry.npmjs.org/next/-/next-14.2.13.tgz",
+ "integrity": "sha512-BseY9YNw8QJSwLYD7hlZzl6QVDoSFHL/URN5K64kVEVpCsSOWeyjbIGK+dZUaRViHTaMQX8aqmnn0PHBbGZezg==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "14.2.13",
+ "@swc/helpers": "0.5.5",
+ "busboy": "1.6.0",
+ "caniuse-lite": "^1.0.30001579",
+ "graceful-fs": "^4.2.11",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.1"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": ">=18.17.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "14.2.13",
+ "@next/swc-darwin-x64": "14.2.13",
+ "@next/swc-linux-arm64-gnu": "14.2.13",
+ "@next/swc-linux-arm64-musl": "14.2.13",
+ "@next/swc-linux-x64-gnu": "14.2.13",
+ "@next/swc-linux-x64-musl": "14.2.13",
+ "@next/swc-win32-arm64-msvc": "14.2.13",
+ "@next/swc-win32-ia32-msvc": "14.2.13",
+ "@next/swc-win32-x64-msvc": "14.2.13"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.41.2",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-assign": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
+ "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/object-hash": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
+ "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz",
+ "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-parse": {
+ "version": "1.0.7",
+ "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
+ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz",
+ "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+ "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/pify": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
+ "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/pirates": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz",
+ "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.4.47",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz",
+ "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.7",
+ "picocolors": "^1.1.0",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/postcss-import": {
+ "version": "15.1.0",
+ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
+ "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "postcss-value-parser": "^4.0.0",
+ "read-cache": "^1.0.0",
+ "resolve": "^1.1.7"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.0.0"
+ }
+ },
+ "node_modules/postcss-js": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
+ "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "camelcase-css": "^2.0.1"
+ },
+ "engines": {
+ "node": "^12 || ^14 || >= 16"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ "peerDependencies": {
+ "postcss": "^8.4.21"
+ }
+ },
+ "node_modules/postcss-load-config": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
+ "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "lilconfig": "^3.0.0",
+ "yaml": "^2.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ },
+ "peerDependencies": {
+ "postcss": ">=8.0.9",
+ "ts-node": ">=9.0.0"
+ },
+ "peerDependenciesMeta": {
+ "postcss": {
+ "optional": true
+ },
+ "ts-node": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/postcss-load-config/node_modules/lilconfig": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz",
+ "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/antonk52"
+ }
+ },
+ "node_modules/postcss-nested": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
+ "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "postcss-selector-parser": "^6.1.1"
+ },
+ "engines": {
+ "node": ">=12.0"
+ },
+ "peerDependencies": {
+ "postcss": "^8.2.14"
+ }
+ },
+ "node_modules/postcss-selector-parser": {
+ "version": "6.1.2",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
+ "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cssesc": "^3.0.0",
+ "util-deprecate": "^1.0.2"
+ },
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/postcss-value-parser": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
+ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/queue-microtask": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+ "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/react": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz",
+ "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "18.3.1",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz",
+ "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0",
+ "scheduler": "^0.23.2"
+ },
+ "peerDependencies": {
+ "react": "^18.3.1"
+ }
+ },
+ "node_modules/read-cache": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
+ "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pify": "^2.3.0"
+ }
+ },
+ "node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/resolve": {
+ "version": "1.22.8",
+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz",
+ "integrity": "sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-core-module": "^2.13.0",
+ "path-parse": "^1.0.7",
+ "supports-preserve-symlinks-flag": "^1.0.0"
+ },
+ "bin": {
+ "resolve": "bin/resolve"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/reusify": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+ "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "iojs": ">=1.0.0",
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/run-parallel": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+ "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "queue-microtask": "^1.2.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.23.2",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz",
+ "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==",
+ "license": "MIT",
+ "dependencies": {
+ "loose-envify": "^1.1.0"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/string-width-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+ "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz",
+ "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/sucrase": {
+ "version": "3.35.0",
+ "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
+ "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.2",
+ "commander": "^4.0.0",
+ "glob": "^10.3.10",
+ "lines-and-columns": "^1.1.6",
+ "mz": "^2.7.0",
+ "pirates": "^4.0.1",
+ "ts-interface-checker": "^0.1.9"
+ },
+ "bin": {
+ "sucrase": "bin/sucrase",
+ "sucrase-node": "bin/sucrase-node"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/supports-preserve-symlinks-flag": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
+ "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "3.4.12",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.12.tgz",
+ "integrity": "sha512-Htf/gHj2+soPb9UayUNci/Ja3d8pTmu9ONTfh4QY8r3MATTZOzmv6UYWF7ZwikEIC8okpfqmGqrmDehua8mF8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "arg": "^5.0.2",
+ "chokidar": "^3.5.3",
+ "didyoumean": "^1.2.2",
+ "dlv": "^1.1.3",
+ "fast-glob": "^3.3.0",
+ "glob-parent": "^6.0.2",
+ "is-glob": "^4.0.3",
+ "jiti": "^1.21.0",
+ "lilconfig": "^2.1.0",
+ "micromatch": "^4.0.5",
+ "normalize-path": "^3.0.0",
+ "object-hash": "^3.0.0",
+ "picocolors": "^1.0.0",
+ "postcss": "^8.4.23",
+ "postcss-import": "^15.1.0",
+ "postcss-js": "^4.0.1",
+ "postcss-load-config": "^4.0.1",
+ "postcss-nested": "^6.0.1",
+ "postcss-selector-parser": "^6.0.11",
+ "resolve": "^1.22.2",
+ "sucrase": "^3.32.0"
+ },
+ "bin": {
+ "tailwind": "lib/cli.js",
+ "tailwindcss": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/thenify": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
+ "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "any-promise": "^1.0.0"
+ }
+ },
+ "node_modules/thenify-all": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
+ "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "thenify": ">= 3.1.0 < 4"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/to-regex-range": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+ "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "is-number": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/ts-interface-checker": {
+ "version": "0.1.13",
+ "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
+ "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
+ "dev": true,
+ "license": "Apache-2.0"
+ },
+ "node_modules/tslib": {
+ "version": "2.7.0",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.7.0.tgz",
+ "integrity": "sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==",
+ "license": "0BSD"
+ },
+ "node_modules/typescript": {
+ "version": "5.6.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
+ "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.19.8",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
+ "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/yaml": {
+ "version": "2.5.1",
+ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.5.1.tgz",
+ "integrity": "sha512-bLQOjaX/ADgQ20isPJRvF0iRUHIxVhYvr53Of7wGcWlO2jvtUlH5m87DsmulFVxRpNLOnI4tB6p/oh8D7kpn9Q==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "yaml": "bin.mjs"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.23.8",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz",
+ "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..ea93752
--- /dev/null
+++ b/package.json
@@ -0,0 +1,27 @@
+{
+ "name": "akiba-souken-archive",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "@types/dateformat": "3.0.1",
+ "dateformat": "4.5.1",
+ "next": "14.2.13",
+ "react": "^18",
+ "react-dom": "^18",
+ "zod": "3.23.8"
+ },
+ "devDependencies": {
+ "@types/node": "^20",
+ "@types/react": "^18",
+ "@types/react-dom": "^18",
+ "postcss": "^8",
+ "tailwindcss": "^3.4.1",
+ "typescript": "^5"
+ }
+}
diff --git a/postcss.config.mjs b/postcss.config.mjs
new file mode 100644
index 0000000..1a69fd2
--- /dev/null
+++ b/postcss.config.mjs
@@ -0,0 +1,8 @@
+/** @type {import('postcss-load-config').Config} */
+const config = {
+ plugins: {
+ tailwindcss: {},
+ },
+};
+
+export default config;
diff --git a/readme.dev.md b/readme.dev.md
new file mode 100644
index 0000000..22358a3
--- /dev/null
+++ b/readme.dev.md
@@ -0,0 +1,36 @@
+# ローカルで確認する方法
+別タブでwebサーバーを開く
+> ws -d .ssg-output -f dev
+
+ターミナルで以下のコマンドを実行
+> npm run build
+
+ブラウザで以下のURLを開く
+http://127.0.0.1:8000/akiba-souken-archive/
+
+# URL一覧
+## /iframe?src=article-67386
+sandboxのiframeで表示するページ
+
+## /article/all/page-1
+全記事の一覧をページングしながら表示
+
+## /article-search [後回し]
+全記事のjsonを読み込んで動的に検索する
+
+## /article/1234
+1つの記事を表示する
+
+## /article/1234/2
+1つの記事の2ページ目以降
+
+## /anime/all
+全てのアニメ一覧
+
+## /anime/123
+個別のアニメ情報
+レビュー、ヒトコト全部入り
+
+# 更新されている記事がある
+https://akiba-souken.com/article/51045/
+https://web.archive.org/web/20240000000000*/https://akiba-souken.com/article/51045/
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..f5441ed
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,5 @@
+# アキバ総研アーカイブ
+2024年10月に閉鎖したアキバ総研( https://akiba-souken.com/ )のアーカイブサイトのレポジトリです。
+
+詳しくは以下のURLを参照.
+https://fushihara.github.io/akiba-souken-archive/
diff --git a/src/app/_components/navigationHeader.tsx b/src/app/_components/navigationHeader.tsx
new file mode 100644
index 0000000..945cc26
--- /dev/null
+++ b/src/app/_components/navigationHeader.tsx
@@ -0,0 +1,35 @@
+import Link from "next/link";
+
+export function NavigationHeader() {
+ return (
+ <nav className="bg-white border-gray-200 dark:bg-gray-900">
+ <div className="max-w-screen-xl flex flex-wrap items-center justify-between mx-auto p-4">
+ <Link href={"/"} className="flex items-center space-x-3 rtl:space-x-reverse">
+ <span className="self-center text-2xl font-semibold whitespace-nowrap dark:text-white">アキバ総研アーカイブ</span>
+ </Link>
+ <button data-collapse-toggle="navbar-default" type="button" className="inline-flex items-center p-2 w-10 h-10 justify-center text-sm text-gray-500 rounded-lg md:hidden hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-gray-200 dark:text-gray-400 dark:hover:bg-gray-700 dark:focus:ring-gray-600" aria-controls="navbar-default" aria-expanded="false">
+ <span className="sr-only">Open main menu</span>
+ <svg className="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 17 14">
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M1 1h15M1 7h15M1 13h15" />
+ </svg>
+ </button>
+ <div className="hidden w-full md:block md:w-auto" id="navbar-default">
+ <ul className="font-medium flex flex-col p-4 md:p-0 mt-4 border border-gray-100 rounded-lg bg-gray-50 md:flex-row md:space-x-8 rtl:space-x-reverse md:mt-0 md:border-0 md:bg-white dark:bg-gray-800 md:dark:bg-gray-900 dark:border-gray-700">
+ <li>
+ <Link href={"/article/all/page-1"}>記事一覧</Link>
+ </li>
+ <li>
+ <Link href={`/article/category`}>カテゴリ一覧</Link>
+ </li>
+ <li>
+ <Link href={`/article/tag`}>タグ一覧</Link>
+ </li>
+ <li>
+ <Link href={"/anime/all"}>アニメ一覧</Link>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </nav>
+ );
+} \ No newline at end of file
diff --git a/src/app/anime/[animeId]/page.tsx b/src/app/anime/[animeId]/page.tsx
new file mode 100644
index 0000000..968ce20
--- /dev/null
+++ b/src/app/anime/[animeId]/page.tsx
@@ -0,0 +1,231 @@
+import "./style.css";
+import style from "./style.module.css";
+import dateformat from "dateformat";
+import { animeLoader, AnimeLoaderData } from "../../../util/animeLoader";
+import Link from "next/link";
+dateformat.i18n.dayNames = [
+ '日', '月', '火', '水', '木', '金', '土',
+ '日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'
+];
+type PageType = {
+ searchParams: Record<string, string>,
+ params: { animeId: string, }
+}
+export async function generateMetadata(context: PageType) {
+ const loadedData = await animeLoader.loadData().then(d => {
+ const r = d.find(a => a.animeId == Number(context.params.animeId))!;
+ return r;
+ });
+ return {
+ title: `アキバ総研アーカイブ:アニメ : ${loadedData.title}`,
+ }
+}
+export default async function Page(context: PageType) {
+ const loadedData = await animeLoader.loadData().then(d => {
+ const r = d.find(a => a.animeId == Number(context.params.animeId))!;
+ return r;
+ });;
+ return (
+ <div className="p-8 pb-20 gap-16 sm:p-20">
+ <h1>アニメ詳細</h1>
+ <div>
+ <span>タイトル:</span>
+ <span>{loadedData.title}</span>
+ </div>
+ <div>
+ <span>主カテゴリ:</span>
+ <span>{loadedData.primaryCategory}</span>
+ </div>
+ <div>
+ <span>開始時期:</span>
+ <span>{loadedData.startSeason}</span>
+ </div>
+ <div>
+ <span>アキバ総研のアニメ個別URL:</span>
+ <span className="inline-flex gap-4">
+ <a href={`https://akiba-souken.com/anime/${loadedData.animeId}/`} target="_blank" className={`${style["a"]}`}>公式</a>
+ <a href={`https://web.archive.org/web/*/https://akiba-souken.com/anime/${loadedData.animeId}/`} target="_blank" className={`${style["a"]}`}>IA検索結果</a>
+ <Link href={`/iframe?src=anime-${loadedData.animeId}`} className={`${style["a"]}`}>IAをiframe</Link>
+ </span>
+ </div>
+ {titleReviewScore(loadedData.titleReviewScore)}
+ {titleReviewList(loadedData.animeId, loadedData.titleReviewList)}
+ {titleHitokotoList(loadedData.titleHitokotoList)}
+ {episodeList(loadedData.episodeList)}
+ {episodeHitokotoList(loadedData.episodeHitokotoList)}
+ </div>
+ );
+}
+/** 作品自体のレビューの点数 */
+function titleReviewScore(titleReviewScore: AnimeLoaderData["titleReviewScore"]) {
+ return (
+ <>
+ <h1>作品レビュー</h1>
+ <div>
+ <span>ストーリー:</span>
+ <span>{titleReviewScore.story ?? "なし"}</span>
+ </div>
+ <div>
+ <span>作画:</span>
+ <span>{titleReviewScore.sakuga ?? "なし"}</span>
+ </div>
+ <div>
+ <span>キャラクター:</span>
+ <span>{titleReviewScore.character ?? "なし"}</span>
+ </div>
+ <div>
+ <span>音楽:</span>
+ <span>{titleReviewScore.music ?? "なし"}</span>
+ </div>
+ <div>
+ <span>オリジナリティ:</span>
+ <span>{titleReviewScore.originality ?? "なし"}</span>
+ </div>
+ <div>
+ <span>演出:</span>
+ <span>{titleReviewScore.storyboard ?? "なし"}</span>
+ </div>
+ <div>
+ <span>声優:</span>
+ <span>{titleReviewScore.voice ?? "なし"}</span>
+ </div>
+ <div>
+ <span>歌:</span>
+ <span>{titleReviewScore.song ?? "なし"}</span>
+ </div>
+ <div>
+ <span>満足度:</span>
+ <span>{titleReviewScore.manzokudo ?? "なし"}</span>
+ </div>
+ </>
+ );
+}
+function titleReviewList(animeId: number, list: AnimeLoaderData["titleReviewList"]) {
+ // レビューの一覧
+ const PPV = 20;
+ const reviewListLinks: JSX.Element[] = [];
+ for (let i = 0; i < Math.ceil(list.length / PPV); i++) {
+ const itemFrom = (i * PPV) + 1;
+ const itemTo = Math.min(itemFrom + PPV - 1, list.length);
+ if (i == 0) {
+ // 1ページ目
+ reviewListLinks.push(<h2 key={`review-header`}>レビュー自体の一覧</h2>)
+ reviewListLinks.push(
+ <div key={`review-list-${i}`}>
+ <span>新着 {itemFrom}~{itemTo}件目</span>
+ <span className="inline-flex gap-4">
+ <a href={`https://akiba-souken.com/anime/${animeId}/review/`} target="_blank" className={`${style["a"]}`}>公式</a>
+ <a href={`https://web.archive.org/web/*/https://akiba-souken.com/anime/${animeId}/review/`} target="_blank" className={`${style["a"]}`}>IA検索結果</a>
+ <Link href={`/iframe?src=anime-${animeId}-review`} className={`${style["a"]}`}>IAをiframe</Link>
+ </span>
+ </div>
+ );
+ } else {
+ // 2ページ目移行
+ const page = i + 1;
+ reviewListLinks.push(
+ <div key={`review-list-${i}`}>
+ <span>新着 {itemFrom}~{itemTo}件目</span>
+ <span className="inline-flex gap-4">
+ <a href={`https://akiba-souken.com/anime/${animeId}/review/?page=${page}`} target="_blank" className={`${style["a"]}`}>公式</a>
+ <a href={`https://web.archive.org/web/*/https://akiba-souken.com/anime/${animeId}/review/?page=${page}`} target="_blank" className={`${style["a"]}`}>IA検索結果</a>
+ <Link href={`/iframe?src=anime-${animeId}-review-p${page}`} className={`${style["a"]}`}>IAをiframe</Link>
+ </span>
+ </div>
+ );
+ }
+ }
+ const reviewList: JSX.Element[] = [];
+ if (0 < list.length) {
+ reviewList.push(<h2 key="head">個別のレビュー一覧</h2>)
+ reviewList.push(<span key="notice">レビューの一覧だとネタバレのレビューが表示されないので、個別のレビューのリンクを作成します</span>)
+ for (const v of list) {
+ const timestampStr = dateformat(new Date(v.timestampSec * 1000), "yyyy/mm/dd(ddd)HH:MM:ss");
+ const index = list.indexOf(v);
+ reviewList.push(
+ <div key={`review-list-${v.reviewId}`}>
+ <span className="inline-flex gap-4">
+ <span>{index + 1}/{list.length}</span>
+ <a href={`https://akiba-souken.com/anime/${animeId}/review/${v.reviewId}/`} target="_blank" className={`${style["a"]}`}>公式</a>
+ <a href={`https://web.archive.org/web/*/https://akiba-souken.com/anime/${animeId}/review/${v.reviewId}/`} target="_blank" className={`${style["a"]}`}>IA検索結果</a>
+ <Link href={`/iframe?src=anime-${animeId}-review-${v.reviewId}`} className={`${style["a"]}`}>IAをiframe</Link>
+ <span>投稿日時:{timestampStr}</span>
+ <span>スコア:{v.score}</span>
+ <span>ネタバレ:{v.isSpoiler ? "はい" : "いいえ"}</span>
+ <span>投稿者:{v.userName}</span>
+ </span>
+ </div>
+ );
+ }
+ }
+ return (<>
+ <h1>作品のレビュー一覧( {list.length} 件 )</h1>
+ {reviewListLinks}
+ {reviewList}
+ </>);
+}
+function titleHitokotoList(list: AnimeLoaderData["titleHitokotoList"]) {
+ const hitokotoList: JSX.Element[] = [];
+ if (0 < list.length) {
+ hitokotoList.push(<div key={`notice`}>ヒトコトは個別のURLが無いので必要最低限のみテキストを転載します</div>)
+ for (const h of list) {
+ const timestampStr = dateformat(new Date(h.timestampSec * 1000), "yyyy/mm/dd(ddd)HH:MM:ss")
+ const index = list.indexOf(h);
+ hitokotoList.push(
+ <div key={`h-${h.hitokotoId}`}>
+ {index + 1}/{list.length} {timestampStr} {h.reviewHtml}
+ </div>
+ )
+ }
+ }
+ return (<>
+ <h1>作品のヒトコト一覧( {list.length} 件 )</h1>
+ {hitokotoList}
+ </>);
+}
+function episodeList(list: AnimeLoaderData["episodeList"]) {
+ const episodeElementList: JSX.Element[] = [];
+ if (0 < list.length) {
+ for (const e of list) {
+ const index = list.indexOf(e);
+ episodeElementList.push(
+ <div key={`e-${e.episodeId}`}>
+ {index + 1}/{list.length} 「{e.subTitle}」 ヒトコト数:{e.reviewCount} スコア:{e.score ?? "なし"}
+ </div>
+ )
+ }
+ }
+ return (<>
+ <h1>作品のエピソード ( {list.length} 件 )</h1>
+ {episodeElementList}
+ </>);
+}
+function episodeHitokotoList(list: AnimeLoaderData["episodeHitokotoList"]) {
+ const hitokotoList: JSX.Element[] = [];
+ const totalHitokotoCount = list.map(h => h.hitokotoList.length).reduce((a, b) => a + b, 0)
+ if (0 < list.length) {
+ hitokotoList.push(<div key={`notice`}>ヒトコトは個別のURLが無いので必要最低限のみテキストを転載します</div>)
+ let index = 0;
+ for (const e of list) {
+ index += 1;
+ for (const h of e.hitokotoList) {
+ const timestampStr = dateformat(new Date(h.timestampSec * 1000), "yyyy/mm/dd(ddd)HH:MM:ss")
+ hitokotoList.push(
+ <div key={`h-${e.episodeId}-${h.hitokotoId}`}>
+ {index}/{totalHitokotoCount} 第{e.episodeId}話のヒトコト. {timestampStr} {h.reviewHtml}
+ </div>
+ )
+ }
+ }
+ }
+ return (<>
+ <h1>作品のエピソードごとのヒトコト ( {totalHitokotoCount} 件 )</h1>
+ {hitokotoList}
+ </>);
+}
+export async function generateStaticParams() {
+ const loadedData = await animeLoader.loadData();
+ return loadedData.map(c => {
+ return { animeId: String(c.animeId) };
+ }) satisfies PageType["params"][];
+} \ No newline at end of file
diff --git a/src/app/anime/[animeId]/style.css b/src/app/anime/[animeId]/style.css
new file mode 100644
index 0000000..b7d67c0
--- /dev/null
+++ b/src/app/anime/[animeId]/style.css
@@ -0,0 +1,3 @@
+h1,h2 {
+ all: revert;
+}
diff --git a/src/app/anime/[animeId]/style.module.css b/src/app/anime/[animeId]/style.module.css
new file mode 100644
index 0000000..fd9a86f
--- /dev/null
+++ b/src/app/anime/[animeId]/style.module.css
@@ -0,0 +1,3 @@
+.a {
+ all: revert;
+} \ No newline at end of file
diff --git a/src/app/anime/all/page.tsx b/src/app/anime/all/page.tsx
new file mode 100644
index 0000000..9c4f4e6
--- /dev/null
+++ b/src/app/anime/all/page.tsx
@@ -0,0 +1,64 @@
+import { animeLoader } from "../../../util/animeLoader";
+import style from "./style.module.css";
+import Link from "next/link";
+type PageType = {
+ searchParams: Record<string, string>,
+ params: {}
+}
+export async function generateMetadata(context: PageType) {
+ return {
+ title: `アキバ総研アーカイブ:アニメ一覧`,
+ }
+}
+export default async function Page(context: PageType) {
+ const loadedData = await animeLoader.loadData();
+ return (
+ <div className="p-1 gap-16">
+ <div className="text-right">全:{loadedData.length}件</div>
+ <table className="w-full">
+ <thead className="bg-white border-b sticky top-0 text-md font-medium text-gray-900">
+ <tr>
+ <th scope="col" className="px-6 py-4 text-left">
+ No
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ カテゴリ
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ 開始時期
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ 満足度
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ タイトル
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ レビュー件数
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ ヒトコト件数
+ </th>
+ </tr>
+ </thead>
+ <tbody className="">
+ {loadedData.map(d => {
+ return (
+ <tr className={`bg-white border-b transition duration-300 ease-in-out hover:bg-gray-100 text-sm text-gray-900 font-light`}>
+ <td className={`px-6 py-1 whitespace-nowrap ${style["akiba-souken-archive-anchor"]}`}>
+ <Link href={`/anime/${d.animeId}`}>{d.animeId}</Link>
+ </td>
+ <td className="px-6 py-1 whitespace-nowrap">{d.primaryCategory}</td>
+ <td className="px-6 py-1 whitespace-nowrap">{d.startSeason}</td>
+ <td className={`px-6 py-1 whitespace-nowrap ${d.titleReviewScore.manzokudo == null ? "text-gray-300" : ""}`}>{d.titleReviewScore.manzokudo ?? "なし"}</td>
+ <td className="px-6 py-1 whitespace-nowrap">{d.title}</td>
+ <td className={`px-6 py-1 whitespace-nowrap ${d.titleReviewList.length == 0 ? "text-gray-300" : ""}`}>{d.titleReviewList.length}</td>
+ <td className={`px-6 py-1 whitespace-nowrap ${d.titleHitokotoList.length == 0 ? "text-gray-300" : ""}`}>{d.titleHitokotoList.length}</td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ </div>
+ );
+}
diff --git a/src/app/anime/all/style.module.css b/src/app/anime/all/style.module.css
new file mode 100644
index 0000000..20abae7
--- /dev/null
+++ b/src/app/anime/all/style.module.css
@@ -0,0 +1,3 @@
+.akiba-souken-archive-anchor {
+ @apply underline text-blue-600 hover:text-blue-800 visited:text-purple-600
+}
diff --git a/src/app/article/_components/articleListElement.tsx b/src/app/article/_components/articleListElement.tsx
new file mode 100644
index 0000000..21ee947
--- /dev/null
+++ b/src/app/article/_components/articleListElement.tsx
@@ -0,0 +1,123 @@
+import Link from "next/link";
+import dateformat from "dateformat";
+import { ArticleLoader } from "../../../util/articleLoader";
+dateformat.i18n.dayNames = [
+ '日', '月', '火', '水', '木', '金', '土',
+ '日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'
+];
+type DisplayData = Awaited<ReturnType<ArticleLoader["loadData"]>>[number];
+export function ArticleListElement(displayData: DisplayData[]) {
+ return (
+ <table className="w-full">
+ <thead className="bg-white border-b sticky top-0 text-md font-medium text-gray-900">
+ <tr>
+ <th scope="col" className="px-6 py-4 text-left">
+ No
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ カテゴリ
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ タイトル
+ </th>
+ <th scope="col" className="px-6 py-4 text-left">
+ 日時
+ </th>
+ </tr>
+ </thead>
+ <tbody className="">
+ {displayData.map(d => {
+ const officialLinkTitle = `公式のakiba-souken.com へのリンク。閉鎖後は繋がらなくなるはず`;
+ const iaSearchResultLinkTitle = `InternetArchive の検索結果へのリンク`;
+ const iframeLinkTitle = `Iframeを使ってInternetArchiveに記録されたアーカイブを表示します`;
+ const topCategory = d.breadLinks[0];
+ const timestampStr = dateformat(new Date(d.timestampMs), "yyyy/mm/dd(ddd)HH:MM");
+ const originalUrl = `https://akiba-souken.com/article/${d.articleId}/`;
+ const page2After = (() => {
+ if (d.maxPageNumber == 1) {
+ return [(<></>)];
+ }
+ const result: JSX.Element[] = [
+ (<hr key={`hr`} />)
+ ];
+ for (let page = 2; page <= d.maxPageNumber; page++) {
+ result.push(
+ <div className="flex gap-4 text-xs text-gray-300" key={`page-${page}`}>
+ <a href={`https://akiba-souken.com/article/${d.articleId}/?page=${page}`} target="_blank" className="transition duration-300 ease-in-out hover:text-gray-900" title={officialLinkTitle}>公式</a>
+ <a href={`https://web.archive.org/web/*/https://akiba-souken.com/article/${d.articleId}/?page=${page}`} target="_blank" className="transition duration-300 ease-in-out hover:text-gray-900" title={iaSearchResultLinkTitle}>IA検索結果</a>
+ <Link href={`/iframe?src=article-${d.articleId}-${page}`} className="transition duration-300 ease-in-out hover:text-gray-900" title={iframeLinkTitle}>IAをiframe</Link> Page:{page}
+ </div>
+ );
+ }
+ return result;
+ })();
+ // パンくずリスト部分を作成
+ let breadElement = (<span key={`notice`}></span>);
+ if (0 < d.breadLinks.length) {
+ const breadChildElement: JSX.Element[] = [];
+ breadChildElement.push(<span className="mr-[-0.5rem]" key={`label`}>パンくずリスト:</span>);
+ for (const bread of d.breadLinks) {
+ if (d.breadLinks.indexOf(bread) != 0) {
+ breadChildElement.push(<span>></span>);
+ }
+ // 1つ目のパンくずリストはカテゴリだけど、2つ目以降のパンくずリストはタグと同じ
+ if (d.breadLinks.indexOf(bread) == 0) {
+ breadChildElement.push(<Link href={`/article/category/${bread}`} className="transition duration-300 ease-in-out hover:text-gray-900" key={`bread-${bread}`}>{bread}</Link>);
+ } else {
+ breadChildElement.push(<Link href={`/article/tag/${bread}`} className="transition duration-300 ease-in-out hover:text-gray-900" key={`bread-${bread}`}>{bread}</Link>);
+ }
+ }
+ breadElement = (
+ <span className="flex gap-0.5">
+ {breadChildElement}
+ </span>
+ );
+ }
+ // タグ部分を作成
+ let tagElement = (<span key={`notice`}>タグ無し</span>);
+ if (d.tags.length != 0) {
+ const tagChildElements: JSX.Element[] = [];
+ tagChildElements.push(<span className="mr-[-0.5rem]" key={`label`}>タグ:</span>);
+ for (const tag of d.tags) {
+ if (d.tags.indexOf(tag) != 0) {
+ tagChildElements.push(<span>/</span>);
+ }
+ tagChildElements.push(<Link href={`/article/tag/${tag}`} className="transition duration-300 ease-in-out hover:text-gray-900" key={`tag-${tag}`}>{tag}</Link>);
+ }
+ tagElement = (
+ <span className="flex gap-0.5">
+ {tagChildElements}
+ </span>
+ );
+ }
+ const hatebuElement = (
+ <a href={`https://b.hatena.ne.jp/entry/${originalUrl}`}>
+ <img src={`https://b.hatena.ne.jp/entry/image/${originalUrl}`} />
+ </a>
+ );
+ return (
+ <tr className="bg-white border-b transition duration-300 ease-in-out hover:bg-gray-100 text-sm text-gray-900 font-light">
+ <td className="px-1 py-1">{d.articleId}</td>
+ <td className="px-1 py-1">
+ <Link href={`/article/category/${topCategory}`} className="transition duration-300 ease-in-out hover:text-gray-900 original-href" >{topCategory}</Link>
+ </td>
+ <td className="px-1 py-1">
+ <div>{d.title}</div>
+ <div className="flex gap-4 text-xs text-gray-300">
+ <a href={originalUrl} target="_blank" className="transition duration-300 ease-in-out hover:text-gray-900" title={officialLinkTitle}>公式</a>
+ <a href={`https://web.archive.org/web/*/${originalUrl}`} target="_blank" className="transition duration-300 ease-in-out hover:text-gray-900" title={iaSearchResultLinkTitle}>IA検索結果</a>
+ <Link href={`/iframe?src=article-${d.articleId}`} className="transition duration-300 ease-in-out hover:text-gray-900" title={iframeLinkTitle}>IAをiframe</Link>
+ {breadElement}
+ {tagElement}
+ {hatebuElement}
+ </div>
+ {page2After}
+ </td>
+ <td className="px-1 py-1">{timestampStr}</td>
+ </tr>
+ );
+ })}
+ </tbody>
+ </table>
+ );
+} \ No newline at end of file
diff --git a/src/app/article/all/[pageId]/page.tsx b/src/app/article/all/[pageId]/page.tsx
new file mode 100644
index 0000000..fd72772
--- /dev/null
+++ b/src/app/article/all/[pageId]/page.tsx
@@ -0,0 +1,165 @@
+import Link from "next/link";
+import { ArticleLoader } from "../../../../util/articleLoader";
+import dateformat from "dateformat";
+import { createPagenation } from "../../../../util/pagenation";
+import { ArticleListElement } from "../../_components/articleListElement";
+dateformat.i18n.dayNames = [
+ '日', '月', '火', '水', '木', '金', '土',
+ '日曜日', '月曜日', '火曜日', '水曜日', '木曜日', '金曜日', '土曜日'
+];
+type PageType = {
+ searchParams: Record<string, string>,
+ params: {
+ pageId: string,
+ }
+}
+export async function generateMetadata(context: PageType) {
+ const pageId = getPageIdNumber(context.params.pageId);
+ return {
+ title: `アキバ総研アーカイブ:ページ ${pageId}`,
+ }
+}
+export default async function Page(context: PageType) {
+ const pageId = getPageIdNumber(context.params.pageId);
+ const al = new ArticleLoader()
+ const loadedData = await al.loadData();
+ const chunkdData = chunk(loadedData, PPV);
+ const displayData = chunkdData[pageId - 1];
+ return (
+ <div className="p-1 gap-16">
+ {pagenationElement(pageId, chunkdData.length)}
+ <div className="text-right">全:{loadedData.length}件</div>
+ {ArticleListElement(displayData)}
+ {pagenationElement(pageId, chunkdData.length)}
+ </div>
+ );
+}
+function pagenationElement(now: number, max: number) {
+ const pagenationData = createPagenation({ now: now, max: max, between: 2 });
+ const liElements: JSX.Element[] = [];
+ for (const v of pagenationData) {
+ if (v.type == "back") {
+ if (v.link == null) {
+ liElements.push(
+ <li key={v.key}>
+ <span className="flex items-center justify-center px-4 h-10 ms-0 leading-tight text-gray-500 bg-white border border-e-0 border-gray-300 rounded-s-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
+ <span className="sr-only">Previous</span>
+ <svg className="w-3 h-3 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 1 1 5l4 4" />
+ </svg>
+ </span>
+ </li>
+ );
+ } else {
+ liElements.push(
+ <li key={v.key}>
+ <Link href={`/article/all/page-${v.link}`} className="flex items-center justify-center px-4 h-10 ms-0 leading-tight text-gray-500 bg-white border border-e-0 border-gray-300 rounded-s-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
+ <span className="sr-only">Previous</span>
+ <svg className="w-3 h-3 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 1 1 5l4 4" />
+ </svg>
+ </Link>
+ </li>
+ );
+ }
+ } else if (v.type == "next") {
+ if (v.link == null) {
+ liElements.push(
+ <li key={v.key}>
+ <span className="flex items-center justify-center px-4 h-10 leading-tight text-gray-500 bg-white border border-gray-300 rounded-e-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
+ <span className="sr-only">Next</span>
+ <svg className="w-3 h-3 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4" />
+ </svg>
+ </span>
+ </li>
+ );
+ } else {
+ liElements.push(
+ <li key={v.key}>
+ <Link href={`/article/all/page-${v.link}`} className="flex items-center justify-center px-4 h-10 leading-tight text-gray-500 bg-white border border-gray-300 rounded-e-lg hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white">
+ <span className="sr-only">Next</span>
+ <svg className="w-3 h-3 rtl:rotate-180" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 6 10">
+ <path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m1 9 4-4-4-4" />
+ </svg>
+ </Link>
+ </li>
+ );
+ }
+ } else if (v.type == "num") {
+ if (v.link == null) {
+ if (v.num == now) {
+ liElements.push(
+ <li key={v.key}>
+ <span
+ className="z-10 flex items-center justify-center px-4 h-10 leading-tight text-blue-600 border border-blue-300 bg-blue-50 hover:bg-blue-100 hover:text-blue-700 dark:border-gray-700 dark:bg-gray-700 dark:text-white"
+ >{v.num}</span>
+ </li>
+ );
+ } else {
+ liElements.push(
+ <li key={v.key}>
+ <span
+ className="flex items-center justify-center px-4 h-10 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
+ >{v.num}</span>
+ </li>
+ );
+ }
+ } else {
+ liElements.push(
+ <li key={v.key}>
+ <Link
+ href={`/article/all/page-${v.link}`}
+ className="flex items-center justify-center px-4 h-10 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
+ >{v.num}</Link>
+ </li>
+ );
+ }
+ } else if (v.type == "sp") {
+ <li key={v.key}>
+ <span
+ className="flex items-center justify-center px-4 h-10 leading-tight text-gray-500 bg-white border border-gray-300 hover:bg-gray-100 hover:text-gray-700 dark:bg-gray-800 dark:border-gray-700 dark:text-gray-400 dark:hover:bg-gray-700 dark:hover:text-white"
+ >...</span>
+ </li>
+ }
+ }
+ return (
+ <nav className="flex justify-center pt-10">
+ <ul className="flex items-center -space-x-px h-10 text-base">
+ {liElements}
+ </ul>
+ </nav>
+ );
+}
+function getPageIdNumber(pageIdStr: string) {
+ const m = pageIdStr.match(/page-(\d+)/)!;
+ const id = Number(m[1]);
+ return id;
+}
+const PPV = Number(process.env["AKIBA_SOUKEN_AR_ITEM_PPV"] ?? "100");
+if (!Number.isInteger(PPV)) {
+ throw new Error(`AKIBA_SOUKEN_AR_ITEM_PPVに整数をセットして下さい`);
+}
+//export const dynamicParams = true;
+export async function generateStaticParams() {
+ const al = new ArticleLoader()
+ const loadedData = await al.loadData();
+ const chunkdData = chunk(loadedData, PPV);
+ return chunkdData.map((data, index) => {
+ return { pageId: `page-${index + 1}`, data: data };
+ });
+}
+// export const generateStaticParams = async () => {
+// return [{ articleid: "123" }];
+// };
+
+function chunk<T = any>(list: T[], len: number) {
+ if (len <= 0) {
+ throw new Error();
+ }
+ const result: T[][] = [];
+ for (let i = 0; i < list.length; i += len) {
+ result.push(list.slice(i, i + len));
+ }
+ return result;
+} \ No newline at end of file
diff --git a/src/app/article/category/[categoryName]/page.tsx b/src/app/article/category/[categoryName]/page.tsx
new file mode 100644
index 0000000..3e60a89
--- /dev/null
+++ b/src/app/article/category/[categoryName]/page.tsx
@@ -0,0 +1,44 @@
+import { ArticleLoader } from "../../../../util/articleLoader";
+import { ArticleListElement } from "../../_components/articleListElement";
+type PageType = {
+ searchParams: Record<string, string>,
+ params: {
+ categoryName: string,
+ }
+}
+export async function generateMetadata(context: PageType) {
+ return {
+ title: `アキバ総研アーカイブ:カテゴリ ${decodeURIComponent(context.params.categoryName)}`,
+ }
+}
+export default async function Page(context: PageType) {
+ const al = new ArticleLoader()
+ const nowPageCategoryName = decodeURIComponent(context.params.categoryName);
+ const loadedData = await al.loadData().then(articles => {
+ const filterd = articles.filter(article => {
+ if (article.breadLinks.length == 0) {
+ return false;
+ }
+ const category = article.breadLinks[0];
+ if (category == nowPageCategoryName) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+ return filterd;
+ });
+ return (
+ <div className="p-1 gap-16">
+ <div className="text-center">カテゴリ:{nowPageCategoryName} の記事一覧</div>
+ <div className="text-right">全:{loadedData.length}件</div>
+ {ArticleListElement(loadedData)}
+ </div>
+ );
+}
+export async function generateStaticParams() {
+ const categoryList = await new ArticleLoader().getCategoryList();
+ return categoryList.map((data, index) => {
+ return { categoryName: data.name };
+ });
+}
diff --git a/src/app/article/category/page.tsx b/src/app/article/category/page.tsx
new file mode 100644
index 0000000..4976054
--- /dev/null
+++ b/src/app/article/category/page.tsx
@@ -0,0 +1,29 @@
+import Link from "next/link";
+import { ArticleLoader } from "../../../util/articleLoader";
+import "./style.css";
+type PageType = {
+ searchParams: Record<string, string>,
+ params: {
+ tagName: string,
+ }
+}
+export async function generateMetadata(context: PageType) {
+ return {
+ title: `アキバ総研アーカイブ:カテゴリ一覧`,
+ }
+}
+export default async function Page(context: PageType) {
+ const tagList = await new ArticleLoader().getCategoryList();
+ const categoryListElement: JSX.Element[] = [];
+ tagList.forEach(t => {
+ categoryListElement.push(<span key={t.name}><Link href={`/article/category/${t.name}`}>{t.name}({t.count})</Link></span>)
+ })
+ return (
+ <div className="p-8 pb-20 gap-16 sm:p-20">
+ <h1>記事にセットされているカテゴリの一覧</h1>
+ <div className="flex gap-2">
+ {categoryListElement}
+ </div>
+ </div>
+ );
+}
diff --git a/src/app/article/category/style.css b/src/app/article/category/style.css
new file mode 100644
index 0000000..f8a5c5f
--- /dev/null
+++ b/src/app/article/category/style.css
@@ -0,0 +1,3 @@
+h1,h2,h3 {
+ all: revert;
+}
diff --git a/src/app/article/tag/[tagName]/page.tsx b/src/app/article/tag/[tagName]/page.tsx
new file mode 100644
index 0000000..3df1f1e
--- /dev/null
+++ b/src/app/article/tag/[tagName]/page.tsx
@@ -0,0 +1,40 @@
+import { ArticleLoader } from "../../../../util/articleLoader";
+import { ArticleListElement } from "../../_components/articleListElement";
+type PageType = {
+ searchParams: Record<string, string>,
+ params: {
+ tagName: string,
+ }
+}
+export async function generateMetadata(context: PageType) {
+ return {
+ title: `アキバ総研アーカイブ:ページ ${context.params.tagName}`,
+ }
+}
+export default async function Page(context: PageType) {
+ const al = new ArticleLoader()
+ const nowPageTagName = decodeURIComponent(context.params.tagName);
+ const loadedData = await al.loadData().then(articles => {
+ const filterd = articles.filter(article => {
+ if (article.tags.includes(nowPageTagName)) {
+ return true;
+ } else {
+ return false;
+ }
+ });
+ return filterd;
+ });
+ return (
+ <div className="p-1 gap-16">
+ <div className="text-center">タグ:{nowPageTagName} の記事一覧</div>
+ <div className="text-right">全:{loadedData.length}件</div>
+ {ArticleListElement(loadedData)}
+ </div>
+ );
+}
+export async function generateStaticParams() {
+ const tagList = await new ArticleLoader().getTagList();
+ return tagList.map((data, index) => {
+ return { tagName: data.name };
+ });
+}
diff --git a/src/app/article/tag/page.tsx b/src/app/article/tag/page.tsx
new file mode 100644
index 0000000..211ca94
--- /dev/null
+++ b/src/app/article/tag/page.tsx
@@ -0,0 +1,30 @@
+import Link from "next/link";
+import { ArticleLoader } from "../../../util/articleLoader";
+import "./style.css";
+type PageType = {
+ searchParams: Record<string, string>,
+ params: {
+ tagName: string,
+ }
+}
+export async function generateMetadata(context: PageType) {
+ return {
+ title: `アキバ総研アーカイブ:タグ一覧`,
+ }
+}
+export default async function Page(context: PageType) {
+ const tagList = await new ArticleLoader().getTagList();
+ const tagsElement: JSX.Element[] = [];
+ tagList.forEach(t => {
+ tagsElement.push(<span key={t.name}><Link href={`/article/tag/${t.name}`}>{t.name}({t.count})</Link></span>)
+ })
+ return (
+ <div className="p-8 pb-20 gap-16 sm:p-20">
+ <h1>著名なタグ一覧</h1>
+ <h1>記事にセットされているタグの一覧</h1>
+ <div className="flex gap-2 flex-wrap">
+ {tagsElement}
+ </div>
+ </div>
+ );
+}
diff --git a/src/app/article/tag/style.css b/src/app/article/tag/style.css
new file mode 100644
index 0000000..f8a5c5f
--- /dev/null
+++ b/src/app/article/tag/style.css
@@ -0,0 +1,3 @@
+h1,h2,h3 {
+ all: revert;
+}
diff --git a/src/app/globals.css b/src/app/globals.css
new file mode 100644
index 0000000..3b8b598
--- /dev/null
+++ b/src/app/globals.css
@@ -0,0 +1,31 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+:root {
+ --background: #ffffff;
+ --foreground: #171717;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #0a0a0a;
+ --foreground: #ededed;
+ }
+}
+
+body {
+ color: var(--foreground);
+ background: var(--background);
+ font-family: Arial, Helvetica, sans-serif;
+}
+
+@layer utilities {
+ .text-balance {
+ text-wrap: balance;
+ }
+}
+
+.original-href {
+ all: revert;
+} \ No newline at end of file
diff --git a/src/app/iframe/page.tsx b/src/app/iframe/page.tsx
new file mode 100644
index 0000000..e26e921
--- /dev/null
+++ b/src/app/iframe/page.tsx
@@ -0,0 +1,52 @@
+'use client'
+import Link from "next/link";
+import { useEffect, useState } from "react";
+type PageType = {
+ searchParams: Record<string, string>,
+ params: {
+ articleId: string,
+ }
+}
+export default function Page(context: PageType) {
+ const [iframeUrl, setIframeUrl] = useState("abount:blank");
+ const [iaSearchPageUrl, setIaSearchPageUrl] = useState("abount:blank");
+ useEffect(() => {
+ const paramString = new URL(document.location.href).searchParams.get("src") ?? "";
+ const showOriginalUrl = parseParam(paramString);
+ if (showOriginalUrl != null) {
+ const iaUrlKey = `20241001000000`;// 同じURLで記事が更新されている場合もあるので、最終版であろう2024/10/01 00:00:00を指定
+ const iframeUrl = `https://web.archive.org/web/${iaUrlKey}if_/${showOriginalUrl}`;
+ setIframeUrl(iframeUrl);
+ const iaSeachPageUrlStr = `https://web.archive.org/web/*/${showOriginalUrl}`;
+ setIaSearchPageUrl(iaSeachPageUrlStr);
+ }
+ }, []);
+ return (
+ <div className="flex-1 flex flex-col">
+ <div className="text-center">InternetArchiveの検索結果のURL: <Link href={iaSearchPageUrl} className="original-href">{iaSearchPageUrl}</Link></div>
+ <div className="text-center">iframeのURL: <Link href={iframeUrl} className="original-href" title="日付の部分は固定値だが、適切な日付に自動的にリダイレクトされるので問題なし">{iframeUrl}</Link></div>
+ <div className="text-center text-xs">※アキバ総研のページをIneternetArchiveで見る時はJavascriptを無効化しないと、強制的にリダイレクトされてしまいます</div>
+ <div className="w-full flex-1 flex flex-col">
+ <div className="text-center text-3xl text-red-600">以下のページはInternetArchiveに保存されている内容です。</div>
+ <iframe src={iframeUrl} sandbox="" className="flex-1 border-solid border-black border p-4 bg-gray-700"></iframe>
+ </div>
+ </div>
+ );
+}
+function parseParam(paramString: string) {
+ let m: RegExpMatchArray | null = null;
+ if (m = paramString.match(/^article-(\d+)$/)) {
+ return `https://akiba-souken.com/article/${m[1]}/`
+ } else if (m = paramString.match(/^article-(\d+)-(\d+)$/)) {
+ return `https://akiba-souken.com/article/${m[1]}/?page=${m[2]}`
+ } else if (m = paramString.match(/^anime-(\d+)$/)) {
+ return `https://akiba-souken.com/anime/${m[1]}/`;
+ } else if (m = paramString.match(/^anime-(\d+)-review$/)) {
+ return `https://akiba-souken.com/anime/${m[1]}/review/`;
+ } else if (m = paramString.match(/^anime-(\d+)-review-p(\d+)$/)) {
+ return `https://akiba-souken.com/anime/${m[1]}/review/?page=${m[2]}`;
+ } else if (m = paramString.match(/^anime-(\d+)-review-(\d+)$/)) {
+ return `https://akiba-souken.com/anime/${m[1]}/review/${m[2]}/`;
+ }
+ return null;
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 0000000..9d5b2b9
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,28 @@
+import type { Metadata } from "next";
+import "./globals.css";
+import { NavigationHeader } from "./_components/navigationHeader";
+
+export const metadata: Metadata = {
+ title: "アキバ総研アーカイブ",
+ //description: "Generated by create next app",
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+ <html lang="en">
+ <body
+ className={`antialiased min-h-screen flex flex-col`}
+ >
+ <NavigationHeader></NavigationHeader>
+ {children}
+ <div>
+ 全てのデータは<a href="https://akiba-souken.com/" target="_blank">アキバ総研</a>より。
+ </div>
+ </body>
+ </html>
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 0000000..58a7429
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,30 @@
+import "./style.css"
+import style from "./style.module.css"
+export default function Home() {
+ return (
+ <div className="min-h-screen p-8 pb-20">
+ アキバ総研( <a href="https://akiba-souken.com/" target="_blank" className={`${style["a"]}`}>https://akiba-souken.com/</a> ) の記事一覧をアーカイブしたページです。このサイト内にアキバ総研の記事本文はありません。<br />
+ アキバ総研は2024/08/01にサービス終了のアナウンスが行われ、2024/08/31に記事の更新が停止しました。<br />
+ このアーカイブは2024/08/31~2024/09/30 の間に取得していますが、特定の瞬間のスナップショットではないので、データの不整合がある可能性があります。<br />
+ アキバ総研は記事ページ内にドメインがakiba-souken.com でない場合にトップページにリダイレクトされるjavascriptが仕込まれていますので、InternetArchiveから閲覧する時はdevtool等でjsをオフにする必要があります。<br />
+ このページはGithubActions&GithubPagesでデプロイされています。生データが欲しい方はレポジトリ https://github.com/fushihara/akiba-souken-archive をチェックアウトする事をおすすめします。
+ <h3>アキバ総研とは?</h3>
+ アキバ総研はカカクコムが2002/08から2024/09まで公開していたアキバ系ニュースサイトです。<br />
+ 初期のURLは http://kakaku.com/akiba/ 形式で、価格.comのサブディレクトリにありました。<br />
+ 2007年頃から http://akiba.kakaku.com/ に移動し、2011年頃から https://akiba-souken.com/ ドメインに移動しました。<br />
+ 過去の記事の継続性についてですが、少なくともkakaku.comドメイン配下のURLは2024/09時点で全てakiba-souken.comのトップページにリダイレクトされてアクセス出来ませんでした。<br />
+ akiba-souken.com内の最古の記事のタイムスタンプは2006/12/14なので、akiba.kakaku.comから akiba-souken.com への移行は行われたのかもしれませんが、最初期の記事はInternetArchiveにしか無い模様です<br />
+ その他にもPCパーツのレビュー機能が2014/09/30のリニューアルで削除されていたりします。記事がメインコンテンツではありますが、それ以外のサブコンテンツは既に永久に失われているものがあります。<br />
+ https://mevius.5ch.net/test/read.cgi/esite/1690495133/595 も参照。
+ <h3>このアーカイブサイトについて</h3>
+ ソースコード、データは以下のレポジトリに一式があります。nodeのnext.jsのSSGでGithubPagesサイトを構築しています。<br />
+ https://github.com/fushihara/akiba-souken-archive<br />
+ このサイトの記事一覧などが欲しい場合は、スクレイピングをするより上記レポジトリからファイルを落としたほうが早いです。<br />
+ スクレイピングに使ったツール一式は以下の通りです。こちらのスクリプトはdenoを使っています。DLしたhtmlをsqliteに保存して、同じ内容で複数アクセスが起きないように工夫しています。<br />
+ xxxxx
+ <p />
+ このアーカイブサイトは閉鎖決定時点の全てのコンテンツを網羅している訳ではありません。<br />
+ 通常のarticle形式以外の、投票( https://akiba-souken.com/vote/ )やアニメまとめ( https://akiba-souken.com/anime/matome/ )、アニメランキング( https://akiba-souken.com/anime/ranking/ )は抜けています。<br />
+ </div>
+ );
+}
diff --git a/src/app/style.css b/src/app/style.css
new file mode 100644
index 0000000..f8a5c5f
--- /dev/null
+++ b/src/app/style.css
@@ -0,0 +1,3 @@
+h1,h2,h3 {
+ all: revert;
+}
diff --git a/src/app/style.module.css b/src/app/style.module.css
new file mode 100644
index 0000000..fd9a86f
--- /dev/null
+++ b/src/app/style.module.css
@@ -0,0 +1,3 @@
+.a {
+ all: revert;
+} \ No newline at end of file
diff --git a/src/util/animeLoader.ts b/src/util/animeLoader.ts
new file mode 100644
index 0000000..120123b
--- /dev/null
+++ b/src/util/animeLoader.ts
@@ -0,0 +1,93 @@
+import { readFile } from "fs/promises";
+import { z } from "zod";
+const zodType = z.array(
+ z.strictObject({
+ animeId: z.number().int().min(0),
+ title: z.string(),
+ primaryCategory: z.string(),
+ startSeason: z.string(),
+ titleReviewScore: z.object({
+ story: z.number().min(0).nullable(),
+ sakuga: z.number().min(0).nullable(),
+ character: z.number().min(0).nullable(),
+ music: z.number().min(0).nullable(),
+ originality: z.number().min(0).nullable(),
+ storyboard: z.number().min(0).nullable(),
+ voice: z.number().min(0).nullable(),
+ song: z.number().min(0).nullable(),
+ manzokudo: z.number().min(0).nullable(),
+ }),
+ titleReviewList: z.array(
+ z.object({
+ reviewId: z.number().int(),
+ userIconUrl: z.string().url(),
+ userName: z.string(),
+ score: z.number(),
+ isSpoiler: z.boolean(),
+ timestampSec: z.number().int().min(0),
+ commentCount: z.number().int().min(0),
+ iineCount: z.number().int().min(0)
+ }),
+ ),
+ titleHitokotoList: z.array(
+ z.object({
+ hitokotoId: z.number().int(),
+ userIcon: z.string().url(),
+ userName: z.string(),
+ reviewHtml: z.string(),
+ timestampSec: z.number().int(),
+ })
+ ),
+ episodeList: z.array(z.object({
+ episodeId: z.number().int().min(0),
+ subTitle: z.string(),
+ reviewCount: z.number().int().min(0),
+ score: z.number().nullable(),
+ })),
+ episodeHitokotoList: z.array(z.object({
+ episodeId: z.number().int().min(0),
+ hitokotoList: z.array(z.object({
+ hitokotoId: z.number().int().min(0),
+ userIcon: z.string(),
+ userName: z.string().min(1),
+ reviewHtml: z.string().min(1),
+ timestampSec: z.number().int().min(0),
+ }))
+ })),
+ })
+);
+const MAX_ITEM_LIMIT = process.env["AKIBA_SOUKEN_MAX_ITEM_LIMIT"];
+export type AnimeLoaderData = z.infer<typeof zodType>[number]
+class AnimeLoader {
+ constructor() { }
+ private dataCache: Awaited<ReturnType<AnimeLoader["loadData_"]>> | null = null;
+ async loadData() {
+ if (this.dataCache != null) {
+ return this.dataCache;
+ }
+ const loadedData = await this.loadData_();
+ this.dataCache = loadedData;
+ return loadedData;
+ }
+ private async loadData_() {
+ const articleJsonPath = process.env["AKIBA_SOUKEN_ANIME_JSON"]!;
+ //console.log(`[${articleJsonPath}]`);
+ const jsonStr = await readFile(articleJsonPath, { encoding: "utf-8" }).then(text => {
+ const jsonObj = JSON.parse(text);
+ return jsonObj;
+ });
+ const parsedObj = zodType.parse(jsonStr).filter(a => {
+ if (a.titleHitokotoList.length != 0) { return true; }
+ if (a.titleReviewList.length != 0) { return true; }
+ if (a.titleReviewScore.manzokudo != null) { return true; }
+ if (a.episodeList.find(e => e.score != null || e.reviewCount != 0)) { return true; }
+ if (a.episodeHitokotoList.length != 0) { return true; }
+ return false;
+ });
+ if (MAX_ITEM_LIMIT != null) {
+ parsedObj.length = Math.min(Number(MAX_ITEM_LIMIT), parsedObj.length);
+ }
+ return parsedObj;
+ }
+}
+export const animeLoader = new AnimeLoader(); \ No newline at end of file
diff --git a/src/util/articleLoader.ts b/src/util/articleLoader.ts
new file mode 100644
index 0000000..787725a
--- /dev/null
+++ b/src/util/articleLoader.ts
@@ -0,0 +1,88 @@
+import { readFile } from "fs/promises";
+import { z } from "zod";
+const zodType = z.array(
+ z.object({
+ articleId: z.number().int().min(0),
+ title: z.string(),
+ timestampMs: z.number().int().min(0),
+ maxPageNumber: z.number().int().min(1),
+ tags: z.array(z.string()),
+ breadLinks: z.array(z.string()).min(1),
+ })
+);
+const MAX_ITEM_LIMIT = process.env["AKIBA_SOUKEN_MAX_ITEM_LIMIT"];
+export class ArticleLoader {
+ constructor() { }
+ async loadData() {
+ const articleJsonPath = process.env["AKIBA_SOUKEN_ARTICLE_JSON"]!;
+ //console.log(`[${articleJsonPath}]`);
+ const jsonStr = await readFile(articleJsonPath, { encoding: "utf-8" }).then(text => {
+ const jsonObj = JSON.parse(text);
+ return jsonObj;
+ });
+ const parsedObj = zodType.parse(jsonStr);
+ parsedObj.sort((a, b) => {
+ return b.timestampMs - a.timestampMs;
+ });
+ if (MAX_ITEM_LIMIT != null) {
+ parsedObj.length = Math.min(Number(MAX_ITEM_LIMIT), parsedObj.length);
+ }
+ return parsedObj;
+ }
+ async getCategoryList() {
+ const loadedData = await this.loadData();
+ // key:カテゴリ名 , val:回数
+ const categoryCount = new Map<string, number>();
+ for (const a of loadedData) {
+ if (a.breadLinks.length == 0) {
+ continue;
+ }
+ const category = a.breadLinks[0];
+ if (categoryCount.has(category)) {
+ categoryCount.set(category, categoryCount.get(category)! + 1);
+ } else {
+ categoryCount.set(category, 1);
+ }
+ }
+ const categoryList: { name: string, count: number }[] = [];
+ for (const [name, count] of categoryCount) {
+ categoryList.push({ name: name, count });
+ }
+ categoryList.sort((a, b) => {
+ return b.count - a.count;
+ });
+ return categoryList;
+ }
+ async getTagList() {
+ const loadedData = await this.loadData();
+ // key:タグ名 , val:回数
+ const tagCount = new Map<string, number>();
+ for (const a of loadedData) {
+ const tags = new Set<string>();
+ a.tags.forEach(t => {
+ tags.add(t);
+ });
+ // パンくずリストの2件目以降はタグ扱いになっている
+ a.breadLinks.forEach((b, index) => {
+ if (index != 0) {
+ tags.add(b);
+ }
+ })
+ for (const tag of tags) {
+ if (tagCount.has(tag)) {
+ tagCount.set(tag, tagCount.get(tag)! + 1);
+ } else {
+ tagCount.set(tag, 1);
+ }
+ }
+ }
+ const tagList: { name: string, count: number }[] = [];
+ for (const [name, count] of tagCount) {
+ tagList.push({ name: name, count });
+ }
+ tagList.sort((a, b) => {
+ return b.count - a.count;
+ });
+ return tagList;
+ }
+}
diff --git a/src/util/pagenation.ts b/src/util/pagenation.ts
new file mode 100644
index 0000000..15ee01b
--- /dev/null
+++ b/src/util/pagenation.ts
@@ -0,0 +1,91 @@
+export type PagenationProp = {
+ now: number,
+ max: number,
+ between: number,
+}
+export function createPagenation(prop: PagenationProp) {
+ if (prop.between < 0) {
+ throw new Error(`betweenは0以上にして下さい`);
+ }
+ if (prop.max < 0) {
+ throw new Error(`maxは0以上にして下さい`);
+ }
+ if (prop.now < 0) {
+ throw new Error(`nowは0以上にして下さい`);
+ }
+ if (prop.max < prop.now) {
+ throw new Error(`nowの値はmaxと同じか、より小さな値にして下さい. now:${prop.now} , max:${prop.max}`);
+ }
+ if (prop.max == 0) {
+ return [];
+ }
+ const MIN = 1;
+ // 左・中・右の3ブロックの変数を作成
+ const blockLeft = MIN;
+ let blockMiddle = [
+ ...Array.from({ length: prop.between }).map((_, index) => {
+ const i = prop.now - prop.between + index;
+ return i;
+ }),
+ prop.now,
+ ...Array.from({ length: prop.between }).map((_, index) => {
+ const i = prop.now + index + 1;
+ return i;
+ }),
+ ];
+ blockMiddle = blockMiddle.filter(i => {
+ if (i <= blockLeft) {
+ return false;
+ }
+ if (prop.max <= i) {
+ return false;
+ }
+ return true;
+ });
+ const blockRight = prop.max;
+ type A = { type: "back", key: string, link: number | null };
+ type B = { type: "next", key: string, link: number | null };
+ type C = { type: "sp", key: string, };
+ type D = { type: "num", key: string, link: number | null, num: number }
+ // 結合を作成
+ const pageIdList: (A | B | C | D)[] = [];
+ {
+ // 左戻る矢印
+ if (prop.now == MIN) {
+ pageIdList.push({ type: "back", key: "back", link: null });
+ } else {
+ pageIdList.push({ type: "back", key: "back", link: prop.now - 1 });
+ }
+ // 最初のページ
+ pageIdList.push({ type: "num", key: `p-${blockLeft}`, link: blockLeft, num: blockLeft });
+ let lastPageId = blockLeft;
+ // 左と中の間の…を入れるかどうか
+ if (0 < blockMiddle.length && lastPageId + 1 != blockMiddle[0]) {
+ pageIdList.push({ type: "sp", key: "sp-left" });
+ }
+ blockMiddle.forEach(m => {
+ pageIdList.push({ type: "num", key: `p-${m}`, link: m, num: m });
+ lastPageId = m;
+ });
+ if (0 < blockMiddle.length && lastPageId + 1 != blockRight) {
+ // 最後のページ
+ pageIdList.push({ type: "sp", key: "sp-right" });
+ }
+ if (lastPageId != blockRight) {
+ pageIdList.push({ type: "num", key: `p-${blockRight}`, link: blockRight, num: blockRight });
+ lastPageId = blockRight;
+ }
+ // 右矢印
+ if (prop.now == prop.max) {
+ pageIdList.push({ type: "next", key: "next", link: null });
+ } else {
+ pageIdList.push({ type: "next", key: "next", link: prop.now + 1 });
+ }
+ pageIdList.forEach(p => {
+ if ("link" in p && p.link == prop.now) {
+ p.link = null;
+ }
+ })
+ }
+ return pageIdList
+} \ No newline at end of file
diff --git a/tailwind.config.ts b/tailwind.config.ts
new file mode 100644
index 0000000..021c393
--- /dev/null
+++ b/tailwind.config.ts
@@ -0,0 +1,19 @@
+import type { Config } from "tailwindcss";
+
+const config: Config = {
+ content: [
+ "./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
+ "./src/components/**/*.{js,ts,jsx,tsx,mdx}",
+ "./src/app/**/*.{js,ts,jsx,tsx,mdx}",
+ ],
+ theme: {
+ extend: {
+ colors: {
+ background: "var(--background)",
+ foreground: "var(--foreground)",
+ },
+ },
+ },
+ plugins: [],
+};
+export default config;
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..35cdabd
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,40 @@
+{
+ "compilerOptions": {
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "target": "ESNext",
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": [
+ "./src/*"
+ ]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+} \ No newline at end of file