(add) misskey hub nextのブログ記事 (#44)
* initial commit * typo * typo * typo * 推敲 * typo * remove something * 推敲 * 推敲 * tweak seo * fix
@ -95,7 +95,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { InstanceInfo, InstanceItem } from '@/types/instances-info';
|
||||
import type { InstanceInfo, InstanceItem, InstancesStatsObj } from '@/types/instances-info';
|
||||
import { resolveObjPath } from '@/assets/js/misc';
|
||||
import langs from '@/assets/data/lang';
|
||||
|
||||
@ -108,11 +108,7 @@ import XIco from 'bi/x.svg';
|
||||
const { t, locale } = useI18n();
|
||||
const route = useRoute();
|
||||
const emits = defineEmits<{
|
||||
(e: 'load', value?: {
|
||||
notesCount?: number;
|
||||
usersCount?: number;
|
||||
instancesCount?: number;
|
||||
}): void;
|
||||
(e: 'load', value?: InstancesStatsObj): void;
|
||||
}>();
|
||||
|
||||
// ▼スマホ用ソート▼
|
||||
|
251
content/blog/2023-12-03-inside-misskey-hub-next.md
Normal file
@ -0,0 +1,251 @@
|
||||
---
|
||||
description: Misskey Hubが新しくなります!新しいMisskey Hubの機能やこだわり、今後のアップデートをご紹介。
|
||||
date: 2023-12-03
|
||||
---
|
||||
|
||||
# インサイド Misskey Hub Next
|
||||
|
||||
:::tip
|
||||
この記事は、[Misskey Alt Advent Calendar 2023](https://adventar.org/calendars/8658) 3日目の記事です。
|
||||
:::
|
||||
|
||||
![](/img/og/misskey-hub-screenshot-d.png)
|
||||
|
||||
こんにちは!Misskey 開発メンバーのかっこかりと申します。[Misskey.ioで某公立高校の部活動広報アカウント](https://misskey.io/@natureofmad_)の運営に参加したりもしています。
|
||||
|
||||
先日[しゅいろママからも発表](/blog/2023-12-01-2023recap/#misskey-hub-next)がありましたように、このたび、Misskeyプロジェクト公式サイト「Misskey Hub」の全面リニューアルを行うことになりました!今回、私が開発主担として、いまご覧いただいているこのサイトを制作しております。そこで、今日はこの新Misskey Hubの**機能・頑張った点・今後のアップデート**についてご紹介していきます!
|
||||
|
||||
## リニューアル後の構成
|
||||
|
||||
現行のMisskey Hubは[VuePress](https://v2.vuepress.vuejs.org/)をベースに作成されています。これは、ドキュメントを用意さえすればサイトの部分はある程度よしなにやってくれるというものです。
|
||||
|
||||
しかし、もともとのMisskey Hubの計画上、ドキュメント以外のページもかなり必要になるということで、ドキュメント生成に特化したフレームワークではなく、より拡張性が高く、汎用的な[Nuxt](https://nuxt.com/)をフレームワークとして採用しました。
|
||||
|
||||
:::warning
|
||||
VuePressよりは、今はどちらかというと[VitePress](https://vitepress.dev/)のほうがメジャーな感じがします。
|
||||
:::
|
||||
|
||||
:::g-details{summary="詳しい技術スタックはこちら"}
|
||||
|
||||
### フレームワーク
|
||||
|
||||
- [Nuxt](https://nuxt.com/)
|
||||
- [Tailwind CSS](https://tailwindcss.com/)
|
||||
- [Bootstrap](https://getbootstrap.com/) (※フォーム・ボタンのみ使用)
|
||||
|
||||
#### Nuxt Modules
|
||||
|
||||
- [Nuxt Content](https://content.nuxt.com/)
|
||||
- [Nuxt i18n](https://i18n.nuxtjs.org/)
|
||||
- [Nuxt Color Mode](https://color-mode.nuxtjs.org/)
|
||||
|
||||
### その他
|
||||
- [Bootstrap Icons](https://icons.getbootstrap.com/)
|
||||
- [ufo](https://github.com/unjs/ufo) - URLのパースなどができるライブラリ。超便利
|
||||
- [AiScript VSCode](https://github.com/aiscript-dev/aiscript-vscode) - AiScriptのコードハイライトに使用
|
||||
|
||||
### 各種サービス
|
||||
|
||||
- [Vercel](https://vercel.com/)
|
||||
- [Crowdin](https://crowdin.com/project/misskey-hub)
|
||||
|
||||
:::
|
||||
|
||||
## Nuxtとは?
|
||||
|
||||
[Nuxt](https://nuxt.com)は、Vue.jsを使ったWebアプリケーションフレームワークです。[静的サイト](/blog/2021-12-01-inside-misskey-hub/#静的サイト-is-何)の作成に使うための[SSG (Static Site Generator) ](/blog/2021-12-01-inside-misskey-hub/#ssg-is-何)の他に、Nuxt自体がサーバー本体となって動作するSSR (Server Side Rendering)、さらにSSGとSSRのいいとこ取りをしたISR (Incremental Static Regeneration) があり、それらを**サイトのディレクトリごとに切り替えて**使うことができます。
|
||||
|
||||
・・・まとめて言うと、NuxtとはWeb制作に使える**とても万能なツール**だということで大丈夫です。
|
||||
|
||||
そして、今回のMisskey Hub Nextでは、ドキュメントページとブログページに**ISR**を、それ以外のページに**SSG**を適用しています。略称多くてこんがらがりそう。
|
||||
|
||||
:::tip
|
||||
プロジェクト名に`Next`と入っているのでReactフレームワークの[Next.js](https://nextjs.org/)のほうを思い浮かべたかもしれませんが、**罠です。** Nuxtで作ってあります。
|
||||
:::
|
||||
|
||||
## ISRとは?
|
||||
|
||||
[SSG](/blog/2021-12-01-inside-misskey-hub/#ssg-is-何)では、サイトの生成時にまとめてすべてのページを書き出してしまう必要があります。しかし、Misskey Hubには大量のドキュメントとブログページ、さらには翻訳版ページもあります。それらをすべて前もって生成すると、**とんでもなく時間がかかってしまいます。**
|
||||
|
||||
![ビルド時間: 27分49秒](https://media.misskeyusercontent.com/io/b7b8180f-302e-4e4d-a1a4-ad60d3985051.png)
|
||||
|
||||
そこで登場するのがISRです。ISR (Incremental Static Regeneration) を使用すると、サイトの書き出しのタイミングではページの生成は行わず、**最初にそのページに訪問されたときにページの生成を行います。** これを、特にページ数の多い[ドキュメントページ](/docs/)と[ブログページ](/blog/)に適用したことで、実に**13倍**も高速な生成が可能となりました。
|
||||
|
||||
![ビルド時間: 1分32秒](https://media.misskeyusercontent.com/io/298919ff-fb33-4fa7-8a5c-cc3c89f65c58.webp)
|
||||
|
||||
## デザイン
|
||||
|
||||
デザインに関しては、現行Misskey Hubのデザインをベースに、主に以下の項目を重視してリニューアルを行いました。
|
||||
|
||||
- ページを見つける導線の改善
|
||||
- スマホでの操作感の改善
|
||||
- ドキュメント更新にかかる手間の改善
|
||||
|
||||
### ページを見つける導線の改善
|
||||
|
||||
現行のMisskey Hubでは、ドキュメントページの一覧などがなく、パッと見てどこに何があるのかがわかりにくい状態でした。
|
||||
|
||||
![](/img/blog/2023-12-03-inside-misskey-hub-next/1.png)
|
||||
|
||||
そこで、Misskey Hub Nextでは、ドキュメントページをディレクトリ構造から抜本的に見直しました。利用者別にページ階層を分けたほか、各セクションに見出しページを設けられるようにしました。
|
||||
|
||||
[ドキュメントのトップページ](/docs/)もかなり整理されたかと思います。
|
||||
|
||||
![](/img/blog/2023-12-03-inside-misskey-hub-next/2.png)
|
||||
|
||||
また、同様にドキュメントページのデザインも変更しました。左側には各ページが階層で表示され、右側には現在のページのもくじが表示されます。
|
||||
|
||||
![](/img/blog/2023-12-03-inside-misskey-hub-next/3.png)
|
||||
|
||||
### スマホでの操作感の改善
|
||||
|
||||
現行のMisskey Hubでは、モバイルナビゲーションが肥大化し、見にくくなってしまっていたほか、一部では文字が背景と重なって見えにくい部分もありました。
|
||||
また、スクロールの際にアニメーションがもたついて背景がカクカクする・・・といった問題も起きていました。
|
||||
|
||||
そこで、Misskey Hub Nextではスマホ表示でのデザインを改善し、一部ではスマホ専用のUIを設けて対応しました。上で紹介したドキュメントページの各種UIも、スマホの小さい画面でもアクセスできるように配置してあります。
|
||||
|
||||
### ドキュメント更新にかかる手間の改善
|
||||
|
||||
現行のMisskey Hubでは、ドキュメントは翻訳版も含めてすべてGithub上で管理されており、Githubを使ったことのない人にとって、ドキュメントの更新のハードルは高いものでした。
|
||||
|
||||
そこで、Misskey Hub Nextでは、翻訳の管理を外部サービス(Crowdin)で行えるように設計。技術的な知識不要で、翻訳に参加できるようになりました。
|
||||
|
||||
依然として新しいドキュメントの追加にはGithubが必要になってしまいますが、コレも今後どうにかしたいなぁ・・・と検討中です。Githubを使える方は、ドキュメントの拡充に引き続きご協力お願いします🙏
|
||||
|
||||
:::tip{label='お知らせ'}
|
||||
|
||||
現在、翻訳にご協力いただける方を募集中です! We are looking for translators for this site!
|
||||
|
||||
[![](/img/blog/2023-12-03-inside-misskey-hub-next/5.png)](https://crowdin.com/project/misskey-hub)
|
||||
|
||||
[翻訳サイト](https://crowdin.com/project/misskey-hub)をご覧頂いた上で、リストに既にある言語へ翻訳する場合は、そのままログインして作業を開始できます。
|
||||
|
||||
リストにない言語でも、[ご連絡](https://misskey.io/@kakkokari_gtyih)いただければ言語を追加します。
|
||||
|
||||
Misskeyをより多くの人に広めるために、ぜひご協力お願いします🙏
|
||||
|
||||
:::
|
||||
|
||||
:::g-details{summary="【マニアック】フォントのはなし"}
|
||||
Misskey Hub Nextでは、フォントの改善も行いました。
|
||||
|
||||
現行のMisskey Hubで使われているフォント(Kosugi Maru)は、英字が歪で、英単語や英文になると読みにくくなっていました。また、日本語の漢字部分が中国語とバッティングして不自然に見えてしまうという問題もありました。
|
||||
|
||||
そこで、Misskey Hub Nextでは、英字に欧文フォントを採用し、各種言語にはそれぞれの国ごとにフォントを切り替えるようにしました。
|
||||
|
||||
### フォント一覧
|
||||
|
||||
#### 欧文フォント
|
||||
|
||||
- [Capriola](https://fonts.google.com/specimen/Capriola) (キャッチコピーやタイトルに使用)
|
||||
- [Nunito](https://fonts.google.com/specimen/Nunito) (本文に使用)
|
||||
|
||||
#### 日本語
|
||||
|
||||
- [源柔ゴシックX](http://jikasei.me/font/genjyuu/)
|
||||
|
||||
#### 韓国語
|
||||
|
||||
- [Pretendard](https://cactus.tistory.com/306)
|
||||
|
||||
#### 中国語
|
||||
|
||||
- [Noto Sans Traditional Chinese](https://fonts.google.com/specimen/Noto+Sans+TC)
|
||||
- [Noto Sans Simplified Chinese](https://fonts.google.com/specimen/Noto+Sans+SC)
|
||||
|
||||
:::
|
||||
|
||||
## 機能紹介
|
||||
|
||||
ここからは、Misskey Hub Nextに搭載された機能をご紹介します!
|
||||
|
||||
### Misskeyサーバー一覧
|
||||
|
||||
Misskeyの[サーバー一覧](/servers/)を、[Misskeyをはじめよう(joinmisskey)](https://join.misskey.page/)のシステムを使用して大幅に改良しました!
|
||||
|
||||
![](/img/blog/2023-12-03-inside-misskey-hub-next/6.png)
|
||||
|
||||
これにより、基本的には**サーバーを追加するのに特別な作業や申請は必要なくなり、新しく見つかったサーバーは自動的に追加されるようになりました!**
|
||||
|
||||
:::tip
|
||||
なので近日中にjoinmisskeyはMisskey Hubに統合されるかも…?
|
||||
:::
|
||||
|
||||
### 【新機能】ツール集
|
||||
|
||||
Misskey関連のさまざまな便利ツールを提供するページ「[ツール集](/tools/)」を新たに開発しました!
|
||||
|
||||
![](/img/blog/2023-12-03-inside-misskey-hub-next/7.png)
|
||||
|
||||
現在
|
||||
|
||||
- MFMお試しコーナー
|
||||
- aid/aidx変換ツール
|
||||
|
||||
をご利用いただけますが、今後も随時拡充していく予定です💪
|
||||
|
||||
:::tip
|
||||
[MFMお試しコーナー](/tools/mfm-playground/)の再現精度は割と高いのでMFMアートづくりにもおすすめです
|
||||
:::
|
||||
|
||||
### 【新機能】AiScriptに対応
|
||||
|
||||
Misskey Hub上で、AiScriptをハイライト表示することができるようになりました!プラグインの作り方やAiScript入門なども、Misskey Hub上で書けるようになるかも…!
|
||||
|
||||
```AiScript
|
||||
for (let i, 100) {
|
||||
<: if (i % 15 == 0) "FizzBuzz"
|
||||
elif (i % 3 == 0) "Fizz"
|
||||
elif (i % 5 == 0) "Buzz"
|
||||
else i
|
||||
}
|
||||
```
|
||||
|
||||
### 藍
|
||||
|
||||
|
||||
:::fukidashi{chara="doya_ai" charaName="藍"}
|
||||
Misskey Hub Nextでは、わたし・藍も色々なところにいます!
|
||||
ほら、こんなところにも!
|
||||
|
||||
![](https://media.misskeyusercontent.com/io/7f980e50-bbd8-4527-b9f7-f6f97564956a.png)
|
||||
|
||||
ほかにも色々なところに隠れているので、ぜひ探してみてくださいね♪
|
||||
:::
|
||||
|
||||
さらに、<ruby>古<rt>いにしえ</rt></ruby>のミスキストや、現行Misskey Hubのヘビーユーザーしか知らないであろう、知る人ぞ知る機能・**藍モード**も移植しました!
|
||||
|
||||
え?何が起きるのかって?
|
||||
まあまあ、ものは試しです。やってみてください↓
|
||||
|
||||
:AiModeB
|
||||
|
||||
[制作:しなむ 様](https://misskey.io/@shinamu476)
|
||||
|
||||
## 今後実装する予定の機能
|
||||
|
||||
※以下はすべて「予定」です。いつ実装されるかわかりませんし、そもそも実装されないかもしれません。
|
||||
|
||||
### APIエンドポイント ドキュメント
|
||||
|
||||
こちらは、現在[株式会社MisskeyHQ (村上さん)](https://misskey.io/notes/9l0i92bvr1)と連携しながら、ドキュメントの拡充に取り組んでいます。もう少しお待ち下さい🙏
|
||||
|
||||
:::tip
|
||||
ドキュメントの拡充にご協力いただける方は、[こちら](https://misskey.io/notes/9l0i92bvr1)をご覧のうえ、ぜひご参加ください!
|
||||
:::
|
||||
|
||||
### シェアボタン中継機能
|
||||
|
||||
MisskeyへWebページをシェアする機能を、Misskey Hubを中継して行えるようにする予定です([Misskey Share](https://misskeyshare.link/) や [DonShare](https://donshare.net/)にあたる機能を実装します)!
|
||||
|
||||
これにより、サーバーごとの投稿処理などの複雑な実装なしで、ウェブサイトにMisskeyへのシェアボタンを埋め込めるようになります🎉
|
||||
|
||||
### プラグイン・テーマストア
|
||||
|
||||
Misskey v2023.11.0から、外部サイトを介してプラグイン・テーマをインストールすることが可能になりました。これを利用して、Misskey Hub上でプラグインやテーマを公開できる仕組みを構築予定です!
|
||||
|
||||
## まとめ
|
||||
|
||||
このように、Misskey Hubは様々な部分で改善・追加を行い、パワーアップします!現在鋭意開発中で、**近いうちに現行のMisskey Hubを置き換えてリニューアルを行う予定です。** それまでの間、Misskey Hub Next は現在のURL(`misskey-hub-next.vercel.app`)でご覧いただけます。ぜひ色々なページをご覧いただき、ご意見やご感想をお寄せください。
|
||||
|
||||
それでは、新しいMisskey Hubの正式リリースを楽しみにお待ちください♪
|
@ -32,6 +32,11 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
const localePath = useLocalePath();
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
route.meta.title = t('_aboutUs.title');
|
||||
route.meta.description = t('_aboutUs.description');
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
@ -68,6 +68,7 @@ import { coreTeamMember, coreTeamEmeriti } from '@/assets/data/team-members';
|
||||
import type { MiHubMember } from '@/assets/data/team-members';
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const { data } = await useFetch<{ contributors: MiHubMember[] }>('https://ungh.cc/repos/misskey-dev/misskey/contributors', {
|
||||
onRequestError: () => {
|
||||
@ -80,7 +81,8 @@ const contributors = computed(() =>
|
||||
!v.username?.includes('[bot]') && !coreTeamMember.map((e) => e.id).includes(v.id) && !coreTeamEmeriti.map((e) => e.id).includes(v.id))
|
||||
);
|
||||
|
||||
const localePath = useLocalePath();
|
||||
route.meta.title = t('_aboutUs._team.title');
|
||||
route.meta.description = t('_aboutUs._team.description');
|
||||
</script>
|
||||
|
||||
<style module>
|
||||
|
@ -65,6 +65,7 @@ const thumbnail = !data.value.thumbnail ? undefined : (parseURL(data.value.thumb
|
||||
|
||||
route.meta.thumbnail = thumbnail;
|
||||
route.meta.title = data.value?.title;
|
||||
route.meta.description = data.value?.description;
|
||||
|
||||
const pd = data.value.date ? new Date(data.value.date).toISOString() : undefined;
|
||||
|
||||
|
@ -32,11 +32,15 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { locale } = useI18n();
|
||||
const { locale, t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
const localeState = useState('miHub_blog_originalLocale', () => locale.value);
|
||||
localeState.value = locale.value;
|
||||
|
||||
const { data } = await useAsyncData('blog', () => queryContent('blog').only(['_path', 'navTitle', 'title', 'date']).sort({ date: -1 }).find());
|
||||
const localePath = useLocalePath();
|
||||
|
||||
route.meta.title = t('_blog.title');
|
||||
route.meta.description = t('_blog.description');
|
||||
</script>
|
||||
|
@ -66,7 +66,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
route.meta.title = t('_brandAssets.title');
|
||||
route.meta.description = t('_brandAssets.description');
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -69,7 +69,12 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const { t } = useI18n();
|
||||
const localePath = useLocalePath();
|
||||
const route = useRoute();
|
||||
|
||||
route.meta.title = t('_other.title');
|
||||
route.meta.description = t('_other.description');
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -43,6 +43,7 @@ if (!data.value) {
|
||||
}
|
||||
|
||||
route.meta.title = data.value?.title;
|
||||
route.meta.description = data.value?.description;
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -44,15 +44,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { InstancesStatsObj } from '@/types/instances-info';
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
type InstancesStatsObj = {
|
||||
notesCount?: number;
|
||||
usersCount?: number;
|
||||
instancesCount?: number;
|
||||
};
|
||||
|
||||
const instancesStats = ref<InstancesStatsObj>();
|
||||
|
||||
function setServerStats(val: InstancesStatsObj) {
|
||||
@ -60,6 +56,7 @@ function setServerStats(val: InstancesStatsObj) {
|
||||
}
|
||||
|
||||
route.meta.title = t('_servers.title');
|
||||
route.meta.description = t('_servers.description');
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
@ -65,6 +65,12 @@ function doDateToAid() {
|
||||
dateToAidResult.value = genAidx(d.getTime());
|
||||
}
|
||||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
route.meta.title = t('_aidConverter.title');
|
||||
route.meta.description = t('_aidConverter.description');
|
||||
</script>
|
||||
|
||||
<style module>
|
||||
|
@ -45,7 +45,12 @@ definePageMeta({
|
||||
layout: 'tools',
|
||||
});
|
||||
|
||||
const { t } = useI18n();
|
||||
const localePath = useLocalePath();
|
||||
const route = useRoute();
|
||||
|
||||
route.meta.title = t('_tools.title');
|
||||
route.meta.description = t('_tools.description');
|
||||
</script>
|
||||
|
||||
<style module>
|
||||
|
@ -72,6 +72,12 @@ function clearEmojiCache() {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
|
||||
const { t } = useI18n();
|
||||
const route = useRoute();
|
||||
|
||||
route.meta.title = t('_mfmPlayground.title');
|
||||
route.meta.description = t('_mfmPlayground.description');
|
||||
</script>
|
||||
|
||||
<style module>
|
||||
|
BIN
public/img/blog/2023-12-03-inside-misskey-hub-next/1.png
Normal file
After Width: | Height: | Size: 48 KiB |
BIN
public/img/blog/2023-12-03-inside-misskey-hub-next/2.png
Normal file
After Width: | Height: | Size: 72 KiB |
BIN
public/img/blog/2023-12-03-inside-misskey-hub-next/3.png
Normal file
After Width: | Height: | Size: 157 KiB |
BIN
public/img/blog/2023-12-03-inside-misskey-hub-next/4.png
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
public/img/blog/2023-12-03-inside-misskey-hub-next/5.png
Normal file
After Width: | Height: | Size: 80 KiB |
BIN
public/img/blog/2023-12-03-inside-misskey-hub-next/6.png
Normal file
After Width: | Height: | Size: 194 KiB |
BIN
public/img/blog/2023-12-03-inside-misskey-hub-next/7.png
Normal file
After Width: | Height: | Size: 80 KiB |
@ -44,4 +44,10 @@ export type InstanceInfo = {
|
||||
/** Instance List */
|
||||
instancesInfos: InstanceItem[];
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
export type InstancesStatsObj = {
|
||||
notesCount?: number;
|
||||
usersCount?: number;
|
||||
instancesCount?: number;
|
||||
};
|
||||
|