diff --git a/README.md b/README.md index 9b3ecb2..118e829 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# support_uwuzu_net -ゆずねっとのサポートページです! -簡易的なものなので今後アプデしていきます。 +# ゆずねっとサポート + +本家:https://github.com/Daichimarukana/support_uwuzu_net diff --git a/contents.json b/contents.json index e9cc19b..92d829a 100644 --- a/contents.json +++ b/contents.json @@ -1,60 +1,60 @@ { - "articles": [ - { - "title": "安全への取り組み", - "id": "safety_policy", - "date": "2025-03-20", - "description": "ゆずねっとで行われている安全への取り組みについてです!", - "content": "ゆずねっとでは、uwuzuの安全機能を含め、ユーザーの皆様に快適で安心してご利用いただけるよう、さまざまな対策を行っています。 \nこのページでは、その一部をご紹介します! \n## 不正アクセスへの対策 \nゆずねっとでは、不正アクセスへの対策として、以下の対策を行っています! \n \n### ログイン通知 \n新たなログインが確認された場合には、ユーザーへそのことを通知する機能が実装されています。 \n \n### ユーザーデータの暗号化・ハッシュ化 \n以下の情報は、各ユーザーごとに発行される鍵を使って暗号化し、安全に保護しています。 \n- メールアドレス \n- 二段階認証の生成コード \n- IPアドレス \n \nまた、以下のデータは、ハッシュ化(一度変換すると元に戻せない形式)した上で保存しています。 \n- パスワード \n- 二段階認証のバックアップコード \n \n### Captcha認証 \nログイン時に、人間による正しい操作であることを確認するためのCaptcha認証を導入しています。 \n \n### Self-XSSにも強い認証方式 \nユーザーが正しくログインしているかを確認するための鍵を2つ用いて認証をしています。 \nこのうち一つが流出してももう片方の鍵を生成したりログインをすることはできません。 \n \n## \"もしも\"に備えたサーバー運営 \nゆずねっとでは、災害やサーバー障害などの\"もしも\"に備えた、運営を行っております。 \n以下のような対策により、ユーザーのデータが決して壊れないように対策しています。 \n \n### 停電対策 \n万が一停電が発生しても、UPS(無停電電源装置)によりサーバーへの電力供給を継続します。 \nさらに、停電が発生すると25秒以内に運営へ通知が届き、5分以上復旧しない場合はデータを保護するためにサーバーを自動的に停止します。 \n \n### データ破損対策 \n万が一ゆずねっとのサーバーが壊れたとしても、データを復旧できるように、ゆずねっとでは定期的にバックアップを保存するようになっています。 \nバックアップは暗号化され、別のサーバーに最大31日間保存されるため、迅速な復旧が可能です。 \n \n### 異常事態対策 \nサーバーに異常が発生し、アクセスできなくなる事態を防ぐため、24時間体制で自動監視を行っています。 \n万が一サーバーが停止すると、即座に運営に通知され、早急に対応できる仕組みになっています。 \n" - }, - { - "title": "ステータスページについて", - "id": "status_uwuzu_net", - "date": "2025-02-11", - "description": "ゆずねっとにサービスステータスページができました!", - "content": "ゆずねっとにサービスステータスページができました! \nゆずねっとの稼働状況やメンテナンスのお知らせなどが表示されます! \n \n是非ご活用ください。 \nゆずねっとステータスページ" - }, - { - "title": "バルス祭り", - "id": "balsu_festival", - "date": "2024-08-30", - "description": "本日考えられるバルス祭りについて書きました", - "content": "バルス祭りについてですが... \n現在ゆずねっとはユーザ数が100人程度のためおそらくゆずねっと上でバルス祭りを行ってもサーバーはダウンしないものと考えております。 \n是非バルスしちゃってください。 \nなお、TLを埋め尽くすほどのユーズはお控えください。 \n \nゆずねっと以外にも簡単にバルスできるボタンを作りましたので是非ご活用ください! \nバルスる" - }, - { - "title": "ユーズで使用できる装飾について", - "id": "use_markdown", - "date": "2024-08-30", - "description": "uwuzuで使用できる装飾一覧です。", - "content": "ユーズで使用できる装飾(一部Markdown)は以下のものです。 \n``` \n# [ここにテキスト] (h1サイズで文字を表示) \n## [ここにテキスト] (h2サイズで文字を表示) \n### [ここにテキスト] (h3サイズで文字を表示) \n[[buruburu [ここにテキスト]]] (文字をブルブルさせて表示) \n`[ここにテキスト]` (インラインコードとして表示) \n***[ここにテキスト]*** (斜体と太字を適用して表示) \n**[ここにテキスト]** (太字を適用して表示) \n*[ここにテキスト]* (斜体を適用して表示) \n~~[ここにテキスト]~~ (文字に取り消し線を入れて表示) \n>>> [ここにテキスト] (引用として表示) \n||[ここにテキスト]|| (カーソルをのせて文字を表示) \n- [ここにテキスト] (箇条書きとして左に点を表示) \n``` \nまた、以下の場合に自動的にユーズに装飾が加わることがあります \n``` \n@[userid] (ユーザー名のリンクで表示) \n:[emojiname]: (カスタム絵文字で表示) \n#[hashtag] (ハッシュタグで表示) \n \nhttps://youtube.com/watch?v=XXXXXXXXXXX (YouTubeの動画埋め込み(youtu.beなども対応)) \nhttps://nicovideo.jp/watch/smXXXXXXX (ニコニコ動画の埋め込み(nico.msなども対応)) \n \nその他URL (URLとして装飾) \n``` \nこれらの装飾を組み合わせて是非面白いユーズを作ってみてください!" - }, - { - "title": "TLにご飯の画像が多すぎる", - "id": "meshitero_timeline", - "date": "2024-08-29", - "description": "ゆずねっとで飯テロが横行している件についてです", - "content": "ご飯の画像が多すぎることに関してですが、これはこれで私(daichimarukana)は良いと思っていますので大丈夫でしょう \nもし深夜時間帯に閲覧してしまい猛烈な食欲に駆り立てられてしまったのであれば申し訳ございません。 \n \nご飯の画像が多すぎることを解消するにはユーザー数を増やすことや様々なトピックのユーズをすることが重要だと考えていますので、是非新規さんを連れてきたりじゃんじゃんユーズしてみてください🤗(利用規約の範囲内でね!) \n" - }, - { - "title": "パスワードを忘れた", - "id": "forgot_password", - "date": "2024-08-29", - "description": "パスワードの復元(変更)方法について書きました", - "content": "パスワードを忘れた場合、ゆずねっとでは二通りの方法でパスワードの復元が行えます。 \n**なお、パスワードの復元にはメールアドレスの設定が必要です。** \n**メールアドレスの設定をされたいない方はパスワードの復元はできません。** \n \n## 1. ユーザーIDとメールアドレスの入力 \nゆずねっとの未ログイン時トップページより、ログインボタンを押し、下部にあるパスワード復元ボタンを押してください。 \nパスワードの復元ページに進みますと、ユーザーIDとメールアドレスの入力ができるので設定したユーザーIDとメールアドレスを入力して次へ進んでください。 \n \n## 2. パスワードの再設定 \n二段階認証を設定しているアカウントでしたら、二段階認証コードと新しいパスワードを入力して次へ進んでください。 \nもし二段階認証を設定していないアカウントでしたら、メールで認証するボタンを押してください。 \nこれによりメールで二段階認証ができるようになります。 \nメールの送信には少々時間がかかりますのでページの読み込みが終るまでお待ち下さい。 \nメールを受信できたら、メールに記載されている二段階認証コードと新しいパスワードを入力して次へ進んでください。 \n \n## 3. ログイン \nこれでパスワードの復元(パスワードリセット)は完了です! \n通常のログインページより新しいパスワードでログインをしてください。" - }, - { - "title": "ユーザーIDを変更したい", - "id": "change_userid", - "date": "2024-08-29", - "description": "結論から申し上げますとユーザーIDの変更はできません。", - "content": "残念ながらuwuzuの仕様上ユーザーIDの変更はできません。" - }, - { - "title": "サポートサイトを開設しました!", - "id": "open_uwuzu_net_support", - "date": "2024-08-29", - "description": "ゆずねっとのサポートサイトを開設しました!", - "content": "ゆずねっとのサポートサイトを開設しました! \n是非このサポートサイトをご活用ください! \n \n## 使い方 \nサイトを開くとトップページに記事一覧が表示されます。 \nお求めの記事をタップすると記事が表示されます! \n \nまた、サイト上部の検索欄より検索も可能です!" - } - ] -} \ No newline at end of file + "articles": [ + { + "title": "安全への取り組み", + "id": "safety_policy", + "date": "2025-03-20", + "description": "ゆずねっとで行われている安全への取り組みについてです!", + "content": "ゆずねっとでは、uwuzuの安全機能を含め、ユーザーの皆様に快適で安心してご利用いただけるよう、さまざまな対策を行っています。 \nこのページでは、その一部をご紹介します! \n## 不正アクセスへの対策 \nゆずねっとでは、不正アクセスへの対策として、以下の対策を行っています! \n \n### ログイン通知 \n新たなログインが確認された場合には、ユーザーへそのことを通知する機能が実装されています。 \n \n### ユーザーデータの暗号化・ハッシュ化 \n以下の情報は、各ユーザーごとに発行される鍵を使って暗号化し、安全に保護しています。 \n- メールアドレス \n- 二段階認証の生成コード \n- IPアドレス \n \nまた、以下のデータは、ハッシュ化(一度変換すると元に戻せない形式)した上で保存しています。 \n- パスワード \n- 二段階認証のバックアップコード \n \n### Captcha認証 \nログイン時に、人間による正しい操作であることを確認するためのCaptcha認証を導入しています。 \n \n### Self-XSSにも強い認証方式 \nユーザーが正しくログインしているかを確認するための鍵を2つ用いて認証をしています。 \nこのうち一つが流出してももう片方の鍵を生成したりログインをすることはできません。 \n \n## \"もしも\"に備えたサーバー運営 \nゆずねっとでは、災害やサーバー障害などの\"もしも\"に備えた、運営を行っております。 \n以下のような対策により、ユーザーのデータが決して壊れないように対策しています。 \n \n### 停電対策 \n万が一停電が発生しても、UPS(無停電電源装置)によりサーバーへの電力供給を継続します。 \nさらに、停電が発生すると25秒以内に運営へ通知が届き、5分以上復旧しない場合はデータを保護するためにサーバーを自動的に停止します。 \n \n### データ破損対策 \n万が一ゆずねっとのサーバーが壊れたとしても、データを復旧できるように、ゆずねっとでは定期的にバックアップを保存するようになっています。 \nバックアップは暗号化され、別のサーバーに最大31日間保存されるため、迅速な復旧が可能です。 \n \n### 異常事態対策 \nサーバーに異常が発生し、アクセスできなくなる事態を防ぐため、24時間体制で自動監視を行っています。 \n万が一サーバーが停止すると、即座に運営に通知され、早急に対応できる仕組みになっています。 \n" + }, + { + "title": "ステータスページについて", + "id": "status_uwuzu_net", + "date": "2025-02-11", + "description": "ゆずねっとにサービスステータスページができました!", + "content": "ゆずねっとにサービスステータスページができました! \nゆずねっとの稼働状況やメンテナンスのお知らせなどが表示されます! \n \n是非ご活用ください。 \nゆずねっとステータスページ" + }, + { + "title": "バルス祭り", + "id": "balsu_festival", + "date": "2024-08-30", + "description": "本日考えられるバルス祭りについて書きました", + "content": "バルス祭りについてですが... \n現在ゆずねっとはユーザ数が100人程度のためおそらくゆずねっと上でバルス祭りを行ってもサーバーはダウンしないものと考えております。 \n是非バルスしちゃってください。 \nなお、TLを埋め尽くすほどのユーズはお控えください。 \n \nゆずねっと以外にも簡単にバルスできるボタンを作りましたので是非ご活用ください! \nバルスる" + }, + { + "title": "ユーズで使用できる装飾について", + "id": "use_markdown", + "date": "2024-08-30", + "description": "uwuzuで使用できる装飾一覧です。", + "content": "ユーズで使用できる装飾(一部Markdown)は以下のものです。 \n``` \n# [ここにテキスト] (h1サイズで文字を表示) \n## [ここにテキスト] (h2サイズで文字を表示) \n### [ここにテキスト] (h3サイズで文字を表示) \n[[buruburu [ここにテキスト]]] (文字をブルブルさせて表示) \n`[ここにテキスト]` (インラインコードとして表示) \n***[ここにテキスト]*** (斜体と太字を適用して表示) \n**[ここにテキスト]** (太字を適用して表示) \n*[ここにテキスト]* (斜体を適用して表示) \n~~[ここにテキスト]~~ (文字に取り消し線を入れて表示) \n>>> [ここにテキスト] (引用として表示) \n||[ここにテキスト]|| (カーソルをのせて文字を表示) \n- [ここにテキスト] (箇条書きとして左に点を表示) \n``` \nまた、以下の場合に自動的にユーズに装飾が加わることがあります \n``` \n@[userid] (ユーザー名のリンクで表示) \n:[emojiname]: (カスタム絵文字で表示) \n#[hashtag] (ハッシュタグで表示) \n \nhttps://youtube.com/watch?v=XXXXXXXXXXX (YouTubeの動画埋め込み(youtu.beなども対応)) \nhttps://nicovideo.jp/watch/smXXXXXXX (ニコニコ動画の埋め込み(nico.msなども対応)) \n \nその他URL (URLとして装飾) \n``` \nこれらの装飾を組み合わせて是非面白いユーズを作ってみてください!" + }, + { + "title": "TLにご飯の画像が多すぎる", + "id": "meshitero_timeline", + "date": "2024-08-29", + "description": "ゆずねっとで飯テロが横行している件についてです", + "content": "ご飯の画像が多すぎることに関してですが、これはこれで私(daichimarukana)は良いと思っていますので大丈夫でしょう \nもし深夜時間帯に閲覧してしまい猛烈な食欲に駆り立てられてしまったのであれば申し訳ございません。 \n \nご飯の画像が多すぎることを解消するにはユーザー数を増やすことや様々なトピックのユーズをすることが重要だと考えていますので、是非新規さんを連れてきたりじゃんじゃんユーズしてみてください🤗(利用規約の範囲内でね!) \n" + }, + { + "title": "パスワードを忘れた", + "id": "forgot_password", + "date": "2024-08-29", + "description": "パスワードの復元(変更)方法について書きました", + "content": "パスワードを忘れた場合、ゆずねっとでは二通りの方法でパスワードの復元が行えます。 \n**なお、パスワードの復元にはメールアドレスの設定が必要です。** \n**メールアドレスの設定をされたいない方はパスワードの復元はできません。** \n \n## 1. ユーザーIDとメールアドレスの入力 \nゆずねっとの未ログイン時トップページより、ログインボタンを押し、下部にあるパスワード復元ボタンを押してください。 \nパスワードの復元ページに進みますと、ユーザーIDとメールアドレスの入力ができるので設定したユーザーIDとメールアドレスを入力して次へ進んでください。 \n \n## 2. パスワードの再設定 \n二段階認証を設定しているアカウントでしたら、二段階認証コードと新しいパスワードを入力して次へ進んでください。 \nもし二段階認証を設定していないアカウントでしたら、メールで認証するボタンを押してください。 \nこれによりメールで二段階認証ができるようになります。 \nメールの送信には少々時間がかかりますのでページの読み込みが終るまでお待ち下さい。 \nメールを受信できたら、メールに記載されている二段階認証コードと新しいパスワードを入力して次へ進んでください。 \n \n## 3. ログイン \nこれでパスワードの復元(パスワードリセット)は完了です! \n通常のログインページより新しいパスワードでログインをしてください。" + }, + { + "title": "ユーザーIDを変更したい", + "id": "change_userid", + "date": "2024-08-29", + "description": "結論から申し上げますとユーザーIDの変更はできません。", + "content": "残念ながらuwuzuの仕様上ユーザーIDの変更はできません。" + }, + { + "title": "サポートサイトを開設しました!", + "id": "open_uwuzu_net_support", + "date": "2024-08-29", + "description": "ゆずねっとのサポートサイトを開設しました!", + "content": "ゆずねっとのサポートサイトを開設しました! \n是非このサポートサイトをご活用ください! \n \n## 使い方 \nサイトを開くとトップページに記事一覧が表示されます。 \nお求めの記事をタップすると記事が表示されます! \n \nまた、サイト上部の検索欄より検索も可能です!" + } + ] +} diff --git a/css/style.css b/css/style.css index 2f8e21e..e14ae1d 100644 --- a/css/style.css +++ b/css/style.css @@ -1,31 +1,36 @@ -@import url('https://fonts.googleapis.com/css2?family=BIZ+UDGothic:wght@400;700&family=BIZ+UDPGothic:wght@400;700&family=Inter:wght@100..900&display=swap'); -body{ +@import url("https://fonts.googleapis.com/css2?family=BIZ+UDGothic:wght@400;700&family=BIZ+UDPGothic:wght@400;700&family=Inter:wght@100..900&display=swap"); + +body { margin: 0px; width: 100%; height: fit-content; - background-color: #F7F7F7; + background-color: #f7f7f7; } -.top{ + +.top { width: 100%; margin: 0px; height: 64px; display: flex; justify-content: space-between; - background-color: #FFC832; + background-color: #ffc832; border-radius: 0px 0px 15px 15px; } -.top .left{ + +.top .left { margin-left: 0px; display: flex; } -.top .left img{ + +.top .left img { width: 48px; height: 48px; margin: 8px; object-fit: cover; border-radius: 8px; } -.top .left a{ + +.top .left a { display: block; text-decoration: none; font-size: 32px; @@ -35,38 +40,44 @@ body{ width: fit-content; font-family: "Inter", "BIZ UDPGothic", sans-serif; margin: 8px 0px; - color: #FFFFFF; + color: #ffffff; } -.top .right{ + +.top .right { margin-right: 0px; display: flex; } -.top .right .searchbox{ + +.top .right .searchbox { width: 200px; height: 28px; margin: 16px; border-radius: 25px; padding-left: 8px; - border: solid 1px #FFFFFF; + border: solid 1px #ffffff; outline: none; } -main{ + +main { margin: 0px auto; width: 1080px; } -main h1{ + +main h1 { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: bold; color: #252525; font-size: 48px; } -main p{ + +main p { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: normal; color: #252525; font-size: 16px; } -main .contents{ + +main .contents { margin-top: 32px; width: 100%; display: flex; @@ -74,108 +85,125 @@ main .contents{ height: fit-content; flex-wrap: wrap; } -main .contents .items{ + +main .contents .items { cursor: pointer; width: calc(50% - 40px); height: fit-content; - background-color: #FFFFFF; + background-color: #ffffff; border-radius: 10px; padding: 16px; margin-bottom: 16px; transition: all 250ms ease-out; } -main .contents .items:hover{ - box-shadow: 0 0px 48px 0 rgba(0, 0, 0, .05); + +main .contents .items:hover { + box-shadow: 0 0px 48px 0 rgba(0, 0, 0, 0.05); } -main .contents .items h1{ + +main .contents .items h1 { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: bold; color: #252525; font-size: 24px; margin: 4px 0px; } -main .contents .items p{ + +main .contents .items p { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: normal; color: #252525; font-size: 14px; margin: 4px 0px; } -main .contents .items .date{ + +main .contents .items .date { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: bold; color: #252525; font-size: 12px; } -main .article{ + +main .article { width: 100%; height: fit-content; } -main .article .date{ + +main .article .date { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: bold; color: #252525; font-size: 16px; } -main .article h1{ + +main .article h1 { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: bold; color: #252525; font-size: 48px; } -main .article h2{ + +main .article h2 { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: bold; color: #252525; font-size: 32px; } -main .article h3{ + +main .article h3 { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: bold; color: #252525; font-size: 24px; } -main .article p{ + +main .article p { font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: normal; color: #252525; font-size: 16px; } -main .article table{ + +main .article table { margin: 16px 0px; width: calc(fit-content + 128px); border: none; border-collapse: collapse; } -main .article td, th { + +main .article td, +th { padding: 4px 16px; border: solid 1px #252525; } -main .article pre{ + +main .article pre { width: 100%; height: fit-content; background-color: #252525; padding: 16px; border-radius: 15px; - color: #F7F7F7; + color: #f7f7f7; font-family: "BIZ UDGothic", sans-serif; } -.link_btn{ + +.link_btn { line-height: 64px; width: fit-content; height: 32px; padding: 8px 16px; - background-color: #FFC832; - color: #F7F7F7; + background-color: #ffc832; + color: #f7f7f7; text-decoration: none; font-family: "Inter", "BIZ UDPGothic", sans-serif; font-weight: normal; font-size: 20px; border-radius: 25px; } + .error { position: absolute; - animation: slideDown 5.0s ease-in-out forwards; + animation: slideDown 5s ease-in-out forwards; margin-top: 32px; margin-right: auto; margin-left: auto; @@ -184,12 +212,13 @@ main .article pre{ width: fit-content; height: 32px; z-index: 9999; - background-color: #FF4848; + background-color: #ff4848; border-radius: 20px; - box-shadow: 0 0px 48px 0 rgba(0, 0, 0, .15); + box-shadow: 0 0px 48px 0 rgba(0, 0, 0, 0.15); text-decoration: none; } -.error p{ + +.error p { margin-top: 4px; margin-bottom: 4px; margin-left: 12px; @@ -197,34 +226,46 @@ main .article pre{ line-height: 24px; font-family: "Inter", "BIZ UDPGothic", sans-serif; font-size: 16px; - color: #F7F7F7; - color: transparent; - text-shadow: 0 0 0 #F7F7F7; + color: #f7f7f7; + color: transparent; + text-shadow: 0 0 0 #f7f7f7; text-align: center; } + @keyframes slideDown { - 0%, 100% { transform: translateY(-40dvh); } - 20%, 90% { transform: translateY(0px); } + 0%, + 100% { + transform: translateY(-40dvh); + } + + 20%, + 90% { + transform: translateY(0px); + } } -@media screen and (max-width: 1080px) { - .top .left a{ +@media screen and (max-width: 581px) { + .top .left a { display: none; } - main{ + + main { margin: 0px auto; width: calc(100% - 32px); overflow-wrap: normal; } - main .contents{ + + main .contents { display: block; height: fit-content; } - main .contents .items{ + + main .contents .items { width: calc(100% - 32px); } - main .article pre{ + + main .article pre { width: calc(100% - 32px); overflow: scroll; } -} \ No newline at end of file +} diff --git a/index.html b/index.html index 58edacf..0674249 100644 --- a/index.html +++ b/index.html @@ -1,159 +1,49 @@ - + - - - ゆずねっとサポート - - - - - - - + + + + + + + ゆずねっとサポート -
-
- - ゆずねっとサポート + + + + -
- + + -
-
-
-

ゆずねっとサポート

-

ゆずねっとのサポートサイトです。
- 右上の検索欄から検索するか下から記事を選択すると閲覧ができます!

+
+
+

ゆずねっとサポート

+

+ ゆずねっとのサポートサイトです。 +
+ 右上の検索欄から検索するか下から記事を選択すると閲覧ができます! +

+
+
-
-
- -
- - - +
+
+ + diff --git a/js/script.js b/js/script.js new file mode 100644 index 0000000..1b17e00 --- /dev/null +++ b/js/script.js @@ -0,0 +1,190 @@ +// エラー表示 +function showError(text) { + const errorMsgElement = document.querySelector("#errorMsg"); + const errorElement = document.querySelector("#error"); + + if (errorMsgElement) { + errorMsgElement.textContent = text; + } + + if (errorElement) { + errorElement.style.display = "block"; + setTimeout(() => { + errorElement.style.display = "none"; + }, 5000); + } +} + +document.addEventListener("DOMContentLoaded", async () => { + try { + // contents.json読み込み + const response = await fetch("/contents.json"); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const data = await response.json(); + const articlesData = data.articles; + + if (!Array.isArray(articlesData)) { + throw new Error("記事データが不正な形式です"); + } + + // #contents読み込み + const contentsContainer = document.querySelector("#contents"); + + if (!contentsContainer) { + throw new Error("#contentsが見つかりません"); + } + + contentsContainer.innerHTML = ""; + + // 記事一覧作成 + articlesData.forEach(article => { + // ID取得 + const articleElement = document.createElement("div"); + articleElement.className = "items"; + articleElement.id = article.id; + + // 日付取得 + const dateElement = document.createElement("div"); + dateElement.className = "date"; + dateElement.textContent = String(article.date); + + // タイトル取得 + const titleElement = document.createElement("h1"); + titleElement.textContent = String(article.title); + + // 説明取得 + const descriptionElement = document.createElement("p"); + descriptionElement.textContent = String(article.description); + + // 親要素に追加 + articleElement.appendChild(dateElement); + articleElement.appendChild(titleElement); + articleElement.appendChild(descriptionElement); + contentsContainer.appendChild(articleElement); + }); + + // URLパラメータから記事IDを取得 + const urlParams = new URLSearchParams(window.location.search); + const articleId = urlParams.get("articles"); + + if (articleId) { + const article = articlesData.find(a => a && a.id === articleId); + if (article) { + showArticle(article); + } else { + showError("記事が存在しませんでした"); + } + } + + // 記事移動 + contentsContainer.addEventListener("click", (e) => { + const itemElement = e.target.closest(".items"); + if (itemElement && itemElement.id) { + const id = itemElement.id; + const article = articlesData.find(a => a && a.id === id); + + if (article) { + showArticle(article); + try { + history.pushState({ articleId: id }, article.title, `?articles=${id}`); + } catch (error) { + console.warn("History API操作に失敗しました:", error); + } + } else { + showError("記事が存在しませんでした"); + } + } + }); + + // 検索 + const searchBox = document.querySelector("#searchbox"); + + if (searchBox) { + searchBox.addEventListener("input", (e) => { + const searchText = (e.target.value || '').toLowerCase(); + const items = document.querySelectorAll("#contents .items"); + + items.forEach(item => { + const titleElement = item.querySelector("h1"); + const descriptionElement = item.querySelector("p"); + + if (titleElement && descriptionElement) { + const title = (titleElement.textContent || '').toLowerCase(); + const description = (descriptionElement.textContent || '').toLowerCase(); + + if (title.includes(searchText) || description.includes(searchText)) { + item.style.display = "block"; + } else { + item.style.display = "none"; + } + } + }); + }); + } + + // 記事表示 + function showArticle(article) { + // #docs・#top取得 + const docsElement = document.querySelector("#docs"); + const topElement = document.querySelector("#top"); + + // データ存在確認 + if (docsElement && topElement && article && article.title && article.date) { + // 記事div作成 + const articleDiv = document.createElement("div"); + articleDiv.className = "article"; + + // タイトル取得 + const titleElement = document.createElement("h1"); + titleElement.textContent = String(article.title); + + // 日付取得 + const dateElement = document.createElement("div"); + dateElement.className = "date"; + dateElement.textContent = String(article.date); + + // 内容取得 + const contentElement = document.createElement("div"); + contentElement.className = "content"; + + // Markdownパース + try { + const content = article.content || ''; + + if (typeof marked !== 'undefined' && typeof marked.parse === 'function') { + contentElement.innerHTML = marked.parse(content); + } else { + contentElement.textContent = content; + } + } catch (error) { + console.error("Markdownパースエラー:", error); + contentElement.textContent = article.content || ''; + } + + // 親要素に追加 + articleDiv.appendChild(titleElement); + articleDiv.appendChild(dateElement); + articleDiv.appendChild(contentElement); + + // 記事表示 + docsElement.innerHTML = ""; + docsElement.appendChild(articleDiv); + docsElement.style.display = "block"; + + // 記事一覧非表示 + const articleList = document.querySelectorAll(".articlelist"); + + for (let i = 0; i < articleList.length; i++) { + articleList[i].style.display = "none"; + } + } + } + } catch (error) { + console.error("データの読み込みに失敗しました:", error); + showError("データの読み込みに失敗しました"); + } +});