Merge pull request 'develop' (#9114) from develop into main

Reviewed-on: https://codeberg.org/thatonecalculator/calckey/pulls/9114
This commit is contained in:
Kainoa Kanter 2022-11-15 04:31:10 +01:00
commit 305f27a7db
112 changed files with 926 additions and 861 deletions

View File

@ -84,6 +84,8 @@
- Phosphor icons instead of FontAwesome - Phosphor icons instead of FontAwesome
- Fully deprecate MkEmojiPickerWindow in favor of MkEmojiPickerDialog - Fully deprecate MkEmojiPickerWindow in favor of MkEmojiPickerDialog
- Link hover effect - Link hover effect
- Replace all `$ts` with i18n
- AVIF support
- Obliteration of Ai-chan - Obliteration of Ai-chan
- [Make showing ads optional](https://github.com/misskey-dev/misskey/pull/8996) - [Make showing ads optional](https://github.com/misskey-dev/misskey/pull/8996)
- [Tapping avatar in mobile opens account modal](https://github.com/misskey-dev/misskey/pull/9056) - [Tapping avatar in mobile opens account modal](https://github.com/misskey-dev/misskey/pull/9056)

View File

@ -74,12 +74,18 @@ corepack enable
- To add custom CSS for all users, edit `./custom/instance.css`. - To add custom CSS for all users, edit `./custom/instance.css`.
- To add static assets (such as images for the splash screen), place them in the `./custom/` directory. They'll then be avaliable on `https://yourinstance.tld/static-assets/filename.ext`. - To add static assets (such as images for the splash screen), place them in the `./custom/` directory. They'll then be avaliable on `https://yourinstance.tld/static-assets/filename.ext`.
## 🧑‍🔬 Configuring a new instance
- Run `cp .config/example.yml .config/default.yml`
- Edit `.config/default.yml`, making sure to fill out required fields.
- Also copy and edit `.config/docker_example.env` to `.config/docker.env` if you're using Docker.
## 🚚 Migrating from Misskey to Calckey ## 🚚 Migrating from Misskey to Calckey
> ⚠️ Because of their changes, migrating from Foundkey is not supported. > ⚠️ Because of their changes, migrating from Foundkey is not supported.
```sh ```sh
cp ../misskey/.config/default.yml ./.config/default.yml # replace `../misskey/` with misskey path, replace `default.yml` with `docker.yml` if you use docker cp ../misskey/.config/default.yml ./.config/default.yml # replace `../misskey/` with misskey path, add `docker.env` if you use Docker
cp -r ../misskey/files . # if you don't use object storage cp -r ../misskey/files . # if you don't use object storage
``` ```
@ -198,3 +204,9 @@ sudo docker compose up -d
- For push notifications, run `npx web-push generate-vapid-keys`, the put the public and private keys into Control Panel > General > ServiceWorker. - For push notifications, run `npx web-push generate-vapid-keys`, the put the public and private keys into Control Panel > General > ServiceWorker.
- For translations, make a [DeepL](https://deepl.com) account and generate an API key, then put it into Control Panel > General > DeepL Translation. - For translations, make a [DeepL](https://deepl.com) account and generate an API key, then put it into Control Panel > General > DeepL Translation.
- For link previews, go to Control Panel > Security > Summaly Proxy and put in `https://summaly.arkjp.net`. - For link previews, go to Control Panel > Security > Summaly Proxy and put in `https://summaly.arkjp.net`.
- To add another admin account:
- Go to the user's page > 3 Dots > About > Moderation > turn on "Moderator"
- Go back to Overview > click the clipboard icon next to the ID
- Run `psql -d calckey` (or whatever the database name is)
- Run `UPDATE "user" SET "isAdmin" = true WHERE id='999999';` (replace 999999 with the copied ID)
- Have the new admin log out and log back in

View File

@ -8,7 +8,7 @@ notifications: "الإشعارات"
username: "اسم المستخدم" username: "اسم المستخدم"
password: "الكلمة السرية" password: "الكلمة السرية"
forgotPassword: "نسيتَ كلمة السر" forgotPassword: "نسيتَ كلمة السر"
fetchingAsApObject: "جارٍ جلبه مِن الفديفرس" fetchingAsApObject: "جارٍ جلبه مِن الفديفرس"
ok: " حسناً" ok: " حسناً"
gotIt: "فهِمت" gotIt: "فهِمت"
cancel: " إلغاء" cancel: " إلغاء"
@ -694,7 +694,7 @@ receiveAnnouncementFromInstance: "استلم إشعارات من هذا المث
emailNotification: "إشعارات البريد الكتروني" emailNotification: "إشعارات البريد الكتروني"
inChannelSearch: "ابحث عن قناة" inChannelSearch: "ابحث عن قناة"
useReactionPickerForContextMenu: "افتح منتقي التفاعلات عند النقر بالزر الأيمن" useReactionPickerForContextMenu: "افتح منتقي التفاعلات عند النقر بالزر الأيمن"
typingUsers: "{users} يكتب(ون)..." typingUsers: "{users} يكتب(ون)"
jumpToSpecifiedDate: "انتقل إلى تاريخ محدد" jumpToSpecifiedDate: "انتقل إلى تاريخ محدد"
showingPastTimeline: "أنت تستعرض حاليًا خيطًا زمنيًا قديمًا" showingPastTimeline: "أنت تستعرض حاليًا خيطًا زمنيًا قديمًا"
clear: "عودة" clear: "عودة"
@ -1029,28 +1029,26 @@ _time:
hour: "سا" hour: "سا"
day: "ي" day: "ي"
_tutorial: _tutorial:
title: "كيف تستخدم Misskey" title: "How to use Calckey"
step1_1: "مرحبًا!" step1_1: "Welcome!"
step1_2: "تدعى هذه الصفحة 'الخيط الزمني' وهي تحوي ملاحظات الأشخاص الذي تتابعهم مرتبة حسب تاريخ نشرها." step1_2: "Let's get you set up. You'll be up and running in no time!"
step1_3: "خيطك الزمني فارغ حاليًا بما أنك لا تتابع أي شخص ولم تنشر أي ملاحظة." step2_1: "First, please fill out your profile."
step2_1: "لننهي إعداد ملفك الشخصي قبل كتابة ملاحظة أو متابعة أشخاص." step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step2_2: "أعطاء معلومات عن شخصيتك يمنح من له نفس إهتماماتك فرصة متابعتك والتفاعل مع ملاحظاتك." step3_1: "Now time to follow some people!"
step3_1: "هل أنهيت إعداد حسابك؟" step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them."
step3_2: "إذا تاليًا لتنشر ملاحظة. أنقر على أيقونة القلم في أعلى الشاشة" step4_1: "Let's get you out there."
step3_3: "املأ النموذج وانقر الزرّ الموجود في أعلى اليمين للإرسال." step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step3_4: "ليس لديك ما تقوله؟ إذا اكتب \"بدأتُ استخدم ميسكي\"." step5_1: "Timelines, timelines everywhere!"
step4_1: "هل نشرت ملاحظتك الأولى؟" step5_2: "Your instance has {timelines} different timelines enabled."
step4_2: "مرحى! يمكنك الآن رؤية ملاحظتك في الخيط الزمني." step5_3: "The Home {icon} timeline is where you can see posts from your followers."
step5_1: "والآن، لنجعل الخيط الزمني أكثر حيوية وذلك بمتابعة بعض المستخدمين." step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance."
step5_2: "تعرض صفحة {features} الملاحظات المتداولة في هذا المثيل ويتيح لك {Explore} العثور على المستخدمين الرائدين. اعثر على الأشخاص الذين يثيرون إهتمامك وتابعهم!" step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend."
step5_3: "لمتابعة مستخدمين ادخل ملفهم الشخصي بالنقر على صورتهم الشخصية ثم اضغط زر 'تابع'." step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers."
step5_4: "إذا كان لدى المستخدم رمز قفل بجوار اسمه ، وجب عليك انتظاره ليقبل طلب المتابعة يدويًا." step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance."
step6_1: "الآن ستتمكن من رؤية ملاحظات المستخدمين المتابَعين في الخيط الزمني." step6_1: "So, what is this place?"
step6_2: "يمكنك التفاعل بسرعة مع الملاحظات عن طريق إضافة \"تفاعل\"." step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"."
step6_3: "لإضافة تفاعل لملاحظة ، انقر فوق علامة \"+\" أسفل للملاحظة واختر الإيموجي المطلوب." step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time."
step7_1: "مبارك ! أنهيت الدورة التعليمية الأساسية لاستخدام ميسكي." step6_4: "Now go, explore, and have fun!"
step7_2: "إذا أردت معرفة المزيد عن ميسكي زر {help}."
step7_3: "حظًا سعيدًا واستمتع بوقتك مع ميسكي! 🚀"
_2fa: _2fa:
alreadyRegistered: "سجلت سلفًا جهازًا للاستيثاق بعاملين." alreadyRegistered: "سجلت سلفًا جهازًا للاستيثاق بعاملين."
registerDevice: "سجّل جهازًا جديدًا" registerDevice: "سجّل جهازًا جديدًا"

View File

@ -8,7 +8,7 @@ notifications: "বিজ্ঞপ্তি"
username: "ব্যবহারকারীর নাম" username: "ব্যবহারকারীর নাম"
password: "পাসওয়ার্ড" password: "পাসওয়ার্ড"
forgotPassword: "পাসওয়ার্ড ভুলে গেছেন" forgotPassword: "পাসওয়ার্ড ভুলে গেছেন"
fetchingAsApObject: "ফেডিভার্স থেকে খবর আনা হচ্ছে..." fetchingAsApObject: "ফেডিভার্স থেকে খবর আনা হচ্ছে"
ok: "ঠিক" ok: "ঠিক"
gotIt: "বুঝেছি" gotIt: "বুঝেছি"
cancel: "বাতিল" cancel: "বাতিল"
@ -1110,28 +1110,26 @@ _time:
hour: "ঘণ্টা" hour: "ঘণ্টা"
day: "দিন" day: "দিন"
_tutorial: _tutorial:
title: "Misskey কিভাবে ব্যাবহার করবেন" title: "How to use Calckey"
step1_1: "স্বাগতম!" step1_1: "Welcome!"
step1_2: "এই স্ক্রীনটিকে \"টাইমলাইন\" বলা হয় এবং কালানুক্রমিক ক্রমে আপনার এবং আপনি যাদের \"অনুসরণ করেন\" তাদের \"নোটগুলি\" দেখায়৷" step1_2: "Let's get you set up. You'll be up and running in no time!"
step1_3: "আপনি আপনার টাইমলাইনে কিছু দেখতে পাবেন না কারণ আপনি এখনও কোনো নোট পোস্ট করেননি এবং আপনি কাউকে অনুসরণ করছেন না৷" step2_1: "First, please fill out your profile."
step2_1: "নোট তৈরি করার আগে বা কাউকে অনুসরণ করার আগে প্রথমে আপনার প্রোফাইলটি সম্পূর্ণ করুন।" step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step2_2: "আপনি কে তা জানা অনেক লোকের জন্য আপনার নোটগুলি দেখা এবং অনুসরণ করাকে সহজ করে তোলে৷" step3_1: "Now time to follow some people!"
step3_1: "আপনি কি সফলভাবে আপনার প্রোফাইল সেট আপ করেছেন?" step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them."
step3_2: "এখন, কিছু নোট পোস্ট করার চেষ্টা করুন। পোস্ট ফর্ম খুলতে পেন্সিল চিহ্নযুক্ত বাটনে ক্লিক করুন।" step4_1: "Let's get you out there."
step3_3: "বিষয়বস্তু লেখার পরে, আপনি ফর্মের উপরের ডানদিকের বাটনে ক্লিক করে পোস্ট করতে পারেন।" step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step3_4: "পোস্ট করার মত কিছু মনে পরছে না? \"আমি মিসকি সেট আপ করছি\" বললে কেমন হয়?" step5_1: "Timelines, timelines everywhere!"
step4_1: "পোস্ট করেছেন?" step5_2: "Your instance has {timelines} different timelines enabled."
step4_2: "সাবাশ! এখন আপনার নোট টাইমলাইনে দেখা যাবে।" step5_3: "The Home {icon} timeline is where you can see posts from your followers."
step5_1: "এখন অন্যদেরকে অনুসরণ করে আপনার টাইমলাইনকে প্রাণবন্ত করে তুলুন।" step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance."
step5_2: "আপনি {featured}-এ জনপ্রিয় নোটগুলি দেখতে পারেন, যাতে আপনি যে ব্যক্তিকে পছন্দ করেন তাকে বেছে নিতে এবং অনুসরণ করতে পারেন, অথবা {explore}-এ জনপ্রিয় ব্যবহারকারীদের দেখতে পারেন৷" step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend."
step5_3: "একজন ব্যবহারকারীকে অনুসরণ করতে, ব্যবহারকারীর আইকনে ক্লিক করুন এবং ব্যবহারকারীর পৃষ্ঠাতে \"অনুসরণ করুন\" বাটনে ক্লিক করুন।" step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers."
step5_4: "যদি ব্যবহারকারীর নামের পাশে একটি লক আইকন থাকে তাহলে আপনার অনুসরণের অনুরোধ গ্রহণ করার জন্য তারা কিছু সময় নিতে পারে।" step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance."
step6_1: "সবকিছু ঠিক থাকলে আপনি টাইমলাইনে অন্য ব্যবহারকারীদের নোট দেখতে পাবেন।" step6_1: "So, what is this place?"
step6_2: "আপনি সহজেই আপনার প্রতিক্রিয়া জানাতে অন্য ব্যক্তির নোটে \"রিঅ্যাকশন\" যোগ করতে পারেন।" step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"."
step6_3: "একটি রিঅ্যাকশন যোগ করতে, নোটে \"+\" চিহ্নে ক্লিক করুন এবং আপনার পছন্দের রিঅ্যাকশন নির্বাচন করুন।" step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time."
step7_1: "অভিনন্দন! আপনি এখন Misskey-র প্রাথমিক টিউটোরিয়ালটি শেষ করেছেন।" step6_4: "Now go, explore, and have fun!"
step7_2: "আপনি যদি Misskey সম্পর্কে আরও জানতে চান, তাহলে {help} এ দেখুন।"
step7_3: "এখন Misskey উপভোগ করুন 🚀"
_2fa: _2fa:
alreadyRegistered: "আপনি ইতিমধ্যে একটি 2-ফ্যাক্টর অথেনটিকেশন ডিভাইস নিবন্ধন করেছেন৷" alreadyRegistered: "আপনি ইতিমধ্যে একটি 2-ফ্যাক্টর অথেনটিকেশন ডিভাইস নিবন্ধন করেছেন৷"
registerDevice: "নতুন ডিভাইস নিবন্ধন করুন" registerDevice: "নতুন ডিভাইস নিবন্ধন করুন"

View File

@ -8,7 +8,7 @@ notifications: "Notificacions"
username: "Nom d'usuari" username: "Nom d'usuari"
password: "Contrasenya" password: "Contrasenya"
forgotPassword: "Contrasenya oblidada" forgotPassword: "Contrasenya oblidada"
fetchingAsApObject: "Cercant en el Fediverse..." fetchingAsApObject: "Cercant en el Fediverse"
ok: "OK" ok: "OK"
gotIt: "Ho he entès!" gotIt: "Ho he entès!"
cancel: "Cancel·lar" cancel: "Cancel·lar"

View File

@ -8,7 +8,7 @@ notifications: "Oznámení"
username: "Uživatelské jméno" username: "Uživatelské jméno"
password: "Heslo" password: "Heslo"
forgotPassword: "Zapomenuté heslo" forgotPassword: "Zapomenuté heslo"
fetchingAsApObject: "Načítám data z Fediversu..." fetchingAsApObject: "Načítám data z Fediversu"
ok: "Potvrdit" ok: "Potvrdit"
gotIt: "Rozumím!" gotIt: "Rozumím!"
cancel: "Zrušit" cancel: "Zrušit"

View File

@ -8,7 +8,7 @@ notifications: "Benachrichtigungen"
username: "Benutzername" username: "Benutzername"
password: "Passwort" password: "Passwort"
forgotPassword: "Passwort vergessen" forgotPassword: "Passwort vergessen"
fetchingAsApObject: "Wird aus dem Fediverse angefragt" fetchingAsApObject: "Wird aus dem Fediverse angefragt"
ok: "OK" ok: "OK"
gotIt: "Verstanden!" gotIt: "Verstanden!"
cancel: "Abbrechen" cancel: "Abbrechen"
@ -1181,28 +1181,26 @@ _time:
hour: "Stunde(n)" hour: "Stunde(n)"
day: "Tag(en)" day: "Tag(en)"
_tutorial: _tutorial:
title: "Wie du Misskey verwendest" title: "Wie man Calckey benutzt"
step1_1: "Willkommen!" step1_1: "Willkommen!"
step1_2: "Diese Seite ist die „Chronik“. Sie zeigt dir deine geschrieben „Notizen“ sowie die aller Benutzer, denen du „folgst“, in chronologischer Reihenfolge." step1_2: "Wir werden Sie einrichten. Sie werden im Handumdrehen einsatzbereit sein!"
step1_3: "Deine Chronik sollte momentan leer sein, da du bis jetzt noch keine Notizen geschrieben hast und auch noch keinen Benutzern folgst." step2_1: "Bitte füllen Sie zuerst Ihr Profil aus."
step2_1: "Lass uns zuerst dein Profil vervollständigen, bevor du Notizen schreibst oder jemandem folgst." step2_2: "Wenn du ein paar Angaben zu deiner Person machst, können andere leichter erkennen, ob sie deine Notizen sehen oder dir folgen wollen."
step2_2: "Informationen darüber, was für eine Person du bist, macht es anderen leichter zu wissen, ob sie deine Notizen sehen wollen und ob sie dir folgen möchten." step3_1: "Jetzt ist es Zeit, einigen Leuten zu folgen!"
step3_1: "Mit dem Einrichten deines Profils fertig?" step3_2: "Deine Home- und Social-Timeline basiert darauf, wem du folgst, also folge für den Anfang ein paar Accounts."
step3_2: "Dann lass uns als nächstes versuchen, eine Notiz zu schreiben. Dies kannst du tun, indem du auf den Knopf mit dem Stift-Icon auf dem Bildschirm drückst." step4_1: "Wir bringen dich nach draußen."
step3_3: "Fülle das Fenster aus und drücke auf den Knopf oben rechts zum Senden." step4_2: "Für deinen ersten Beitrag machen manche Leute gerne einen {introduction} Beitrag oder ein einfaches \"Hallo Welt!\""
step3_4: "Fällt dir nichts ein, das du schreiben möchtest? Versuch's mit \"Hallo Misskey!\"" step5_1: "Timelines, Timelines überall!"
step4_1: "Fertig mit dem Senden deiner ersten Notiz?" step5_2: "Deine Instanz hat {Zeitleisten} verschiedene Zeitleisten aktiviert."
step4_2: "Falls deine Notiz nun in deiner Chronik auftaucht, hast du alles richtig gemacht." step5_3: "Die Zeitleiste Home {icon} ist die Zeitleiste, in der du die Beiträge deiner Follower sehen kannst."
step5_1: "Lass uns nun deiner Chronik etwas mehr Leben einhauchen, indem du einigen anderen Benutzern folgst." step5_4: "In der lokalen {Icon} Zeitleiste kannst du die Beiträge aller anderen Mitglieder dieser Instanz sehen."
step5_2: "{featured} zeigt dir beliebte Notizen dieser Instanz. In {explore} kannst du beliebte Benutzer finden. Schau dort, ob du Benutzer findest, die dich interessieren." step5_5: "In der Zeitleiste Empfohlen {icon} kannst du Beiträge von Instanzen sehen, die von den Administratoren empfohlen werden."
step5_3: "Klicke zum Anzeigen des Profils eines Benutzers auf dessen Profilbild und dann auf den \"Folgen\"-Knopf, um diesem zu folgen." step5_6: "In der sozialen {icon} Zeitleiste kannst du Beiträge von Freunden deiner Follower sehen."
step5_4: "Je nach Benutzer kann es etwas Zeit in Anspruch nehmen, bis dieser deine Follow-Anfrage bestätigt." step5_7: "In der globalen {icon} Zeitleiste kannst du Beiträge von allen anderen verbundenen Instanzen sehen."
step6_1: "Wenn du nun auch die Notizen anderer Benutzer in deiner Chronik siehst, hast du auch diesmal alles richtig gemacht." step6_1: "Also, was ist das hier?"
step6_2: "Du kannst ebenso „Reaktionen“ verwenden, um schnell auf Notizen anderer Benutzer zu reagieren." step6_2: "Nun, du bist nicht nur Calckey beigetreten. Du bist einem Portal zum Fediversum beigetreten, einem zusammenhängenden Netzwerk von Tausenden von Servern, genannt \"Instanzen\"."
step6_3: "Um eine Reaktion anzufügen, klicke auf das „+“-Symbol in der Notiz und wähle ein Emoji aus, mit dem du reagieren möchtest." step6_3: "Jeder Server funktioniert auf unterschiedliche Weise, und nicht auf allen Servern läuft Calckey. Dieser hier aber schon! Es ist ein bisschen kompliziert, aber du wirst den Dreh schnell raus haben."
step7_1: "Glückwunsch! Du hast die Einführung in die Verwendung von Misskey abgeschlossen." step6_4: "Jetzt geh, erkunde und hab Spaß!"
step7_2: "Wenn du mehr über Misskey lernen möchtest, schau dich im {help}-Bereich um."
step7_3: "Und nun, viel Spaß mit Misskey! 🚀"
_2fa: _2fa:
alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert." alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert."
registerDevice: "Neues Gerät registrieren" registerDevice: "Neues Gerät registrieren"

View File

@ -1,14 +1,14 @@
--- ---
_lang_: "English" _lang_: "English"
headlineMisskey: "An open source, decentralized social media platform that's free forever! 🚀" headlineMisskey: "An open source, decentralized social media platform that's free forever! 🚀"
introMisskey: "Welcome! Calckey is an open source, decentralized microblogging service.\nCreate \"notes\" to share your thoughts with everyone around you. 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes. 👍\nLet's explore a new world! 🚀" introMisskey: "Welcome! Calckey is an open source, decentralized social media platform that's free forever! 🚀"
monthAndDay: "{month}/{day}" monthAndDay: "{month}/{day}"
search: "Search" search: "Search"
notifications: "Notifications" notifications: "Notifications"
username: "Username" username: "Username"
password: "Password" password: "Password"
forgotPassword: "Forgot password" forgotPassword: "Forgot password"
fetchingAsApObject: "Fetching from the Fediverse..." fetchingAsApObject: "Fetching from the Fediverse"
ok: "OK" ok: "OK"
gotIt: "Got it!" gotIt: "Got it!"
cancel: "Cancel" cancel: "Cancel"
@ -732,7 +732,7 @@ emailNotification: "Email notifications"
publish: "Publish" publish: "Publish"
inChannelSearch: "Search in channel" inChannelSearch: "Search in channel"
useReactionPickerForContextMenu: "Open reaction picker on right-click" useReactionPickerForContextMenu: "Open reaction picker on right-click"
typingUsers: "{users} is/are typing..." typingUsers: "{users} is typing"
jumpToSpecifiedDate: "Jump to specific date" jumpToSpecifiedDate: "Jump to specific date"
showingPastTimeline: "Currently displaying an old timeline" showingPastTimeline: "Currently displaying an old timeline"
clear: "Return" clear: "Return"
@ -919,6 +919,7 @@ updateAvailable: "There might be an update available!"
swipeOnDesktop: "Allow mobile-style swiping on desktop" swipeOnDesktop: "Allow mobile-style swiping on desktop"
logoImageUrl: "Logo image URL" logoImageUrl: "Logo image URL"
showAdminUpdates: "Indicate a new Calckey version is avaliable (admin only)" showAdminUpdates: "Indicate a new Calckey version is avaliable (admin only)"
replayTutorial: "Replay tutorial"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server." description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server."
@ -1218,7 +1219,7 @@ _tutorial:
step2_1: "First, please fill out your profile." step2_1: "First, please fill out your profile."
step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you." step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step3_1: "Now time to follow some people!" step3_1: "Now time to follow some people!"
step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started." step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them."
step4_1: "Let's get you out there." step4_1: "Let's get you out there."
step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\"" step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step5_1: "Timelines, timelines everywhere!" step5_1: "Timelines, timelines everywhere!"

View File

@ -8,7 +8,7 @@ notifications: "Notificaciones"
username: "Nombre de usuario" username: "Nombre de usuario"
password: "Contraseña" password: "Contraseña"
forgotPassword: "Olvidé mi Contraseña" forgotPassword: "Olvidé mi Contraseña"
fetchingAsApObject: "Recuperando desde el Fediverso..." fetchingAsApObject: "Recuperando desde el Fediverso"
ok: "OK" ok: "OK"
gotIt: "¡Lo tengo!" gotIt: "¡Lo tengo!"
cancel: "Cancelar" cancel: "Cancelar"
@ -1181,28 +1181,26 @@ _time:
hour: "Horas" hour: "Horas"
day: "Días" day: "Días"
_tutorial: _tutorial:
title: "Cómo usar Misskey" title: "Cómo usar Calckey"
step1_1: "Bienvenido" step1_1: "¡Bienvenido!"
step1_2: "Esta imagen se llama \"Linea de tiempo\" y muestra en orden cronológico las \"notas\" tuyas y de la gente que \"sigues\"" step1_2: "Vamos a configurarte. Estarás listo y funcionando en poco tiempo"
step1_3: "Si no estás escribiendo ninguna nota y no estás siguiendo a nadie, es esperable que no se muestre nada en la linea de tiempo" step2_1: "En primer lugar, rellena tu perfil"
step2_1: "Antes de crear notas y seguir a alguien, primero vamos a crear tu perfil" step2_2: "Proporcionar algo de información sobre quién eres hará que sea más fácil para los demás saber si quieren ver tus notas o seguirte."
step2_2: "Si provees información sobre quien eres, será más fácil para que otros usuarios te sigan" step3_1: "¡Ahora es el momento de seguir a algunas personas!"
step3_1: "¿Has podido crear tu perfil sin problemas?" step3_2: "Tu página de inicio y tus líneas de tiempo sociales se basan en quién sigues, así que intenta seguir un par de cuentas para empezar.\nHaz clic en el círculo más en la parte superior derecha de un perfil para seguirlos."
step3_2: "Con esto, prueba hacer una nota. Aprieta el botón con forma de lápiz que está arriba de la imagen y abre el formulario." step4_1: "Vamos a salir a la calle"
step3_3: "Si has escrito el contenido, aprieta el botón que está arriba a la derecha del formulario para postear." step4_2: "Para tu primer post, a algunas personas les gusta hacer un post de {introduction} o un simple \"¡Hola mundo!\""
step3_4: "¿No se te ocurre un contenido? Prueba con decir \"Empecé a usar Misskey\"" step5_1: "¡Líneas de tiempo, líneas de tiempo por todas partes!"
step4_1: "¿Has posteado?" step5_2: "Su instancia tiene {timelines} diferentes líneas de tiempo habilitadas"
step4_2: "Si tu nota puede verse en la linea de tiempo, fue todo un éxito." step5_3: "La línea de tiempo Inicio {icon} es donde puedes ver las publicaciones de tus seguidores."
step5_1: "Luego, ponte a seguir a otra gente y haz que tu linea de tiempo esté más animada." step5_4: "La línea de tiempo Local {icon} es donde puedes ver las publicaciones de todos los demás en esta instancia."
step5_2: "Puedes ver las notas destacadas en {featured} y desde allí seguir a usuarios que te importan. También puedes buscar usuario destacados en {explore}." step5_5: "La línea de tiempo {icon} recomendada es donde puedes ver las publicaciones de las instancias que los administradores recomiendan."
step5_3: "Para seguir a un usuario, haz click en su avatar para ver su página de usuario y allí apretar el botón \"seguir\"" step5_6: "La línea de tiempo Social {icon} es donde puedes ver las publicaciones de los amigos de tus seguidores."
step5_4: "De esa manera, puede pasar un tiempo hasta que el usuario apruebe al seguidor." step5_7: "La línea de tiempo Global {icon} es donde puedes ver las publicaciones de todas las demás instancias conectadas."
step6_1: "Si puedes ver en la linea de tiempo las notas de otros usuarios, fue todo un éxito." step6_1: "Entonces, ¿qué es este lugar?"
step6_2: "En las notas de otros usuarios puedes añadir una \"reacción\", para poder responder rápidamente." step6_2: "Bueno, no sólo te has unido a Calckey. Te has unido a un portal del Fediverso, una red interconectada de miles de servidores, llamada \"instancias\""
step6_3: "Para añadir una reacción, haz click en el botón \"+\" de la nota y elige la reacción que prefieras." step6_3: "Cada servidor funciona de forma diferente, y no todos los servidores ejecutan Calckey. Sin embargo, ¡éste lo hace! Es un poco complicado, pero le cogerás el tranquillo enseguida"
step7_1: "Así terminó la explicación del funcionamiento básico de Misskey. Eso fue todo." step6_4: "¡Ahora ve, explora y diviértete!"
step7_2: "Si quieres conocer más sobre Misskey, prueba con la sección {help}."
step7_3: "Así, disfruta de Misskey 🚀"
_2fa: _2fa:
alreadyRegistered: "Ya has completado la configuración." alreadyRegistered: "Ya has completado la configuración."
registerDevice: "Registrar dispositivo" registerDevice: "Registrar dispositivo"

View File

@ -8,7 +8,7 @@ notifications: "Notifications"
username: "Nom dutilisateur·rice" username: "Nom dutilisateur·rice"
password: "Mot de passe" password: "Mot de passe"
forgotPassword: "Mot de passe oublié" forgotPassword: "Mot de passe oublié"
fetchingAsApObject: "Récupération depuis le fédiverse" fetchingAsApObject: "Récupération depuis le fédiverse"
ok: "OK" ok: "OK"
gotIt: "Jai compris !" gotIt: "Jai compris !"
cancel: "Annuler" cancel: "Annuler"
@ -1103,28 +1103,26 @@ _time:
hour: "h" hour: "h"
day: "j" day: "j"
_tutorial: _tutorial:
title: "Comment utiliser Misskey" title: "Comment utiliser Calckey"
step1_1: "Bienvenue," step1_1 : "Bienvenue!"
step1_2: "Cette page est appelée « un fil ». Elle affiche les « notes » des personnes auxquelles vous êtes abonné dans un ordre chronologique." step1_2 : "On va vous installer. Vous serez opérationnel en un rien de temps"
step1_3: "Votre fil est actuellement vide vu que vous ne suivez aucun compte et que vous navez publié aucune note, pour linstant." step2_1 : "Tout d'abord, remplissez votre profil"
step2_1: "Procédons dabord à la préparation de votre profil avant décrire une note et/ou de vous abonner à un compte." step2_2 : "En fournissant quelques informations sur qui vous êtes, il sera plus facile pour les autres de savoir s'ils veulent voir vos notes ou vous suivre."
step2_2: "En fournissant quelques informations sur vous, il sera plus facile pour les autres de sabonner à votre compte." step3_1 : "Maintenant il est temps de suivre des gens !"
step3_1: "Vous avez fini de créer votre profil ?" step3_2 : "Votre page d'accueil et vos timelines sociales sont basées sur les personnes que vous suivez, alors essayez de suivre quelques comptes pour commencer.\nCliquez sur le cercle plus en haut à droite d'un profil pour le suivre."
step3_2: "Létape suivante consiste à créer une note. Vous pouvez commencer en cliquant sur licône crayon sur lécran." step4_1 : "On y va."
step3_3: "Remplissez le cadran et cliquez sur le bouton en haut à droite pour envoyer." step4_2 : "Pour votre premier post, certaines personnes aiment faire un post {introduction} ou un simple post 'Hello world'."
step3_4: "Vous navez rien à dire ? Essayez décrire « Jai commencé à utiliser Misskey »." step5_1 : "Lignes de temps, lignes de temps partout !"
step4_1: "Avez-vous publié votre première note ?" step5_2 : "Votre instance a {timelines} différentes chronologies activées !"
step4_2: "Youpi ! Celle-ci est maintenant affichée sur votre fil dactualité." step5_3 : "La timeline Home {icon} est l'endroit où vous pouvez voir les publications de vos followers."
step5_1: "Maintenant, essayons de nous abonner à dautres personnes afin de rendre votre fil plus vivant." step5_4 : "La timeline locale {icon} est l'endroit où vous pouvez voir les messages de tout le monde sur cette instance."
step5_2: "La page {featured} affiche les notes en tendance sur la présente instance et {explore} vous permet de trouver des utilisateur·rice·s en tendance. Essayez de vous abonner aux gens que vous aimez !" step5_5 : "La timeline {icon} recommandée est l'endroit où vous pouvez voir les messages des instances que les administrateurs recommandent."
step5_3: "Pour pouvoir suivre dautres utilisateur·rice, cliquez sur leur avatar afin dafficher la page du profil utilisateur ensuite appuyez sur le bouton « Sabonner »." step5_6 : "La timeline {icon} sociale est l'endroit où vous pouvez voir les publications des amis de vos followers."
step5_4: "Si lautre utilisateur possède une icône sous forme dun cadenas à côté de son nom, il devra accepter votre demande dabonnement manuellement." step5_7 : "La timeline globale {icon} est l'endroit où vous pouvez voir les messages de toutes les autres instances connectées."
step6_1: "Maintenant, vous êtes en mesure de voir safficher les notes des autres utilisateur·rice·s sur votre propre fil." step6_1 : "Alors quel est cet endroit ?"
step6_2: "Vous avez également la possibilité dintéragir rapidement avec les notes des autres utilisateur·rice·s en ajoutant des « réactions »." step6_2 : "Eh bien, vous ne venez pas de rejoindre Calckey. Vous avez rejoint un portail vers le Fediverse, un réseau interconnecté de milliers de serveurs, appelés \"instances\"."
step6_3: "Pour ajouter une réaction à une note, cliquez sur le signe « + » de celle-ci et sélectionnez lémoji souhaité." step6_3 : "Chaque serveur fonctionne différemment, et tous les serveurs n'utilisent pas Calckey. Cependant, celui-ci le fait ! C'est un peu délicat, mais vous aurez le coup de main en un rien de temps."
step7_1: "Félicitations ! Vous avez atteint la fin du tutoriel de base pour lutilisation de Misskey." step6_4 : "Maintenant, allez-y, explorez et amusez-vous !"
step7_2: "Si vous désirez en savoir plus sur Misskey, jetez un œil sur la section {help}."
step7_3: "Bon courage et amusez-vous bien sur Misskey ! 🚀"
_2fa: _2fa:
alreadyRegistered: "Configuration déjà achevée." alreadyRegistered: "Configuration déjà achevée."
registerDevice: "Ajouter un nouvel appareil" registerDevice: "Ajouter un nouvel appareil"

View File

@ -8,7 +8,7 @@ notifications: "Pemberitahuan"
username: "Nama Pengguna" username: "Nama Pengguna"
password: "Kata sandi" password: "Kata sandi"
forgotPassword: "Lupa Kata Sandi" forgotPassword: "Lupa Kata Sandi"
fetchingAsApObject: "Mengambil data dari Fediverse..." fetchingAsApObject: "Mengambil data dari Fediverse"
ok: "OK" ok: "OK"
gotIt: "Saya mengerti" gotIt: "Saya mengerti"
cancel: "Batalkan" cancel: "Batalkan"
@ -727,7 +727,7 @@ emailNotification: "Pemberitahuan surel"
publish: "Terbitkan" publish: "Terbitkan"
inChannelSearch: "Cari di kanal" inChannelSearch: "Cari di kanal"
useReactionPickerForContextMenu: "Buka pemilih reaksi dengan klik-kanan" useReactionPickerForContextMenu: "Buka pemilih reaksi dengan klik-kanan"
typingUsers: "{users} sedang mengetik..." typingUsers: "{users} sedang mengetik"
jumpToSpecifiedDate: "Loncat ke tanggal spesifik" jumpToSpecifiedDate: "Loncat ke tanggal spesifik"
showingPastTimeline: "Sedang menampilkan linimasa lama" showingPastTimeline: "Sedang menampilkan linimasa lama"
clear: "Bersihkan" clear: "Bersihkan"

View File

@ -8,7 +8,7 @@ notifications: "Notifiche"
username: "Nome utente" username: "Nome utente"
password: "Password" password: "Password"
forgotPassword: "Hai dimenticato la tua password?" forgotPassword: "Hai dimenticato la tua password?"
fetchingAsApObject: "Recuperando dal Fediverso..." fetchingAsApObject: "Recuperando dal Fediverso"
ok: "OK" ok: "OK"
gotIt: "Ho capito" gotIt: "Ho capito"
cancel: "Annulla" cancel: "Annulla"
@ -1026,28 +1026,26 @@ _time:
hour: "ore" hour: "ore"
day: "giorni" day: "giorni"
_tutorial: _tutorial:
title: "Come usare Misskey" titolo: "Come usare Calckey"
step1_1: "Benvenuto/a!" step1_1: "Benvenuto!"
step1_2: "Questa pagina si chiama una \" Timeline \". Mostra in ordine cronologico le \" note \" delle persone che segui." step1_2: "Vediamo di configurarla. Sarete operativi in men che non si dica!"
step1_3: "Attualmente la tua Timeline è vuota perché non segui alcun account e non hai pubblicato alcuna nota ancora." step2_1: "Per prima cosa, compila il tuo profilo"
step2_1: "Prima di scrivere una nota o di seguire un account, imposta il tuo profilo!" step2_2: "Fornendo alcune informazioni su chi siete, sarà più facile per gli altri capire se vogliono vedere le vostre note o seguirvi"
step2_2: "Aggiungere qualche informazione su di te aumenterà le tue possibilità di essere seguit@ da altre persone. " step3_1: "Ora è il momento di seguire alcune persone!"
step3_1: "Hai finito di impostare il tuo profilo?" step3_2: "La vostra home e le vostre timeline social si basano su chi seguite, quindi provate a seguire un paio di account per iniziare.\nCliccate sul cerchio più in alto a destra di un profilo per seguirlo"
step3_2: "Ora, puoi pubblicare una nota. Facciamo una prova! Premi il pulsante a forma di penna in cima allo schermo per aprire una finestra di dialogo. " step4_1: "Fatevi conoscere"
step3_3: "Scritto il testo della nota, puoi pubblicarla premendo il pulsante nella parte superiore destra della finestra di dialogo." step4_2: "Per il vostro primo post, alcuni preferiscono fare un post di {introduction} o un semplice \"Ciao mondo!\""
step3_4: "Non ti viene niente in mente? Perché non scrivi semplicemente \"Ho appena cominciato a usare Misskey\"?" step5_1: "Linee temporali, linee temporali dappertutto!"
step4_1: "Hai pubblicato qualcosa?" step5_2: "La tua istanza ha attivato {timelines} diverse timelines"
step4_2: "Se puoi visualizzare la tua nota sulla timeline, ce l'hai fatta!" step5_3: "La timeline Home {icon} è quella in cui si possono vedere i post dei propri follower"
step5_1: "Adesso, cerca di seguire altre persone per vivacizzare la tua timeline. " step5_4: "La timeline Locale {icon} è quella in cui si possono vedere i post di tutti gli altri utenti di questa istanza"
step5_2: "La pagina {featured} mostra le note di tendenza su questa istanza, e magari ti aiuterà a trovare account che ti piacciono e che vorrai seguire. Oppure, potrai trovare utenti popolari usando {explore}." step5_5: "La timeline Raccomandati {icon} è quella in cui si possono vedere i post delle istanze raccomandate dagli amministratori"
step5_3: "Per seguire altrə utenti, clicca sul loro avatar per aprire la pagina di profilo dove puoi premere il pulsante \"Seguire\". " step5_6: "La timeline Social {icon} è quella in cui si possono vedere i post degli amici dei propri follower"
step5_4: "Alcunə utenti scelgono di confermare manualmente le richieste di follow che ricevono, quindi a seconda delle persone potrebbe volerci un pò prima che la tua richiesta sia accolta." step5_7: "La timeline Globale {icon} è quella in cui si possono vedere i post di ogni altra istanza collegata"
step6_1: "Ora, se puoi visualizzare le note di altrə utenti sulla tua timeline, ce l'hai fatta!" step6_1: "Allora, cos'è questo posto?"
step6_2: "Puoi inviare una risposta rapida alle note di altrə utenti mandando loro \"reazioni\"." step6_2: "Beh, non ti sei semplicemente unito a Calckey. Sei entrato in un portale del Fediverse, una rete interconnessa di migliaia di server, chiamata \"istanze\""
step6_3: "Per inviare una reazione, premi l'icona + della nota e scegli l'emoji che vuoi mandare." step6_3: "Ogni server funziona in modo diverso, e non tutti i server eseguono Calckey. Questo però lo fa! È un po' complicato, ma ci riuscirete in poco tempo"
step7_1: "Complimenti! Sei arrivat@ alla fine dell'esercitazione di base su come usare Misskey. " step6_4: "Ora andate, esplorate e divertitevi!"
step7_2: "Se vuoi saperne di più su Misskey, puoi dare un'occhiata alla sezione {help}."
step7_3: "Da ultimo, buon divertimento su Misskey! 🚀"
_2fa: _2fa:
registerDevice: "Aggiungi dispositivo" registerDevice: "Aggiungi dispositivo"
_permissions: _permissions:

View File

@ -919,6 +919,7 @@ updateAvailable: "アップデートがありますよ"
swipeOnDesktop: "デスクトップでモバイルスタイルのスワイプを可能にする" swipeOnDesktop: "デスクトップでモバイルスタイルのスワイプを可能にする"
logoImageUrl: "ロゴのURL" logoImageUrl: "ロゴのURL"
showAdminUpdates: "新しいCalckeyのバージョンが利用可能であることを示す(管理者のみ)" showAdminUpdates: "新しいCalckeyのバージョンが利用可能であることを示す(管理者のみ)"
replayTutorial: "リプレイチュートリアル"
_sensitiveMediaDetection: _sensitiveMediaDetection:
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。" description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"
@ -1247,7 +1248,7 @@ _tutorial:
step3_1: "さあ、何人かの人をフォローする時間です!" step3_1: "さあ、何人かの人をフォローする時間です!"
step3_2: "あなたのホームとソーシャルタイムラインは、あなたが誰をフォローしているかで決まります。 まずは、いくつかのアカウントをフォローしてみましょう。" step3_2: "あなたのホームとソーシャルタイムラインは、あなたが誰をフォローしているかで決まります。 まずは、いくつかのアカウントをフォローしてみましょう。"
step4_1: "さあ、外に出てみましょう。" step4_1: "さあ、外に出てみましょう。"
step4_2: "最初の投稿は、{自己紹介}の投稿や、シンプルに「こんにちは、世界よ!」的な投稿をするのが好きな人もいます。" step4_2: "最初の投稿は、{introduction}の投稿や、シンプルに「こんにちは、世界よ!」的な投稿をするのが好きな人もいます。"
step5_1: "タイムライン、タイムラインだらけ!" step5_1: "タイムライン、タイムラインだらけ!"
step5_2: "あなたのインスタンスは{timelines}異なるタイムラインを有効にしています。" step5_2: "あなたのインスタンスは{timelines}異なるタイムラインを有効にしています。"
step5_3: "ホーム{icon}のタイムラインは、あなたのフォロワーからの投稿を見ることができます。" step5_3: "ホーム{icon}のタイムラインは、あなたのフォロワーからの投稿を見ることができます。"

View File

@ -1006,8 +1006,6 @@ _time:
minute: "分" minute: "分"
hour: "時間" hour: "時間"
day: "日" day: "日"
_tutorial:
step3_1: "プロフィール設定はええ感じにできたか?"
_2fa: _2fa:
alreadyRegistered: "もう設定終わっとるわ。" alreadyRegistered: "もう設定終わっとるわ。"
_permissions: _permissions:

View File

@ -6,7 +6,7 @@ search: "ಹುಡುಕು"
notifications: "ಅಧಿಸೂಚನೆಗಳು" notifications: "ಅಧಿಸೂಚನೆಗಳು"
username: "ಬಳಕೆಹೆಸರು" username: "ಬಳಕೆಹೆಸರು"
password: "ಗುಪ್ತಪದ" password: "ಗುಪ್ತಪದ"
fetchingAsApObject: "ಒಕ್ಕೂಟದಿಂದ ಪಡೆಯಲಾಗುತ್ತಿದೆ..." fetchingAsApObject: "ಒಕ್ಕೂಟದಿಂದ ಪಡೆಯಲಾಗುತ್ತಿದೆ"
ok: "ಸರಿ" ok: "ಸರಿ"
gotIt: "ಅರ್ಥವಾಯಿತು!" gotIt: "ಅರ್ಥವಾಯಿತು!"
cancel: "ರದ್ದು" cancel: "ರದ್ದು"

View File

@ -730,7 +730,7 @@ emailNotification: "메일 알림"
publish: "게시" publish: "게시"
inChannelSearch: "채널에서 검색" inChannelSearch: "채널에서 검색"
useReactionPickerForContextMenu: "우클릭하여 리액션 선택기 열기" useReactionPickerForContextMenu: "우클릭하여 리액션 선택기 열기"
typingUsers: "{users} 님이 입력하고 있어요.." typingUsers: "{users} 님이 입력하고 있어요"
jumpToSpecifiedDate: "특정 날짜로 이동" jumpToSpecifiedDate: "특정 날짜로 이동"
showingPastTimeline: "과거의 타임라인을 표시하고 있어요" showingPastTimeline: "과거의 타임라인을 표시하고 있어요"
clear: "지우기" clear: "지우기"
@ -1179,29 +1179,6 @@ _time:
minute: "분" minute: "분"
hour: "시간" hour: "시간"
day: "일" day: "일"
_tutorial:
title: "Misskey의 사용 방법"
step1_1: "환영합니다!"
step1_2: "이 페이지는 \"타임라인\"이라고 불립니다. 당신이 \"팔로우\"하고 있는 사람들의 \"노트\"가 시간순으로 나타납니다."
step1_3: "아직 아무 유저도 팔로우하고 있지 않기에 타임라인은 비어 있을 것입니다."
step2_1: "새 노트를 작성하거나 다른 사람을 팔로우하기 전에, 먼저 프로필을 완성해보도록 합시다."
step2_2: "당신이 어떤 사람인지를 알린다면, 다른 사람들이 당신을 팔로우할 확률이 올라갈 것입니다."
step3_1: "프로필 설정은 잘 끝내셨나요?"
step3_2: "그럼 시험삼아 노트를 작성해 보세요. 화면에 있는 연필 버튼을 누르면 작성 폼이 열립니다."
step3_3: "내용을 작성한 후, 폼 오른쪽 상단의 버튼을 눌러 노트를 올릴 수 있습니다."
step3_4: "쓸 말이 없나요? \"Misskey 시작했어요!\" 같은 건 어떨까요? :>"
step4_1: "노트 작성을 끝내셨나요?"
step4_2: "당신의 노트가 타임라인에 표시되어 있다면 성공입니다."
step5_1: "이제, 다른 사람을 팔로우하여 타임라인을 활기차게 만들어보도록 합시다."
step5_2: "{featured}에서 이 인스턴스의 인기 노트를 보실 수 있습니다. {explore}에서는 인기 사용자를 찾을 수 있구요. 마음에 드는 사람을 골라 팔로우해 보세요!"
step5_3: "다른 유저를 팔로우하려면 해당 유저의 아이콘을 클릭하여 프로필 페이지를 띄운 후, 팔로우 버튼을 눌러 주세요."
step5_4: "사용자에 따라 팔로우가 승인될 때까지 시간이 걸릴 수 있습니다."
step6_1: "타임라인에 다른 사용자의 노트가 나타난다면 성공입니다."
step6_2: "다른 유저의 노트에 \"리액션\"을 붙여 간단하게 당신의 반응을 전달할 수도 있습니다."
step6_3: "리액션을 붙이려면, 노트의 \"+\" 버튼을 클릭하고 원하는 이모지를 선택합니다."
step7_1: "이것으로 Misskey의 기본 튜토리얼을 마치겠습니다. 수고하셨습니다!"
step7_2: "Misskey에 대해 더 알고 싶으시다면 {help}를 참고해 주세요."
step7_3: "그럼 Misskey를 즐기세요! 🚀"
_2fa: _2fa:
alreadyRegistered: "이미 설정이 완료되었습니다." alreadyRegistered: "이미 설정이 완료되었습니다."
registerDevice: "디바이스 등록" registerDevice: "디바이스 등록"

View File

@ -8,7 +8,7 @@ notifications: "Powiadomienia"
username: "Nazwa użytkownika" username: "Nazwa użytkownika"
password: "Hasło" password: "Hasło"
forgotPassword: "Nie pamiętam hasła" forgotPassword: "Nie pamiętam hasła"
fetchingAsApObject: "Pobieranie z Fediwersum" fetchingAsApObject: "Pobieranie z Fediwersum"
ok: "OK" ok: "OK"
gotIt: "Rozumiem!" gotIt: "Rozumiem!"
cancel: "Anuluj" cancel: "Anuluj"
@ -716,7 +716,7 @@ emailNotification: "Powiadomienia e-mail"
publish: "Publikuj" publish: "Publikuj"
inChannelSearch: "Szukaj na kanale" inChannelSearch: "Szukaj na kanale"
useReactionPickerForContextMenu: "Otwórz wybornik reakcji prawym kliknięciem" useReactionPickerForContextMenu: "Otwórz wybornik reakcji prawym kliknięciem"
typingUsers: "{users} pisze(-ą)..." typingUsers: "{users} pisze"
jumpToSpecifiedDate: "Przejdź do określonej daty" jumpToSpecifiedDate: "Przejdź do określonej daty"
showingPastTimeline: "Obecnie wyświetla starą oś czasu" showingPastTimeline: "Obecnie wyświetla starą oś czasu"
clear: "Wróć" clear: "Wróć"
@ -1112,28 +1112,26 @@ _time:
hour: "godz." hour: "godz."
day: "dzień" day: "dzień"
_tutorial: _tutorial:
title: "Jak korzystać z Misskey" title: "Jak korzystać z Calckey"
step1_1: "Witaj!" step1_1: "Witamy!"
step1_2: "Ta strona nazywa się „oś czasu”. Pokazuje chronologicznie uporządkowane wpisy osób, które „śledzisz”." step1_2: "Pozwól, że cię skonfigurujemy. Będziesz działać w mgnieniu oka!"
step1_3: "Twoja oś czasu jest jeszcze pusta, ponieważ nie opublikowałeś(-aś) jeszcze żadnych wpisów i nie obserwujesz jeszcze nikogo." step2_1: "Po pierwsze, proszę wypełnić swój profil"
step2_1: "Ukończmy konfigurację profilu zanim utworzymy wpis lub zaczniemy kogoś obserwować." step2_2: "Podanie kilku informacji o tym, kim jesteś, ułatwi innym stwierdzenie, czy chcą zobaczyć Twoje notatki lub śledzić Cię."
step2_2: "Podanie pewnych informacji o tym, kim jesteś, ułatwi innym określenie, czy chcą widzieć Twoje wpisy lub Cię obserwować." step3_1: "Teraz czas na śledzenie niektórych osób!"
step3_1: "Zakończyłeś(-aś) konfigurację profilu?" step3_2: "Twoje domowe i społeczne linie czasu opierają się na tym, kogo śledzisz, więc spróbuj śledzić kilka kont, aby zacząć.\nKliknij kółko z plusem w prawym górnym rogu profilu, aby go śledzić."
step3_2: "Następnie spróbujmy opublikować wpis. Możesz to zrobić, naciskając przycisk z ikoną ołówka na ekranie." step4_1: "Pozwól, że się tam dostaniesz."
step3_3: "Wypełnij pole i kliknij przycisk w prawym górnym rogu by wysłać post." step4_2: "Dla twojego pierwszego postu, niektórzy ludzie lubią zrobić {introduction} post lub prosty \"Hello world!\""
step3_4: "Nie masz nic do powiedzenia? Spróbuj \"ustawiam swój misskey\"!" step5_1: "Timelines, timelines everywhere!"
step4_1: "Zakończyłeś publikowanie pierwszego wpisu?" step5_2: "Twoja instancja ma włączone {timelines} różne timelines"
step4_2: "Hurra! Teraz Twój pierwszy wpis powinien być wyświetlany na Twojej osi czasu." step5_3: "Oś czasu Home {icon} to miejsce, w którym możesz zobaczyć posty od swoich zwolenników"
step5_1: "Teraz spróbujmy ożywić Twoją oś czasu, przez zaobserwowanie innych ludzi." step5_4: "The Local {icon} timeline to miejsce, w którym możesz zobaczyć posty od wszystkich innych osób na tej instancji."
step5_2: "{featured} pokaże Ci popularne wpisy na tej instancji. {explore} pozwoli Ci znaleźć popularnych użytkowników. Spróbuj znaleźć tam osoby, które chcesz obserwować!" step5_5: "Oś czasu Recommended {icon} to miejsce, gdzie możesz zobaczyć posty z instancji, które admini polecają."
step5_3: "Aby obserwować innych użytkowników, kliknij ich ikonę i naciśnij przycisk \"Obserwuj\" na ich profilu." step5_6: "Oś czasu Social {icon} to miejsce, w którym możesz zobaczyć posty od znajomych swoich followersów."
step5_4: "Jeśli inny użytkownik ma ikonę kłódki obok swojej nazwy, może minąć trochę czasu, zanim ten użytkownik ręcznie zatwierdzi Twoją prośbę o obserwowanie." step5_7: "The Global {icon} timeline to miejsce, gdzie możesz zobaczyć posty z każdej innej połączonej instancji."
step6_1: "Powinieneś teraz widzieć wpisy innych użytkowników na swojej osi czasu." step6_1: "Więc, co to jest to miejsce?"
step6_2: "Możesz także umieścić „reakcje” na wpisach innych osób, aby szybko na nie odpowiedzieć." step6_2: "Cóż, nie dołączyłeś po prostu do Calckey. Dołączyłeś do portalu do Fediverse, połączonej sieci tysięcy serwerów, zwanych instancjami."
step6_3: "Aby dodać \"reakcję\", naciśnij znak \"+\" na wpisie innego użytkownika i wybierz emotikonę, którą chcesz zareagować." step6_3: "Każdy serwer działa w inny sposób, i nie wszystkie serwery działają z Calckey. Ten jednak działa! Jest to trochę skomplikowane, ale w krótkim czasie załapiesz o co chodzi."
step7_1: "Gratulacje! Ukończyłeś podstawowy samouczek Misskey." step6_4: "A teraz idź, odkrywaj i baw się dobrze!"
step7_2: "Jeśli chcesz dowiedzieć się więcej o Misskey, wypróbuj sekcję {help}."
step7_3: "A teraz powodzenia i baw się dobrze z Misskey! 🚀"
_2fa: _2fa:
alreadyRegistered: "Zarejestrowałeś już urządzenie do uwierzytelniania dwuskładnikowego." alreadyRegistered: "Zarejestrowałeś już urządzenie do uwierzytelniania dwuskładnikowego."
registerDevice: "Zarejestruj nowe urządzenie" registerDevice: "Zarejestruj nowe urządzenie"

View File

@ -8,7 +8,7 @@ notifications: "Notificări"
username: "Nume de utilizator" username: "Nume de utilizator"
password: "Parolă" password: "Parolă"
forgotPassword: "Am uitat parola" forgotPassword: "Am uitat parola"
fetchingAsApObject: "Se aduce din Fediverse..." fetchingAsApObject: "Se aduce din Fediverse"
ok: "OK" ok: "OK"
gotIt: "Am înțeles!" gotIt: "Am înțeles!"
cancel: "Anulează" cancel: "Anulează"

View File

@ -1108,28 +1108,26 @@ _time:
hour: "ч" hour: "ч"
day: "сут" day: "сут"
_tutorial: _tutorial:
title: "Как пользоваться Misskey" title: "Как использовать Calckey"
step1_1: "Добро пожаловать!" step1_1: "Добро пожаловать!"
step1_2: "Эта страница называется «лента». Здесь будут появляться «заметки»: ваши личные и тех, на кого вы «подписаны». Они будут располагаться в порядке времени их появления." step1_2: "Давайте настроим вас. Вы будете работать в кратчайшие сроки!"
step1_3: "Правда, ваша лента пока пуста. Она начнёт заполняться, когда вы будете писать свои заметки и подписываться на других." step2_1: "Сначала, пожалуйста, заполните свой профиль"
step2_1: "Давайте, заполним профиль, прежде чем начать писать заметки и подписываться на других." step2_2: "Предоставив некоторую информацию о себе, другим людям будет легче понять, хотят ли они видеть ваши записи или следить за вами."
step2_2: "То, что вы расскажете в профиле, поможет лучше вас узнать, а значит, многим будет легче присоединиться — вы скорее получите новых подписчиков и читателей." step3_1: "Теперь пора следить за некоторыми людьми!"
step3_1: "Успешно заполнили профиль?" step3_2: "Ваша домашняя и социальная ленты основаны на том, за кем вы следите, поэтому для начала попробуйте следить за парой аккаунтов.\nНажмите на кружок с плюсом в правом верхнем углу профиля, чтобы следить за ним."
step3_2: "Что ж, теперь самое время опубликуовать заметку. Если нажать вверху страницы на изображение карандаша, появится форма для текста." step4_1: "Давайте выйдем на вас"
step3_3: "Напишите в неё, что хотите, и нажмите на кнопку в правом верхнем углу." step4_2: "Для своего первого сообщения некоторые люди любят делать {introduction} сообщение или простое \"Hello world!\""
step3_4: "Ничего не приходит в голову? Как насчёт: «Я новенький, пока осваиваюсь в Misskey»?" step5_1: "Временные рамки, везде временные рамки!"
step4_1: "С написанием первой заметки покончено?" step5_2: "В вашем экземпляре включены {timelines} различных временных линий."
step4_2: "Отлично, теперь она должна появиться в вашей ленте." step5_3: "Главная {icon} временная шкала - это шкала, где вы можете видеть сообщения ваших подписчиков."
step5_1: "А теперь самое время немного оживить ленту, подписавшись на других." step5_4: "Местная {icon} временная шкала - это шкала, где вы можете видеть сообщения всех остальных пользователей данного экземпляра"
step5_2: "На странице «{featured}» собраны популярные сегодня заметки, читая которые, вы можете найти кого-то вам интересного, а на странице «{explore}» можно посмотреть, кто популярен у остальных." step5_5: "Временная шкала Рекомендуемые {icon} - это шкала, где вы можете видеть сообщения от инстанций, рекомендованных администраторами."
step5_3: "Чтобы подписаться на кого-нибудь, щёлкните по его аватару и в открывшемся профиле нажмите кнопку «Подписаться»." step5_6: "На временной шкале Social {icon} отображаются сообщения от друзей ваших подписчиков"
step5_4: "Некоторые пользователи (около их имени «висит замок») вручную подтверждают чужие подписки. Так что иногда подписка начинает работать не сразу.\n" step5_7: "Глобальная {icon} временная шкала - это место, где вы можете видеть сообщения от всех других подключенных экземпляров"
step6_1: "Если теперь в ленте видны и чужие заметки, значит у вас получилось." step6_1: "Итак, что это за место?"
step6_2: "Здесь можно непринуждённо выразить свои чувства к чьей-то заметке, отметив «реакцию» под ней." step6_2: "Ну, вы не просто присоединились к Кальки. Вы присоединились к порталу в Fediverse, взаимосвязанной сети из тысяч серверов, называемых \"инстансами\"."
step6_3: "Отмечайте реакции, нажмая на символ «+» под заметкой и выбирая значок по душе." step6_3: "Каждый сервер работает по-своему, и не на всех серверах работает Calckey. Но этот работает! Это немного сложно, но вы быстро разберетесь"
step7_1: "На этом вводный урок по использованию Misskey закончен. Спасибо, что прошли его до конца!" step6_4: "Теперь идите, изучайте и развлекайтесь!"
step7_2: "Хотите изучить Misskey глубже — добро пожаловать в раздел «{help}»."
step7_3: "Приятно вам провести время с Misskey🚀"
_2fa: _2fa:
alreadyRegistered: "Двухфакторная аутентификация уже настроена." alreadyRegistered: "Двухфакторная аутентификация уже настроена."
registerDevice: "Зарегистрируйте ваше устройство" registerDevice: "Зарегистрируйте ваше устройство"

View File

@ -729,7 +729,7 @@ emailNotification: "Emailové upozornenia"
publish: "Zverejniť" publish: "Zverejniť"
inChannelSearch: "Hľadať v kanáli" inChannelSearch: "Hľadať v kanáli"
useReactionPickerForContextMenu: "Otvoriť výber reakcií na pravý klik" useReactionPickerForContextMenu: "Otvoriť výber reakcií na pravý klik"
typingUsers: "{users} píše/u" typingUsers: "{users} píše"
jumpToSpecifiedDate: "Skočiť na konkrétny dátum" jumpToSpecifiedDate: "Skočiť na konkrétny dátum"
showingPastTimeline: "Práve vidíte starú časovú os" showingPastTimeline: "Práve vidíte starú časovú os"
clear: "Vrátiť" clear: "Vrátiť"
@ -1176,28 +1176,26 @@ _time:
hour: "hod" hour: "hod"
day: "dní" day: "dní"
_tutorial: _tutorial:
title: "Ako používať Misskey" title: "How to use Calckey"
step1_1: "Vitajte!" step1_1: "Welcome!"
step1_2: "Táto stránka sa volá \"časová os\". Zobrazuje chronologicky zoradené \"poznámky\" od ľudí, ktorých sledujete." step1_2: "Let's get you set up. You'll be up and running in no time!"
step1_3: "Vaša časová os je teraz prázdna pretože ste nepridali žiadne poznámky ani nikoho zatiaľ nesledujete." step2_1: "First, please fill out your profile."
step2_1: "Podˇme dokončiť nastavenia vášho profilu pred napísaním poznámky alebo sledovaním niekoho." step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step2_2: "Poskytnutím informácií o vás uľahčíte ostatným, či chcú vidieť alebo sledovať vaše poznámky." step3_1: "Now time to follow some people!"
step3_1: "Dokončili ste nastavovanie svojho profilu?" step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them."
step3_2: "Poďme vyskúšať napísať poznámku. Môžete to spraviť stlačením ikony ceruzky na vrchu obrazovky." step4_1: "Let's get you out there."
step3_3: "Vyplňte polia a stlačte tlačítko vpravo hore." step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step3_4: "Nemáte čo povedať? Skúste \"len si nastavujem môj msky\"!" step5_1: "Timelines, timelines everywhere!"
step4_1: "Napísali ste svoju prvú poznámku?" step5_2: "Your instance has {timelines} different timelines enabled."
step4_2: "Hurá! Teraz by vaša prvá poznámka mala byť na vašej časovej osi." step5_3: "The Home {icon} timeline is where you can see posts from your followers."
step5_1: "Teraz skúsme oživiť časovú os sledovaním nejakých ľudí." step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance."
step5_2: "{featured} zobrazí populárne poznámku na tomto serveri. {explore} môžete objavovať populárnych používateľov. Skúste tam nájsť ľudí, ktorých by ste radi sledovali!" step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend."
step5_3: "Ak chcete sledovať ďalších používateľov, kliknite na ich ikonu a stlačte tlačidlo \"Sledovať\" na ich profile." step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers."
step5_4: "Ak má niektorý používateľ ikonu zámku vedľa svojho mena, znamená to, že môže trvať určitý čas, kým daný používateľ schváli vašu žiadosť o sledovanie." step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance."
step6_1: "Teraz by ste mali vidieť poznámky ďalších používateľov na svojej časovej osi." step6_1: "So, what is this place?"
step6_2: "Môžete dať \"reakcie\" na poznámky ďalších ľudí ako rýchlu odpoveď." step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"."
step6_3: "Reakciu pridáte kliknutím na \"+\" niekoho poznámke a vybratím emoji, ktorou chcete reagovať." step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time."
step7_1: "Gralujeme! Dokončili ste základného sprievodcu Misskey." step6_4: "Now go, explore, and have fun!"
step7_2: "Ak sa chcete naučiť viac o Misskey, skúste sekciu {help}."
step7_3: "A teraz, veľa šťastia, bavte sa s Misskey! 🚀"
_2fa: _2fa:
alreadyRegistered: "Už ste zaregistrovali 2-faktorové autentifikačné zariadenie." alreadyRegistered: "Už ste zaregistrovali 2-faktorové autentifikačné zariadenie."
registerDevice: "Registrovať nové zariadenie" registerDevice: "Registrovať nové zariadenie"

View File

@ -8,7 +8,7 @@ notifications: "Notifikationer"
username: "Användarnamn" username: "Användarnamn"
password: "Lösenord" password: "Lösenord"
forgotPassword: "Glömt lösenord" forgotPassword: "Glömt lösenord"
fetchingAsApObject: "Hämtar från Fediversum..." fetchingAsApObject: "Hämtar från Fediversum"
ok: "OK" ok: "OK"
gotIt: "Uppfattat!" gotIt: "Uppfattat!"
cancel: "Avbryt" cancel: "Avbryt"

View File

@ -8,7 +8,7 @@ notifications: "การเเจ้งเตือน"
username: "ชื่อผู้ใช้" username: "ชื่อผู้ใช้"
password: "รหัสผ่าน" password: "รหัสผ่าน"
forgotPassword: "ลืมรหัสผ่าน?" forgotPassword: "ลืมรหัสผ่าน?"
fetchingAsApObject: "กำลังดึงข้อมูล จาก เฟดิเวิร์ส..." fetchingAsApObject: "กำลังดึงข้อมูล จาก เฟดิเวิร์ส"
ok: "ตกลง" ok: "ตกลง"
gotIt: "เข้าใจแล้ว !" gotIt: "เข้าใจแล้ว !"
cancel: "ยกเลิก" cancel: "ยกเลิก"
@ -730,7 +730,7 @@ emailNotification: "การแจ้งเตือนทางอีเมล
publish: "เผยแพร่" publish: "เผยแพร่"
inChannelSearch: "ค้นหาในช่อง" inChannelSearch: "ค้นหาในช่อง"
useReactionPickerForContextMenu: "เปิดตัวเลือกปฏิกิริยาเมื่อคลิกขวา" useReactionPickerForContextMenu: "เปิดตัวเลือกปฏิกิริยาเมื่อคลิกขวา"
typingUsers: "{users} กำลัง/กำลังพิมพ์..." typingUsers: "{users} กำลัง"
jumpToSpecifiedDate: "ข้ามไปยังวันที่เฉพาะเจาะจง" jumpToSpecifiedDate: "ข้ามไปยังวันที่เฉพาะเจาะจง"
showingPastTimeline: "กำลังแสดงผลไทม์ไลน์เก่า" showingPastTimeline: "กำลังแสดงผลไทม์ไลน์เก่า"
clear: "ล้าง" clear: "ล้าง"

View File

@ -8,7 +8,7 @@ notifications: "Сповіщення"
username: "Ім'я користувача" username: "Ім'я користувача"
password: "Пароль" password: "Пароль"
forgotPassword: "Я забув пароль" forgotPassword: "Я забув пароль"
fetchingAsApObject: "Отримуємо з федіверсу..." fetchingAsApObject: "Отримуємо з федіверсу"
ok: "OK" ok: "OK"
gotIt: "Зрозуміло!" gotIt: "Зрозуміло!"
cancel: "Скасувати" cancel: "Скасувати"
@ -940,28 +940,26 @@ _time:
hour: "г" hour: "г"
day: "д" day: "д"
_tutorial: _tutorial:
title: "Як користуватись Misskey" title: "Як використовувати Calckey"
step1_1: "Ласкаво просимо!" step1_1: "Ласкаво просимо!"
step1_2: "Ця сторінка має назву \"стрічка подій\". На ній з'являються записи користувачів на яких ви підписані." step1_2: "Давайте налаштуємо вас. Ви будете працювати в найкоротші терміни!"
step1_3: "Наразі ваша стрічка порожня, оскільки ви ще не написали жодної нотатки і не підписані на інших." step2_1: "Спочатку, будь ласка, заповніть свій профіль"
step2_1: "Перш ніж зробити запис або підписатись на когось, спочатку заповніть свій обліковий запис." step2_2: "Надавши деяку інформацію про себе, іншим людям буде легше зрозуміти, чи хочуть вони бачити ваші записи або стежити за вами."
step2_2: "Надання деякої інформації про себе дозволить іншим користувачам підписатись на вас." step3_1: "Тепер настав час стежити за деякими людьми!"
step3_1: "Ви успішно налаштували свій обліковий запис?" step3_2: "Ваша домашня і соціальна стрічки ґрунтуються на тому, за ким ви стежите, тому для початку спробуйте стежити за кількома акаунтами.\nНатисніть на гурток із плюсом у правому верхньому кутку профілю, щоб стежити за ним."
step3_2: "Наступним кроком є написання нотатки. Це можна зробити, натиснувши зображення олівця на екрані." step4_1: "Давайте вийдемо на вас"
step3_3: "Після написання вмісту ви можете опублікувати його, натиснувши кнопку у верхньому правому куті форми." step4_2: "Для свого першого повідомлення деякі люди люблять робити {introduction} повідомлення або просте \"Hello world!\""
step3_4: "Не знаєте що написати? Спробуйте \"налаштовую свій msky\"!" step5_1: "Тимчасові рамки, скрізь тимчасові рамки!"
step4_1: "Ви розмістили свій перший запис?" step5_2: "У вашому екземплярі включені {timelines} різних часових ліній."
step4_2: "Ура! Ваш перший запис відображається на вашій стрічці подій." step5_3: "Головна {icon} часова шкала - це шкала, де ви можете бачити повідомлення ваших підписників."
step5_1: "Настав час оживити вашу стрічку подій підписавшись на інших користувачів." step5_4: "Місцева {icon} тимчасова шкала - це шкала, де ви можете бачити повідомлення всіх інших користувачів даного екземпляра"
step5_2: "{featured} показує популярні записи , а {explore} популярних користувачів з цього інстансу. Спробуйте підписатись на користувача, який вам сподобався!" step5_5: "Тимчасова шкала Рекомендовані {icon} - це шкала, де ви можете бачити повідомлення від інстанцій, рекомендованих адміністраторами."
step5_3: "Щоб підписатись на інших користувачів, нажміть на їхнє зображення, а потім на кнопку \"підписатись\"." step5_6: "На часовій шкалі Social {icon} відображаються повідомлення від друзів ваших підписників"
step5_4: "Якщо користувач має замок при імені, то йому потрібно буде вручну підтвердити вашу заявку на підписку." step5_7: "Глобальна {icon} часова шкала - це місце, де ви можете бачити повідомлення від усіх інших підключених екземплярів"
step6_1: "Тепер ви повинні бачити записи інших користувачів на вашій стрічці подій." step6_1: "Отже, що це за місце?"
step6_2: "Також ви можете швидко відповісти, або \"відреагувати\" на записи інших користувачів." step6_2: "Ну, ви не просто приєдналися до Кальки. Ви приєдналися до порталу в Fediverse, взаємопов'язаної мережі з тисяч серверів, званих \"інстансами\"."
step6_3: "Щоб \"відреагувати\", нажміть на знак плюс \"+\" на записі і виберіть емоджі яким ви хочете \"відреагувати\"." step6_3: "Кожен сервер працює по-своєму, і не на всіх серверах працює Calckey. Але цей працює! Це трохи складно, але ви швидко розберетеся"
step7_1: "Вітаю! Ви пройшли ознайомлення з Misskey." step6_4: "Тепер ідіть, вивчайте і розважайтеся!"
step7_2: "Якщо ви хочете більше дізнатись про Misskey, зайдіть в розділ {help}."
step7_3: "Насолоджуйтесь Misskey! 🚀"
_2fa: _2fa:
registerKey: "Зареєструвати новий ключ безпеки" registerKey: "Зареєструвати новий ключ безпеки"
_permissions: _permissions:

View File

@ -8,7 +8,7 @@ notifications: "Thông báo"
username: "Tên người dùng" username: "Tên người dùng"
password: "Mật khẩu" password: "Mật khẩu"
forgotPassword: "Quên mật khẩu" forgotPassword: "Quên mật khẩu"
fetchingAsApObject: "Đang nạp dữ liệu từ Fediverse..." fetchingAsApObject: "Đang nạp dữ liệu từ Fediverse"
ok: "Đồng ý" ok: "Đồng ý"
gotIt: "Đã hiểu!" gotIt: "Đã hiểu!"
cancel: "Hủy" cancel: "Hủy"
@ -730,7 +730,7 @@ emailNotification: "Thông báo email"
publish: "Đăng" publish: "Đăng"
inChannelSearch: "Tìm trong kênh" inChannelSearch: "Tìm trong kênh"
useReactionPickerForContextMenu: "Nhấn chuột phải để mở bộ chọn biểu cảm" useReactionPickerForContextMenu: "Nhấn chuột phải để mở bộ chọn biểu cảm"
typingUsers: "{users} đang nhập" typingUsers: "{users} đang nhập"
jumpToSpecifiedDate: "Đến một ngày cụ thể" jumpToSpecifiedDate: "Đến một ngày cụ thể"
showingPastTimeline: "Hiện đang hiển thị dòng thời gian cũ" showingPastTimeline: "Hiện đang hiển thị dòng thời gian cũ"
clear: "Hoàn lại" clear: "Hoàn lại"
@ -1181,28 +1181,26 @@ _time:
hour: "giờ" hour: "giờ"
day: "ngày" day: "ngày"
_tutorial: _tutorial:
title: "Cách dùng Misskey" title: "How to use Calckey"
step1_1: "Xin chào!" step1_1: "Welcome!"
step1_2: "Trang này gọi là \"bảng tin\". Nó hiện \"tút\" từ những người mà bạn \"theo dõi\" theo thứ tự thời gian." step1_2: "Let's get you set up. You'll be up and running in no time!"
step1_3: "Bảng tin của bạn đang trống, bởi vì bạn chưa đăng tút nào hoặc chưa theo dõi ai." step2_1: "First, please fill out your profile."
step2_1: "Hãy hoàn thành việc thiết lập hồ sơ của bạn trước khi viết tút hoặc theo dõi bất kỳ ai." step2_2: "Providing some information about who you are will make it easier for others to tell if they want to see your notes or follow you."
step2_2: "Cung cấp một số thông tin giới thiệu bạn là ai sẽ giúp người khác dễ dàng biết được họ muốn đọc tút hay theo dõi bạn." step3_1: "Now time to follow some people!"
step3_1: "Hoàn thành thiết lập hồ sơ của bạn?" step3_2: "Your home and social timelines are based off of who you follow, so try following a couple accounts to get started.\nClick the plus circle on the top right of a profile to follow them."
step3_2: "Sau đó, hãy thử đăng một tút tiếp theo. Bạn có thể làm như vậy bằng cách nhấn vào nút có biểu tượng bút chì trên màn hình." step4_1: "Let's get you out there."
step3_3: "Nhập nội dung vào khung soạn thảo và nhấn nút đăng ở góc trên." step4_2: "For your first post, some people like to made a {introduction} post or a simple \"Hello world!\""
step3_4: "Chưa biết nói gì? Thử \"Tôi mới tham gia Misskey\"!" step5_1: "Timelines, timelines everywhere!"
step4_1: "Đăng xong tút đầu tiên của bạn?" step5_2: "Your instance has {timelines} different timelines enabled."
step4_2: "De! Tút đầu tiên của bạn đã hiện trên bảng tin." step5_3: "The Home {icon} timeline is where you can see posts from your followers."
step5_1: "Bây giờ, hãy thử làm cho bảng tin của bạn sinh động hơn bằng cách theo dõi những người khác." step5_4: "The Local {icon} timeline is where you can see posts from everyone else on this instance."
step5_2: "{feature} sẽ hiển thị cho bạn các tút nổi bật trên máy chủ này. {explore} sẽ cho phép bạn tìm thấy những người dùng thú vị. Hãy thử tìm những người bạn muốn theo dõi ở đó!" step5_5: "The Recommended {icon} timeline is where you can see posts from instances the admins recommend."
step5_3: "Để theo dõi những người dùng khác, hãy nhấn vào ảnh đại diện của họ và nhấn nút \"Theo dõi\" trên hồ sơ của họ." step5_6: "The Social {icon} timeline is where you can see posts from friends of your followers."
step5_4: "Nếu người dùng khác có biểu tượng ổ khóa bên cạnh tên của họ, có thể mất một khoảng thời gian để người dùng đó phê duyệt yêu cầu theo dõi của bạn theo cách thủ công." step5_7: "The Global {icon} timeline is where you can see posts from every other connected instance."
step6_1: "Bạn sẽ có thể xem tút của những người dùng khác trên bảng tin của mình ngay bây giờ." step6_1: "So, what is this place?"
step6_2: "Bạn cũng có thể đặt \"biểu cảm\" trên tút của người khác để phản hồi nhanh chúng." step6_2: "Well, you didn't just join Calckey. You joined a portal to the Fediverse, an interconnected network of thousands of servers, called \"instances\"."
step6_3: "Để đính kèm \"biểu cảm\", hãy nhấn vào dấu \"+\" trên tút của người dùng khác rồi chọn biểu tượng cảm xúc mà bạn muốn dùng." step6_3: "Each server works in different ways, and not all servers run Calckey. This one does though! It's a bit complicated, but you'll get the hang of it in no time."
step7_1: "Xin chúc mừng! Bây giờ bạn đã hoàn thành phần hướng dẫn cơ bản của Misskey." step6_4: "Now go, explore, and have fun!"
step7_2: "Nếu bạn muốn tìm hiểu thêm về Misskey, hãy thử phần {help}."
step7_3: "Bây giờ, chúc may mắn và vui vẻ với Misskey! 🚀"
_2fa: _2fa:
alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước." alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước."
registerDevice: "Đăng ký một thiết bị" registerDevice: "Đăng ký một thiết bị"

View File

@ -8,7 +8,7 @@ notifications: "通知"
username: "用户名" username: "用户名"
password: "密码" password: "密码"
forgotPassword: "忘记密码" forgotPassword: "忘记密码"
fetchingAsApObject: "正在联邦宇宙查询中..." fetchingAsApObject: "正在联邦宇宙查询中"
ok: "OK" ok: "OK"
gotIt: "我明白了" gotIt: "我明白了"
cancel: "取消" cancel: "取消"
@ -1181,28 +1181,26 @@ _time:
hour: "小时" hour: "小时"
day: "日" day: "日"
_tutorial: _tutorial:
title: "Misskey的使用方法" title: "如何使用Calckey"
step1_1: "欢迎!" step1_1: "欢迎!"
step1_2: "这个页面叫做「时间线」,它会按照时间顺序显示所有你「关注」的人所发的「帖子」。" step1_2: "让我们把你安排好。你很快就会启动并运行!"
step1_3: "如果你并没有发布任何帖子,也没有关注其他的人,你的时间线页面应当什么都没有显示。" step2_1: "首先,请完成您的个人资料。"
step2_1: "在您想要发帖或关注其他人之前,请先设置一下个人资料吧。" step2_2: "通过提供一些关于你自己的信息,其他人会更容易了解他们是否想看到你的帖子或关注你。"
step2_2: "如果别人能够更加的了解你,关注你的概率也会得到提升。" step3_1: "现在是时候跟随一些人了!"
step3_1: "已经设置完个人资料了吗?" step3_2: "你的主页和社交馈送是基于你所关注的人,所以试着先关注几个账户。{n点击个人资料右上角的加号圈就可以关注它。"
step3_2: "那么接下来,试着写一些什么东西来发布吧。你可以通过点击屏幕上的铅笔图标来打开投稿页面。" step4_1: "让我们出去找你。"
step3_3: "写完内容后,点击窗口右上方的按钮就可以投稿。" step4_2: "对于他们的第一条信息,有些人喜欢做{introduction}或一个简单的 \"hello world!\""
step3_4: "不知道说些什么好吗那就写下「Misskey我来啦」这样的话吧。" step5_1: "时间限制,到处是时间限制!"
step4_1: "将你的话语发布出去了吗?" step5_2: "您的实例已启用各种时间线的{timelines}。"
step4_2: "太棒了!现在你可以在你的时间线中看到你刚刚发布的帖子了。" step5_3: "主{icon}时间线是你可以看到你的订阅者的帖子的时间线。"
step5_1: "接下来,关注其他人来使时间线更生动吧。" step5_4: "本地{icon}时间线是你可以看到实例中所有其他用户的信息的时间线。"
step5_2: "{featured}将向您展示热门趋势的帖子。 {explore}将让您找到热门用户。 尝试关注您喜欢的人!" step5_5: "推荐的{icon}时间线 - 是时间轴,你可以看到管理员推荐的实例的信息"
step5_3: "要关注其他用户,请单击他的头像,然后在他的个人资料上按下“关注”按钮。" step5_6: "社交{icon}时间线显示来自你的订阅者朋友的信息。"
step5_4: "如果用户的名称旁边有锁定图标,则该用户需要手动批准您的关注请求。" step5_7: "全球{icon}时间线是你可以看到来自所有其他连接的实例的消息。"
step6_1: "现在,您将可以在时间线上看到其他用户的帖子。" step6_1: "那么,这里是什么地方?"
step6_2: "您还可以在其他人的帖子上进行「回应」,以快速做出简单回复。" step6_2: "好吧你不只是加入卡尔基。你已经加入了Fediverse的一个门户这是一个由成千上万台服务器组成的互联网络被称为 \"实例\""
step6_3: "在他人的贴子上按下「+」图标,即可选择想要的表情来进行「回应」。" step6_3: "每个服务器的工作方式不同并不是所有的服务器都运行Calckey。但这个人确实如此! 这有点复杂,但你很快就会明白的。"
step7_1: "对Misskey基本操作的简单介绍就到此结束了。 辛苦了!" step6_4: "现在去学习并享受乐趣!"
step7_2: "如果你想了解更多有关Misskey的信息请参见{help}。"
step7_3: "接下来享受Misskey带来的乐趣吧🚀"
_2fa: _2fa:
alreadyRegistered: "此设备已被注册" alreadyRegistered: "此设备已被注册"
registerDevice: "注册设备" registerDevice: "注册设备"

View File

@ -8,7 +8,7 @@ notifications: "通知"
username: "使用者名稱" username: "使用者名稱"
password: "密碼" password: "密碼"
forgotPassword: "忘記密碼" forgotPassword: "忘記密碼"
fetchingAsApObject: "從聯邦宇宙取得中..." fetchingAsApObject: "從聯邦宇宙取得中"
ok: "OK" ok: "OK"
gotIt: "知道了" gotIt: "知道了"
cancel: "取消" cancel: "取消"
@ -730,7 +730,7 @@ emailNotification: "郵件通知"
publish: "發佈" publish: "發佈"
inChannelSearch: "頻道内搜尋" inChannelSearch: "頻道内搜尋"
useReactionPickerForContextMenu: "點擊右鍵開啟反應工具欄" useReactionPickerForContextMenu: "點擊右鍵開啟反應工具欄"
typingUsers: "{users}輸入中..." typingUsers: "{users}輸入中"
jumpToSpecifiedDate: "跳轉到特定日期" jumpToSpecifiedDate: "跳轉到特定日期"
showingPastTimeline: "顯示過往的時間線" showingPastTimeline: "顯示過往的時間線"
clear: "清除" clear: "清除"
@ -1181,28 +1181,26 @@ _time:
hour: "小時" hour: "小時"
day: "日" day: "日"
_tutorial: _tutorial:
title: "Misskey使用方法" title: "如何使用Calckey"
step1_1: "歡迎!" step1_1: "欢迎!"
step1_2: "此為「時間軸」頁面,它會按照時間順序顯示你「追隨」的人發出的「貼文」" step1_2: "让我们把你安排好。你很快就会启动并运行!"
step1_3: "由於你沒有發佈任何貼文,也沒有追隨任何人,所以你的時間軸目前是空的。" step2_1: "首先,请完成您的个人资料。"
step2_1: "在發文或追隨其他人之前先讓我們設定一下個人資料吧。" step2_2: "通过提供一些关于你自己的信息,其他人会更容易了解他们是否想看到你的帖子或关注你。"
step2_2: "提供一些關於自己的資訊來讓其他人更有追隨你的意願。" step3_1: "现在是时候跟随一些人了!"
step3_1: "個人資料都設定好了嗎?" step3_2: "你的主页和社交馈送是基于你所关注的人,所以试着先关注几个账户。{n点击个人资料右上角的加号圈就可以关注它。"
step3_2: "接下來,讓我們來試試看發個文,按一下畫面上的鉛筆圖示來開始" step4_1: "让我们出去找你。"
step3_3: "輸入完內容後,按視窗右上角的按鈕來發文" step4_2: "对于他们的第一条信息,有些人喜欢做{introduction}或一个简单的 \"hello world!\""
step3_4: "不知道該寫什麼內容嗎試試看「開始使用Misskey了」如何。" step5_1: "时间限制,到处是时间限制!"
step4_1: "貼文發出去了嗎?" step5_2: "您的实例已启用各种时间线的{timelines}。"
step4_2: "如果你的貼文出現在時間軸上,就代表發文成功。" step5_3: "主{icon}时间线是你可以看到你的订阅者的帖子的时间线。"
step5_1: "現在試試看追隨其他人來讓你的時間軸變得更生動吧。" step5_4: "本地{icon}时间线是你可以看到实例中所有其他用户的信息的时间线。"
step5_2: "你會在{featured}上看到受歡迎的貼文,你也可以從列表中追隨你喜歡的人,或者在{explore}上找到熱門使用者。" step5_5: "推荐的{icon}时间线 - 是时间轴,你可以看到管理员推荐的实例的信息"
step5_3: "想要追隨其他人,只要點擊他們的大頭貼並按「追隨」即可。" step5_6: "社交{icon}时间线显示来自你的订阅者朋友的信息。"
step5_4: "如果使用者的名字旁有鎖頭的圖示,代表他們需要手動核准你的追隨請求。" step5_7: "全球{icon}时间线是你可以看到来自所有其他连接的实例的消息。"
step6_1: "現在你可以在時間軸上看到其他用戶的貼文。" step6_1: "那么,这里是什么地方?"
step6_2: "你也可以對別人的貼文作出「情感」,作出簡單的回覆。" step6_2: "好吧你不只是加入卡尔基。你已经加入了Fediverse的一个门户这是一个由成千上万台服务器组成的互联网络被称为 \"实例\""
step6_3: "在他人的貼文按下\"+\"圖標,即可選擇喜好的表情符號進行回應。" step6_3: "每个服务器的工作方式不同并不是所有的服务器都运行Calckey。但这个人确实如此! 这有点复杂,但你很快就会明白的。"
step7_1: "以上為Misskey的基本操作說明教學在此告一段落。辛苦了。" step6_4: "现在去学习并享受乐趣!"
step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
step7_3: "那麼祝您在Misskey玩的開心~ 🚀"
_2fa: _2fa:
alreadyRegistered: "此設備已經被註冊過了" alreadyRegistered: "此設備已經被註冊過了"
registerDevice: "註冊裝置" registerDevice: "註冊裝置"

View File

@ -1,6 +1,6 @@
{ {
"name": "calckey", "name": "calckey",
"version": "12.119.0-calc.12.5", "version": "12.119.0-calc.13",
"codename": "aqua", "codename": "aqua",
"repository": { "repository": {
"type": "git", "type": "git",

View File

@ -36,7 +36,7 @@
"archiver": "5.3.1", "archiver": "5.3.1",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autwh": "0.1.0", "autwh": "0.1.0",
"aws-sdk": "2.1251.0", "aws-sdk": "2.1253.0",
"bcryptjs": "2.4.3", "bcryptjs": "2.4.3",
"blurhash": "1.1.5", "blurhash": "1.1.5",
"bull": "4.10.1", "bull": "4.10.1",
@ -80,7 +80,7 @@
"mocha": "10.1.0", "mocha": "10.1.0",
"multer": "1.4.4-lts.1", "multer": "1.4.4-lts.1",
"nested-property": "4.0.0", "nested-property": "4.0.0",
"node-fetch": "3.2.10", "node-fetch": "3.3.0",
"nodemailer": "6.8.0", "nodemailer": "6.8.0",
"nsfwjs": "2.4.2", "nsfwjs": "2.4.2",
"oauth": "^0.9.15", "oauth": "^0.9.15",
@ -111,7 +111,7 @@
"stringz": "2.1.0", "stringz": "2.1.0",
"summaly": "2.7.0", "summaly": "2.7.0",
"syslog-pro": "1.0.0", "syslog-pro": "1.0.0",
"systeminformation": "5.12.13", "systeminformation": "5.12.14",
"tesseract.js": "^3.0.3", "tesseract.js": "^3.0.3",
"tinycolor2": "1.4.2", "tinycolor2": "1.4.2",
"tmp": "0.2.1", "tmp": "0.2.1",

View File

@ -63,7 +63,7 @@ export async function getFileInfo(path: string, opts: {
let height: number | undefined; let height: number | undefined;
let orientation: number | undefined; let orientation: number | undefined;
if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/bmp', 'image/tiff', 'image/svg+xml', 'image/vnd.adobe.photoshop'].includes(type.mime)) { if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/bmp', 'image/tiff', 'image/svg+xml', 'image/vnd.adobe.photoshop', 'image/avif'].includes(type.mime)) {
const imageSize = await detectImageSize(path).catch(e => { const imageSize = await detectImageSize(path).catch(e => {
warnings.push(`detectImageSize failed: ${e}`); warnings.push(`detectImageSize failed: ${e}`);
return undefined; return undefined;
@ -90,7 +90,7 @@ export async function getFileInfo(path: string, opts: {
let blurhash: string | undefined; let blurhash: string | undefined;
if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/svg+xml'].includes(type.mime)) { if (['image/jpeg', 'image/gif', 'image/png', 'image/apng', 'image/webp', 'image/svg+xml', 'image/avif'].includes(type.mime)) {
blurhash = await getBlurhash(path).catch(e => { blurhash = await getBlurhash(path).catch(e => {
warnings.push(`getBlurhash failed: ${e}`); warnings.push(`getBlurhash failed: ${e}`);
return undefined; return undefined;

View File

@ -2,7 +2,7 @@ import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
const dictionary = { const dictionary = {
'safe-file': FILE_TYPE_BROWSERSAFE, 'safe-file': FILE_TYPE_BROWSERSAFE,
'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/svg+xml'], 'sharp-convertible-image': ['image/jpeg', 'image/png', 'image/gif', 'image/apng', 'image/vnd.mozilla.apng', 'image/webp', 'image/svg+xml', 'image/avif'],
}; };
export const isMimeImage = (mime: string, type: keyof typeof dictionary): boolean => dictionary[type].includes(mime); export const isMimeImage = (mime: string, type: keyof typeof dictionary): boolean => dictionary[type].includes(mime);

View File

@ -60,7 +60,7 @@ export const DriveFileRepository = db.getRepository(DriveFile).extend({
} }
} }
const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml'].includes(file.type); const isImage = file.type && ['image/png', 'image/apng', 'image/gif', 'image/jpeg', 'image/webp', 'image/svg+xml', 'image/avif'].includes(file.type);
return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url); return thumbnail ? (file.thumbnailUrl || (isImage ? (file.webpublicUrl || file.url) : null)) : (file.webpublicUrl || file.url);
}, },

View File

@ -59,7 +59,7 @@ export default async function(ctx: Koa.Context) {
const convertFile = async () => { const convertFile = async () => {
if (isThumbnail) { if (isThumbnail) {
if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(mime)) { if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml', 'image/avif'].includes(mime)) {
return await convertToWebp(path, 498, 280); return await convertToWebp(path, 498, 280);
} else if (mime.startsWith('video/')) { } else if (mime.startsWith('video/')) {
return await GenerateVideoThumbnail(path); return await GenerateVideoThumbnail(path);

View File

@ -42,9 +42,6 @@ const app = new Koa();
//#region Bull Dashboard //#region Bull Dashboard
const bullBoardPath = '/queue'; const bullBoardPath = '/queue';
// used as a url param to prevent caching css and images
const nowDateMs = Date.now();
// Authenticate // Authenticate
app.use(async (ctx, next) => { app.use(async (ctx, next) => {
if (ctx.path === bullBoardPath || ctx.path.startsWith(bullBoardPath + '/')) { if (ctx.path === bullBoardPath || ctx.path.startsWith(bullBoardPath + '/')) {
@ -298,7 +295,6 @@ router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => {
icon: meta.iconUrl, icon: meta.iconUrl,
themeColor: meta.themeColor, themeColor: meta.themeColor,
privateMode: meta.privateMode, privateMode: meta.privateMode,
nowDateMs: nowDateMs,
}); });
ctx.set('Cache-Control', 'public, max-age=15'); ctx.set('Cache-Control', 'public, max-age=15');
} else { } else {
@ -344,7 +340,6 @@ router.get('/notes/:note', async (ctx, next) => {
icon: meta.iconUrl, icon: meta.iconUrl,
privateMode: meta.privateMode, privateMode: meta.privateMode,
themeColor: meta.themeColor, themeColor: meta.themeColor,
nowDateMs: nowDateMs,
}); });
ctx.set('Cache-Control', 'public, max-age=15'); ctx.set('Cache-Control', 'public, max-age=15');
@ -382,7 +377,6 @@ router.get('/@:user/pages/:page', async (ctx, next) => {
icon: meta.iconUrl, icon: meta.iconUrl,
themeColor: meta.themeColor, themeColor: meta.themeColor,
privateMode: meta.privateMode, privateMode: meta.privateMode,
nowDateMs: nowDateMs,
}); });
if (['public'].includes(page.visibility)) { if (['public'].includes(page.visibility)) {
@ -416,7 +410,6 @@ router.get('/clips/:clip', async (ctx, next) => {
privateMode: meta.privateMode, privateMode: meta.privateMode,
icon: meta.iconUrl, icon: meta.iconUrl,
themeColor: meta.themeColor, themeColor: meta.themeColor,
nowDateMs: nowDateMs,
}); });
ctx.set('Cache-Control', 'public, max-age=15'); ctx.set('Cache-Control', 'public, max-age=15');
@ -443,7 +436,6 @@ router.get('/gallery/:post', async (ctx, next) => {
icon: meta.iconUrl, icon: meta.iconUrl,
themeColor: meta.themeColor, themeColor: meta.themeColor,
privateMode: meta.privateMode, privateMode: meta.privateMode,
nowDateMs: nowDateMs,
}); });
ctx.set('Cache-Control', 'public, max-age=15'); ctx.set('Cache-Control', 'public, max-age=15');
@ -469,7 +461,6 @@ router.get('/channels/:channel', async (ctx, next) => {
icon: meta.iconUrl, icon: meta.iconUrl,
themeColor: meta.themeColor, themeColor: meta.themeColor,
privateMode: meta.privateMode, privateMode: meta.privateMode,
nowDateMs: nowDateMs,
}); });
ctx.set('Cache-Control', 'public, max-age=15'); ctx.set('Cache-Control', 'public, max-age=15');
@ -545,7 +536,6 @@ router.get('(.*)', async ctx => {
themeColor: meta.themeColor, themeColor: meta.themeColor,
randomMOTD: motd[Math.floor(Math.random() * motd.length)], randomMOTD: motd[Math.floor(Math.random() * motd.length)],
privateMode: meta.privateMode, privateMode: meta.privateMode,
nowDateMs: nowDateMs,
}); });
ctx.set('Cache-Control', 'public, max-age=3'); ctx.set('Cache-Control', 'public, max-age=3');
}); });

View File

@ -20,23 +20,26 @@ doctype html
html html
- var timestamp = Date.now();
head head
meta(charset='utf-8') meta(charset='utf-8')
meta(name='application-name' content='Calckey') meta(name='application-name' content='Calckey')
meta(name='referrer' content='origin') meta(name='referrer' content='origin')
meta(name='darkreader-lock' content='')
meta(name='theme-color' content= themeColor || '#31748f') meta(name='theme-color' content= themeColor || '#31748f')
meta(name='theme-color-orig' content= themeColor || '#31748f') meta(name='theme-color-orig' content= themeColor || '#31748f')
meta(property='twitter:card' content='summary') meta(property='twitter:card' content='summary')
meta(property='og:site_name' content= instanceName || 'Calckey') meta(property='og:site_name' content= instanceName || 'Calckey')
meta(name='viewport' content='width=device-width, initial-scale=1') meta(name='viewport' content='width=device-width, initial-scale=1')
link(rel='icon' href= icon || `/favicon.ico?${ nowDateMs }`) link(rel='icon' href= icon || `/favicon.ico?${ timestamp }`)
link(rel='apple-touch-icon' href= icon || `/apple-touch-icon.png?${ nowDateMs }`) link(rel='apple-touch-icon' href= icon || `/apple-touch-icon.png?${ timestamp }`)
link(rel='manifest' href='/manifest.json') link(rel='manifest' href='/manifest.json')
link(rel='prefetch' href=`/static-assets/badges/info.png?${ nowDateMs }`) link(rel='prefetch' href=`/static-assets/badges/info.png?${ timestamp }`)
link(rel='prefetch' href=`/static-assets/badges/not-found.png?${ nowDateMs }`) link(rel='prefetch' href=`/static-assets/badges/not-found.png?${ timestamp }`)
link(rel='prefetch' href=`/static-assets/badges/error.png?${ nowDateMs }`) link(rel='prefetch' href=`/static-assets/badges/error.png?${ timestamp }`)
link(rel='stylesheet' href='/assets/phosphor/icons.css') link(rel='stylesheet' href='/assets/phosphor/icons.css')
link(rel='stylesheet' href=`/static-assets/instance.css?${ nowDateMs }`) link(rel='stylesheet' href=`/static-assets/instance.css?${ timestamp }`)
link(rel='modulepreload' href=`/assets/${clientEntry.file}`) link(rel='modulepreload' href=`/assets/${clientEntry.file}`)
each href in clientEntry.css each href in clientEntry.css
@ -77,7 +80,7 @@ html
br br
| Please turn on your JavaScript | Please turn on your JavaScript
div#splash div#splash
img#splashIcon(src= splashIcon || `/static-assets/splash.png?${ nowDateMs }`) img#splashIcon(src= splashIcon || `/static-assets/splash.png?${ timestamp }`)
span#splashText span#splashText
block randomMOTD block randomMOTD
= randomMOTD = randomMOTD

View File

@ -49,6 +49,7 @@ async function save(file: DriveFile, path: string, name: string, type: string, h
if (type === 'image/png') ext = '.png'; if (type === 'image/png') ext = '.png';
if (type === 'image/webp') ext = '.webp'; if (type === 'image/webp') ext = '.webp';
if (type === 'image/apng') ext = '.apng'; if (type === 'image/apng') ext = '.apng';
if (type === 'image/avif') ext = '.avif';
if (type === 'image/vnd.mozilla.apng') ext = '.apng'; if (type === 'image/vnd.mozilla.apng') ext = '.apng';
} }
@ -171,7 +172,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
} }
} }
if (!['image/jpeg', 'image/png', 'image/webp', 'image/svg+xml'].includes(type)) { if (!['image/jpeg', 'image/png', 'image/webp', 'image/svg+xml', 'image/avif'].includes(type)) {
logger.debug('web image and thumbnail not created (not an required file)'); logger.debug('web image and thumbnail not created (not an required file)');
return { return {
webpublic: null, webpublic: null,
@ -238,7 +239,7 @@ export async function generateAlts(path: string, type: string, generateWeb: bool
let thumbnail: IImage | null = null; let thumbnail: IImage | null = null;
try { try {
if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml'].includes(type)) { if (['image/jpeg', 'image/webp', 'image/png', 'image/svg+xml', 'image/avif'].includes(type)) {
thumbnail = await convertSharpToWebp(img, 498, 280); thumbnail = await convertSharpToWebp(img, 498, 280);
} else { } else {
logger.debug('thumbnail not created (not an required file)'); logger.debug('thumbnail not created (not an required file)');

View File

@ -13,7 +13,7 @@
"@rollup/pluginutils": "^4.2.1", "@rollup/pluginutils": "^4.2.1",
"@syuilo/aiscript": "0.11.1", "@syuilo/aiscript": "0.11.1",
"@vitejs/plugin-vue": "3.2.0", "@vitejs/plugin-vue": "3.2.0",
"@vue/compiler-sfc": "3.2.44", "@vue/compiler-sfc": "3.2.45",
"autobind-decorator": "2.4.0", "autobind-decorator": "2.4.0",
"autosize": "5.0.1", "autosize": "5.0.1",
"blurhash": "1.1.5", "blurhash": "1.1.5",
@ -58,7 +58,7 @@
"uuid": "9.0.0", "uuid": "9.0.0",
"vanilla-tilt": "1.7.3", "vanilla-tilt": "1.7.3",
"vite": "^3.2.3", "vite": "^3.2.3",
"vue": "3.2.44", "vue": "3.2.45",
"vue-isyourpasswordsafe": "^2.0.0", "vue-isyourpasswordsafe": "^2.0.0",
"vue-plyr": "^7.0.0", "vue-plyr": "^7.0.0",
"vue-prism-editor": "2.0.0-alpha.2", "vue-prism-editor": "2.0.0-alpha.2",

View File

@ -221,7 +221,12 @@ export async function openAccountMenu(opts: {
icon: 'ph-users-bold ph-lg', icon: 'ph-users-bold ph-lg',
text: i18n.ts.manageAccounts, text: i18n.ts.manageAccounts,
to: '/settings/accounts', to: '/settings/accounts',
}]], ev.currentTarget ?? ev.target, { },{
type: 'button',
icon: 'ph-sign-out-bold ph-lg',
text: i18n.ts.logout,
action: () => { signout(); },
},]], ev.currentTarget ?? ev.target, {
align: 'left', align: 'left',
}); });
} else { } else {

View File

@ -20,7 +20,7 @@
<div v-show="showBody" ref="content" class="content" :class="{ omitted }"> <div v-show="showBody" ref="content" class="content" :class="{ omitted }">
<slot></slot> <slot></slot>
<button v-if="omitted" class="fade _button" @click="() => { ignoreOmit = true; omitted = false; }"> <button v-if="omitted" class="fade _button" @click="() => { ignoreOmit = true; omitted = false; }">
<span>{{ $ts.showMore }}</span> <span>{{ i18n.ts.showMore }}</span>
</button> </button>
</div> </div>
</transition> </transition>
@ -29,6 +29,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import { i18n } from '@/i18n';
export default defineComponent({ export default defineComponent({
props: { props: {
@ -73,6 +74,7 @@ export default defineComponent({
showBody: this.expanded, showBody: this.expanded,
omitted: null, omitted: null,
ignoreOmit: false, ignoreOmit: false,
i18n,
}; };
}, },
mounted() { mounted() {

View File

@ -46,7 +46,7 @@
</section> </section>
<section> <section>
<header><i class="far fa-clock ph-fw ph-lg"></i> {{ i18n.ts.recentUsed }}</header> <header><i class="ph-alarm-bold ph-fw ph-lg"></i> {{ i18n.ts.recentUsed }}</header>
<div class="body"> <div class="body">
<button <button
v-for="emoji in recentlyUsedEmojis" v-for="emoji in recentlyUsedEmojis"

View File

@ -18,15 +18,15 @@
<div class="xkpnjxcv _formRoot"> <div class="xkpnjxcv _formRoot">
<template v-for="item in Object.keys(form).filter(item => !form[item].hidden)"> <template v-for="item in Object.keys(form).filter(item => !form[item].hidden)">
<FormInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1" class="_formBlock"> <FormInput v-if="form[item].type === 'number'" v-model="values[item]" type="number" :step="form[item].step || 1" class="_formBlock">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
<template v-if="form[item].description" #caption>{{ form[item].description }}</template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template>
</FormInput> </FormInput>
<FormInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text" class="_formBlock"> <FormInput v-else-if="form[item].type === 'string' && !form[item].multiline" v-model="values[item]" type="text" class="_formBlock">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
<template v-if="form[item].description" #caption>{{ form[item].description }}</template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template>
</FormInput> </FormInput>
<FormTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]" class="_formBlock"> <FormTextarea v-else-if="form[item].type === 'string' && form[item].multiline" v-model="values[item]" class="_formBlock">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
<template v-if="form[item].description" #caption>{{ form[item].description }}</template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template>
</FormTextarea> </FormTextarea>
<FormSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]" class="_formBlock"> <FormSwitch v-else-if="form[item].type === 'boolean'" v-model="values[item]" class="_formBlock">
@ -34,15 +34,15 @@
<template v-if="form[item].description" #caption>{{ form[item].description }}</template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template>
</FormSwitch> </FormSwitch>
<FormSelect v-else-if="form[item].type === 'enum'" v-model="values[item]" class="_formBlock"> <FormSelect v-else-if="form[item].type === 'enum'" v-model="values[item]" class="_formBlock">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
<option v-for="item in form[item].enum" :key="item.value" :value="item.value">{{ item.label }}</option> <option v-for="item in form[item].enum" :key="item.value" :value="item.value">{{ item.label }}</option>
</FormSelect> </FormSelect>
<FormRadios v-else-if="form[item].type === 'radio'" v-model="values[item]" class="_formBlock"> <FormRadios v-else-if="form[item].type === 'radio'" v-model="values[item]" class="_formBlock">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
<option v-for="item in form[item].options" :key="item.value" :value="item.value">{{ item.label }}</option> <option v-for="item in form[item].options" :key="item.value" :value="item.value">{{ item.label }}</option>
</FormRadios> </FormRadios>
<FormRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :text-converter="form[item].textConverter" class="_formBlock"> <FormRange v-else-if="form[item].type === 'range'" v-model="values[item]" :min="form[item].min" :max="form[item].max" :step="form[item].step" :text-converter="form[item].textConverter" class="_formBlock">
<template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ $ts.optional }})</span></template> <template #label><span v-text="form[item].label || item"></span><span v-if="form[item].required === false"> ({{ i18n.ts.optional }})</span></template>
<template v-if="form[item].description" #caption>{{ form[item].description }}</template> <template v-if="form[item].description" #caption>{{ form[item].description }}</template>
</FormRange> </FormRange>
<MkButton v-else-if="form[item].type === 'button'" class="_formBlock" @click="form[item].action($event, values)"> <MkButton v-else-if="form[item].type === 'button'" class="_formBlock" @click="form[item].action($event, values)">
@ -64,6 +64,7 @@ import FormRange from './form/range.vue';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
import FormRadios from './form/radios.vue'; import FormRadios from './form/radios.vue';
import XModalWindow from '@/components/MkModalWindow.vue'; import XModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -93,6 +94,7 @@ export default defineComponent({
data() { data() {
return { return {
values: {}, values: {},
i18n,
}; };
}, },

View File

@ -1,12 +1,13 @@
<template> <template>
<div class="mk-google"> <div class="mk-google">
<input v-model="query" type="search" :placeholder="q"> <input v-model="query" type="search" :placeholder="q">
<button @click="search"><i class="ph-magnifying-glass-bold ph-lg"></i> {{ $ts.searchByGoogle }}</button> <button @click="search"><i class="ph-magnifying-glass-bold ph-lg"></i> {{ i18n.ts.searchByGoogle }}</button>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref } from 'vue'; import { ref } from 'vue';
import { i18n } from '@/i18n';
const props = defineProps<{ const props = defineProps<{
q: string; q: string;

View File

@ -5,7 +5,7 @@
</div> </div>
<div class="value"> <div class="value">
<slot name="value"></slot> <slot name="value"></slot>
<button v-if="copy" v-tooltip="i18n.ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copy_"><i class="far fa-copy"></i></button> <button v-if="copy" v-tooltip="i18n.ts.copy" class="_textButton" style="margin-left: 0.5em;" @click="copy_"><i class="ph-clipboard-text-bold"></i></button>
</div> </div>
</div> </div>
</template> </template>

View File

@ -2,8 +2,8 @@
<div class="mk-media-banner"> <div class="mk-media-banner">
<div v-if="media.isSensitive && hide" class="sensitive" @click="hide = false"> <div v-if="media.isSensitive && hide" class="sensitive" @click="hide = false">
<span class="icon"><i class="ph-warning-bold ph-lg"></i></span> <span class="icon"><i class="ph-warning-bold ph-lg"></i></span>
<b>{{ $ts.sensitive }}</b> <b>{{ i18n.ts.sensitive }}</b>
<span>{{ $ts.clickToShow }}</span> <span>{{ i18n.ts.clickToShow }}</span>
</div> </div>
<div v-else-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" class="audio"> <div v-else-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" class="audio">
<VuePlyr <VuePlyr
@ -49,6 +49,7 @@ import VuePlyr from 'vue-plyr';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import { ColdDeviceStorage } from '@/store'; import { ColdDeviceStorage } from '@/store';
import 'vue-plyr/dist/vue-plyr.css'; import 'vue-plyr/dist/vue-plyr.css';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
media: misskey.entities.DriveFile; media: misskey.entities.DriveFile;
@ -74,6 +75,9 @@ onMounted(() => {
margin-top: 4px; margin-top: 4px;
overflow: hidden; overflow: hidden;
--plyr-color-main: var(--accent); --plyr-color-main: var(--accent);
--plyr-audio-controls-background: var(--panelHighlight);
--plyr-audio-controls-background-hover: var(--accentedBg);
--plyr-audio-control-color: var(--navFg);
> .download, > .download,
> .sensitive { > .sensitive {

View File

@ -10,9 +10,9 @@
</header> </header>
<textarea id="captioninput" v-model="inputValue" autofocus :placeholder="input.placeholder" @keydown="onInputKeydown"></textarea> <textarea id="captioninput" v-model="inputValue" autofocus :placeholder="input.placeholder" @keydown="onInputKeydown"></textarea>
<div v-if="(showOkButton || showCaptionButton || showCancelButton)" class="buttons"> <div v-if="(showOkButton || showCaptionButton || showCancelButton)" class="buttons">
<MkButton inline primary :disabled="remainingLength < 0" @click="ok">{{ $ts.ok }}</MkButton> <MkButton inline primary :disabled="remainingLength < 0" @click="ok">{{ i18n.ts.ok }}</MkButton>
<MkButton inline @click="caption">{{ $ts.caption }}</MkButton> <MkButton inline @click="caption">{{ i18n.ts.caption }}</MkButton>
<MkButton inline @click="cancel">{{ $ts.cancel }}</MkButton> <MkButton inline @click="cancel">{{ i18n.ts.cancel }}</MkButton>
</div> </div>
</div> </div>
</div> </div>
@ -37,6 +37,7 @@ import MkModal from '@/components/MkModal.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import bytes from '@/filters/bytes'; import bytes from '@/filters/bytes';
import number from '@/filters/number'; import number from '@/filters/number';
import { i18n } from '@/i18n';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -78,7 +79,8 @@ export default defineComponent({
data() { data() {
return { return {
inputValue: this.input.default ? this.input.default : null inputValue: this.input.default ? this.input.default : null,
i18n,
}; };
}, },

View File

@ -3,8 +3,8 @@
<ImgWithBlurhash class="bg" :hash="image.blurhash" :title="image.comment" :alt="image.comment"/> <ImgWithBlurhash class="bg" :hash="image.blurhash" :title="image.comment" :alt="image.comment"/>
<div class="text"> <div class="text">
<div class="wrapper"> <div class="wrapper">
<b style="display: block;"><i class="ph-warning-bold ph-lg"></i> {{ $ts.sensitive }}</b> <b style="display: block;"><i class="ph-warning-bold ph-lg"></i> {{ i18n.ts.sensitive }}</b>
<span style="display: block;">{{ $ts.clickToShow }}</span> <span style="display: block;">{{ i18n.ts.clickToShow }}</span>
</div> </div>
</div> </div>
</div> </div>
@ -16,7 +16,7 @@
<ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.comment" :title="image.comment" :cover="false"/> <ImgWithBlurhash :hash="image.blurhash" :src="url" :alt="image.comment" :title="image.comment" :cover="false"/>
<div v-if="image.type === 'image/gif'" class="gif">GIF</div> <div v-if="image.type === 'image/gif'" class="gif">GIF</div>
</a> </a>
<button v-tooltip="$ts.hide" class="_button hide" @click="hide = true"><i class="ph-eye-slash-bold ph-lg"></i></button> <button v-tooltip="i18n.ts.hide" class="_button hide" @click="hide = true"><i class="ph-eye-slash-bold ph-lg"></i></button>
</div> </div>
</template> </template>
@ -26,6 +26,7 @@ import * as misskey from 'misskey-js';
import { getStaticImageUrl } from '@/scripts/get-static-image-url'; import { getStaticImageUrl } from '@/scripts/get-static-image-url';
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
const props = defineProps<{ const props = defineProps<{
image: misskey.entities.DriveFile; image: misskey.entities.DriveFile;

View File

@ -1,8 +1,8 @@
<template> <template>
<div v-if="hide" class="icozogqfvdetwohsdglrbswgrejoxbdj" @click="hide = false"> <div v-if="hide" class="icozogqfvdetwohsdglrbswgrejoxbdj" @click="hide = false">
<div> <div>
<b><i class="ph-warning-bold ph-lg"></i> {{ $ts.sensitive }}</b> <b><i class="ph-warning-bold ph-lg"></i> {{ i18n.ts.sensitive }}</b>
<span>{{ $ts.clickToShow }}</span> <span>{{ i18n.ts.clickToShow }}</span>
</div> </div>
</div> </div>
<div v-else class="kkjnbbplepmiyuadieoenjgutgcmtsvu"> <div v-else class="kkjnbbplepmiyuadieoenjgutgcmtsvu">
@ -46,6 +46,7 @@ import VuePlyr from 'vue-plyr';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
import 'vue-plyr/dist/vue-plyr.css'; import 'vue-plyr/dist/vue-plyr.css';
import { i18n } from '@/i18n';
const props = defineProps<{ const props = defineProps<{
video: misskey.entities.DriveFile; video: misskey.entities.DriveFile;

View File

@ -2,7 +2,7 @@
<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')"> <MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
<div ref="rootEl" class="hrmcaedk _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }"> <div ref="rootEl" class="hrmcaedk _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }">
<div class="header" @contextmenu="onContextmenu"> <div class="header" @contextmenu="onContextmenu">
<button v-if="history.length > 0" v-tooltip="$ts.goBack" class="_button" @click="back()"><i class="ph--left-bold ph-lg"></i></button> <button v-if="history.length > 0" v-tooltip="i18n.ts.goBack" class="_button" @click="back()"><i class="ph--left-bold ph-lg"></i></button>
<span v-else style="display: inline-block; width: 20px"></span> <span v-else style="display: inline-block; width: 20px"></span>
<span v-if="pageMetadata?.value" class="title"> <span v-if="pageMetadata?.value" class="title">
<i v-if="pageMetadata?.value.icon" class="icon" :class="pageMetadata?.value.icon"></i> <i v-if="pageMetadata?.value.icon" class="icon" :class="pageMetadata?.value.icon"></i>

View File

@ -33,14 +33,14 @@
<MkTime v-if="withTime" :time="notification.createdAt" class="time"/> <MkTime v-if="withTime" :time="notification.createdAt" class="time"/>
</header> </header>
<MkA v-if="notification.type === 'reaction'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> <MkA v-if="notification.type === 'reaction'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<i class="ph-quotes-bold ph-lg"></i> <i class="ph-quotes-fill ph-lg"></i>
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
<i class="ph-quotes-bold ph-lg"></i> <i class="ph-quotes-fill ph-lg"></i>
</MkA> </MkA>
<MkA v-if="notification.type === 'renote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)"> <MkA v-if="notification.type === 'renote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note.renote)">
<i class="ph-quotes-bold ph-lg"></i> <i class="ph-quotes-fill ph-lg"></i>
<Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.renote.emojis"/> <Mfm :text="getNoteSummary(notification.note.renote)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.renote.emojis"/>
<i class="ph-quotes-bold ph-lg"></i> <i class="ph-quotes-fill ph-lg"></i>
</MkA> </MkA>
<MkA v-if="notification.type === 'reply'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> <MkA v-if="notification.type === 'reply'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
@ -52,14 +52,14 @@
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
</MkA> </MkA>
<MkA v-if="notification.type === 'pollVote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> <MkA v-if="notification.type === 'pollVote'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<i class="ph-quotes-bold ph-lg"></i> <i class="ph-quotes-fill ph-lg"></i>
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
<i class="ph-quotes-bold ph-lg"></i> <i class="ph-quotes-fill ph-lg"></i>
</MkA> </MkA>
<MkA v-if="notification.type === 'pollEnded'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)"> <MkA v-if="notification.type === 'pollEnded'" class="text" :to="notePage(notification.note)" :title="getNoteSummary(notification.note)">
<i class="ph-quotes-bold ph-lg"></i> <i class="ph-quotes-fill ph-lg"></i>
<Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/> <Mfm :text="getNoteSummary(notification.note)" :plain="true" :nowrap="!full" :custom-emojis="notification.note.emojis"/>
<i class="ph-quotes-bold ph-lg"></i> <i class="ph-quotes-fill ph-lg"></i>
</MkA> </MkA>
<span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span> <span v-if="notification.type === 'follow'" class="text" style="opacity: 0.6;">{{ i18n.ts.youGotNewFollower }}<div v-if="full"><MkFollowButton :user="notification.user" :full="true"/></div></span>
<span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ i18n.ts.followRequestAccepted }}</span> <span v-if="notification.type === 'followRequestAccepted'" class="text" style="opacity: 0.6;">{{ i18n.ts.followRequestAccepted }}</span>

View File

@ -18,6 +18,7 @@
import { defineComponent, defineAsyncComponent } from 'vue'; import { defineComponent, defineAsyncComponent } from 'vue';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -41,6 +42,7 @@ export default defineComponent({
data() { data() {
return { return {
menu: null as Promise<null> | null, menu: null as Promise<null> | null,
i18n,
}; };
}, },
@ -73,7 +75,7 @@ export default defineComponent({
}, },
async rename(file) { async rename(file) {
const { canceled, result } = await os.inputText({ const { canceled, result } = await os.inputText({
title: this.$ts.enterFileName, title: i18n.ts.enterFileName,
default: file.name, default: file.name,
allowEmpty: false, allowEmpty: false,
}); });
@ -89,9 +91,9 @@ export default defineComponent({
async describe(file) { async describe(file) {
os.popup(defineAsyncComponent(() => import('@/components/MkMediaCaption.vue')), { os.popup(defineAsyncComponent(() => import('@/components/MkMediaCaption.vue')), {
title: this.$ts.describeFile, title: i18n.ts.describeFile,
input: { input: {
placeholder: this.$ts.inputNewDescription, placeholder: i18n.ts.inputNewDescription,
default: file.comment !== null ? file.comment : '', default: file.comment !== null ? file.comment : '',
}, },
image: file, image: file,
@ -112,19 +114,19 @@ export default defineComponent({
showFileMenu(file, ev: MouseEvent) { showFileMenu(file, ev: MouseEvent) {
if (this.menu) return; if (this.menu) return;
this.menu = os.popupMenu([{ this.menu = os.popupMenu([{
text: this.$ts.renameFile, text: i18n.ts.renameFile,
icon: 'ph-cursor-text-bold ph-lg', icon: 'ph-cursor-text-bold ph-lg',
action: () => { this.rename(file); }, action: () => { this.rename(file); },
}, { }, {
text: file.isSensitive ? this.$ts.unmarkAsSensitive : this.$ts.markAsSensitive, text: file.isSensitive ? i18n.ts.unmarkAsSensitive : i18n.ts.markAsSensitive,
icon: file.isSensitive ? 'ph-eye-slash-bold ph-lg' : 'ph-eye-bold ph-lg', icon: file.isSensitive ? 'ph-eye-slash-bold ph-lg' : 'ph-eye-bold ph-lg',
action: () => { this.toggleSensitive(file); }, action: () => { this.toggleSensitive(file); },
}, { }, {
text: this.$ts.describeFile, text: i18n.ts.describeFile,
icon: 'ph-cursor-text-bold ph-lg', icon: 'ph-cursor-text-bold ph-lg',
action: () => { this.describe(file); }, action: () => { this.describe(file); },
}, { }, {
text: this.$ts.attachCancel, text: i18n.ts.attachCancel,
icon: 'ph-circle-wavy-warning-bold ph-lg', icon: 'ph-circle-wavy-warning-bold ph-lg',
action: () => { this.detachMedia(file.id); }, action: () => { this.detachMedia(file.id); },
}], ev.currentTarget ?? ev.target).then(() => this.menu = null); }], ev.currentTarget ?? ev.target).then(() => this.menu = null);

View File

@ -5,7 +5,7 @@
<template #prefix><i class="ph-key-bold ph-lg"></i></template> <template #prefix><i class="ph-key-bold ph-lg"></i></template>
</MkInput> </MkInput>
<MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-signup-username @update:modelValue="onChangeUsername"> <MkInput v-model="username" class="_formBlock" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :spellcheck="false" required data-cy-signup-username @update:modelValue="onChangeUsername">
<template #label>{{ i18n.ts.username }} <div v-tooltip:dialog="i18n.ts.usernameInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template> <template #label>{{ i18n.ts.username }} <div v-tooltip:dialog="i18n.ts.usernameInfo" class="_button _help"><i class="ph-question-bold"></i></div></template>
<template #prefix>@</template> <template #prefix>@</template>
<template #suffix>@{{ host }}</template> <template #suffix>@{{ host }}</template>
<template #caption> <template #caption>
@ -19,7 +19,7 @@
</template> </template>
</MkInput> </MkInput>
<MkInput v-if="instance.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail"> <MkInput v-if="instance.emailRequiredForSignup" v-model="email" class="_formBlock" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail">
<template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="far fa-question-circle"></i></div></template> <template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="ph-question-bold"></i></div></template>
<template #prefix><i class="ph-envelope-simple-open-bold ph-lg"></i></template> <template #prefix><i class="ph-envelope-simple-open-bold ph-lg"></i></template>
<template #caption> <template #caption>
<span v-if="emailState === 'wait'" style="color:#999"><i class="ph-circle-notch-bold ph-lg fa-pulse ph-fw ph-lg"></i> {{ i18n.ts.checking }}</span> <span v-if="emailState === 'wait'" style="color:#999"><i class="ph-circle-notch-bold ph-lg fa-pulse ph-fw ph-lg"></i> {{ i18n.ts.checking }}</span>

View File

@ -10,19 +10,19 @@
@closed="$emit('closed')" @closed="$emit('closed')"
@ok="ok()" @ok="ok()"
> >
<template #header>{{ title || $ts.generateAccessToken }}</template> <template #header>{{ title || i18n.ts.generateAccessToken }}</template>
<div v-if="information" class="_section"> <div v-if="information" class="_section">
<MkInfo warn>{{ information }}</MkInfo> <MkInfo warn>{{ information }}</MkInfo>
</div> </div>
<div class="_section"> <div class="_section">
<MkInput v-model="name"> <MkInput v-model="name">
<template #label>{{ $ts.name }}</template> <template #label>{{ i18n.ts.name }}</template>
</MkInput> </MkInput>
</div> </div>
<div class="_section"> <div class="_section">
<div style="margin-bottom: 16px;"><b>{{ $ts.permission }}</b></div> <div style="margin-bottom: 16px;"><b>{{ i18n.ts.permission }}</b></div>
<MkButton inline @click="disableAll">{{ $ts.disableAll }}</MkButton> <MkButton inline @click="disableAll">{{ i18n.ts.disableAll }}</MkButton>
<MkButton inline @click="enableAll">{{ $ts.enableAll }}</MkButton> <MkButton inline @click="enableAll">{{ i18n.ts.enableAll }}</MkButton>
<MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ $t(`_permissions.${kind}`) }}</MkSwitch> <MkSwitch v-for="kind in (initialPermissions || kinds)" :key="kind" v-model="permissions[kind]">{{ $t(`_permissions.${kind}`) }}</MkSwitch>
</div> </div>
</XModalWindow> </XModalWindow>
@ -36,6 +36,7 @@ import MkSwitch from './form/switch.vue';
import MkButton from './MkButton.vue'; import MkButton from './MkButton.vue';
import MkInfo from './MkInfo.vue'; import MkInfo from './MkInfo.vue';
import XModalWindow from '@/components/MkModalWindow.vue'; import XModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
title?: string | null; title?: string | null;

View File

@ -2,7 +2,7 @@
<XModalWindow <XModalWindow
ref="dialog" ref="dialog"
:width="800" :width="800"
@close="dialog.close()" @close="dialog?.close()"
@closed="$emit('closed')" @closed="$emit('closed')"
> >
<template #header>{{ i18n.ts._tutorial.title }}</template> <template #header>{{ i18n.ts._tutorial.title }}</template>
@ -33,9 +33,8 @@
<h3>{{ i18n.ts._tutorial.step2_1 }}</h3> <h3>{{ i18n.ts._tutorial.step2_1 }}</h3>
<div>{{ i18n.ts._tutorial.step2_2 }}</div> <div>{{ i18n.ts._tutorial.step2_2 }}</div>
<br/> <br/>
<XSettings/> <XSettings :save-button="true"/>
<br/> <br/>
<MkButton class="ok" primary @click="tutorial++"><i class="ph-check-bold ph-lg"></i> {{ i18n.ts.next }}</MkButton>
</div> </div>
<div v-else-if="tutorial === 2" key="3" class="_content"> <div v-else-if="tutorial === 2" key="3" class="_content">
<h3>{{ i18n.ts._tutorial.step3_1 }}</h3> <h3>{{ i18n.ts._tutorial.step3_1 }}</h3>

View File

@ -8,8 +8,8 @@
</div> </div>
<div v-else> <div v-else>
<div class="wszdbhzo"> <div class="wszdbhzo">
<div><i class="ph-warning-bold ph-lg"></i> {{ $ts.somethingHappened }}</div> <div><i class="ph-warning-bold ph-lg"></i> {{ i18n.ts.somethingHappened }}</div>
<MkButton inline class="retry" @click="retry"><i class="ph-arrow-clockwise-bold ph-lg"></i> {{ $ts.retry }}</MkButton> <MkButton inline class="retry" @click="retry"><i class="ph-arrow-clockwise-bold ph-lg"></i> {{ i18n.ts.retry }}</MkButton>
</div> </div>
</div> </div>
</transition> </transition>
@ -18,6 +18,7 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, PropType, ref, watch } from 'vue'; import { defineComponent, PropType, ref, watch } from 'vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n';
export default defineComponent({ export default defineComponent({
components: { components: {

View File

@ -9,9 +9,9 @@
<div v-else class="menu"> <div v-else class="menu">
<div class="body"> <div class="body">
<div>Ads by {{ host }}</div> <div>Ads by {{ host }}</div>
<!--<MkButton class="button" primary>{{ $ts._ad.like }}</MkButton>--> <!--<MkButton class="button" primary>{{ i18n.ts._ad.like }}</MkButton>-->
<MkButton v-if="chosen.ratio !== 0" class="button" @click="reduceFrequency">{{ $ts._ad.reduceFrequencyOfThisAd }}</MkButton> <MkButton v-if="chosen.ratio !== 0" class="button" @click="reduceFrequency">{{ i18n.ts._ad.reduceFrequencyOfThisAd }}</MkButton>
<button class="_textButton" @click="toggleMenu">{{ $ts._ad.back }}</button> <button class="_textButton" @click="toggleMenu">{{ i18n.ts._ad.back }}</button>
</div> </div>
</div> </div>
</div> </div>
@ -25,6 +25,7 @@ import { host } from '@/config';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n';
type Ad = (typeof instance)['ads'][number]; type Ad = (typeof instance)['ads'][number];

View File

@ -11,6 +11,7 @@ export const FILE_TYPE_BROWSERSAFE = [
'image/bmp', 'image/bmp',
'image/tiff', 'image/tiff',
'image/x-icon', 'image/x-icon',
'image/avif',
// OggS // OggS
'audio/opus', 'audio/opus',

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="driuhtrh"> <div class="driuhtrh">
<div class="query"> <div class="query">
<MkInput v-model="q" class="" :placeholder="$ts.search"> <MkInput v-model="q" class="" :placeholder="i18n.ts.search">
<template #prefix><i class="ph-magnifying-glass-bold ph-lg"></i></template> <template #prefix><i class="ph-magnifying-glass-bold ph-lg"></i></template>
</MkInput> </MkInput>
@ -13,14 +13,14 @@
</div> </div>
<MkFolder v-if="searchEmojis" class="emojis"> <MkFolder v-if="searchEmojis" class="emojis">
<template #header>{{ $ts.searchResult }}</template> <template #header>{{ i18n.ts.searchResult }}</template>
<div class="zuvgdzyt"> <div class="zuvgdzyt">
<XEmoji v-for="emoji in searchEmojis" :key="emoji.name" class="emoji" :emoji="emoji"/> <XEmoji v-for="emoji in searchEmojis" :key="emoji.name" class="emoji" :emoji="emoji"/>
</div> </div>
</MkFolder> </MkFolder>
<MkFolder v-for="category in customEmojiCategories" :key="category" class="emojis"> <MkFolder v-for="category in customEmojiCategories" :key="category" class="emojis">
<template #header>{{ category || $ts.other }}</template> <template #header>{{ category || i18n.ts.other }}</template>
<div class="zuvgdzyt"> <div class="zuvgdzyt">
<XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" class="emoji" :emoji="emoji"/> <XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" class="emoji" :emoji="emoji"/>
</div> </div>
@ -38,6 +38,7 @@ import MkFolder from '@/components/MkFolder.vue';
import MkTab from '@/components/MkTab.vue'; import MkTab from '@/components/MkTab.vue';
import * as os from '@/os'; import * as os from '@/os';
import { emojiCategories, emojiTags } from '@/instance'; import { emojiCategories, emojiTags } from '@/instance';
import { i18n } from '@/i18n';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -57,6 +58,7 @@ export default defineComponent({
tags: emojiTags, tags: emojiTags,
selectedTags: new Set(), selectedTags: new Set(),
searchEmojis: null, searchEmojis: null,
i18n,
}; };
}, },

View File

@ -107,6 +107,7 @@ function save() {
const headerActions = $computed(() => [{ const headerActions = $computed(() => [{
asFullButton: true, asFullButton: true,
icon: 'ph-test-tube-bold ph-lg',
text: i18n.ts.testEmail, text: i18n.ts.testEmail,
handler: testEmail, handler: testEmail,
}, { }, {

View File

@ -175,17 +175,17 @@ const menu = (ev: MouseEvent) => {
action: async () => { action: async () => {
os.api('export-custom-emojis', { os.api('export-custom-emojis', {
}) })
.then(() => { .then(() => {
os.alert({ os.alert({
type: 'info', type: 'info',
text: i18n.ts.exportRequested, text: i18n.ts.exportRequested,
});
}).catch((err) => {
os.alert({
type: 'error',
text: err.message,
});
}); });
}).catch((err) => {
os.alert({
type: 'error',
text: err.message,
});
});
}, },
}, { }, {
icon: 'ph-upload-simple-bold ph-lg', icon: 'ph-upload-simple-bold ph-lg',
@ -195,17 +195,17 @@ const menu = (ev: MouseEvent) => {
os.api('admin/emoji/import-zip', { os.api('admin/emoji/import-zip', {
fileId: file.id, fileId: file.id,
}) })
.then(() => { .then(() => {
os.alert({ os.alert({
type: 'info', type: 'info',
text: i18n.ts.importRequested, text: i18n.ts.importRequested,
});
}).catch((err) => {
os.alert({
type: 'error',
text: err.message,
});
}); });
}).catch((err) => {
os.alert({
type: 'error',
text: err.message,
});
});
}, },
}], ev.currentTarget ?? ev.target); }], ev.currentTarget ?? ev.target);
}; };
@ -282,9 +282,11 @@ const headerActions = $computed(() => [{
const headerTabs = $computed(() => [{ const headerTabs = $computed(() => [{
key: 'local', key: 'local',
icon: 'ph-hand-fist-bold ph-lg',
title: i18n.ts.local, title: i18n.ts.local,
}, { }, {
key: 'remote', key: 'remote',
icon: 'ph-planet-bold ph-lg',
title: i18n.ts.remote, title: i18n.ts.remote,
}]); }]);

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="_debobigegoItem"> <div class="_debobigegoItem">
<div class="_debobigegoLabel"><i class="ph-microchip-bold ph-lg"></i> {{ $ts.cpuAndMemory }}</div> <div class="_debobigegoLabel"><i class="ph-microchip-bold ph-lg"></i> {{ i18n.ts.cpuAndMemory }}</div>
<div class="_debobigegoPanel xhexznfu"> <div class="_debobigegoPanel xhexznfu">
<div> <div>
<canvas :ref="cpumem"></canvas> <canvas :ref="cpumem"></canvas>
@ -17,7 +17,7 @@
</div> </div>
</div> </div>
<div class="_debobigegoItem"> <div class="_debobigegoItem">
<div class="_debobigegoLabel"><i class="ph-hard-drives-bold ph-lg"></i> {{ $ts.disk }}</div> <div class="_debobigegoLabel"><i class="ph-hard-drives-bold ph-lg"></i> {{ i18n.ts.disk }}</div>
<div class="_debobigegoPanel xhexznfu"> <div class="_debobigegoPanel xhexznfu">
<div> <div>
<canvas :ref="disk"></canvas> <canvas :ref="disk"></canvas>
@ -34,7 +34,7 @@
</div> </div>
</div> </div>
<div class="_debobigegoItem"> <div class="_debobigegoItem">
<div class="_debobigegoLabel"><i class="ph-swap-bold ph-lg"></i> {{ $ts.network }}</div> <div class="_debobigegoLabel"><i class="ph-swap-bold ph-lg"></i> {{ i18n.ts.network }}</div>
<div class="_debobigegoPanel xhexznfu"> <div class="_debobigegoPanel xhexznfu">
<div> <div>
<canvas :ref="net"></canvas> <canvas :ref="net"></canvas>
@ -53,43 +53,44 @@
<script lang="ts"> <script lang="ts">
import { defineComponent, markRaw } from 'vue'; import { defineComponent, markRaw } from 'vue';
import { import {
Chart, Chart,
ArcElement, ArcElement,
LineElement, LineElement,
BarElement, BarElement,
PointElement, PointElement,
BarController, BarController,
LineController, LineController,
CategoryScale, CategoryScale,
LinearScale, LinearScale,
Legend, Legend,
Title, Title,
Tooltip, Tooltip,
SubTitle SubTitle,
} from 'chart.js'; } from 'chart.js';
import MkwFederation from '../../widgets/federation.vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import MkSelect from '@/components/form/select.vue'; import MkSelect from '@/components/form/select.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import MkContainer from '@/components/MkContainer.vue'; import MkContainer from '@/components/MkContainer.vue';
import MkFolder from '@/components/MkFolder.vue'; import MkFolder from '@/components/MkFolder.vue';
import MkwFederation from '../../widgets/federation.vue';
import { version, url } from '@/config'; import { version, url } from '@/config';
import bytes from '@/filters/bytes'; import bytes from '@/filters/bytes';
import number from '@/filters/number'; import number from '@/filters/number';
import { i18n } from '@/i18n';
Chart.register( Chart.register(
ArcElement, ArcElement,
LineElement, LineElement,
BarElement, BarElement,
PointElement, PointElement,
BarController, BarController,
LineController, LineController,
CategoryScale, CategoryScale,
LinearScale, LinearScale,
Legend, Legend,
Title, Title,
Tooltip, Tooltip,
SubTitle SubTitle,
); );
const alpha = (hex, a) => { const alpha = (hex, a) => {
@ -132,6 +133,7 @@ export default defineComponent({
overviewHeight: '1fr', overviewHeight: '1fr',
queueHeight: '1fr', queueHeight: '1fr',
paused: false, paused: false,
i18n,
}; };
}, },
@ -155,13 +157,13 @@ export default defineComponent({
this.connection.on('statsLog', this.onStatsLog); this.connection.on('statsLog', this.onStatsLog);
this.connection.send('requestLog', { this.connection.send('requestLog', {
id: Math.random().toString().substr(2, 8), id: Math.random().toString().substr(2, 8),
length: 150 length: 150,
}); });
this.$nextTick(() => { this.$nextTick(() => {
this.queueConnection.send('requestLog', { this.queueConnection.send('requestLog', {
id: Math.random().toString().substr(2, 8), id: Math.random().toString().substr(2, 8),
length: 200 length: 200,
}); });
}); });
}); });
@ -190,7 +192,7 @@ export default defineComponent({
borderWidth: 2, borderWidth: 2,
borderColor: '#31748f', borderColor: '#31748f',
backgroundColor: alpha('#31748f', 0.1), backgroundColor: alpha('#31748f', 0.1),
data: [] data: [],
}, { }, {
label: 'MEM (active)', label: 'MEM (active)',
pointRadius: 0, pointRadius: 0,
@ -198,17 +200,17 @@ export default defineComponent({
borderWidth: 2, borderWidth: 2,
borderColor: '#c4a7e7', borderColor: '#c4a7e7',
backgroundColor: alpha('#c4a7e7', 0.02), backgroundColor: alpha('#c4a7e7', 0.02),
data: [] data: [],
}, { }, {
label: 'MEM (used)', label: 'MEM (used)',
pointRadius: 0, pointRadius: 0,
tension: 0, tension: 0,
borderWidth: 2, borderWidth: 2,
borderColor: '#935dbf', borderColor: '#ebbcba',
borderDash: [5, 5], borderDash: [5, 5],
fill: false, fill: false,
data: [] data: [],
}] }],
}, },
options: { options: {
aspectRatio: 3, aspectRatio: 3,
@ -217,14 +219,14 @@ export default defineComponent({
left: 16, left: 16,
right: 16, right: 16,
top: 16, top: 16,
bottom: 0 bottom: 0,
} },
}, },
legend: { legend: {
position: 'bottom', position: 'bottom',
labels: { labels: {
boxWidth: 16, boxWidth: 16,
} },
}, },
scales: { scales: {
x: { x: {
@ -235,7 +237,7 @@ export default defineComponent({
}, },
ticks: { ticks: {
display: false, display: false,
} },
}, },
y: { y: {
position: 'right', position: 'right',
@ -246,15 +248,15 @@ export default defineComponent({
}, },
ticks: { ticks: {
display: false, display: false,
max: 100 max: 100,
} },
} },
}, },
tooltips: { tooltips: {
intersect: false, intersect: false,
mode: 'index', mode: 'index',
} },
} },
})); }));
}, },
@ -271,7 +273,7 @@ export default defineComponent({
borderWidth: 2, borderWidth: 2,
borderColor: '#94a029', borderColor: '#94a029',
backgroundColor: alpha('#94a029', 0.1), backgroundColor: alpha('#94a029', 0.1),
data: [] data: [],
}, { }, {
label: 'Out', label: 'Out',
pointRadius: 0, pointRadius: 0,
@ -279,8 +281,8 @@ export default defineComponent({
borderWidth: 2, borderWidth: 2,
borderColor: '#ff9156', borderColor: '#ff9156',
backgroundColor: alpha('#ff9156', 0.1), backgroundColor: alpha('#ff9156', 0.1),
data: [] data: [],
}] }],
}, },
options: { options: {
aspectRatio: 3, aspectRatio: 3,
@ -289,14 +291,14 @@ export default defineComponent({
left: 16, left: 16,
right: 16, right: 16,
top: 16, top: 16,
bottom: 0 bottom: 0,
} },
}, },
legend: { legend: {
position: 'bottom', position: 'bottom',
labels: { labels: {
boxWidth: 16, boxWidth: 16,
} },
}, },
scales: { scales: {
x: { x: {
@ -306,8 +308,8 @@ export default defineComponent({
zeroLineColor: this.gridColor, zeroLineColor: this.gridColor,
}, },
ticks: { ticks: {
display: false display: false,
} },
}, },
y: { y: {
position: 'right', position: 'right',
@ -318,14 +320,14 @@ export default defineComponent({
}, },
ticks: { ticks: {
display: false, display: false,
} },
} },
}, },
tooltips: { tooltips: {
intersect: false, intersect: false,
mode: 'index', mode: 'index',
} },
} },
})); }));
}, },
@ -342,7 +344,7 @@ export default defineComponent({
borderWidth: 2, borderWidth: 2,
borderColor: '#94a029', borderColor: '#94a029',
backgroundColor: alpha('#94a029', 0.1), backgroundColor: alpha('#94a029', 0.1),
data: [] data: [],
}, { }, {
label: 'Write', label: 'Write',
pointRadius: 0, pointRadius: 0,
@ -350,8 +352,8 @@ export default defineComponent({
borderWidth: 2, borderWidth: 2,
borderColor: '#ff9156', borderColor: '#ff9156',
backgroundColor: alpha('#ff9156', 0.1), backgroundColor: alpha('#ff9156', 0.1),
data: [] data: [],
}] }],
}, },
options: { options: {
aspectRatio: 3, aspectRatio: 3,
@ -360,14 +362,14 @@ export default defineComponent({
left: 16, left: 16,
right: 16, right: 16,
top: 16, top: 16,
bottom: 0 bottom: 0,
} },
}, },
legend: { legend: {
position: 'bottom', position: 'bottom',
labels: { labels: {
boxWidth: 16, boxWidth: 16,
} },
}, },
scales: { scales: {
x: { x: {
@ -377,8 +379,8 @@ export default defineComponent({
zeroLineColor: this.gridColor, zeroLineColor: this.gridColor,
}, },
ticks: { ticks: {
display: false display: false,
} },
}, },
y: { y: {
position: 'right', position: 'right',
@ -389,14 +391,14 @@ export default defineComponent({
}, },
ticks: { ticks: {
display: false, display: false,
} },
} },
}, },
tooltips: { tooltips: {
intersect: false, intersect: false,
mode: 'index', mode: 'index',
} },
} },
})); }));
}, },
@ -458,7 +460,7 @@ export default defineComponent({
resume() { resume() {
this.paused = false; this.paused = false;
}, },
} },
}); });
</script> </script>

View File

@ -10,7 +10,7 @@
<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/> <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
</div> </div>
<div v-if="$i && !announcement.isRead" class="_footer"> <div v-if="$i && !announcement.isRead" class="_footer">
<MkButton primary @click="read(items, announcement, i)"><i class="ph-check-bold ph-lg"></i> {{ $ts.gotIt }}</MkButton> <MkButton primary @click="read(items, announcement, i)"><i class="ph-check-bold ph-lg"></i> {{ i18n.ts.gotIt }}</MkButton>
</div> </div>
</section> </section>
</MkPagination> </MkPagination>

View File

@ -2,7 +2,7 @@
<MkStickyContainer> <MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
<div ref="rootEl" v-hotkey.global="keymap" v-size="{ min: [800] }" class="tqmomfks"> <div ref="rootEl" v-hotkey.global="keymap" v-size="{ min: [800] }" class="tqmomfks">
<div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> <div v-if="queue > 0" class="new"><button class="_buttonPrimary" @click="top()">{{ i18n.ts.newNoteRecived }}</button></div>
<div class="tl _block"> <div class="tl _block">
<XTimeline <XTimeline
ref="tlEl" :key="antennaId" ref="tlEl" :key="antennaId"

View File

@ -7,14 +7,14 @@
<p class="description">{{ app.description }}</p> <p class="description">{{ app.description }}</p>
</div> </div>
<div class="_content"> <div class="_content">
<h2>{{ $ts._auth.permissionAsk }}</h2> <h2>{{ i18n.ts._auth.permissionAsk }}</h2>
<ul> <ul>
<li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li> <li v-for="p in app.permission" :key="p">{{ $t(`_permissions.${p}`) }}</li>
</ul> </ul>
</div> </div>
<div class="_footer"> <div class="_footer">
<MkButton inline @click="cancel">{{ $ts.cancel }}</MkButton> <MkButton inline @click="cancel">{{ i18n.ts.cancel }}</MkButton>
<MkButton inline primary @click="accept">{{ $ts.accept }}</MkButton> <MkButton inline primary @click="accept">{{ i18n.ts.accept }}</MkButton>
</div> </div>
</section> </section>
</template> </template>
@ -23,6 +23,7 @@
import { defineComponent } from 'vue'; import { defineComponent } from 'vue';
import MkButton from '@/components/MkButton.vue'; import MkButton from '@/components/MkButton.vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n';
const emit = defineEmits<{ const emit = defineEmits<{
(ev: 'denied'): void; (ev: 'denied'): void;

View File

@ -12,15 +12,15 @@
@accepted="accepted" @accepted="accepted"
/> />
<div v-if="state == 'denied'" class="denied"> <div v-if="state == 'denied'" class="denied">
<h1>{{ $ts._auth.denied }}</h1> <h1>{{ i18n.ts._auth.denied }}</h1>
</div> </div>
<div v-if="state == 'accepted'" class="accepted"> <div v-if="state == 'accepted'" class="accepted">
<h1>{{ session.app.isAuthorized ? $t('already-authorized') : $ts.allowed }}</h1> <h1>{{ session.app.isAuthorized ? $t('already-authorized') : i18n.ts.allowed }}</h1>
<p v-if="session.app.callbackUrl">{{ $ts._auth.callback }}<MkEllipsis/></p> <p v-if="session.app.callbackUrl">{{ i18n.ts._auth.callback }}<MkEllipsis/></p>
<p v-if="!session.app.callbackUrl">{{ $ts._auth.pleaseGoBack }}</p> <p v-if="!session.app.callbackUrl">{{ i18n.ts._auth.pleaseGoBack }}</p>
</div> </div>
<div v-if="state == 'fetch-session-error'" class="error"> <div v-if="state == 'fetch-session-error'" class="error">
<p>{{ $ts.somethingHappened }}</p> <p>{{ i18n.ts.somethingHappened }}</p>
</div> </div>
</div> </div>
<div v-else class="signin"> <div v-else class="signin">
@ -34,6 +34,7 @@ import XForm from './auth.form.vue';
import MkSignin from '@/components/MkSignin.vue'; import MkSignin from '@/components/MkSignin.vue';
import * as os from '@/os'; import * as os from '@/os';
import { login } from '@/account'; import { login } from '@/account';
import { i18n } from '@/i18n';
export default defineComponent({ export default defineComponent({
components: { components: {

View File

@ -1,7 +1,7 @@
<template> <template>
<MkStickyContainer> <MkStickyContainer>
<template #header><MkPageHeader :actions="headerActions"/></template> <template #header><MkPageHeader :actions="headerActions"/></template>
<MkSpacer :content-max="800"> <MkSpacer :content-max="800">
<div v-if="clip"> <div v-if="clip">
<div class="okzinsic _panel"> <div class="okzinsic _panel">
<div v-if="clip.description" class="description"> <div v-if="clip.description" class="description">
@ -20,7 +20,7 @@
<script lang="ts" setup> <script lang="ts" setup>
import { computed, watch, provide } from 'vue'; import { computed, watch, provide } from 'vue';
import * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import XNotes from '@/components/MkNotes.vue'; import XNotes from '@/components/MkNotes.vue';
import { $i } from '@/account'; import { $i } from '@/account';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';

View File

@ -1,3 +1,5 @@
4.8 KiB
<template> <template>
<MkSpacer :content-max="1200"> <MkSpacer :content-max="1200">
<MkTab v-model="origin" style="margin-bottom: var(--margin);"> <MkTab v-model="origin" style="margin-bottom: var(--margin);">
@ -6,27 +8,27 @@
</MkTab> </MkTab>
<div v-if="origin === 'local'"> <div v-if="origin === 'local'">
<template v-if="tag == null"> <template v-if="tag == null">
<MkFolder v-show="thereArePinnedUsers" class="_gap" persist-key="explore-pinned-users"> <MkFolder class="_gap" persist-key="explore-pinned-users">
<template #header><i class="ph-bookmark-simple-bold ph-lg ph-fw ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template> <template #header><i class="fas fa-bookmark fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.pinnedUsers }}</template>
<XUserList :pagination="pinnedUsers"/> <XUserList :pagination="pinnedUsers"/>
</MkFolder> </MkFolder>
<MkFolder class="_gap" persist-key="explore-popular-users"> <MkFolder v-if="$i != null" class="_gap" persist-key="explore-popular-users">
<template #header><i class="ph-chart-line-up-bold ph-lg ph-fw ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
<XUserList :pagination="popularUsers"/> <XUserList :pagination="popularUsers"/>
</MkFolder> </MkFolder>
<MkFolder class="_gap" persist-key="explore-recently-updated-users"> <MkFolder v-if="$i != null" class="_gap" persist-key="explore-recently-updated-users">
<template #header><i class="ph-chat-bold ph-lg ph-fw ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template>
<XUserList :pagination="recentlyUpdatedUsers"/> <XUserList :pagination="recentlyUpdatedUsers"/>
</MkFolder> </MkFolder>
<MkFolder class="_gap" persist-key="explore-recently-registered-users"> <MkFolder v-if="$i != null" class="_gap" persist-key="explore-recently-registered-users">
<template #header><i class="ph-plus-bold ph-lg ph-fw ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyRegisteredUsers }}</template> <template #header><i class="fas fa-plus fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyRegisteredUsers }}</template>
<XUserList :pagination="recentlyRegisteredUsers"/> <XUserList :pagination="recentlyRegisteredUsers"/>
</MkFolder> </MkFolder>
</template> </template>
</div> </div>
<div v-else> <div v-else>
<MkFolder ref="tagsEl" :foldable="true" :expanded="false" class="_gap"> <MkFolder ref="tagsEl" :foldable="true" :expanded="false" class="_gap">
<template #header><i class="ph-hash-bold ph-lg ph-fw ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularTags }}</template> <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularTags }}</template>
<div class="vxjfqztj"> <div class="vxjfqztj">
<MkA v-for="tag in tagsLocal" :key="'local:' + tag.tag" :to="`/explore/tags/${tag.tag}`" class="local">{{ tag.tag }}</MkA> <MkA v-for="tag in tagsLocal" :key="'local:' + tag.tag" :to="`/explore/tags/${tag.tag}`" class="local">{{ tag.tag }}</MkA>
@ -35,21 +37,21 @@
</MkFolder> </MkFolder>
<MkFolder v-if="tag != null" :key="`${tag}`" class="_gap"> <MkFolder v-if="tag != null" :key="`${tag}`" class="_gap">
<template #header><i class="ph-hash-bold ph-lg ph-fw ph-lg" style="margin-right: 0.5em;"></i>{{ tag }}</template> <template #header><i class="fas fa-hashtag fa-fw" style="margin-right: 0.5em;"></i>{{ tag }}</template>
<XUserList :pagination="tagUsers"/> <XUserList :pagination="tagUsers"/>
</MkFolder> </MkFolder>
<template v-if="tag == null"> <template v-if="tag == null && $i != null">
<MkFolder class="_gap"> <MkFolder class="_gap">
<template #header><i class="ph-chart-line-up-bold ph-lg ph-fw ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template> <template #header><i class="fas fa-chart-line fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.popularUsers }}</template>
<XUserList :pagination="popularUsersF"/> <XUserList :pagination="popularUsersF"/>
</MkFolder> </MkFolder>
<MkFolder class="_gap"> <MkFolder class="_gap">
<template #header><i class="ph-chat-bold ph-lg ph-fw ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template> <template #header><i class="fas fa-comment-alt fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyUpdatedUsers }}</template>
<XUserList :pagination="recentlyUpdatedUsersF"/> <XUserList :pagination="recentlyUpdatedUsersF"/>
</MkFolder> </MkFolder>
<MkFolder class="_gap"> <MkFolder class="_gap">
<template #header><i class="ph-rocket-launch-bold ph-lg ph-fw ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyDiscoveredUsers }}</template> <template #header><i class="fas fa-rocket fa-fw" style="margin-right: 0.5em;"></i>{{ i18n.ts.recentlyDiscoveredUsers }}</template>
<XUserList :pagination="recentlyRegisteredUsersF"/> <XUserList :pagination="recentlyRegisteredUsersF"/>
</MkFolder> </MkFolder>
</template> </template>
@ -65,26 +67,22 @@ import MkTab from '@/components/MkTab.vue';
import number from '@/filters/number'; import number from '@/filters/number';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { $i } from '@/account';
import { instance } from '@/instance'; import { instance } from '@/instance';
const props = defineProps<{ const props = defineProps<{
tag?: string; tag?: string;
}>(); }>();
let origin = $ref('local'); let origin = $ref('local');
let tagsEl = $ref<InstanceType<typeof MkFolder>>(); let tagsEl = $ref<InstanceType<typeof MkFolder>>();
let tagsLocal = $ref([]); let tagsLocal = $ref([]);
let tagsRemote = $ref([]); let tagsRemote = $ref([]);
let thereArePinnedUsers = $ref(false);
watch(() => props.tag, () => { watch(() => props.tag, () => {
if (tagsEl) tagsEl.toggleContent(props.tag == null); if (tagsEl) tagsEl.toggleContent(props.tag == null);
}); });
watch(() => pinnedUsersList, () => {
if (pinnedUsersList?.length > 0) thereArePinnedUsers = true;
});
const tagUsers = $computed(() => ({ const tagUsers = $computed(() => ({
endpoint: 'hashtags/users' as const, endpoint: 'hashtags/users' as const,
limit: 30, limit: 30,
@ -95,10 +93,6 @@ const tagUsers = $computed(() => ({
}, },
})); }));
const pinnedUsersList = await os.api('pinned-users');
if (pinnedUsersList?.length > 0) { thereArePinnedUsers = true; }
const pinnedUsers = { endpoint: 'pinned-users' }; const pinnedUsers = { endpoint: 'pinned-users' };
const popularUsers = { endpoint: 'users', limit: 10, noPaging: true, params: { const popularUsers = { endpoint: 'users', limit: 10, noPaging: true, params: {
state: 'alive', state: 'alive',

View File

@ -38,7 +38,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, watch } from 'vue'; import { computed, watch, onMounted } from 'vue';
import { Virtual } from 'swiper'; import { Virtual } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/vue'; import { Swiper, SwiperSlide } from 'swiper/vue';
import XFeatured from './explore.featured.vue'; import XFeatured from './explore.featured.vue';
@ -118,4 +118,8 @@ function syncSlide(index) {
swiperRef.slideTo(index); swiperRef.slideTo(index);
} }
onMounted(() => {
syncSlide(tabs.indexOf(swiperRef.activeIndex));
});
</script> </script>

View File

@ -18,8 +18,8 @@
</div> </div>
<div class="actions"> <div class="actions">
<div class="like"> <div class="like">
<MkButton v-if="post.isLiked" v-tooltip="i18n.ts._gallery.unlike" class="button" primary @click="unlike()"><i class="ph-heart-bold ph-lg"></i><span v-if="post.likedCount > 0" class="count">{{ post.likedCount }}</span></MkButton> <MkButton v-if="post.isLiked" v-tooltip="i18n.ts._gallery.unlike" class="button" primary @click="unlike()"><i class="ph-heart-fill ph-lg"></i><span v-if="post.likedCount > 0" class="count">{{ post.likedCount }}</span></MkButton>
<MkButton v-else v-tooltip="i18n.ts._gallery.like" class="button" @click="like()"><i class="far fa-heart"></i><span v-if="post.likedCount > 0" class="count">{{ post.likedCount }}</span></MkButton> <MkButton v-else v-tooltip="i18n.ts._gallery.like" class="button" @click="like()"><i class="ph-heart-bold"></i><span v-if="post.likedCount > 0" class="count">{{ post.likedCount }}</span></MkButton>
</div> </div>
<div class="other"> <div class="other">
<button v-if="$i && $i.id === post.user.id" v-tooltip="i18n.ts.edit" v-click-anime class="_button" @click="edit"><i class="ph-pencil-bold ph-lg ph-fw ph-lg"></i></button> <button v-if="$i && $i.id === post.user.id" v-tooltip="i18n.ts.edit" v-click-anime class="_button" @click="edit"><i class="ph-pencil-bold ph-lg ph-fw ph-lg"></i></button>

View File

@ -17,10 +17,6 @@
<MkPagination v-slot="{items}" :pagination="dmsPagination"> <MkPagination v-slot="{items}" :pagination="dmsPagination">
<MkChatPreview v-for="message in items" :key="message.id" class="yweeujhr message _block" :message="message"/> <MkChatPreview v-for="message in items" :key="message.id" class="yweeujhr message _block" :message="message"/>
</MkPagination> </MkPagination>
<div v-if="messages.length == 0" class="_fullinfo">
<img src="/static-assets/badges/info.png" class="_ghost" alt="Info"/>
<div>{{ i18n.ts.noHistory }}</div>
</div>
</div> </div>
</swiper-slide> </swiper-slide>
<swiper-slide> <swiper-slide>
@ -68,7 +64,12 @@ const tabs = ['dms', 'groups'];
let tab = $ref(tabs[0]); let tab = $ref(tabs[0]);
watch($$(tab), () => (syncSlide(tabs.indexOf(tab)))); watch($$(tab), () => (syncSlide(tabs.indexOf(tab))));
const headerActions = $computed(() => []); const headerActions = $computed(() => [{
asFullButton: true,
icon: 'ph-plus-bold ph-lg',
text: i18n.ts.addUser,
handler: startMenu,
}]);
const headerTabs = $computed(() => [{ const headerTabs = $computed(() => [{
key: 'dms', key: 'dms',
@ -126,6 +127,19 @@ function onRead(ids): void {
} }
} }
function startMenu(ev) {
os.popupMenu([{
text: i18n.ts.messagingWithUser,
icon: 'fas fa-user',
action: () => { startUser(); },
}, {
text: i18n.ts.messagingWithGroup,
icon: 'fas fa-users',
action: () => { startGroup(); },
}], ev.currentTarget ?? ev.target);
}
async function startUser(): void { async function startUser(): void {
os.selectUser().then(user => { os.selectUser().then(user => {
router.push(`/my/messaging/${Acct.toString(user)}`); router.push(`/my/messaging/${Acct.toString(user)}`);
@ -169,6 +183,8 @@ function syncSlide(index) {
} }
onMounted(() => { onMounted(() => {
syncSlide(tabs.indexOf(swiperRef.activeIndex));
connection = markRaw(stream.useChannel('messagingIndex')); connection = markRaw(stream.useChannel('messagingIndex'));
connection.on('message', onMessage); connection.on('message', onMessage);

View File

@ -8,17 +8,17 @@
</button> </button>
<div v-if="!message.isDeleted" class="content"> <div v-if="!message.isDeleted" class="content">
<Mfm v-if="message.text" ref="text" class="text" :text="message.text" :i="$i"/> <Mfm v-if="message.text" ref="text" class="text" :text="message.text" :i="$i"/>
<div v-if="message.file" class="file">
<XMediaList v-if="message.file.type.split('/')[0] == 'image' || message.file.type.split('/')[0] == 'video'" :media-list="[message.file]" max-width="400px" style="border-radius: 5px"/>
<a v-else :href="message.file.url" rel="noopener" target="_blank" :title="message.file.name">
<p>{{ message.file.name }}</p>
</a>
</div>
</div> </div>
<div v-else class="content"> <div v-else class="content">
<p class="is-deleted">{{ i18n.ts.deleted }}</p> <p class="is-deleted">{{ i18n.ts.deleted }}</p>
</div> </div>
</div> </div>
<div v-if="message.file" class="file" width="400px">
<XMediaList v-if="message.file.type.split('/')[0] == 'image' || message.file.type.split('/')[0] == 'video'" width="400px" :media-list="[message.file]" max-width="400px" style="border-radius: 5px"/>
<a v-else :href="message.file.url" rel="noopener" target="_blank" :title="message.file.name">
<p>{{ message.file.name }}</p>
</a>
</div>
<div></div> <div></div>
<MkUrlPreview v-for="url in urls" :key="url" :url="url" style="margin: 8px 0;"/> <MkUrlPreview v-for="url in urls" :key="url" :url="url" style="margin: 8px 0;"/>
<footer> <footer>

View File

@ -1,38 +1,38 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-lightning-bold ph-lg"></i> {{ $ts._pages.blocks.button }}</template> <template #header><i class="ph-lightning-bold ph-lg"></i> {{ i18n.ts._pages.blocks.button }}</template>
<section class="xfhsjczc"> <section class="xfhsjczc">
<MkInput v-model="value.text"><template #label>{{ $ts._pages.blocks._button.text }}</template></MkInput> <MkInput v-model="value.text"><template #label>{{ i18n.ts._pages.blocks._button.text }}</template></MkInput>
<MkSwitch v-model="value.primary"><span>{{ $ts._pages.blocks._button.colored }}</span></MkSwitch> <MkSwitch v-model="value.primary"><span>{{ i18n.ts._pages.blocks._button.colored }}</span></MkSwitch>
<MkSelect v-model="value.action"> <MkSelect v-model="value.action">
<template #label>{{ $ts._pages.blocks._button.action }}</template> <template #label>{{ i18n.ts._pages.blocks._button.action }}</template>
<option value="dialog">{{ $ts._pages.blocks._button._action.dialog }}</option> <option value="dialog">{{ i18n.ts._pages.blocks._button._action.dialog }}</option>
<option value="resetRandom">{{ $ts._pages.blocks._button._action.resetRandom }}</option> <option value="resetRandom">{{ i18n.ts._pages.blocks._button._action.resetRandom }}</option>
<option value="pushEvent">{{ $ts._pages.blocks._button._action.pushEvent }}</option> <option value="pushEvent">{{ i18n.ts._pages.blocks._button._action.pushEvent }}</option>
<option value="callAiScript">{{ $ts._pages.blocks._button._action.callAiScript }}</option> <option value="callAiScript">{{ i18n.ts._pages.blocks._button._action.callAiScript }}</option>
</MkSelect> </MkSelect>
<template v-if="value.action === 'dialog'"> <template v-if="value.action === 'dialog'">
<MkInput v-model="value.content"><template #label>{{ $ts._pages.blocks._button._action._dialog.content }}</template></MkInput> <MkInput v-model="value.content"><template #label>{{ i18n.ts._pages.blocks._button._action._dialog.content }}</template></MkInput>
</template> </template>
<template v-else-if="value.action === 'pushEvent'"> <template v-else-if="value.action === 'pushEvent'">
<MkInput v-model="value.event"><template #label>{{ $ts._pages.blocks._button._action._pushEvent.event }}</template></MkInput> <MkInput v-model="value.event"><template #label>{{ i18n.ts._pages.blocks._button._action._pushEvent.event }}</template></MkInput>
<MkInput v-model="value.message"><template #label>{{ $ts._pages.blocks._button._action._pushEvent.message }}</template></MkInput> <MkInput v-model="value.message"><template #label>{{ i18n.ts._pages.blocks._button._action._pushEvent.message }}</template></MkInput>
<MkSelect v-model="value.var"> <MkSelect v-model="value.var">
<template #label>{{ $ts._pages.blocks._button._action._pushEvent.variable }}</template> <template #label>{{ i18n.ts._pages.blocks._button._action._pushEvent.variable }}</template>
<option :value="null">{{ $t('_pages.blocks._button._action._pushEvent.no-variable') }}</option> <option :value="null">{{ $t('_pages.blocks._button._action._pushEvent.no-variable') }}</option>
<option v-for="v in hpml.getVarsByType()" :value="v.name">{{ v.name }}</option> <option v-for="v in hpml.getVarsByType()" :value="v.name">{{ v.name }}</option>
<optgroup :label="$ts._pages.script.pageVariables"> <optgroup :label="i18n.ts._pages.script.pageVariables">
<option v-for="v in hpml.getPageVarsByType()" :value="v">{{ v }}</option> <option v-for="v in hpml.getPageVarsByType()" :value="v">{{ v }}</option>
</optgroup> </optgroup>
<optgroup :label="$ts._pages.script.enviromentVariables"> <optgroup :label="i18n.ts._pages.script.enviromentVariables">
<option v-for="v in hpml.getEnvVarsByType()" :value="v">{{ v }}</option> <option v-for="v in hpml.getEnvVarsByType()" :value="v">{{ v }}</option>
</optgroup> </optgroup>
</MkSelect> </MkSelect>
</template> </template>
<template v-else-if="value.action === 'callAiScript'"> <template v-else-if="value.action === 'callAiScript'">
<MkInput v-model="value.fn"><template #label>{{ $ts._pages.blocks._button._action._callAiScript.functionName }}</template></MkInput> <MkInput v-model="value.fn"><template #label>{{ i18n.ts._pages.blocks._button._action._callAiScript.functionName }}</template></MkInput>
</template> </template>
</section> </section>
</XContainer> </XContainer>
@ -45,6 +45,7 @@ import XContainer from '../page-editor.container.vue';
import MkSelect from '@/components/form/select.vue'; import MkSelect from '@/components/form/select.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import MkSwitch from '@/components/form/switch.vue'; import MkSwitch from '@/components/form/switch.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any, value: any,

View File

@ -1,19 +1,19 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-paint-brush-household-bold ph-lg"></i> {{ $ts._pages.blocks.canvas }}</template> <template #header><i class="ph-paint-brush-household-bold ph-lg"></i> {{ i18n.ts._pages.blocks.canvas }}</template>
<section style="padding: 0 16px 0 16px;"> <section style="padding: 0 16px 0 16px;">
<MkInput v-model="value.name"> <MkInput v-model="value.name">
<template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template> <template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template>
<template #label>{{ $ts._pages.blocks._canvas.id }}</template> <template #label>{{ i18n.ts._pages.blocks._canvas.id }}</template>
</MkInput> </MkInput>
<MkInput v-model="value.width" type="number"> <MkInput v-model="value.width" type="number">
<template #label>{{ $ts._pages.blocks._canvas.width }}</template> <template #label>{{ i18n.ts._pages.blocks._canvas.width }}</template>
<template #suffix>px</template> <template #suffix>px</template>
</MkInput> </MkInput>
<MkInput v-model="value.height" type="number"> <MkInput v-model="value.height" type="number">
<template #label>{{ $ts._pages.blocks._canvas.height }}</template> <template #label>{{ i18n.ts._pages.blocks._canvas.height }}</template>
<template #suffix>px</template> <template #suffix>px</template>
</MkInput> </MkInput>
</section> </section>
@ -25,6 +25,7 @@
import { } from 'vue'; import { } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any value: any

View File

@ -1,18 +1,18 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-lightning-bold ph-lg"></i> {{ $ts._pages.blocks.counter }}</template> <template #header><i class="ph-lightning-bold ph-lg"></i> {{ i18n.ts._pages.blocks.counter }}</template>
<section style="padding: 0 16px 0 16px;"> <section style="padding: 0 16px 0 16px;">
<MkInput v-model="value.name"> <MkInput v-model="value.name">
<template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template> <template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template>
<template #label>{{ $ts._pages.blocks._counter.name }}</template> <template #label>{{ i18n.ts._pages.blocks._counter.name }}</template>
</MkInput> </MkInput>
<MkInput v-model="value.text"> <MkInput v-model="value.text">
<template #label>{{ $ts._pages.blocks._counter.text }}</template> <template #label>{{ i18n.ts._pages.blocks._counter.text }}</template>
</MkInput> </MkInput>
<MkInput v-model="value.inc" type="number"> <MkInput v-model="value.inc" type="number">
<template #label>{{ $ts._pages.blocks._counter.inc }}</template> <template #label>{{ i18n.ts._pages.blocks._counter.inc }}</template>
</MkInput> </MkInput>
</section> </section>
</XContainer> </XContainer>
@ -23,6 +23,7 @@
import { } from 'vue'; import { } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any value: any

View File

@ -1,7 +1,7 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-question-bold ph-lg"></i> {{ $ts._pages.blocks.if }}</template> <template #header><i class="ph-question-bold ph-lg"></i> {{ i18n.ts._pages.blocks.if }}</template>
<template #func> <template #func>
<button class="_button" @click="add()"> <button class="_button" @click="add()">
<i class="ph-plus-bold ph-lg"></i> <i class="ph-plus-bold ph-lg"></i>
@ -10,12 +10,12 @@
<section class="romcojzs"> <section class="romcojzs">
<MkSelect v-model="value.var"> <MkSelect v-model="value.var">
<template #label>{{ $ts._pages.blocks._if.variable }}</template> <template #label>{{ i18n.ts._pages.blocks._if.variable }}</template>
<option v-for="v in hpml.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option> <option v-for="v in hpml.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option>
<optgroup :label="$ts._pages.script.pageVariables"> <optgroup :label="i18n.ts._pages.script.pageVariables">
<option v-for="v in hpml.getPageVarsByType('boolean')" :value="v">{{ v }}</option> <option v-for="v in hpml.getPageVarsByType('boolean')" :value="v">{{ v }}</option>
</optgroup> </optgroup>
<optgroup :label="$ts._pages.script.enviromentVariables"> <optgroup :label="i18n.ts._pages.script.enviromentVariables">
<option v-for="v in hpml.getEnvVarsByType('boolean')" :value="v">{{ v }}</option> <option v-for="v in hpml.getEnvVarsByType('boolean')" :value="v">{{ v }}</option>
</optgroup> </optgroup>
</MkSelect> </MkSelect>

View File

@ -1,7 +1,7 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-image-bold ph-lg"></i> {{ $ts._pages.blocks.image }}</template> <template #header><i class="ph-image-bold ph-lg"></i> {{ i18n.ts._pages.blocks.image }}</template>
<template #func> <template #func>
<button @click="choose()"> <button @click="choose()">
<i class="ph-folder-notch-open-bold ph-lg"></i> <i class="ph-folder-notch-open-bold ph-lg"></i>
@ -20,6 +20,7 @@ import { onMounted } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue'; import MkDriveFileThumbnail from '@/components/MkDriveFileThumbnail.vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
value: any value: any

View File

@ -1,14 +1,14 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-sticker-bold ph-lg"></i> {{ $ts._pages.blocks.note }}</template> <template #header><i class="ph-sticker-bold ph-lg"></i> {{ i18n.ts._pages.blocks.note }}</template>
<section style="padding: 0 16px 0 16px;"> <section style="padding: 0 16px 0 16px;">
<MkInput v-model="id"> <MkInput v-model="id">
<template #label>{{ $ts._pages.blocks._note.id }}</template> <template #label>{{ i18n.ts._pages.blocks._note.id }}</template>
<template #caption>{{ $ts._pages.blocks._note.idDescription }}</template> <template #caption>{{ i18n.ts._pages.blocks._note.idDescription }}</template>
</MkInput> </MkInput>
<MkSwitch v-model="value.detailed"><span>{{ $ts._pages.blocks._note.detailed }}</span></MkSwitch> <MkSwitch v-model="value.detailed"><span>{{ i18n.ts._pages.blocks._note.detailed }}</span></MkSwitch>
<XNote v-if="note && !value.detailed" :key="note.id + ':normal'" v-model:note="note" style="margin-bottom: 16px;"/> <XNote v-if="note && !value.detailed" :key="note.id + ':normal'" v-model:note="note" style="margin-bottom: 16px;"/>
<XNoteDetailed v-if="note && value.detailed" :key="note.id + ':detail'" v-model:note="note" style="margin-bottom: 16px;"/> <XNoteDetailed v-if="note && value.detailed" :key="note.id + ':detail'" v-model:note="note" style="margin-bottom: 16px;"/>
@ -25,6 +25,7 @@ import MkSwitch from '@/components/form/switch.vue';
import XNote from '@/components/MkNote.vue'; import XNote from '@/components/MkNote.vue';
import XNoteDetailed from '@/components/MkNoteDetailed.vue'; import XNoteDetailed from '@/components/MkNoteDetailed.vue';
import * as os from '@/os'; import * as os from '@/os';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
value: any value: any

View File

@ -1,18 +1,18 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-lightning-bold ph-lg"></i> {{ $ts._pages.blocks.numberInput }}</template> <template #header><i class="ph-lightning-bold ph-lg"></i> {{ i18n.ts._pages.blocks.numberInput }}</template>
<section style="padding: 0 16px 0 16px;"> <section style="padding: 0 16px 0 16px;">
<MkInput v-model="value.name"> <MkInput v-model="value.name">
<template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template> <template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template>
<template #label>{{ $ts._pages.blocks._numberInput.name }}</template> <template #label>{{ i18n.ts._pages.blocks._numberInput.name }}</template>
</MkInput> </MkInput>
<MkInput v-model="value.text"> <MkInput v-model="value.text">
<template #label>{{ $ts._pages.blocks._numberInput.text }}</template> <template #label>{{ i18n.ts._pages.blocks._numberInput.text }}</template>
</MkInput> </MkInput>
<MkInput v-model="value.default" type="number"> <MkInput v-model="value.default" type="number">
<template #label>{{ $ts._pages.blocks._numberInput.default }}</template> <template #label>{{ i18n.ts._pages.blocks._numberInput.default }}</template>
</MkInput> </MkInput>
</section> </section>
</XContainer> </XContainer>
@ -23,6 +23,7 @@
import { } from 'vue'; import { } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any value: any

View File

@ -1,12 +1,12 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-paper-plane-tilt-bold ph-lg"></i> {{ $ts._pages.blocks.post }}</template> <template #header><i class="ph-paper-plane-tilt-bold ph-lg"></i> {{ i18n.ts._pages.blocks.post }}</template>
<section style="padding: 16px;"> <section style="padding: 16px;">
<MkTextarea v-model="value.text"><template #label>{{ $ts._pages.blocks._post.text }}</template></MkTextarea> <MkTextarea v-model="value.text"><template #label>{{ i18n.ts._pages.blocks._post.text }}</template></MkTextarea>
<MkSwitch v-model="value.attachCanvasImage"><span>{{ $ts._pages.blocks._post.attachCanvasImage }}</span></MkSwitch> <MkSwitch v-model="value.attachCanvasImage"><span>{{ i18n.ts._pages.blocks._post.attachCanvasImage }}</span></MkSwitch>
<MkInput v-if="value.attachCanvasImage" v-model="value.canvasId"><template #label>{{ $ts._pages.blocks._post.canvasId }}</template></MkInput> <MkInput v-if="value.attachCanvasImage" v-model="value.canvasId"><template #label>{{ i18n.ts._pages.blocks._post.canvasId }}</template></MkInput>
</section> </section>
</XContainer> </XContainer>
</template> </template>
@ -18,6 +18,7 @@ import XContainer from '../page-editor.container.vue';
import MkTextarea from '@/components/form/textarea.vue'; import MkTextarea from '@/components/form/textarea.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import MkSwitch from '@/components/form/switch.vue'; import MkSwitch from '@/components/form/switch.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any value: any

View File

@ -1,13 +1,13 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-lightning-bold ph-lg"></i> {{ $ts._pages.blocks.radioButton }}</template> <template #header><i class="ph-lightning-bold ph-lg"></i> {{ i18n.ts._pages.blocks.radioButton }}</template>
<section style="padding: 0 16px 16px 16px;"> <section style="padding: 0 16px 16px 16px;">
<MkInput v-model="value.name"><template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template><template #label>{{ $ts._pages.blocks._radioButton.name }}</template></MkInput> <MkInput v-model="value.name"><template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template><template #label>{{ i18n.ts._pages.blocks._radioButton.name }}</template></MkInput>
<MkInput v-model="value.title"><template #label>{{ $ts._pages.blocks._radioButton.title }}</template></MkInput> <MkInput v-model="value.title"><template #label>{{ i18n.ts._pages.blocks._radioButton.title }}</template></MkInput>
<MkTextarea v-model="values"><template #label>{{ $ts._pages.blocks._radioButton.values }}</template></MkTextarea> <MkTextarea v-model="values"><template #label>{{ i18n.ts._pages.blocks._radioButton.values }}</template></MkTextarea>
<MkInput v-model="value.default"><template #label>{{ $ts._pages.blocks._radioButton.default }}</template></MkInput> <MkInput v-model="value.default"><template #label>{{ i18n.ts._pages.blocks._radioButton.default }}</template></MkInput>
</section> </section>
</XContainer> </XContainer>
</template> </template>
@ -18,6 +18,7 @@ import { watch } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import MkTextarea from '@/components/form/textarea.vue'; import MkTextarea from '@/components/form/textarea.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import { i18n } from '@/i18n';
const props = withDefaults(defineProps<{ const props = withDefaults(defineProps<{
value: any value: any

View File

@ -1,12 +1,12 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-lightning-bold ph-lg"></i> {{ $ts._pages.blocks.switch }}</template> <template #header><i class="ph-lightning-bold ph-lg"></i> {{ i18n.ts._pages.blocks.switch }}</template>
<section class="kjuadyyj"> <section class="kjuadyyj">
<MkInput v-model="value.name"><template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template><template #label>{{ $ts._pages.blocks._switch.name }}</template></MkInput> <MkInput v-model="value.name"><template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template><template #label>{{ i18n.ts._pages.blocks._switch.name }}</template></MkInput>
<MkInput v-model="value.text"><template #label>{{ $ts._pages.blocks._switch.text }}</template></MkInput> <MkInput v-model="value.text"><template #label>{{ i18n.ts._pages.blocks._switch.text }}</template></MkInput>
<MkSwitch v-model="value.default"><span>{{ $ts._pages.blocks._switch.default }}</span></MkSwitch> <MkSwitch v-model="value.default"><span>{{ i18n.ts._pages.blocks._switch.default }}</span></MkSwitch>
</section> </section>
</XContainer> </XContainer>
</template> </template>
@ -17,6 +17,7 @@ import { } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import MkSwitch from '@/components/form/switch.vue'; import MkSwitch from '@/components/form/switch.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any value: any

View File

@ -1,12 +1,12 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-lightning-bold ph-lg"></i> {{ $ts._pages.blocks.textInput }}</template> <template #header><i class="ph-lightning-bold ph-lg"></i> {{ i18n.ts._pages.blocks.textInput }}</template>
<section style="padding: 0 16px 0 16px;"> <section style="padding: 0 16px 0 16px;">
<MkInput v-model="value.name"><template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template><template #label>{{ $ts._pages.blocks._textInput.name }}</template></MkInput> <MkInput v-model="value.name"><template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template><template #label>{{ i18n.ts._pages.blocks._textInput.name }}</template></MkInput>
<MkInput v-model="value.text"><template #label>{{ $ts._pages.blocks._textInput.text }}</template></MkInput> <MkInput v-model="value.text"><template #label>{{ i18n.ts._pages.blocks._textInput.text }}</template></MkInput>
<MkInput v-model="value.default" type="text"><template #label>{{ $ts._pages.blocks._textInput.default }}</template></MkInput> <MkInput v-model="value.default" type="text"><template #label>{{ i18n.ts._pages.blocks._textInput.default }}</template></MkInput>
</section> </section>
</XContainer> </XContainer>
</template> </template>
@ -16,6 +16,7 @@
import { } from 'vue'; import { } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any value: any

View File

@ -1,7 +1,7 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-align-left-bold ph-lg"></i> {{ $ts._pages.blocks.text }}</template> <template #header><i class="ph-align-left-bold ph-lg"></i> {{ i18n.ts._pages.blocks.text }}</template>
<section class="vckmsadr"> <section class="vckmsadr">
<textarea v-model="value.text"></textarea> <textarea v-model="value.text"></textarea>
@ -13,6 +13,7 @@
/* eslint-disable vue/no-mutating-props */ /* eslint-disable vue/no-mutating-props */
import { } from 'vue'; import { } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any value: any

View File

@ -1,12 +1,12 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-lightning-bold ph-lg"></i> {{ $ts._pages.blocks.textareaInput }}</template> <template #header><i class="ph-lightning-bold ph-lg"></i> {{ i18n.ts._pages.blocks.textareaInput }}</template>
<section style="padding: 0 16px 16px 16px;"> <section style="padding: 0 16px 16px 16px;">
<MkInput v-model="value.name"><template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template><template #label>{{ $ts._pages.blocks._textareaInput.name }}</template></MkInput> <MkInput v-model="value.name"><template #prefix><i class="ph-magic-wand-bold ph-lg"></i></template><template #label>{{ i18n.ts._pages.blocks._textareaInput.name }}</template></MkInput>
<MkInput v-model="value.text"><template #label>{{ $ts._pages.blocks._textareaInput.text }}</template></MkInput> <MkInput v-model="value.text"><template #label>{{ i18n.ts._pages.blocks._textareaInput.text }}</template></MkInput>
<MkTextarea v-model="value.default"><template #label>{{ $ts._pages.blocks._textareaInput.default }}</template></MkTextarea> <MkTextarea v-model="value.default"><template #label>{{ i18n.ts._pages.blocks._textareaInput.default }}</template></MkTextarea>
</section> </section>
</XContainer> </XContainer>
</template> </template>
@ -17,6 +17,7 @@ import { } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import MkTextarea from '@/components/form/textarea.vue'; import MkTextarea from '@/components/form/textarea.vue';
import MkInput from '@/components/form/input.vue'; import MkInput from '@/components/form/input.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any value: any

View File

@ -1,7 +1,7 @@
<template> <template>
<!-- eslint-disable vue/no-mutating-props --> <!-- eslint-disable vue/no-mutating-props -->
<XContainer :draggable="true" @remove="() => $emit('remove')"> <XContainer :draggable="true" @remove="() => $emit('remove')">
<template #header><i class="ph-align-left-bold ph-lg"></i> {{ $ts._pages.blocks.textarea }}</template> <template #header><i class="ph-align-left-bold ph-lg"></i> {{ i18n.ts._pages.blocks.textarea }}</template>
<section class="ihymsbbe"> <section class="ihymsbbe">
<textarea v-model="value.text"></textarea> <textarea v-model="value.text"></textarea>
@ -13,6 +13,7 @@
/* eslint-disable vue/no-mutating-props */ /* eslint-disable vue/no-mutating-props */
import { } from 'vue'; import { } from 'vue';
import XContainer from '../page-editor.container.vue'; import XContainer from '../page-editor.container.vue';
import { i18n } from '@/i18n';
withDefaults(defineProps<{ withDefaults(defineProps<{
value: any value: any

View File

@ -81,11 +81,11 @@ export default defineComponent({
} }
&.warn { &.warn {
border: solid 2px #dec44c; border: solid 2px #f6c177;
} }
&.error { &.error {
border: solid 2px #f00; border: solid 2px #eb6f92;
} }
& + .cpjygsrt { & + .cpjygsrt {
@ -131,14 +131,14 @@ export default defineComponent({
} }
> .warn { > .warn {
color: #b19e49; color: #ea9d34;
margin: 0; margin: 0;
padding: 16px 16px 0 16px; padding: 16px 16px 0 16px;
font-size: 14px; font-size: 14px;
} }
> .error { > .error {
color: #f00; color: #b4637a;
margin: 0; margin: 0;
padding: 16px 16px 0 16px; padding: 16px 16px 0 16px;
font-size: 14px; font-size: 14px;

View File

@ -9,7 +9,7 @@
</template> </template>
<section v-if="modelValue.type === null" class="pbglfege" @click="changeType()"> <section v-if="modelValue.type === null" class="pbglfege" @click="changeType()">
{{ $ts._pages.script.emptySlot }} {{ i18n.ts._pages.script.emptySlot }}
</section> </section>
<section v-else-if="modelValue.type === 'text'" class="tbwccoaw"> <section v-else-if="modelValue.type === 'text'" class="tbwccoaw">
<input v-model="modelValue.value"/> <input v-model="modelValue.value"/>
@ -18,7 +18,7 @@
<textarea v-model="modelValue.value"></textarea> <textarea v-model="modelValue.value"></textarea>
</section> </section>
<section v-else-if="modelValue.type === 'textList'" class="tbwccoaw"> <section v-else-if="modelValue.type === 'textList'" class="tbwccoaw">
<textarea v-model="modelValue.value" :placeholder="$ts._pages.script.blocks._textList.info"></textarea> <textarea v-model="modelValue.value" :placeholder="i18n.ts._pages.script.blocks._textList.info"></textarea>
</section> </section>
<section v-else-if="modelValue.type === 'number'" class="tbwccoaw"> <section v-else-if="modelValue.type === 'number'" class="tbwccoaw">
<input v-model="modelValue.value" type="number"/> <input v-model="modelValue.value" type="number"/>
@ -26,13 +26,13 @@
<section v-else-if="modelValue.type === 'ref'" class="hpdwcrvs"> <section v-else-if="modelValue.type === 'ref'" class="hpdwcrvs">
<select v-model="modelValue.value"> <select v-model="modelValue.value">
<option v-for="v in hpml.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option> <option v-for="v in hpml.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
<optgroup :label="$ts._pages.script.argVariables"> <optgroup :label="i18n.ts._pages.script.argVariables">
<option v-for="v in fnSlots" :value="v.name">{{ v.name }}</option> <option v-for="v in fnSlots" :value="v.name">{{ v.name }}</option>
</optgroup> </optgroup>
<optgroup :label="$ts._pages.script.pageVariables"> <optgroup :label="i18n.ts._pages.script.pageVariables">
<option v-for="v in hpml.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option> <option v-for="v in hpml.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
</optgroup> </optgroup>
<optgroup :label="$ts._pages.script.enviromentVariables"> <optgroup :label="i18n.ts._pages.script.enviromentVariables">
<option v-for="v in hpml.getEnvVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option> <option v-for="v in hpml.getEnvVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
</optgroup> </optgroup>
</select> </select>
@ -42,7 +42,7 @@
</section> </section>
<section v-else-if="modelValue.type === 'fn'" class="" style="padding:0 16px 16px 16px;"> <section v-else-if="modelValue.type === 'fn'" class="" style="padding:0 16px 16px 16px;">
<MkTextarea v-model="slots"> <MkTextarea v-model="slots">
<template #label>{{ $ts._pages.script.blocks._fn.slots }}</template> <template #label>{{ i18n.ts._pages.script.blocks._fn.slots }}</template>
<template #caption>{{ $t('_pages.script.blocks._fn.slots-info') }}</template> <template #caption>{{ $t('_pages.script.blocks._fn.slots-info') }}</template>
</MkTextarea> </MkTextarea>
<XV v-if="modelValue.value.expression" v-model="modelValue.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :hpml="hpml" :fn-slots="modelValue.value.slots" :name="name"/> <XV v-if="modelValue.value.expression" v-model="modelValue.value.expression" :title="$t(`_pages.script.blocks._fn.arg1`)" :get-expected-type="() => null" :hpml="hpml" :fn-slots="modelValue.value.slots" :name="name"/>
@ -66,6 +66,7 @@ import { blockDefs } from '@/scripts/hpml/index';
import * as os from '@/os'; import * as os from '@/os';
import { isLiteralValue } from '@/scripts/hpml/expr'; import { isLiteralValue } from '@/scripts/hpml/expr';
import { funcDefs } from '@/scripts/hpml/lib'; import { funcDefs } from '@/scripts/hpml/lib';
import { i18n } from '@/i18n';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -110,6 +111,7 @@ export default defineComponent({
error: null, error: null,
warn: null, warn: null,
slots: '', slots: '',
i18n,
}; };
}, },
@ -215,7 +217,7 @@ export default defineComponent({
methods: { methods: {
async changeType() { async changeType() {
const { canceled, result: type } = await os.select({ const { canceled, result: type } = await os.select({
title: this.$ts._pages.selectType, title: i18n.ts._pages.selectType,
groupedItems: this.getScriptBlockList(this.getExpectedType ? this.getExpectedType() : null) groupedItems: this.getScriptBlockList(this.getExpectedType ? this.getExpectedType() : null)
}); });
if (canceled) return; if (canceled) return;

View File

@ -3,42 +3,42 @@
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template>
<MkSpacer :content-max="700"> <MkSpacer :content-max="700">
<div class="jqqmcavi"> <div class="jqqmcavi">
<MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ph-arrow-square-out-bold ph-lg"></i> {{ $ts._pages.viewPage }}</MkButton> <MkButton v-if="pageId" class="button" inline link :to="`/@${ author.username }/pages/${ currentName }`"><i class="ph-arrow-square-out-bold ph-lg"></i> {{ i18n.ts._pages.viewPage }}</MkButton>
<MkButton v-if="!readonly" inline primary class="button" @click="save"><i class="ph-floppy-disk-back-bold ph-lg"></i> {{ $ts.save }}</MkButton> <MkButton v-if="!readonly" inline primary class="button" @click="save"><i class="ph-floppy-disk-back-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton>
<MkButton v-if="pageId" inline class="button" @click="duplicate"><i class="ph-clipboard-text-bold ph-lg"></i> {{ $ts.duplicate }}</MkButton> <MkButton v-if="pageId" inline class="button" @click="duplicate"><i class="ph-clipboard-text-bold ph-lg"></i> {{ i18n.ts.duplicate }}</MkButton>
<MkButton v-if="pageId && !readonly" inline class="button" danger @click="del"><i class="ph-trash-bold ph-lg"></i> {{ $ts.delete }}</MkButton> <MkButton v-if="pageId && !readonly" inline class="button" danger @click="del"><i class="ph-trash-bold ph-lg"></i> {{ i18n.ts.delete }}</MkButton>
</div> </div>
<div v-if="tab === 'settings'"> <div v-if="tab === 'settings'">
<div class="_formRoot"> <div class="_formRoot">
<MkInput v-model="title" class="_formBlock"> <MkInput v-model="title" class="_formBlock">
<template #label>{{ $ts._pages.title }}</template> <template #label>{{ i18n.ts._pages.title }}</template>
</MkInput> </MkInput>
<MkInput v-model="summary" class="_formBlock"> <MkInput v-model="summary" class="_formBlock">
<template #label>{{ $ts._pages.summary }}</template> <template #label>{{ i18n.ts._pages.summary }}</template>
</MkInput> </MkInput>
<MkInput v-model="name" class="_formBlock"> <MkInput v-model="name" class="_formBlock">
<template #prefix>{{ url }}/@{{ author.username }}/pages/</template> <template #prefix>{{ url }}/@{{ author.username }}/pages/</template>
<template #label>{{ $ts._pages.url }}</template> <template #label>{{ i18n.ts._pages.url }}</template>
</MkInput> </MkInput>
<MkSwitch v-model="alignCenter" class="_formBlock">{{ $ts._pages.alignCenter }}</MkSwitch> <MkSwitch v-model="alignCenter" class="_formBlock">{{ i18n.ts._pages.alignCenter }}</MkSwitch>
<MkSelect v-model="font" class="_formBlock"> <MkSelect v-model="font" class="_formBlock">
<template #label>{{ $ts._pages.font }}</template> <template #label>{{ i18n.ts._pages.font }}</template>
<option value="serif">{{ $ts._pages.fontSerif }}</option> <option value="serif">{{ i18n.ts._pages.fontSerif }}</option>
<option value="sans-serif">{{ $ts._pages.fontSansSerif }}</option> <option value="sans-serif">{{ i18n.ts._pages.fontSansSerif }}</option>
</MkSelect> </MkSelect>
<MkSwitch v-model="hideTitleWhenPinned" class="_formBlock">{{ $ts._pages.hideTitleWhenPinned }}</MkSwitch> <MkSwitch v-model="hideTitleWhenPinned" class="_formBlock">{{ i18n.ts._pages.hideTitleWhenPinned }}</MkSwitch>
<div class="eyeCatch"> <div class="eyeCatch">
<MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="ph-plus-bold ph-lg"></i> {{ $ts._pages.eyeCatchingImageSet }}</MkButton> <MkButton v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage"><i class="ph-plus-bold ph-lg"></i> {{ i18n.ts._pages.eyeCatchingImageSet }}</MkButton>
<div v-else-if="eyeCatchingImage"> <div v-else-if="eyeCatchingImage">
<img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name" style="max-width: 100%;"/> <img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name" style="max-width: 100%;"/>
<MkButton v-if="!readonly" @click="removeEyeCatchingImage()"><i class="ph-trash-bold ph-lg"></i> {{ $ts._pages.eyeCatchingImageRemove }}</MkButton> <MkButton v-if="!readonly" @click="removeEyeCatchingImage()"><i class="ph-trash-bold ph-lg"></i> {{ i18n.ts._pages.eyeCatchingImageRemove }}</MkButton>
</div> </div>
</div> </div>
</div> </div>
@ -107,6 +107,7 @@ import { mainRouter } from '@/router';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
import { $i } from '@/account'; import { $i } from '@/account';
const XDraggable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); const XDraggable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default));
const props = defineProps<{ const props = defineProps<{

View File

@ -18,8 +18,8 @@
</div> </div>
<div class="actions"> <div class="actions">
<div class="like"> <div class="like">
<MkButton v-if="page.isLiked" v-tooltip="i18n.ts._pages.unlike" class="button" primary @click="unlike()"><i class="ph-heart-bold ph-lg"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton> <MkButton v-if="page.isLiked" v-tooltip="i18n.ts._pages.unlike" class="button" primary @click="unlike()"><i class="ph-heart-fill ph-lg"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton>
<MkButton v-else v-tooltip="i18n.ts._pages.like" class="button" @click="like()"><i class="far fa-heart"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton> <MkButton v-else v-tooltip="i18n.ts._pages.like" class="button" @click="like()"><i class="ph-heart-bold"></i><span v-if="page.likedCount > 0" class="count">{{ page.likedCount }}</span></MkButton>
</div> </div>
<div class="other"> <div class="other">
<button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="ph-repeat-bold ph-lg ph-fw ph-lg"></i></button> <button v-tooltip="i18n.ts.shareWithNote" v-click-anime class="_button" @click="shareWithNote"><i class="ph-repeat-bold ph-lg ph-fw ph-lg"></i></button>
@ -44,8 +44,8 @@
</div> </div>
</div> </div>
<div class="footer"> <div class="footer">
<div><i class="far fa-clock"></i> {{ i18n.ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div> <div><i class="ph-alarm-bold"></i> {{ i18n.ts.createdAt }}: <MkTime :time="page.createdAt" mode="detail"/></div>
<div v-if="page.createdAt != page.updatedAt"><i class="far fa-clock"></i> {{ i18n.ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div> <div v-if="page.createdAt != page.updatedAt"><i class="ph-alarm-bold"></i> {{ i18n.ts.updatedAt }}: <MkTime :time="page.updatedAt" mode="detail"/></div>
</div> </div>
<MkAd :prefer="['horizontal', 'horizontal-big']"/> <MkAd :prefer="['horizontal', 'horizontal-big']"/>
<MkContainer :max-height="300" :foldable="true" class="other"> <MkContainer :max-height="300" :foldable="true" class="other">
@ -195,7 +195,7 @@ definePageMetadata(computed(() => page ? {
} }
> .content { > .content {
margin-top: 16px; margin: 1rem;
padding: 16px 0 0 0; padding: 16px 0 0 0;
} }

View File

@ -38,8 +38,8 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { computed, inject } from 'vue'; import { computed, watch, onMounted } from 'vue';
import Virtual from 'swiper'; import { Virtual } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/vue'; import { Swiper, SwiperSlide } from 'swiper/vue';
import MkPagePreview from '@/components/MkPagePreview.vue'; import MkPagePreview from '@/components/MkPagePreview.vue';
import MkPagination from '@/components/MkPagination.vue'; import MkPagination from '@/components/MkPagination.vue';
@ -56,6 +56,7 @@ const router = useRouter();
let tab = $ref('featured'); let tab = $ref('featured');
const tabs = ['featured', 'my', 'liked']; const tabs = ['featured', 'my', 'liked'];
watch($$(tab), () => (syncSlide(tabs.indexOf(tab))));
const featuredPagesPagination = { const featuredPagesPagination = {
endpoint: 'pages/featured' as const, endpoint: 'pages/featured' as const,
@ -113,6 +114,10 @@ function onSlideChange() {
function syncSlide(index) { function syncSlide(index) {
swiperRef.slideTo(index); swiperRef.slideTo(index);
} }
onMounted(() => {
syncSlide(tabs.indexOf(swiperRef.activeIndex));
});
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -52,7 +52,7 @@
</template> </template>
</I18n> </I18n>
</li> </li>
<li>{{ i18n.ts._2fa.step2 }}<br><img :src="twoFactorData.qr"><p>{{ $ts._2fa.step2Url }}<br>{{ twoFactorData.url }}</p></li> <li>{{ i18n.ts._2fa.step2 }}<br><img :src="twoFactorData.qr"><p>{{ i18n.ts._2fa.step2Url }}<br>{{ twoFactorData.url }}</p></li>
<li> <li>
{{ i18n.ts._2fa.step3 }}<br> {{ i18n.ts._2fa.step3 }}<br>
<MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false"><template #label>{{ i18n.ts.token }}</template></MkInput> <MkInput v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" :spellcheck="false"><template #label>{{ i18n.ts.token }}</template></MkInput>

View File

@ -9,7 +9,7 @@
<template #label>{{ i18n.ts.display }}</template> <template #label>{{ i18n.ts.display }}</template>
<option value="sideFull">{{ i18n.ts._menuDisplay.sideFull }}</option> <option value="sideFull">{{ i18n.ts._menuDisplay.sideFull }}</option>
<option value="sideIcon">{{ i18n.ts._menuDisplay.sideIcon }}</option> <option value="sideIcon">{{ i18n.ts._menuDisplay.sideIcon }}</option>
<option value="top">{{ i18n.ts._menuDisplay.top }}</option> <!-- <option value="top">{{ i18n.ts._menuDisplay.top }}</option> -->
<!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ i18n.ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 --> <!-- <MkRadio v-model="menuDisplay" value="hide" disabled>{{ i18n.ts._menuDisplay.hide }}</MkRadio>--> <!-- TODO: サイドバーを完全に隠せるようにすると別途ハンバーガーボタンのようなものをUIに表示する必要があり面倒 -->
</FormRadios> </FormRadios>

View File

@ -1,5 +1,9 @@
<template> <template>
<div class="_formRoot"> <div class="_formRoot">
<div v-if="saveButton == true">
<MkButton primary @click="save">{{ i18n.ts.save }}</MkButton>
</div>
<br/>
<div class="llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }"> <div class="llvierxe" :style="{ backgroundImage: $i.bannerUrl ? `url(${ $i.bannerUrl })` : null }">
<div class="avatar"> <div class="avatar">
<MkAvatar class="avatar" :user="$i" :disable-link="true" @click="changeAvatar"/> <MkAvatar class="avatar" :user="$i" :disable-link="true" @click="changeAvatar"/>
@ -56,6 +60,9 @@
<FormSwitch v-model="profile.isCat" class="_formBlock">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></FormSwitch> <FormSwitch v-model="profile.isCat" class="_formBlock">{{ i18n.ts.flagAsCat }}<template #caption>{{ i18n.ts.flagAsCatDescription }}</template></FormSwitch>
<FormSwitch v-model="profile.showTimelineReplies" class="_formBlock">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></FormSwitch> <FormSwitch v-model="profile.showTimelineReplies" class="_formBlock">{{ i18n.ts.flagShowTimelineReplies }}<template #caption>{{ i18n.ts.flagShowTimelineRepliesDescription }} {{ i18n.ts.reflectMayTakeTime }}</template></FormSwitch>
<FormSwitch v-model="profile.isBot" class="_formBlock">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></FormSwitch> <FormSwitch v-model="profile.isBot" class="_formBlock">{{ i18n.ts.flagAsBot }}<template #caption>{{ i18n.ts.flagAsBotDescription }}</template></FormSwitch>
<div v-if="saveButton == true">
<MkButton primary @click="save">{{ i18n.ts.save }}</MkButton>
</div>
</div> </div>
</template> </template>
@ -78,16 +85,22 @@ import { langmap } from '@/scripts/langmap';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
const profile = reactive({ const profile = reactive({
name: $i.name, name: $i?.name,
description: $i.description, description: $i?.description,
location: $i.location, location: $i?.location,
birthday: $i.birthday, birthday: $i?.birthday,
lang: $i.lang, lang: $i?.lang,
isBot: $i.isBot, isBot: $i?.isBot,
isCat: $i.isCat, isCat: $i?.isCat,
showTimelineReplies: $i.showTimelineReplies, showTimelineReplies: $i?.showTimelineReplies,
}); });
const props = withDefaults(defineProps<{
saveButton?: boolean,
}>(), {});
let saveButton = $ref(props.saveButton ?? false);
watch(() => profile, () => { watch(() => profile, () => {
save(); save();
}, { }, {

View File

@ -12,7 +12,7 @@
<FormLink v-for="webhook in items" :key="webhook.id" :to="`/settings/webhook/edit/${webhook.id}`" class="_formBlock"> <FormLink v-for="webhook in items" :key="webhook.id" :to="`/settings/webhook/edit/${webhook.id}`" class="_formBlock">
<template #icon> <template #icon>
<i v-if="webhook.active === false" class="ph-pause-circle-bold ph-lg"></i> <i v-if="webhook.active === false" class="ph-pause-circle-bold ph-lg"></i>
<i v-else-if="webhook.latestStatus === null" class="far fa-circle"></i> <i v-else-if="webhook.latestStatus === null" class="ph-circle-fill"></i>
<i v-else-if="[200, 201, 204].includes(webhook.latestStatus)" class="ph-check-bold ph-lg" :style="{ color: 'var(--success)' }"></i> <i v-else-if="[200, 201, 204].includes(webhook.latestStatus)" class="ph-check-bold ph-lg" :style="{ color: 'var(--success)' }"></i>
<i v-else class="ph-warning-bold ph-lg" :style="{ color: 'var(--error)' }"></i> <i v-else class="ph-warning-bold ph-lg" :style="{ color: 'var(--error)' }"></i>
</template> </template>

View File

@ -1,11 +1,15 @@
<template> <template>
<div> <div>
<MkPagination v-slot="{items}" ref="list" :pagination="pagination"> <MkSpacer :content-max="800">
<MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap"> <MkPagination v-slot="{items}" ref="list" :pagination="pagination">
<b>{{ item.name }}</b> <MkA v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap">
<div v-if="item.description" class="description">{{ item.description }}</div> <div class="_panel">
</MkA> <b>{{ item.name }}</b>
</MkPagination> <Mfm v-if="item.description" class="description" :text="item.description"/>
</div>
</MkA>
</MkPagination>
</MkSpacer>
</div> </div>
</template> </template>

View File

@ -1,10 +1,12 @@
<template> <template>
<div> <div>
<MkPagination v-slot="{items}" :pagination="pagination"> <MkSpacer :content-max="800">
<div class="jrnovfpt"> <MkPagination v-slot="{items}" :pagination="pagination">
<MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/> <div class="jrnovfpt">
</div> <MkGalleryPostPreview v-for="post in items" :key="post.id" :post="post" class="post"/>
</MkPagination> </div>
</MkPagination>
</MkSpacer>
</div> </div>
</template> </template>

View File

@ -1,6 +1,6 @@
<template> <template>
<MkContainer> <MkContainer>
<template #header><i class="ph-chart-bar-bold ph-lg" style="margin-right: 0.5em;"></i>{{ $ts.activity }}</template> <template #header><i class="ph-chart-bar-bold ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.activity }}</template>
<template #func> <template #func>
<button class="_button" @click="showMenu"> <button class="_button" @click="showMenu">
<i class="ph-dots-three-outline-bold ph-lg"></i> <i class="ph-dots-three-outline-bold ph-lg"></i>

View File

@ -1,6 +1,6 @@
<template> <template>
<MkContainer :max-height="300" :foldable="true"> <MkContainer id="photos-container" :max-height="300" :foldable="true">
<template #header><i class="ph-image-bold ph-lg" style="margin-right: 0.5em;"></i>{{ $ts.images }}</template> <template #header><i class="ph-image-bold ph-lg" style="margin-right: 0.5em;"></i>{{ i18n.ts.images }}</template>
<div class="ujigsodd"> <div class="ujigsodd">
<MkLoading v-if="fetching"/> <MkLoading v-if="fetching"/>
<div v-if="!fetching && images.length > 0" class="stream"> <div v-if="!fetching && images.length > 0" class="stream">
@ -13,7 +13,7 @@
<ImgWithBlurhash :hash="image.file.blurhash" :src="thumbnail(image.file)" :title="image.file.name"/> <ImgWithBlurhash :hash="image.file.blurhash" :src="thumbnail(image.file)" :title="image.file.name"/>
</MkA> </MkA>
</div> </div>
<p v-if="!fetching && images.length == 0" class="empty">{{ $ts.nothing }}</p> <p v-if="!fetching && images.length == 0" class="empty">{{ i18n.ts.nothing }}</p>
</div> </div>
</MkContainer> </MkContainer>
</template> </template>
@ -27,6 +27,7 @@ import * as os from '@/os';
import MkContainer from '@/components/MkContainer.vue'; import MkContainer from '@/components/MkContainer.vue';
import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue';
import { defaultStore } from '@/store'; import { defaultStore } from '@/store';
import { i18n } from '@/i18n';
const props = defineProps<{ const props = defineProps<{
user: misskey.entities.UserDetailed; user: misskey.entities.UserDetailed;
@ -51,6 +52,7 @@ onMounted(() => {
'image/gif', 'image/gif',
'image/apng', 'image/apng',
'image/vnd.mozilla.apng', 'image/vnd.mozilla.apng',
'image/avif',
]; ];
os.api('users/notes', { os.api('users/notes', {
userId: props.user.id, userId: props.user.id,
@ -72,6 +74,10 @@ onMounted(() => {
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
#photos-container {
--stickyTop: 0;
}
.ujigsodd { .ujigsodd {
padding: 8px; padding: 8px;

View File

@ -1,56 +1,41 @@
<template> <template>
<MkStickyContainer> <MkStickyContainer>
<template #header><MkPageHeader v-model:tab="tab" :actions="headerActions" :tabs="headerTabs"/></template> <template #header>
<div v-if="user"> <MkPageHeader
<swiper v-model:tab="tab"
:modules="[Virtual]" :actions="headerActions"
:space-between="20" :tabs="headerTabs"
:virtual="true" />
:allow-touch-move="!(deviceKind === 'desktop' && !defaultStore.state.swipeOnDesktop)" </template>
@swiper="setSwiperRef" <div>
@slide-change="onSlideChange" <transition name="fade" mode="out-in">
> <div v-if="user">
<swiper-slide> <XHome v-if="tab === 'home'" :user="user"/>
<XHome :user="user"/> <XReactions v-else-if="tab === 'reactions'" :user="user"/>
</swiper-slide> <XClips v-else-if="tab === 'clips'" :user="user"/>
<swiper-slide> <XPages v-else-if="tab === 'pages'" :user="user"/>
<XLikedPosts :user="user"/> <XGallery v-else-if="tab === 'gallery'" :user="user"/>
</swiper-slide> </div>
<swiper-slide> <MkError v-else-if="error" @retry="fetchUser()"/>
<XReactions :user="user"/> <MkLoading v-else/>
</swiper-slide> </transition>
<swiper-slide>
<XClips :user="user"/>
</swiper-slide>
<swiper-slide>
<XPages :user="user"/>
</swiper-slide>
<swiper-slide>
<XGallery :user="user"/>
</swiper-slide>
</swiper>
</div> </div>
<MkError v-else-if="error" @retry="fetchUser()"/>
<MkLoading v-else/>
</MkStickyContainer> </MkStickyContainer>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { defineAsyncComponent, computed, inject, onMounted, onUnmounted, watch } from 'vue'; import { defineAsyncComponent, computed, watch } from 'vue';
import { Virtual } from 'swiper'; import calcAge from 's-age';
import { Swiper, SwiperSlide } from 'swiper/vue';
import * as Acct from 'misskey-js/built/acct'; import * as Acct from 'misskey-js/built/acct';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
import { getScrollPosition } from '@/scripts/scroll';
import number from '@/filters/number';
import { userPage, acct as getAcct } from '@/filters/user'; import { userPage, acct as getAcct } from '@/filters/user';
import * as os from '@/os'; import * as os from '@/os';
import { useRouter } from '@/router'; import { useRouter } from '@/router';
import { definePageMetadata } from '@/scripts/page-metadata'; import { definePageMetadata } from '@/scripts/page-metadata';
import { deviceKind } from '@/scripts/device-kind';
import { i18n } from '@/i18n'; import { i18n } from '@/i18n';
import { $i } from '@/account'; import { $i } from '@/account';
import { defaultStore } from '@/store';
import 'swiper/scss';
import 'swiper/scss/virtual';
const XHome = defineAsyncComponent(() => import('./home.vue')); const XHome = defineAsyncComponent(() => import('./home.vue'));
const XReactions = defineAsyncComponent(() => import('./reactions.vue')); const XReactions = defineAsyncComponent(() => import('./reactions.vue'));
@ -58,36 +43,32 @@ const XClips = defineAsyncComponent(() => import('./clips.vue'));
const XPages = defineAsyncComponent(() => import('./pages.vue')); const XPages = defineAsyncComponent(() => import('./pages.vue'));
const XGallery = defineAsyncComponent(() => import('./gallery.vue')); const XGallery = defineAsyncComponent(() => import('./gallery.vue'));
const props = withDefaults(defineProps<{ const props = withDefaults(
acct: string; defineProps<{
page?: string; acct: string;
}>(), { page?: string;
page: 'home', }>(),
}); {
page: 'home',
},
);
const router = useRouter(); const router = useRouter();
let tabs = ['home']; let tab = $ref(props.page);
let user = $ref<null | misskey.entities.UserDetailed>(null); let user = $ref<null | misskey.entities.UserDetailed>(null);
if (($i && ($i.id === user?.id)) || user?.publicReactions) {
tabs.push('reactions');
}
if ((user?.instance != null)) {
tabs.push('clips', 'pages', 'gallery');
}
let tab = $ref(tabs[0]);
watch($$(tab), () => (syncSlide(tabs.indexOf(tab))));
let error = $ref(null); let error = $ref(null);
function fetchUser(): void { function fetchUser(): void {
if (props.acct == null) return; if (props.acct == null) return;
user = null; user = null;
os.api('users/show', Acct.parse(props.acct)).then(u => { os.api('users/show', Acct.parse(props.acct))
user = u; .then((u) => {
}).catch(err => { user = u;
error = err; })
}); .catch((err) => {
error = err;
});
} }
watch(() => props.acct, fetchUser, { watch(() => props.acct, fetchUser, {
@ -96,54 +77,56 @@ watch(() => props.acct, fetchUser, {
const headerActions = $computed(() => []); const headerActions = $computed(() => []);
const headerTabs = $computed(() => user ? [{ const headerTabs = $computed(() =>
key: 'home', user
title: i18n.ts.overview, ? [
icon: 'ph-house-bold ph-lg', {
}, ...($i && ($i.id === user.id)) || user.publicReactions ? [{ key: 'home',
key: 'reactions', title: i18n.ts.overview,
title: i18n.ts.reaction, icon: 'ph-user-bold ph-large',
icon: 'ph-smiley-bold ph-lg', },
}] : [], { ...(($i && $i.id === user.id) || user.publicReactions
key: 'clips', ? [{
title: i18n.ts.clips, key: 'reactions',
icon: 'ph-paperclip-bold ph-lg', title: i18n.ts.reaction,
}, { icon: 'ph-smiley-bold ph-large',
key: 'pages', }] : []),
title: i18n.ts.pages, ...(user.instance == null ? [{
icon: 'ph-file-text-bold ph-lg', key: 'clips',
}, { title: i18n.ts.clips,
key: 'gallery', icon: 'ph-paperclip-bold ph-large',
title: i18n.ts.gallery, }, {
icon: 'ph-image-square-bold ph-lg', key: 'pages',
}] : null); title: i18n.ts.pages,
icon: 'ph-file-text-bold ph-large',
}, {
key: 'gallery',
title: i18n.ts.gallery,
icon: 'ph-image-square-bold ph-large',
}] : []),
]
: null,
);
definePageMetadata(computed(() => user ? { definePageMetadata(
icon: 'ph-user-bold ph-lg', computed(() =>
title: user.name ? `${user.name} (@${user.username})` : `@${user.username}`, user
subtitle: `@${getAcct(user)}`, ? {
userName: user, icon: 'fas fa-user',
avatar: user, title: user.name
path: `/@${user.username}`, ? `${user.name} (@${user.username})`
share: { : `@${user.username}`,
title: user.name, subtitle: `@${getAcct(user)}`,
}, userName: user,
} : null)); avatar: user,
path: `/@${user.username}`,
let swiperRef = null; share: {
title: user.name,
function setSwiperRef(swiper) { },
swiperRef = swiper; }
syncSlide(tabs.indexOf(tab)); : null,
} ),
);
function onSlideChange() {
tab = tabs[swiperRef.activeIndex];
}
function syncSlide(index) {
swiperRef.slideTo(index);
}
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -1,8 +1,10 @@
<template> <template>
<div> <div>
<MkPagination v-slot="{items}" ref="list" :pagination="pagination"> <MkSpacer :content-max="800">
<MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_gap"/> <MkPagination v-slot="{items}" ref="list" :pagination="pagination">
</MkPagination> <MkPagePreview v-for="page in items" :key="page.id" :page="page" class="_gap"/>
</MkPagination>
</MkSpacer>
</div> </div>
</template> </template>

View File

@ -1,15 +1,17 @@
<template> <template>
<div> <div>
<MkPagination v-slot="{items}" ref="list" :pagination="pagination"> <MkSpacer :content-max="800">
<div v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap afdcfbfb"> <MkPagination v-slot="{items}" ref="list" :pagination="pagination">
<div class="header"> <div v-for="item in items" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap afdcfbfb">
<MkAvatar class="avatar" :user="user"/> <div class="header">
<MkReactionIcon class="reaction" :reaction="item.type" :custom-emojis="item.note.emojis" :no-style="true"/> <MkAvatar class="avatar" :user="user"/>
<MkTime :time="item.createdAt" class="createdAt"/> <MkReactionIcon class="reaction" :reaction="item.type" :custom-emojis="item.note.emojis" :no-style="true"/>
<MkTime :time="item.createdAt" class="createdAt"/>
</div>
<MkNote :key="item.id" :note="item.note"/>
</div> </div>
<MkNote :key="item.id" :note="item.note"/> </MkPagination>
</div> </MkSpacer>
</MkPagination>
</div> </div>
</template> </template>

View File

@ -10,22 +10,22 @@
</h1> </h1>
<div class="about"> <div class="about">
<!-- eslint-disable-next-line vue/no-v-html --> <!-- eslint-disable-next-line vue/no-v-html -->
<div class="desc" v-html="meta.description || $ts.headlineMisskey"></div> <div class="desc" v-html="meta.description || i18n.ts.headlineMisskey"></div>
</div> </div>
<div class="action"> <div class="action">
<MkButton class="signup" inline gradate @click="signup()">{{ $ts.signup }}</MkButton> <MkButton class="signup" inline gradate @click="signup()">{{ i18n.ts.signup }}</MkButton>
<MkButton class="signin" inline @click="signin()">{{ $ts.login }}</MkButton> <MkButton class="signin" inline @click="signin()">{{ i18n.ts.login }}</MkButton>
</div> </div>
<div v-if="onlineUsersCount && stats" class="status"> <div v-if="onlineUsersCount && stats" class="status">
<div> <div>
<I18n :src="$ts.nUsers" text-tag="span" class="users"> <I18n :src="i18n.ts.nUsers" text-tag="span" class="users">
<template #n><b>{{ number(stats.originalUsersCount) }}</b></template> <template #n><b>{{ number(stats.originalUsersCount) }}</b></template>
</I18n> </I18n>
<I18n :src="$ts.nNotes" text-tag="span" class="notes"> <I18n :src="i18n.ts.nNotes" text-tag="span" class="notes">
<template #n><b>{{ number(stats.originalNotesCount) }}</b></template> <template #n><b>{{ number(stats.originalNotesCount) }}</b></template>
</I18n> </I18n>
</div> </div>
<I18n :src="$ts.onlineUsersCount" text-tag="span" class="online"> <I18n :src="i18n.ts.onlineUsersCount" text-tag="span" class="online">
<template #n><b>{{ onlineUsersCount }}</b></template> <template #n><b>{{ onlineUsersCount }}</b></template>
</I18n> </I18n>
</div> </div>
@ -47,6 +47,7 @@ import XTimeline from './welcome.timeline.vue';
import { host, instanceName } from '@/config'; import { host, instanceName } from '@/config';
import * as os from '@/os'; import * as os from '@/os';
import number from '@/filters/number'; import number from '@/filters/number';
import { i18n } from '@/i18n';
export default defineComponent({ export default defineComponent({
components: { components: {
@ -64,6 +65,7 @@ export default defineComponent({
stats: null, stats: null,
tags: [], tags: [],
onlineUsersCount: null, onlineUsersCount: null,
i18n,
}; };
}, },
@ -109,13 +111,13 @@ export default defineComponent({
os.pageWindow('/about'); os.pageWindow('/about');
} }
}, { }, {
text: this.$ts.aboutMisskey, text: i18n.ts.aboutMisskey,
icon: 'ph-info-bold ph-lg', icon: 'ph-info-bold ph-lg',
action: () => { action: () => {
os.pageWindow('/about-calckey'); os.pageWindow('/about-calckey');
} }
}, null, { }, null, {
text: this.$ts.help, text: i18n.ts.help,
icon: 'ph-question-bold ph-lg', icon: 'ph-question-bold ph-lg',
action: () => { action: () => {
window.open(`https://misskey-hub.net/help.md`, '_blank'); window.open(`https://misskey-hub.net/help.md`, '_blank');

Some files were not shown because too many files have changed in this diff Show More