Compare commits
225 Commits
Author | SHA1 | Date | |
---|---|---|---|
6f1048c006 | |||
d686e70f2b | |||
70f524b82d | |||
79c6475028 | |||
7dee5309dc | |||
958ec7b03f | |||
9153434906 | |||
3a08364c24 | |||
a9beeab502 | |||
38c901069a | |||
3f7606060e | |||
777f20e9be | |||
8e39aecffe | |||
9e1ab54097 | |||
0f9e09f4bd | |||
743ebc17b9 | |||
9bc4af76b8 | |||
46fa26426d | |||
58d0dc1795 | |||
bc11702f7d | |||
6288de5813 | |||
bbf59c7d9f | |||
d7787bacf7 | |||
23ae0515c4 | |||
fe88b34b8a | |||
74aa031a22 | |||
6aeed212d9 | |||
45b972c059 | |||
7ecfc007a9 | |||
fc78c75bab | |||
59493a0cd9 | |||
6060c6d56e | |||
6cdbb27169 | |||
ed8b073e54 | |||
7dd193636c | |||
01d018510c | |||
1c273a0a75 | |||
fa2c7658a0 | |||
84ca3a7d45 | |||
902c73e6ac | |||
3b626f72e4 | |||
f5ce137a6b | |||
3ce9d12361 | |||
2fe2f3b1eb | |||
17b3ee41db | |||
56d2a5d5d3 | |||
3a17ff0983 | |||
9a9270bbe9 | |||
512eee4f51 | |||
db01fa0eef | |||
0c49a1ebd5 | |||
636d6394e3 | |||
c67c091b3a | |||
333604898c | |||
076ac3b614 | |||
0e1468b159 | |||
66409029e7 | |||
8ec6b2ec11 | |||
14736620ec | |||
831ca53b63 | |||
6138d46509 | |||
c3003cb363 | |||
4277e53433 | |||
6516bd2ade | |||
27d22f954a | |||
88f5e8e8e2 | |||
fd2ae6d3cf | |||
238edd36f7 | |||
5d847f9808 | |||
ac914af9c3 | |||
636f90ca0c | |||
29469bb7c6 | |||
4f043b1841 | |||
85008303f5 | |||
3432d6e615 | |||
855c990a17 | |||
eef2dc2f62 | |||
fa54140973 | |||
e770d32916 | |||
8aeabf530c | |||
6fbf1cfc28 | |||
5480df35bc | |||
8b5104d564 | |||
3c1192d6bf | |||
eb8ef35122 | |||
8a31e5fd0f | |||
751728db84 | |||
e695f54ef0 | |||
b2ed45ae38 | |||
5e36f75f8a | |||
7ac13a386c | |||
56c8ad9df3 | |||
f1ab918ecd | |||
42af8c7695 | |||
0de2e150cb | |||
1ac498c8fe | |||
545d29a40a | |||
73bbef2922 | |||
26567cdeb2 | |||
84941cbb97 | |||
cd5b24d4eb | |||
c8abd512e1 | |||
de9bd2651b | |||
c432310cae | |||
1c95cdffdc | |||
c3ec668e16 | |||
2af79e9855 | |||
f6ac6f9c6f | |||
d8c835fa51 | |||
a97c14a7b7 | |||
54ecf97c22 | |||
9c4e64b7b5 | |||
ef44eda69e | |||
f1a7ab639b | |||
0d8286cb2a | |||
8e4ad4b919 | |||
9a09ed6290 | |||
9ca36021b0 | |||
eaebb95827 | |||
b8cce2067c | |||
07a0631964 | |||
ebc2b05231 | |||
4c79dd4e96 | |||
abc57519a7 | |||
a23a10d375 | |||
01e7716170 | |||
0a29ce13b6 | |||
6d0ee61661 | |||
9c684fd6c4 | |||
cbb8edd5ed | |||
f933fa0e78 | |||
b3e5198f23 | |||
6e042ca344 | |||
9381842af2 | |||
a6644c540a | |||
063427a660 | |||
6846067a5d | |||
b7273c90ae | |||
a6f5e23069 | |||
f37f22b163 | |||
9440fdb2d0 | |||
96c19b2607 | |||
b34c1379e9 | |||
0fc965f342 | |||
617db05808 | |||
6a20ab687c | |||
6c3bcdad54 | |||
aeea275ec2 | |||
b3c6e28717 | |||
70691e1523 | |||
2cb032b0e0 | |||
ebbf5268ac | |||
b2030e8403 | |||
82493bb741 | |||
aa15901c23 | |||
0bd4d069a2 | |||
ab871c6991 | |||
feec5e88fc | |||
a091cbb93a | |||
a59ab79da0 | |||
8ca4d39440 | |||
41d041f701 | |||
3e516474c2 | |||
405c9c258b | |||
24e389bcf3 | |||
933c83679e | |||
a7aa924949 | |||
0eb84fe874 | |||
2f451fa058 | |||
2465a377a1 | |||
697219e946 | |||
4478298a0a | |||
925f89720a | |||
0f71d9f47a | |||
a7ec54ea97 | |||
1d43d18b19 | |||
0bcb25a983 | |||
ec1f2a285b | |||
c2c06694da | |||
5343b005df | |||
eb783f827c | |||
6064c3ae47 | |||
05862a420f | |||
393ad283e1 | |||
841c8d619f | |||
5c2ed9e5d3 | |||
d39aabc143 | |||
4628f507e5 | |||
8b05816860 | |||
3b8ea90fdc | |||
638458e2c1 | |||
bd8d7c3d0f | |||
4b747d20b7 | |||
351b236014 | |||
e9251debe0 | |||
22c4842154 | |||
788edd3622 | |||
5afe96e15a | |||
899fc8a7bd | |||
2afcdda058 | |||
c2fc637575 | |||
82d2b0608f | |||
047a46d966 | |||
e97dd13e81 | |||
067e9ec6f4 | |||
64397708fd | |||
5ae6b0058f | |||
c201326906 | |||
3bc2fe8527 | |||
c76c1fc4ef | |||
fb3fa06ae7 | |||
48353cccae | |||
65bf863093 | |||
87a6ef0581 | |||
af583d2ef6 | |||
68d49bc770 | |||
d848195a6e | |||
3c5324bbbb | |||
e2d59293fb | |||
411f038f34 | |||
26f2ae093b | |||
6a53ccf814 | |||
114523e69e | |||
d5caf22d8c | |||
a8f945ae8e |
6
.gitattributes
vendored
6
.gitattributes
vendored
@ -1,5 +1,7 @@
|
||||
*.svg -diff -text
|
||||
*.psd -diff -text
|
||||
*.ai -diff -text
|
||||
yarn.lock -diff -text
|
||||
package-lock.json -diff -text
|
||||
*.mqo -diff -text
|
||||
*.glb -diff -text
|
||||
*.blend -diff -text
|
||||
*.afdesign -diff -text
|
||||
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -1,5 +1,6 @@
|
||||
# Visual Studio Code
|
||||
/.vscode
|
||||
!/.vscode/extensions.json
|
||||
|
||||
# Intelij-IDEA
|
||||
/.idea
|
||||
@ -7,9 +8,6 @@
|
||||
# Node.js
|
||||
/node_modules
|
||||
|
||||
# yarn
|
||||
yarn.lock
|
||||
|
||||
# config
|
||||
/.config/*
|
||||
!/.config/example.yml
|
||||
@ -32,3 +30,10 @@ api-docs.json
|
||||
.DS_Store
|
||||
/files
|
||||
ormconfig.json
|
||||
|
||||
# blender backups
|
||||
*.blend1
|
||||
*.blend2
|
||||
*.blend3
|
||||
*.blend4
|
||||
*.blend5
|
||||
|
@ -1 +1 @@
|
||||
v12.1.0
|
||||
v12.8.1
|
||||
|
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@ -2,10 +2,10 @@
|
||||
"recommendations": [
|
||||
"ducksoupdev.vue2",
|
||||
"editorconfig.editorconfig",
|
||||
"eg2.tslint",
|
||||
"eg2.vscode-npm-script",
|
||||
"hollowtree.vue-snippets",
|
||||
"ms-vscode.typescript-javascript-grammar",
|
||||
"ms-vscode.vscode-typescript-tslint-plugin",
|
||||
"octref.vetur",
|
||||
"sysoev.language-stylus"
|
||||
]
|
||||
|
173
CHANGELOG.md
173
CHANGELOG.md
@ -1,25 +1,164 @@
|
||||
ChangeLog
|
||||
=========
|
||||
|
||||
If you encounter any problems with updating, please try the following:
|
||||
1. `npm run clean` or `npm run cleanall`
|
||||
2. Retry update (Don't forget `npm i`)
|
||||
11.29.0 (2019/08/19)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* Room: ソファ追加
|
||||
* Room: 螺旋階段追加
|
||||
* Room: ゴミ箱追加
|
||||
|
||||
Migration
|
||||
------------------------------
|
||||
#### 1
|
||||
```
|
||||
npm i -g ts-node
|
||||
```
|
||||
### 🐛Fixes
|
||||
* Room: 部屋を離れても裏でレンダリングが続く問題を修正
|
||||
* Room: アバターのレンダリングを修正
|
||||
* Room: ライティングの調整
|
||||
|
||||
#### 2
|
||||
```
|
||||
npm run migrate
|
||||
```
|
||||
11.28.2 (2019/08/18)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* 他人の部屋なのに部屋編集UIが表示されるのを修正
|
||||
* オブジェクトストレージを使用している場合Roomで画像を読み込めない問題を修正
|
||||
|
||||
11.28.1 (2019/08/18)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* オブジェクトストレージを使用している場合Roomで画像を読み込めない問題を修正
|
||||
* Roomで家具を移動など確定せずに「しまう」と部屋ごと消える問題を修正
|
||||
|
||||
11.28.0 (2019/08/18)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* 自分の部屋を作れるように
|
||||
* Delキーを押して投稿を削除するときに確認ダイアログを出すように
|
||||
* Elasticsearchのインデックス名をconfigで変更できるように
|
||||
|
||||
### 🐛Fixes
|
||||
* コンテンツを遡ってる途中に新しいアイテムが先頭に追加されると上限に達している場合末尾のアイテムが削除される問題を修正
|
||||
* ユーザー名が突き抜けるのを修正
|
||||
* ユーザー一覧とかでサイレンス・凍結されたユーザーの状態が表示されてなかったのを修正
|
||||
* タイトルやアイコンがきちんと設定されないことがあるのを修正
|
||||
* ドライブアップロード直後に取得できるURLがoriginalじゃない問題を修正
|
||||
* リモートユーザー向けのNoteUnreadsレコードが作成される問題を修正
|
||||
* Hashtagがupdateできない問題を修正
|
||||
* ハッシュタグの更新がタグの数だけ並列で行われてDBを重くしてしまうことがあるのを修正
|
||||
|
||||
11.27.1 (2019/08/01)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* オブジェクトストレージに関する問題を修正
|
||||
|
||||
11.27.0 (2019/07/29)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* 「削除して編集」機能を追加
|
||||
* HTTPリクエストのKeep-AliveとPrxoy対応(サーバーのパフォーマンス向上)
|
||||
* 通知を種類でフィルタリングして表示できるように
|
||||
* モバイルで通知ページを表示することができるように
|
||||
* 非ログイン時の警告処理
|
||||
|
||||
### 🐛Fixes
|
||||
* リモートの絵文字が更新されない問題を修正
|
||||
* リンクバリデーションリンクが一瞬表示されてしまう問題を修正
|
||||
* 選択していない状態でウィジェットが追加できる問題を修正
|
||||
|
||||
11.26.2 (2019/07/25)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* すでに使われたことのあるユーザー名を再度使えないように
|
||||
* モバイルのウィジェットページが常に i/update-client-setting を呼び続ける問題を修正
|
||||
* 投稿フォームのヘッダに添付ファイル数がちゃんと表示されない問題を修正
|
||||
|
||||
11.26.1 (2019/07/21)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* リモートアンケートの期限が保存されないのを修正
|
||||
* 自分をブロックしているユーザーがおすすめユーザーに表示されている問題を修正
|
||||
* スクロールしてると絵文字ピッカーの位置がずれる問題を修正
|
||||
* 投稿フォームが画面外にはみ出さないように
|
||||
* 投稿フォームの「引用付き」の表示が見づらい問題の修正
|
||||
* 投稿フォームにもうリノートや引用ノートのデータがある場合はリンクを貼っても「引用として添付しますか?」のダイアログボックスが出ないように
|
||||
* 「タイムライン上部に投稿フォームを表示する」機能の使用時、ノートを投稿しても引用ノートのデータが残る問題の修正
|
||||
* デスクトップ版のアクティビティウィジェットの日付とデータの表示が変だったのを修正
|
||||
|
||||
11.26.0 (2019/07/19)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* モデレーターログを記録して確認できるように
|
||||
* プロフィールに追加情報を設定できるように
|
||||
* Mastodonのリンクの所有者認証に対応
|
||||
* AP: Delete Person アクティビティを配信するように
|
||||
* AP: Delete アクティビティの後にフォロー解除するように
|
||||
* AP: アカウント削除でもDelete activityを配信するように
|
||||
* Pages: ラジオボタンを追加
|
||||
* AdminページのUsers Viewでユーザーのレコードをクリックすることですぐユーザーを照会できるように
|
||||
* AdminページのUsers Viewでユーザー一覧からユーザー名とホスト情報で検索できるように
|
||||
* 特定ホストへのメンションの特別処理をクライアントに追加
|
||||
* 設定画面でデスクトップ・モバイルモード変更時はすぐにrefreshするか伺うように
|
||||
* ペーストされたファイル名のテンプレート変更時すぐどのようになるか見れるように
|
||||
* (コ`・ヘ・´ケ)を追加
|
||||
|
||||
### 🐛Fixes
|
||||
* ログインのログが正しく保存されない問題を修正
|
||||
* 同じユーザー名のユーザーが作成できてしまうことがある問題を修正
|
||||
* 報告されたレポート内容が表示されない問題を修正
|
||||
* リモートのプロフィールの追加情報が表示されない問題を修正
|
||||
* 「見つける」のタグが大文字小文字区別されている問題を修正
|
||||
* 管理画面のインスタンス一覧でソートが正しく機能していない問題を修正
|
||||
* プロフィール設定でバナーに動画を設定すると以降編集ができない問題を修正
|
||||
* ウェブ検索エンジンの設定でグリッチが発生する問題を修正
|
||||
* MFMの引用がインライン表示になっている問題を修正
|
||||
* アンケートの期限入力部分のタイトル表示がおかしい問題を修正
|
||||
* 画面上の項目がすべていなくなると実際はロードされてないだけのファイルやフォルダーがあるにも関わらず「もっと読み込む」ボタンがなくなり「このフォルダーは空です」っていうplaceholderが表示されてしまう問題を修正
|
||||
* proxy-media後のContent-Typeが違う問題を修正
|
||||
* ビルド時にエラーが出るのを修正
|
||||
|
||||
11.25.1 (2019/07/09)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* ページイベントが正しくストリームに流れてこない問題を修正
|
||||
* フォントの調整
|
||||
|
||||
11.25.0 (2019/07/08)
|
||||
--------------------
|
||||
### ✨Improvements
|
||||
* パスワード無しログイン機能
|
||||
* MisskeyPagesにイベント送信ボタンを追加
|
||||
* MisskeyPagesでボタンを目立たせることができるように
|
||||
* ページをピン留めできるように
|
||||
* デッキのカラム内でページを見れるように
|
||||
* 既定のアップロード先フォルダを設定できるように
|
||||
* ファイルをペーストしてアップロードするときのファイル名をカスタマイズできるように
|
||||
* 公開以外へのリプライ時元の公開範囲で指定したユーザー情報を引き継ぐように
|
||||
* デスクトップ版を使うかモバイル版を使うか手動で設定できるように
|
||||
* フォルダーを削除できないときダイアログボックスで知らせるように
|
||||
|
||||
### 🐛Fixes
|
||||
* postgres redis cache の option が適用されない問題を修正
|
||||
* 一部MFMテキストが折り返されず突き抜ける問題を修正
|
||||
|
||||
11.24.2 (2019/07/05)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* チャートAPIの返り値が文字列になっていることがある問題を修正
|
||||
* チャートに数字が表示される問題を修正
|
||||
* ペタバイト単位が表示できない問題を修正
|
||||
* SingleLineなMFMが折り返されてしまう問題を修正
|
||||
|
||||
11.24.1 (2019/07/05)
|
||||
--------------------
|
||||
### 🐛Fixes
|
||||
* WebAuthnでログインできない問題を修正
|
||||
* 絵文字の変更事項のmetaへの反映が最大1時間遅延される問題を修正
|
||||
* ハッシュタグのトレンドの計算を5分単位で丸めるように
|
||||
* APNGでもMIME typeはimage/pngにするように
|
||||
* カスタム絵文字リアクションがたまに文字になってしまう問題を修正
|
||||
|
||||
11.24.0 (2019/07/05)
|
||||
--------------------
|
||||
注意: このアップデート後に、`node built/tools/accept-migration Init 1000000000000`してください。
|
||||
### 注意
|
||||
- このアップデート後に、`node built/tools/accept-migration Init 1000000000000`してください。
|
||||
- プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
|
||||
### ✨Improvements
|
||||
* WebAuthnサポート
|
||||
@ -609,9 +748,9 @@ mongodb:
|
||||
db: misskey
|
||||
```
|
||||
3. migration ブランチに切り替え
|
||||
4. `npm i`
|
||||
5. `npm run build`
|
||||
6. `npm run migrate`
|
||||
4. `yarn install`
|
||||
5. `yarn build`
|
||||
6. `yarn migrate`
|
||||
7. master ブランチに戻す
|
||||
8. enjoy
|
||||
|
||||
|
@ -7,18 +7,24 @@ Feature suggestions and bug reports are filed in https://github.com/syuilo/missk
|
||||
* Please search existing issues to avoid duplication. If your issue is already filed, please add your reaction or comment to the existing one.
|
||||
* If you have multiple independent issues, please submit them separately.
|
||||
|
||||
## Branches
|
||||
* **master** branch is tracking the latest release and used for production purposes.
|
||||
* **develop** branch is where we work for the next release.
|
||||
* **l10n_develop** branch is reserved for localization management.
|
||||
|
||||
## Localization (l10n)
|
||||
Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management.
|
||||
You can improve our translations with your Crowdin account.
|
||||
Changes you make in Crowdin will be merged into develop branch.
|
||||
Your changes in Crowdin are automatically submitted as a PR (with the title "New Crowdin translations") to the repository.
|
||||
The owner [@syuilo](https://github.com/syuilo) merges the PR into the develop branch before the next release.
|
||||
|
||||
If you can't find the language you want to contribute with, please open an issue.
|
||||
If your language is not listed in Crowdin, please open an issue.
|
||||
|
||||

|
||||
|
||||
## Internationalization (i18n)
|
||||
Misskey uses [vue-i18n](https://github.com/kazupon/vue-i18n).
|
||||
Misskey uses the Vue.js plugin [Vue I18n](https://github.com/kazupon/vue-i18n).
|
||||
Documentation of Vue I18n is available at http://kazupon.github.io/vue-i18n/introduction.html .
|
||||
|
||||
## Documentation
|
||||
* Documents for contributors are located in [`/docs`](/docs).
|
||||
@ -29,9 +35,23 @@ Misskey uses [vue-i18n](https://github.com/kazupon/vue-i18n).
|
||||
* Test codes are located in [`/test`](/test).
|
||||
|
||||
## Continuous integration
|
||||
Misskey uses CircleCI for automated test.
|
||||
Misskey uses CircleCI for executing automated tests.
|
||||
Configuration files are located in [`/.circleci`](/.circleci).
|
||||
|
||||
## Adding MisskeyRoom items
|
||||
* Use English for material, object and texture names
|
||||
* Use meter for unit of length
|
||||
* Your PR should include all source files of your models (for later editing)
|
||||
* Your PR must include the glTF binary files (.glb) of your models
|
||||
|
||||
If you have no experience on 3D modeling, we suggest to use the free 3DCG software [Blender](https://www.blender.org/).
|
||||
You can find information on glTF 2.0 at [glTF 2.0 — Blender Manual]( https://docs.blender.org/manual/en/dev/addons/io_scene_gltf2.html).
|
||||
|
||||
## FAQ
|
||||
### How to resolve conflictions occurred at yarn.lock?
|
||||
|
||||
Just execute `yarn` to fix it.
|
||||
|
||||
## Glossary
|
||||
### AP
|
||||
Stands for _**A**ctivity**P**ub_.
|
||||
@ -51,11 +71,15 @@ Convert な(na) to にゃ(nya)
|
||||
#### Denyaize
|
||||
Revert Nyaize
|
||||
|
||||
## Code style
|
||||
### セミコロンを省略しない
|
||||
ASI Hazardを避けるためでもある
|
||||
## TypeScript Coding Style
|
||||
### Do not omit semicolons
|
||||
This is to avoid Automatic Semicolon Insertion (ASI) hazard.
|
||||
|
||||
### 中括弧を省略しない
|
||||
Ref:
|
||||
* https://www.ecma-international.org/ecma-262/#sec-automatic-semicolon-insertion
|
||||
* https://github.com/tc39/ecma262/pull/1062
|
||||
|
||||
### Do not omit curly brackets
|
||||
Bad:
|
||||
``` ts
|
||||
if (foo)
|
||||
@ -73,18 +97,38 @@ if (foo) {
|
||||
}
|
||||
```
|
||||
|
||||
ただし**`if`が一行**の時だけは省略しても良い
|
||||
As a special case, you can omit the curly brackets if
|
||||
|
||||
* the body of the `if`-statement have only one statement and,
|
||||
* the `if`-statement does not have `else`-clause.
|
||||
|
||||
Good:
|
||||
``` ts
|
||||
if (foo) bar;
|
||||
```
|
||||
|
||||
### `export default`を使わない
|
||||
インテリセンスと相性が悪かったりするため
|
||||
Make sure that the condition and the body statement are on the same line.
|
||||
|
||||
参考:
|
||||
* https://gfx.hatenablog.com/entry/2017/11/24/135343
|
||||
### Do not use `==` when it can simply be replaced with `===`.
|
||||
🥰
|
||||
|
||||
### Use only boolean (or null related) values in the condition of an `if`-statement.
|
||||
Bad:
|
||||
``` ts
|
||||
if (foo.length)
|
||||
```
|
||||
|
||||
Good:
|
||||
``` ts
|
||||
if (foo.length > 0)
|
||||
```
|
||||
|
||||
### Do not use `export default`
|
||||
This is because the current language support does not work well with `export default`.
|
||||
|
||||
Ref:
|
||||
* https://basarat.gitbooks.io/typescript/docs/tips/defaultIsBad.html
|
||||
* https://gfx.hatenablog.com/entry/2017/11/24/135343
|
||||
|
||||
Bad:
|
||||
``` ts
|
||||
@ -220,8 +264,7 @@ const user = await Users.findOne(userId).then(ensure);
|
||||
|
||||
### Migration作成方法
|
||||
```
|
||||
npm i -g ts-node
|
||||
ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前
|
||||
npx ts-node ./node_modules/typeorm/cli.js migration:generate -n 変更の名前
|
||||
```
|
||||
|
||||
作成されたスクリプトは不必要な変更を含むため除去してください。
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM node:12.5-alpine AS base
|
||||
FROM node:12.8-alpine AS base
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
@ -23,9 +23,9 @@ RUN apk add --no-cache \
|
||||
zlib-dev
|
||||
|
||||
COPY package.json ./
|
||||
RUN npm i
|
||||
RUN yarn install
|
||||
COPY . ./
|
||||
RUN npm run build
|
||||
RUN yarn build
|
||||
|
||||
FROM base AS runner
|
||||
|
||||
|
18
README.md
18
README.md
@ -108,50 +108,41 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/776209" alt="Denshi" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/557245" alt="mkatze" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
|
||||
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=776209">Denshi</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=557245">mkatze</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
||||
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/16869916" alt="見当かなみ" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/12718187" alt="Peter G." width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
|
||||
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
|
||||
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td>
|
||||
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=17463605">Sampot</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpe?token-time=2145916800&token-hash=CPxGQhKIlEaa6WUcgbyHixyKEhakiw9RFdOhsIJBQ_o%3D" alt="takimura" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/619ab87cc08448439222631ebb26802f/1.gif?token-time=2145916800&token-hash=o27K7M02s1z-LkDUEO5Oa7cu-GviRXeOXxryi4o_6VU%3D" alt="Atsuko Tominaga" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5923936/2a743cbfbff946c2af3f09026047c0da/2.png?token-time=2145916800&token-hash=h6yphW1qnM0n_NOWaf8qtszMRLXEwIxfk5beu4RxdT0%3D" alt="noellabo" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/10789744/97175095d8f04c0f86225ff47cb98d40/1.jpeg?token-time=2145916800&token-hash=l4AoMR7Nj7K4yAHrkrk2hAoggPkbSPm12m1nmbe9Pb8%3D" alt="Naoki Hirayama" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
|
||||
<td><a href="https://www.patreon.com/takimura">takimura</a></td>
|
||||
@ -161,7 +152,6 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><a href="https://www.patreon.com/noellabo">noellabo</a></td>
|
||||
<td><a href="https://www.patreon.com/Corset">CG</a></td>
|
||||
<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
|
||||
<td><a href="https://www.patreon.com/spinlock">Naoki Hirayama</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
|
||||
@ -175,7 +165,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||
</tr></table>
|
||||
|
||||
**Last updated:** Mon, 01 Jul 2019 21:44:06 UTC
|
||||
**Last updated:** Mon, 05 Aug 2019 20:46:06 UTC
|
||||
<!-- PATREON_END -->
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 148 KiB After Width: | Height: | Size: 148 KiB |
@ -68,7 +68,7 @@ Build misskey with the following:
|
||||
*5.* Init DB
|
||||
----------------------------------------------------------------
|
||||
``` shell
|
||||
docker-compose run --rm web npm run init
|
||||
docker-compose run --rm web yarn run init
|
||||
```
|
||||
|
||||
*6.* That is it.
|
||||
|
@ -68,7 +68,7 @@ cp docker_example.env docker.env
|
||||
*5.* データベースを初期化
|
||||
----------------------------------------------------------------
|
||||
``` shell
|
||||
docker-compose run --rm web npm run init
|
||||
docker-compose run --rm web yarn run init
|
||||
```
|
||||
|
||||
*6.* 以上です!
|
||||
|
@ -25,10 +25,9 @@ server {
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 http2;
|
||||
listen [::]:443 http2;
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name example.tld;
|
||||
ssl on;
|
||||
ssl_session_cache shared:ssl_session_cache:10m;
|
||||
|
||||
# To use Let's Encrypt certificate
|
||||
|
@ -27,6 +27,7 @@ Please install and setup these softwares:
|
||||
* **[Redis](https://redis.io/)**
|
||||
|
||||
##### Optional
|
||||
* [Yarn](https://yarnpkg.com/) *Optional but recommended for security reason. If you won't install it, use `npx yarn` instead of `yarn`.*
|
||||
* [Elasticsearch](https://www.elastic.co/) - required to enable the search feature
|
||||
* [FFmpeg](https://www.ffmpeg.org/)
|
||||
|
||||
@ -50,7 +51,7 @@ Please install and setup these softwares:
|
||||
|
||||
5. Install misskey dependencies.
|
||||
|
||||
`npm install`
|
||||
`yarn`
|
||||
|
||||
*4.* Configure Misskey
|
||||
----------------------------------------------------------------
|
||||
@ -65,21 +66,20 @@ Please install and setup these softwares:
|
||||
|
||||
Build misskey with the following:
|
||||
|
||||
`NODE_ENV=production npm run build`
|
||||
`NODE_ENV=production yarn build`
|
||||
|
||||
If you're on Debian, you will need to install the `build-essential`, `python` package.
|
||||
|
||||
If you're still encountering errors about some modules, use node-gyp:
|
||||
|
||||
1. `npm install -g node-gyp`
|
||||
2. `node-gyp configure`
|
||||
3. `node-gyp build`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
1. `npx node-gyp configure`
|
||||
2. `npx node-gyp build`
|
||||
3. `NODE_ENV=production yarn build`
|
||||
|
||||
*6.* Init DB
|
||||
----------------------------------------------------------------
|
||||
``` shell
|
||||
npm run init
|
||||
yarn run init
|
||||
```
|
||||
|
||||
*7.* That is it.
|
||||
@ -130,12 +130,16 @@ You can check if the service is running with `systemctl status misskey`.
|
||||
### How to update your Misskey server to the latest version
|
||||
1. `git checkout master`
|
||||
2. `git pull`
|
||||
3. `npm install`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
5. Check [ChangeLog](../CHANGELOG.md) for migration information
|
||||
3. `yarn install`
|
||||
4. `NODE_ENV=production yarn build`
|
||||
5. `yarn migrate`
|
||||
6. Restart your Misskey process to apply changes
|
||||
7. Enjoy
|
||||
|
||||
If you encounter any problems with updating, please try the following:
|
||||
1. `yarn clean` or `yarn cleanall`
|
||||
2. Retry update (Don't forget `yarn install`
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
If you have any questions or troubles, feel free to contact us!
|
||||
|
@ -27,7 +27,8 @@ Installez les paquets suivants :
|
||||
* **[Redis](https://redis.io/)**
|
||||
|
||||
##### Optionnels
|
||||
* [Elasticsearch](https://www.elastic.co/) - requis pour pouvoir activer la fonctionnalité de recherche
|
||||
* [Yarn](https://yarnpkg.com/) - *recommander pour des raisons de sécurité. Si vous ne l'installez pas, utilisez `npx yarn` au lieu de` yarn`.*
|
||||
* [Elasticsearch](https://www.elastic.co/) - *requis pour pouvoir activer la fonctionnalité de recherche.*
|
||||
* [FFmpeg](https://www.ffmpeg.org/)
|
||||
|
||||
*3.* Installation de Misskey
|
||||
@ -50,7 +51,7 @@ Installez les paquets suivants :
|
||||
|
||||
5. Installez les dépendances de misskey.
|
||||
|
||||
`npm install`
|
||||
`yarn install`
|
||||
|
||||
*4.* Création du fichier de configuration
|
||||
----------------------------------------------------------------
|
||||
@ -65,23 +66,22 @@ Installez les paquets suivants :
|
||||
|
||||
Construisez Misskey comme ceci :
|
||||
|
||||
`NODE_ENV=production npm run build`
|
||||
`NODE_ENV=production yarn build`
|
||||
|
||||
Si vous êtes sous Debian, vous serez amené à installer les paquets `build-essential` et `python`.
|
||||
|
||||
Si vous rencontrez des erreurs concernant certains modules, utilisez node-gyp:
|
||||
|
||||
1. `npm install -g node-gyp`
|
||||
2. `node-gyp configure`
|
||||
3. `node-gyp build`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
1. `npx node-gyp configure`
|
||||
2. `npx node-gyp build`
|
||||
3. `NODE_ENV=production yarn build`
|
||||
|
||||
*6.* C'est tout.
|
||||
----------------------------------------------------------------
|
||||
Excellent ! Maintenant, vous avez un environnement prêt pour lancer Misskey
|
||||
|
||||
### Lancement conventionnel
|
||||
Lancez tout simplement `NODE_ENV=production npm start`. Bonne chance et amusez-vous bien !
|
||||
Lancez tout simplement `NODE_ENV=production yarn start`. Bonne chance et amusez-vous bien !
|
||||
|
||||
### Démarrage avec systemd
|
||||
|
||||
@ -124,9 +124,9 @@ Vous pouvez vérifier si le service a démarré en utilisant la commande `system
|
||||
### Méthode de mise à jour vers la plus récente version de Misskey
|
||||
1. `git checkout master`
|
||||
2. `git pull`
|
||||
3. `npm install`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
5. Consultez [ChangeLog](../CHANGELOG.md) pour les information de migration.
|
||||
3. `yarn install`
|
||||
4. `NODE_ENV=production yarn build`
|
||||
5. `yarn migrate`
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
|
@ -27,6 +27,8 @@ adduser --disabled-password --disabled-login misskey
|
||||
* **[Redis](https://redis.io/)**
|
||||
|
||||
##### オプション
|
||||
* [Yarn](https://yarnpkg.com/)
|
||||
* セキュリティの観点から推奨されます。 yarn をインストールしない方針の場合は、文章中の `yarn` を適宜 `npx yarn` と読み替えてください。
|
||||
* [Elasticsearch](https://www.elastic.co/)
|
||||
* 検索機能を有効にするためにはインストールが必要です。
|
||||
* [FFmpeg](https://www.ffmpeg.org/)
|
||||
@ -51,7 +53,7 @@ adduser --disabled-password --disabled-login misskey
|
||||
|
||||
5. Misskeyの依存パッケージをインストール
|
||||
|
||||
`npm install`
|
||||
`yarn install`
|
||||
|
||||
*4.* 設定ファイルを作成する
|
||||
----------------------------------------------------------------
|
||||
@ -66,20 +68,19 @@ adduser --disabled-password --disabled-login misskey
|
||||
|
||||
次のコマンドでMisskeyをビルドしてください:
|
||||
|
||||
`NODE_ENV=production npm run build`
|
||||
`NODE_ENV=production yarn build`
|
||||
|
||||
Debianをお使いであれば、`build-essential`パッケージをインストールする必要があります。
|
||||
|
||||
何らかのモジュールでエラーが発生する場合はnode-gypを使ってください:
|
||||
1. `npm install -g node-gyp`
|
||||
2. `node-gyp configure`
|
||||
3. `node-gyp build`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
1. `npx node-gyp configure`
|
||||
2. `npx node-gyp build`
|
||||
3. `NODE_ENV=production yarn build`
|
||||
|
||||
*6.* データベースを初期化
|
||||
----------------------------------------------------------------
|
||||
``` shell
|
||||
npm run init
|
||||
yarn run init
|
||||
```
|
||||
|
||||
*7.* 以上です!
|
||||
@ -87,7 +88,7 @@ npm run init
|
||||
お疲れ様でした。これでMisskeyを動かす準備は整いました。
|
||||
|
||||
### 通常起動
|
||||
`NODE_ENV=production npm start`するだけです。GLHF!
|
||||
`NODE_ENV=production yarn start`するだけです。GLHF!
|
||||
|
||||
### systemdを用いた起動
|
||||
1. systemdサービスのファイルを作成
|
||||
@ -120,7 +121,7 @@ npm run init
|
||||
|
||||
3. systemdを再読み込みしmisskeyサービスを有効化
|
||||
|
||||
`systemctl daemon-reload ; systemctl enable misskey`
|
||||
`systemctl daemon-reload; systemctl enable misskey`
|
||||
|
||||
4. misskeyサービスの起動
|
||||
|
||||
@ -131,11 +132,11 @@ npm run init
|
||||
### Misskeyを最新バージョンにアップデートする方法:
|
||||
1. `git checkout master`
|
||||
2. `git pull`
|
||||
3. `npm install`
|
||||
4. `NODE_ENV=production npm run build`
|
||||
5. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する
|
||||
3. `yarn install`
|
||||
4. `NODE_ENV=production yarn build`
|
||||
5. `yarn migrate`
|
||||
|
||||
なにか問題が発生した場合は、`npm run clean`または`npm run cleanall`すると直る場合があります。
|
||||
なにか問題が発生した場合は、`yarn clean`または`yarn cleanall`すると直る場合があります。
|
||||
|
||||
----------------------------------------------------------------
|
||||
|
||||
|
@ -47,7 +47,11 @@ gulp.task('build:copy:views', () =>
|
||||
gulp.src('./src/server/web/views/**/*').pipe(gulp.dest('./built/server/web/views'))
|
||||
);
|
||||
|
||||
gulp.task('build:copy', gulp.parallel('build:copy:views', () =>
|
||||
gulp.task('build:copy:fonts', () =>
|
||||
gulp.src('./node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/client/assets/fonts/'))
|
||||
);
|
||||
|
||||
gulp.task('build:copy', gulp.parallel('build:copy:views', 'build:copy:fonts', () =>
|
||||
gulp.src([
|
||||
'./src/const.json',
|
||||
'./src/server/web/views/**/*',
|
||||
|
@ -34,6 +34,19 @@ common:
|
||||
signup: "Registrovat"
|
||||
signout: "Odhlásit"
|
||||
reload-to-apply-the-setting: "Pro uplatnění tohoto nastavení musíte znovu načíst tuto stránku. Chcete ji načíst teď?"
|
||||
delete-confirm: "Opravdu chcete smazat tento příspěvek?"
|
||||
signin-required: "Přihlašte se, prosím"
|
||||
notification-type: "Typy oznámení"
|
||||
notification-types:
|
||||
all: "Všechny"
|
||||
pollVote: "Hlasy"
|
||||
follow: "Sledovaní"
|
||||
receiveFollowRequest: "Žádost o sledování"
|
||||
reply: "Odpovědi"
|
||||
quote: "Citace"
|
||||
renote: "Renotovat"
|
||||
mention: "Zmínky"
|
||||
reaction: "Reakce"
|
||||
got-it: "Rozumím!"
|
||||
customization-tips:
|
||||
title: "Tipy pro přizpůsobení"
|
||||
@ -89,6 +102,7 @@ common:
|
||||
"read:notifications": "Prohlížet oznámení"
|
||||
"write:notifications": "Pracovat s oznámeními"
|
||||
"read:reactions": "Prohlížet reakce"
|
||||
"write:reactions": "Narabět s reakcemi"
|
||||
"write:votes": "Hlasovat"
|
||||
empty-timeline-info:
|
||||
follow-users-to-make-your-timeline: "Poznámky sledujících se zobrazí ve vaší časové ose"
|
||||
@ -98,10 +112,12 @@ common:
|
||||
hide-contents: "Schovat obsah"
|
||||
reply-placeholder: "Odpovědět na tento příspěvek"
|
||||
quote-placeholder: "Citovat tento příspěvek"
|
||||
quote-attached: "Přiložit citaci"
|
||||
submit: "Odeslat"
|
||||
reply: "Odpovědět"
|
||||
renote: "Renotovat"
|
||||
attach-media-from-local: "Uplodovat soubor z vašeho zařízení"
|
||||
posting: "Posílání"
|
||||
attach-media-from-local: "Nahrát soubor z vašeho zařízení"
|
||||
insert-a-kao: "v('ω')v"
|
||||
create-poll: "Vytvořit anketu"
|
||||
text-remain: "zbývá ještě {} znaků"
|
||||
@ -178,6 +194,7 @@ common:
|
||||
remember-note-visibility: "Zapamatovat viditelnost příspěvků"
|
||||
web-search-engine: "Webové vyhledávače"
|
||||
web-search-engine-desc: "Například: https://www.google.com/?#q={{query}}"
|
||||
paste: "Vložit"
|
||||
keep-cw: "Zachovat varování o obsahu"
|
||||
keep-cw-desc: "Při odpovědi na příspěvek bude varování o obsahu nastaveno stejně jako původní příspěvek."
|
||||
i-like-sushi: "Mam radši sushi (než puding)"
|
||||
@ -261,6 +278,8 @@ common:
|
||||
load-raw-images: "Zobrazovat obrázky v původní kvalitě"
|
||||
load-remote-media: "Zobrazovat média ze vzdáleného serveru"
|
||||
sync: "Synchronizace"
|
||||
save: "Uložit"
|
||||
saved: "Uloženo"
|
||||
search: "Hledání"
|
||||
delete: "Odstranit"
|
||||
loading: "Načítám..."
|
||||
@ -451,9 +470,12 @@ common/views/components/messaging.vue:
|
||||
no-history: "Žádná historie"
|
||||
user: "Uživatel"
|
||||
group: "Skupina"
|
||||
start-with-user: "Zahájit konverzaci s uživatelem"
|
||||
start-with-group: "Zahájit skupinovou konverzaci"
|
||||
common/views/components/messaging-room.vue:
|
||||
new-message: "Máte novou zprávu"
|
||||
common/views/components/messaging-room.form.vue:
|
||||
input-message-here: "Sem zadejte zprávu"
|
||||
send: "Odeslat"
|
||||
attach-from-local: "Přiložit soubory z Vašeho zařízení"
|
||||
common/views/components/messaging-room.message.vue:
|
||||
@ -482,6 +504,7 @@ common/views/components/note-menu.vue:
|
||||
unpin: "Odepnout"
|
||||
delete: "Odstranit"
|
||||
delete-confirm: "Opravdu chcete smazat tento příspěvek?"
|
||||
delete-and-edit: "Smazat a upravit"
|
||||
remote: "Ukázat originální poznámku"
|
||||
common/views/components/user-menu.vue:
|
||||
mention: "Zmínění"
|
||||
@ -537,6 +560,8 @@ common/views/components/emoji-picker.vue:
|
||||
objects: "Objekty"
|
||||
symbols: "Symboly"
|
||||
flags: "Vlajky"
|
||||
common/views/components/settings/app-type.vue:
|
||||
info: "Pro aktivování změn musíte znovu načíst stránky."
|
||||
common/views/components/signin.vue:
|
||||
username: "Přezdívka"
|
||||
password: "Heslo"
|
||||
@ -639,6 +664,7 @@ common/views/components/profile-editor.vue:
|
||||
saved: "Profil byl úspěšně aktualizován"
|
||||
uploading: "Nahrávám"
|
||||
upload-failed: "Nahrávání selhalo"
|
||||
unable-to-process: "Operace nemohla být dokončena."
|
||||
email: "Nastavení e-mailů"
|
||||
email-address: "Emailová adresa"
|
||||
email-verified: "Váš e-mail byl ověřen"
|
||||
@ -655,6 +681,9 @@ common/views/components/profile-editor.vue:
|
||||
danger-zone: "Nebezpečná zóna"
|
||||
delete-account: "Smazat účet"
|
||||
account-deleted: "Váš účet byl smazán. Může chvilku trvat než zmizí všechna data."
|
||||
profile-metadata: "Metadata profilu"
|
||||
metadata-label: "Popis"
|
||||
metadata-content: "Obsah"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Uživatel"
|
||||
rename: "Přejmenovat seznam"
|
||||
@ -728,6 +757,7 @@ desktop:
|
||||
avatar: "Avatar"
|
||||
uploading-avatar: "Nahrál nový avatar"
|
||||
avatar-updated: "Vaše avatar byl aktualizován"
|
||||
unable-to-process: "Operace nemohla být dokončena."
|
||||
invalid-filetype: "Tento formát souboru není podporován"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Černá ... Celkem"
|
||||
@ -764,7 +794,7 @@ desktop/views/components/drive.file.vue:
|
||||
rename: "Přejmenovat"
|
||||
copy-url: "Kopírovat URL"
|
||||
download: "Stáhnout"
|
||||
else-files: "Více..."
|
||||
else-files: "Ostatní"
|
||||
set-as-avatar: "Nastavit jako avatar"
|
||||
set-as-banner: "Nastavit jako baner"
|
||||
open-in-app: "Otevřít v aplikaci"
|
||||
@ -782,6 +812,7 @@ desktop/views/components/drive.folder.vue:
|
||||
rename: "Přejmenovat"
|
||||
rename-folder: "Přejmenovat složku"
|
||||
input-new-folder-name: "Zadejte nové jméno"
|
||||
else-folders: "Ostatní"
|
||||
desktop/views/components/drive.vue:
|
||||
empty-drive-description: "Klikněte pravým tlačítkem myši pro otevření menu, nebo sem přetáhněte soubor pro nahrání."
|
||||
empty-folder: "Tato složka je prázdná"
|
||||
@ -1035,6 +1066,8 @@ admin/views/users.vue:
|
||||
reset-password-confirm: "Opravdu chcete resetovat Vaše heslo?"
|
||||
password-updated: "Heslo je nyní \"{password}\""
|
||||
update-remote-user: "Aktualizovat informace o vzdáleném účtu"
|
||||
username: "Přezdívka"
|
||||
host: "Hostitel"
|
||||
users:
|
||||
title: "Uživatel"
|
||||
state:
|
||||
@ -1051,6 +1084,11 @@ admin/views/users.vue:
|
||||
admin/views/moderators.vue:
|
||||
add-moderator:
|
||||
title: "Vytvořit moderátora"
|
||||
logs:
|
||||
title: "Logy"
|
||||
moderator: "Moderátoři"
|
||||
type: "Operace"
|
||||
info: "Informace"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
title: "Přidat emoji"
|
||||
@ -1242,6 +1280,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "Pro '{q}' nebyly nalezeny žádné příspěvky."
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Vybrat soubory"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Oznámení"
|
||||
mobile/views/pages/user/home.vue:
|
||||
activity: "Aktivita"
|
||||
frequently-replied-users: "Častá zmínění"
|
||||
@ -1265,6 +1305,8 @@ deck/deck.user-column.vue:
|
||||
dev/views/new-app.vue:
|
||||
app-name-desc: "Jméno vaší aplikace"
|
||||
pages:
|
||||
pin-this-page: "Připnout"
|
||||
unpin-this-page: "Odepnout"
|
||||
like: "Lajk"
|
||||
title: "Titulek"
|
||||
blocks:
|
||||
@ -1286,6 +1328,8 @@ pages:
|
||||
_action:
|
||||
_dialog:
|
||||
content: "Obsah"
|
||||
_radioButton:
|
||||
title: "Titulek"
|
||||
script:
|
||||
categories:
|
||||
random: "Náhodně"
|
||||
@ -1306,3 +1350,9 @@ pages:
|
||||
arg1: "Seznamy"
|
||||
types:
|
||||
array: "Seznamy"
|
||||
room:
|
||||
translate: "Přesunout"
|
||||
save: "Uložit"
|
||||
furnitures:
|
||||
moon: "Po"
|
||||
bin: "Koš"
|
||||
|
@ -35,6 +35,13 @@ common:
|
||||
signout: "Log ud"
|
||||
reload-to-apply-the-setting: "Denne indstilling slår først igennem, når du har genindlæst siden. Vil du genindlæse siden nu?"
|
||||
fetching-as-ap-object: "Tilladelse til sammenkobling"
|
||||
delete-confirm: "Er du helt sikker på, at du vil slette denne post?"
|
||||
notification-types:
|
||||
all: "Alle"
|
||||
follow: "Følger"
|
||||
reply: "Svar"
|
||||
renote: "Gen-postering"
|
||||
reaction: "Reaktion"
|
||||
got-it: "Det er OK"
|
||||
customization-tips:
|
||||
title: "Tips om tilpasning"
|
||||
@ -251,6 +258,8 @@ common:
|
||||
disable-via-mobile: "Marker aldrig posten som \"fra mobil\""
|
||||
load-raw-images: "Vis vedhæftede bilag i original kvalitet"
|
||||
load-remote-media: "Vis medie-materiale fra en ekstern server"
|
||||
save: "Gem"
|
||||
saved: "Gemt"
|
||||
search: "Søg"
|
||||
delete: "Slet"
|
||||
loading: "Henter"
|
||||
@ -568,6 +577,8 @@ common/views/components/emoji-picker.vue:
|
||||
objects: "Objekt"
|
||||
symbols: "Symboler"
|
||||
flags: "Flag"
|
||||
common/views/components/settings/app-type.vue:
|
||||
info: "Du er nødt til at genindlæse siden, før ændringerne slår igennem."
|
||||
common/views/components/signin.vue:
|
||||
username: "Brugernavn"
|
||||
password: "Adgangskode"
|
||||
@ -682,6 +693,7 @@ common/views/components/profile-editor.vue:
|
||||
saved: "Profil er opdateret med succes"
|
||||
uploading: "Overfører"
|
||||
upload-failed: "Fejl ved overførsel"
|
||||
unable-to-process: "Handlingen kunne ikke gennemføres."
|
||||
email: "Email indstillinger"
|
||||
email-address: "Email adresse"
|
||||
email-verified: "Din email er blevet bekræftet"
|
||||
@ -701,6 +713,7 @@ common/views/components/profile-editor.vue:
|
||||
danger-zone: "Risici"
|
||||
delete-account: "Slet kontoen"
|
||||
account-deleted: "Kontoen er slettet. Det kan vare lidt, inden alle data forsvinder."
|
||||
metadata-content: "Indhold"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Bruger"
|
||||
rename: "Omdøb listen"
|
||||
@ -807,6 +820,7 @@ desktop:
|
||||
uploading-avatar: "Overfør en ny avatar"
|
||||
avatar-updated: "Avatar er overført med succes"
|
||||
choose-avatar: "Vælg et billede til din avatar"
|
||||
unable-to-process: "Handlingen kunne ikke gennemføres."
|
||||
invalid-filetype: "Denne filtype kan ikke benyttes her"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Sort ... Total"
|
||||
@ -847,7 +861,7 @@ desktop/views/components/drive.file.vue:
|
||||
unmark-as-sensitive: "Fjern markering som 'følsom'"
|
||||
copy-url: "Kopier webadresse"
|
||||
download: "Download"
|
||||
else-files: "Andre"
|
||||
else-files: "Avanceret"
|
||||
set-as-avatar: "Vælg som avatar"
|
||||
set-as-banner: "Vælg som banner"
|
||||
open-in-app: "Åbn i app"
|
||||
@ -866,6 +880,7 @@ desktop/views/components/drive.folder.vue:
|
||||
rename: "Omdøb"
|
||||
rename-folder: "Omdøb mappe"
|
||||
input-new-folder-name: "Angiv nyt navn"
|
||||
else-folders: "Avanceret"
|
||||
desktop/views/components/drive.vue:
|
||||
search: "Søg"
|
||||
empty-draghover: "Smid det her! Fordi du ved, at jeg er meget sød, ikke?"
|
||||
@ -995,6 +1010,7 @@ common/views/components/drive-settings.vue:
|
||||
max: "Kapacitet"
|
||||
in-use: "I brug"
|
||||
stats: "Statistik"
|
||||
default-upload-folder-name: "Mappe(r)"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "Annuller / Bloker"
|
||||
mute: "Annuller"
|
||||
@ -1272,6 +1288,8 @@ admin/views/users.vue:
|
||||
remote-user-updated: "Informationen om den eksterne bruger er nu blevet opdateret."
|
||||
delete-all-files: "Slet alle filer"
|
||||
delete-all-files-confirm: "Er du sikker på, at alle filerne skal slettes?"
|
||||
username: "Brugernavn"
|
||||
host: "Vært"
|
||||
users:
|
||||
title: "Bruger"
|
||||
sort:
|
||||
@ -1302,6 +1320,11 @@ admin/views/moderators.vue:
|
||||
added: "Redaktør er oprettet"
|
||||
remove: "Fjern"
|
||||
removed: "Redaktøren er nu fjernet"
|
||||
logs:
|
||||
title: "Logs"
|
||||
moderator: "Redaktører"
|
||||
type: "Drift"
|
||||
info: "Information"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
title: "Tilføj emoji"
|
||||
@ -1590,6 +1613,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "Ingen poster fundet for \"{q}\""
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Vælg fil(er)"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notifikationer"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Logget ind som {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@ -1664,6 +1689,8 @@ dev/views/new-app.vue:
|
||||
app-name: "Navn på app"
|
||||
app-name-placeholder: "F.eks. Misskey for iOS"
|
||||
pages:
|
||||
pin-this-page: "Tilknyt til din profil"
|
||||
unpin-this-page: "Fjern tilknytning til din profil"
|
||||
like: "Synes om"
|
||||
title: "Titel"
|
||||
blocks:
|
||||
@ -1709,6 +1736,10 @@ pages:
|
||||
_dialog:
|
||||
content: "Indhold"
|
||||
resetRandom: "Nulstil tilfældigt tal"
|
||||
_radioButton:
|
||||
name: "Variabel navn"
|
||||
title: "Titel"
|
||||
default: "Standard værdi"
|
||||
script:
|
||||
categories:
|
||||
flow: "Kontrol"
|
||||
@ -1879,3 +1910,9 @@ pages:
|
||||
enviromentVariables: "Miljø variabel"
|
||||
pageVariables: "Side element"
|
||||
argVariables: "Input slot"
|
||||
room:
|
||||
translate: "Flyt"
|
||||
save: "Gem"
|
||||
furnitures:
|
||||
moon: "Man"
|
||||
bin: "Skraldespand"
|
||||
|
@ -35,6 +35,10 @@ common:
|
||||
signout: "Ausloggen"
|
||||
reload-to-apply-the-setting: "Die Seite muss zum Übernehmen dieser Einstellung aktualisiert werden. Soll die Seite jetzt neu geladen werden?"
|
||||
fetching-as-ap-object: "Hole Daten…"
|
||||
delete-confirm: "Diesen Beitrag löschen?"
|
||||
notification-types:
|
||||
reply: "Antworten"
|
||||
renote: "Anmerkung"
|
||||
got-it: "Verstanden!"
|
||||
customization-tips:
|
||||
title: "Anpassung-Tipps"
|
||||
@ -248,6 +252,8 @@ common:
|
||||
disable-via-mobile: "Beitrag nicht als „vom Handy“ markieren"
|
||||
load-raw-images: "Anhänge in voller Größe laden"
|
||||
load-remote-media: "Zeige Inhalte von fremden Servern"
|
||||
save: "Speichern"
|
||||
saved: "Gespeichert"
|
||||
search: "Suche"
|
||||
delete: "Löschen"
|
||||
loading: "Laden"
|
||||
@ -565,6 +571,7 @@ common/views/components/profile-editor.vue:
|
||||
avatar: "Avatar"
|
||||
banner: "Banner"
|
||||
save: "Speichern"
|
||||
unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
|
||||
export: "Exportieren"
|
||||
import: "Importieren"
|
||||
export-targets:
|
||||
@ -598,6 +605,7 @@ common/views/widgets/memo.vue:
|
||||
save: "Speichern"
|
||||
desktop:
|
||||
banner: "Banner"
|
||||
unable-to-process: "Der Vorgang konnte nicht abgeschlossen werden"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Schwarz ... komplett"
|
||||
notes: "Blau ... Hinweise"
|
||||
@ -632,7 +640,6 @@ desktop/views/components/drive.file.vue:
|
||||
rename: "Umbenennen"
|
||||
copy-url: "URL kopieren"
|
||||
download: "Download"
|
||||
else-files: "Anderes…"
|
||||
set-as-avatar: "Als Avatar festlegen"
|
||||
set-as-banner: "Setze als Banner"
|
||||
open-in-app: "In der App öffnen"
|
||||
@ -783,6 +790,7 @@ admin/views/drive.vue:
|
||||
local: "Lokal"
|
||||
delete: "Löschen"
|
||||
admin/views/users.vue:
|
||||
username: "Benutzername"
|
||||
users:
|
||||
origin:
|
||||
local: "Lokal"
|
||||
@ -872,6 +880,8 @@ mobile/views/pages/note.vue:
|
||||
next: "Nächster Kommentar"
|
||||
mobile/views/pages/search.vue:
|
||||
search: "Suchen"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Benachrichtigungen"
|
||||
mobile/views/pages/user/home.vue:
|
||||
activity: "Aktivität"
|
||||
keywords: "Schlagwörter"
|
||||
@ -911,6 +921,8 @@ dev/views/new-app.vue:
|
||||
authority-desc: "Nur die hier eingetragenen Berechtigungen, werden per API zur Verfügung stehen."
|
||||
authority-warning: "Dies kann auch nach dem erstellen der Anwendung geändert werden, allerdings werden dann alle bisher generierten Token ungültig."
|
||||
pages:
|
||||
pin-this-page: "An die Profilseite pinnen"
|
||||
unpin-this-page: "Lösen"
|
||||
like: "Gefällt mir"
|
||||
blocks:
|
||||
post: "\"Neuer Beitrag\"-Formular"
|
||||
@ -934,3 +946,8 @@ pages:
|
||||
arg1: "Listen"
|
||||
types:
|
||||
array: "Listen"
|
||||
room:
|
||||
save: "Speichern"
|
||||
furnitures:
|
||||
moon: "Mo"
|
||||
bin: "Papierkorb"
|
||||
|
@ -30,11 +30,25 @@ common:
|
||||
customize-home: "Customize home layout"
|
||||
featured-notes: "Featured notes"
|
||||
dark-mode: "Dark Mode"
|
||||
signin: "Log In"
|
||||
signin: "Login"
|
||||
signup: "Sign up"
|
||||
signout: "Logout"
|
||||
reload-to-apply-the-setting: "You'll need to reload the page to reflect this setting. Do you want to reload it now?"
|
||||
fetching-as-ap-object: "Inquiring to union"
|
||||
fetching-as-ap-object: "Inquiring to fediverse"
|
||||
unfollow-confirm: "Do you want to unfollow {name}?"
|
||||
delete-confirm: "Are you sure you want to delete this post?"
|
||||
signin-required: "Please login"
|
||||
notification-type: "Notification Type"
|
||||
notification-types:
|
||||
all: "All"
|
||||
pollVote: "Votes"
|
||||
follow: "Following"
|
||||
receiveFollowRequest: "Follow requests"
|
||||
reply: "Reply"
|
||||
quote: "Quote"
|
||||
renote: "Renote"
|
||||
mention: "Mentions"
|
||||
reaction: "Reaction"
|
||||
got-it: "Got it!"
|
||||
customization-tips:
|
||||
title: "Customization tips"
|
||||
@ -122,6 +136,7 @@ common:
|
||||
add-visible-user: "Add a user"
|
||||
cw-placeholder: "Comments for the post (optional)"
|
||||
username-prompt: "Please enter username"
|
||||
enter-file-name: "Edit file name"
|
||||
weekday-short:
|
||||
sunday: "S"
|
||||
monday: "M"
|
||||
@ -188,6 +203,11 @@ common:
|
||||
remember-note-visibility: "Remember post visibility"
|
||||
web-search-engine: "Web search engine"
|
||||
web-search-engine-desc: "Example: https://www.google.com/?#q={{query}}"
|
||||
paste: "Paste"
|
||||
pasted-file-name: "Template for pasted file name"
|
||||
pasted-file-name-desc: "Example: \"yyyy-MM-dd HH-mm-ss [{{number}}]\" → \"2018-03-20 21-30-24 1\""
|
||||
paste-dialog: "Edit the pasted file name"
|
||||
paste-dialog-desc: "Display a dialog to edit the file name when you paste a file."
|
||||
keep-cw: "Preserve content warning"
|
||||
keep-cw-desc: "When replying to a post, the same content warning is set by default to the reply, as has been set by the original post."
|
||||
i-like-sushi: "I prefer sushi rather than pudding"
|
||||
@ -195,6 +215,7 @@ common:
|
||||
use-avatar-reversi-stones: "Use avatar as a stone in reversi"
|
||||
disable-animated-mfm: "Disable animated texts in a post"
|
||||
disable-showing-animated-images: "Do not play animated images"
|
||||
enable-quick-notification-view: "Enable Quick Notification View"
|
||||
suggest-recent-hashtags: "Show recent popular hashtags on the post form"
|
||||
always-show-nsfw: "Always show NSFW contents"
|
||||
always-mark-nsfw: "Always mark posts with media attachments as NSFW"
|
||||
@ -271,8 +292,20 @@ common:
|
||||
load-raw-images: "Show attached images in original quality"
|
||||
load-remote-media: "Show media from a remote server"
|
||||
sync: "Sync"
|
||||
save: "Save"
|
||||
saved: "Saved"
|
||||
home-profile: "Home profile"
|
||||
deck-profile: "Deck profile"
|
||||
room: "Room"
|
||||
_room:
|
||||
graphicsQuality: "Graphics Quality"
|
||||
_graphicsQuality:
|
||||
ultra: "Ultra"
|
||||
high: "High"
|
||||
medium: "Medium"
|
||||
low: "Low"
|
||||
cheep: "Cheep"
|
||||
useOrthographicCamera: "Use Orthographic Camera"
|
||||
search: "Search"
|
||||
delete: "Delete"
|
||||
loading: "Loading"
|
||||
@ -522,6 +555,8 @@ common/views/components/note-menu.vue:
|
||||
unpin: "Unpin"
|
||||
delete: "Delete"
|
||||
delete-confirm: "Are you sure you want to delete this post?"
|
||||
delete-and-edit: "Delete and Edit"
|
||||
delete-and-edit-confirm: "Are you sure you want to delete this note and edit it? You will lose all reactions, renotes and replies to it."
|
||||
remote: "Show original note"
|
||||
pin-limit-exceeded: "You can't pin any more posts."
|
||||
common/views/components/user-menu.vue:
|
||||
@ -592,6 +627,14 @@ common/views/components/emoji-picker.vue:
|
||||
objects: "Objects"
|
||||
symbols: "Symbols"
|
||||
flags: "Flags"
|
||||
common/views/components/settings/app-type.vue:
|
||||
title: "Mode"
|
||||
intro: "You can specify whether you want to use the desktop, or the mobile layout."
|
||||
choices:
|
||||
auto: "Choose layout automatically"
|
||||
desktop: "Always use the desktop layout"
|
||||
mobile: "Always use the mobile layout"
|
||||
info: "You need to reload the page for the changes to take effect."
|
||||
common/views/components/signin.vue:
|
||||
username: "Username"
|
||||
password: "Password"
|
||||
@ -601,7 +644,7 @@ common/views/components/signin.vue:
|
||||
signin-with-twitter: "Log in with Twitter"
|
||||
signin-with-github: "Sign in with GitHub"
|
||||
signin-with-discord: "Sign in with Discord"
|
||||
login-failed: "Logging in has failed. Make sure you have entered the correct username and password."
|
||||
login-failed: "Unable to log in. The username or password you entered is incorrect."
|
||||
tap-key: "Click on the Security Key to log in"
|
||||
enter-2fa-code: "Enter your verification code"
|
||||
common/views/components/signup.vue:
|
||||
@ -695,7 +738,7 @@ common/views/components/profile-editor.vue:
|
||||
you-can-include-hashtags: "You can also include hashtags in your profile description."
|
||||
language: "Language"
|
||||
birthday: "Birthday"
|
||||
avatar: "Icon"
|
||||
avatar: "Avatar"
|
||||
banner: "Banner"
|
||||
is-cat: "This account is a Cat"
|
||||
is-bot: "This account is a Bot"
|
||||
@ -708,6 +751,9 @@ common/views/components/profile-editor.vue:
|
||||
saved: "Profile updated successfully"
|
||||
uploading: "Uploading"
|
||||
upload-failed: "Failed to upload"
|
||||
unable-to-process: "The operation could not be completed."
|
||||
avatar-not-an-image: "The file specified as an avatar is not an image"
|
||||
banner-not-an-image: "The file specified as a banner is not an image"
|
||||
email: "Email settings"
|
||||
email-address: "Email Address"
|
||||
email-verified: "Your email has been verified."
|
||||
@ -727,6 +773,9 @@ common/views/components/profile-editor.vue:
|
||||
danger-zone: "Cautious options"
|
||||
delete-account: "Remove the account"
|
||||
account-deleted: "The account has been deleted. It may take some time until all of the data disappears."
|
||||
profile-metadata: "Profile metadata"
|
||||
metadata-label: "Label"
|
||||
metadata-content: "Content"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "User"
|
||||
rename: "Rename list"
|
||||
@ -836,6 +885,7 @@ desktop:
|
||||
uploading-avatar: "Uploading a new avatar"
|
||||
avatar-updated: "Successfully updated the avatar"
|
||||
choose-avatar: "Select an image for the avatar"
|
||||
unable-to-process: "The operation could not be completed."
|
||||
invalid-filetype: "This filetype is not acceptable here"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Black ... Total"
|
||||
@ -876,7 +926,7 @@ desktop/views/components/drive.file.vue:
|
||||
unmark-as-sensitive: "Unmark as 'sensitive'"
|
||||
copy-url: "Copy URL"
|
||||
download: "Download"
|
||||
else-files: "Others"
|
||||
else-files: "Other"
|
||||
set-as-avatar: "Set as an avatar"
|
||||
set-as-banner: "Set as a banner"
|
||||
open-in-app: "Open in app"
|
||||
@ -886,15 +936,20 @@ desktop/views/components/drive.file.vue:
|
||||
copied: "Copied"
|
||||
copied-url-to-clipboard: "URL has been copied to clipboard"
|
||||
desktop/views/components/drive.folder.vue:
|
||||
upload-folder: "Default Upload location"
|
||||
unable-to-process: "The operation could not be completed."
|
||||
circular-reference-detected: "The destination folder is a subfolder of the folder you wish to move."
|
||||
unhandled-error: "Unknown error"
|
||||
unable-to-delete: "Unable to delete"
|
||||
has-child-files-or-folders: "Since this folder is not empty, it can not be deleted."
|
||||
contextmenu:
|
||||
move-to-this-folder: "Move to this folder"
|
||||
show-in-new-window: "Open in new window"
|
||||
rename: "Rename"
|
||||
rename-folder: "Rename folder"
|
||||
input-new-folder-name: "Enter new name"
|
||||
else-folders: "Other"
|
||||
set-as-upload-folder: "Set as default upload folder"
|
||||
desktop/views/components/drive.vue:
|
||||
search: "Search"
|
||||
empty-draghover: "Drop it here! Yep, cuz you know I'm cute, right?"
|
||||
@ -987,7 +1042,7 @@ desktop/views/components/settings.2fa.vue:
|
||||
url: "https://www.google.com/landing/2step/"
|
||||
caution: "If you lose access to your registered device, you won't be able to connect to Misskey anymore!"
|
||||
register: "Register a device"
|
||||
already-registered: "Your account is currently registered to an authenticator application"
|
||||
already-registered: "This device is already registered"
|
||||
unregister: "Unregister"
|
||||
unregistered: "Two-factor authentication has been disabled."
|
||||
enter-password: "Enter the password"
|
||||
@ -1009,6 +1064,7 @@ desktop/views/components/settings.2fa.vue:
|
||||
register-security-key: "Complete Key registration"
|
||||
something-went-wrong: "Wow! There was a problem registering the Key:"
|
||||
key-unregistered: "The Key has been deleted"
|
||||
use-password-less-login: "Use Password-less login"
|
||||
common/views/components/media-image.vue:
|
||||
sensitive: "NSFW"
|
||||
click-to-show: "Click to show"
|
||||
@ -1033,6 +1089,9 @@ common/views/components/drive-settings.vue:
|
||||
max: "Max"
|
||||
in-use: "In use"
|
||||
stats: "Statistics"
|
||||
default-upload-folder: "Default upload folder location"
|
||||
default-upload-folder-name: "Folder(s)"
|
||||
change-default-upload-folder: "Change folder"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "Mute / Block"
|
||||
mute: "Mute"
|
||||
@ -1086,6 +1145,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
groups: "Groups"
|
||||
follow-requests: "Follow requests"
|
||||
admin: "Admin"
|
||||
room: "Room"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "Games"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@ -1339,6 +1399,8 @@ admin/views/users.vue:
|
||||
remote-user-updated: "The information regarding the remote user has been updated."
|
||||
delete-all-files: "Delete all files"
|
||||
delete-all-files-confirm: "Are you sure that you want to delete all files?"
|
||||
username: "Username"
|
||||
host: "Host"
|
||||
users:
|
||||
title: "Users"
|
||||
sort:
|
||||
@ -1369,6 +1431,12 @@ admin/views/moderators.vue:
|
||||
added: "Registered a Moderator."
|
||||
remove: "Discharge"
|
||||
removed: "The moderator has been discharged"
|
||||
logs:
|
||||
title: "Logs"
|
||||
moderator: "Moderators"
|
||||
type: "Operations"
|
||||
at: "Timestamp"
|
||||
info: "Information"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
title: "Add emoji"
|
||||
@ -1657,6 +1725,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "No posts were found for '{q}'"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Choose files"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notifications"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Signed in as {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@ -1715,6 +1785,7 @@ deck/deck.user-column.vue:
|
||||
activity: "Activity"
|
||||
timeline: "Timeline"
|
||||
pinned-notes: "Pinned posts"
|
||||
pinned-page: "Pinned page"
|
||||
docs:
|
||||
edit-this-page-on-github: "Found an error, or do you want to contribute to the documentation?"
|
||||
edit-this-page-on-github-link: "Edit this page at GitHub!"
|
||||
@ -1749,6 +1820,8 @@ pages:
|
||||
are-you-sure-delete: "Do you want to delete this page?"
|
||||
page-deleted: "The page has been deleted"
|
||||
edit-this-page: "Edit this page"
|
||||
pin-this-page: "Pin to your profile"
|
||||
unpin-this-page: "Unpin"
|
||||
view-source: "View Source"
|
||||
view-page: "View page"
|
||||
like: "Like"
|
||||
@ -1767,6 +1840,7 @@ pages:
|
||||
url: "Page URL"
|
||||
summary: "Summary of page"
|
||||
align-center: "Center align"
|
||||
hide-title-when-pinned: "Hide page title when pinned to profile"
|
||||
font: "Font"
|
||||
fontSerif: "Serif"
|
||||
fontSansSerif: "Sans Serif"
|
||||
@ -1820,12 +1894,25 @@ pages:
|
||||
inc: "Increase number"
|
||||
_button:
|
||||
text: "Title"
|
||||
colored: "Color"
|
||||
action: "Operation when the button pressed"
|
||||
_action:
|
||||
dialog: "Show a dialog"
|
||||
_dialog:
|
||||
content: "Content"
|
||||
resetRandom: "Reset a random number"
|
||||
pushEvent: "Send an event"
|
||||
_pushEvent:
|
||||
event: "Name of the event"
|
||||
message: "Message to display when pressed"
|
||||
variable: "Variable to send"
|
||||
no-variable: "None"
|
||||
radioButton: "Choices"
|
||||
_radioButton:
|
||||
name: "Variable name"
|
||||
title: "Title"
|
||||
values: "Item of choices that delimited by line breaks"
|
||||
default: "Default value"
|
||||
script:
|
||||
categories:
|
||||
flow: "Control"
|
||||
@ -1998,3 +2085,55 @@ pages:
|
||||
enviromentVariables: "Environment variable"
|
||||
pageVariables: "Page element"
|
||||
argVariables: "Input slot"
|
||||
room:
|
||||
add-furniture: "Place furniture"
|
||||
translate: "Move"
|
||||
rotate: "Rotate"
|
||||
exit: "Deselect"
|
||||
remove: "Remove"
|
||||
save: "Save"
|
||||
chooseImage: "Select an image"
|
||||
room-type: "Room type"
|
||||
carpet-color: "Color of carpet"
|
||||
rooms:
|
||||
default: "Default"
|
||||
washitsu: "Japanese-style"
|
||||
furnitures:
|
||||
milk: "Milk carton"
|
||||
bed: "Bed"
|
||||
low-table: "Low Table"
|
||||
desk: "Desk"
|
||||
chair: "Chair"
|
||||
chair2: "Chair 2"
|
||||
fan: "Fan"
|
||||
pc: "Computer"
|
||||
plant: "Houseplant"
|
||||
plant2: "Houseplant 2"
|
||||
eraser: "Eraser"
|
||||
pencil: "Pencil"
|
||||
pudding: "Pudding"
|
||||
cardboard-box: "Cardboard Box"
|
||||
cardboard-box2: "Cardboard Box 2"
|
||||
cardboard-box3: "Cardboard Box 3"
|
||||
book: "Book"
|
||||
book2: "Book 2"
|
||||
piano: "Piano"
|
||||
facial-tissue: "Facial tissue"
|
||||
server: "Servers"
|
||||
moon: "M"
|
||||
corkboard: "Cork board"
|
||||
mousepad: "Mousepad"
|
||||
monitor: "Monitor"
|
||||
keyboard: "Keyboard"
|
||||
carpet-stripe: "Carpet (stripe)"
|
||||
mat: "Mat"
|
||||
color-box: "Bookshelf"
|
||||
wall-clock: "Wall clock"
|
||||
photoframe: "Picture frame"
|
||||
cube: "Cube"
|
||||
tv: "TV"
|
||||
pinguin: "Penguin"
|
||||
rubik-cube: "Rubik's Cube"
|
||||
poster-h: "Poster (Horizontal)"
|
||||
poster-v: "Poster (Vertical)"
|
||||
bin: "Trash"
|
||||
|
@ -31,6 +31,11 @@ common:
|
||||
signin: "Iniciar sesión"
|
||||
signup: "¡Regístrate!"
|
||||
signout: "Cerrar sesión"
|
||||
delete-confirm: "¿Seguro que quieres borrar la publicación?"
|
||||
notification-types:
|
||||
all: "Todo"
|
||||
reply: "Responder"
|
||||
renote: "Volver a publicar"
|
||||
got-it: "¡Listo!"
|
||||
customization-tips:
|
||||
title: "Consejos de personalización"
|
||||
@ -197,6 +202,8 @@ common:
|
||||
update-available-desc: "Las actualizaciones se aplicarán cuando la página se vuelva a cargar."
|
||||
advanced-settings: "Configuraciones avanzadas"
|
||||
navbar-position-left: "Izquierda"
|
||||
save: "Guardar"
|
||||
saved: "Guardado"
|
||||
search: "Buscar"
|
||||
delete: "eliminar"
|
||||
loading: "cargando"
|
||||
@ -458,6 +465,8 @@ common/views/components/emoji-picker.vue:
|
||||
objects: "Objetos"
|
||||
symbols: "Símbolos"
|
||||
flags: "Países"
|
||||
common/views/components/settings/app-type.vue:
|
||||
info: "Necesitas recargar la página para que los cambios tengan efecto."
|
||||
common/views/components/signin.vue:
|
||||
username: "Usuario"
|
||||
password: "Contraseña"
|
||||
@ -565,6 +574,7 @@ common/views/components/profile-editor.vue:
|
||||
saved: "Perfil actualizado con exito"
|
||||
uploading: "Subiendo"
|
||||
upload-failed: "Error al subir"
|
||||
unable-to-process: "La operación no se puede llevar a cabo"
|
||||
email: "Preferencias de correo"
|
||||
email-address: "Correo electrónico"
|
||||
email-verified: "Tu cuenta de correo ha sido verificada."
|
||||
@ -667,6 +677,7 @@ desktop:
|
||||
uploading-avatar: "Cargando un nuevo avatar"
|
||||
avatar-updated: "Avatar actualizado"
|
||||
choose-avatar: "Escoge una imagen de avatar"
|
||||
unable-to-process: "La operación no se puede llevar a cabo"
|
||||
invalid-filetype: "Este tipo de archivo no es compatible aquí"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Negro ... Total"
|
||||
@ -725,6 +736,7 @@ desktop/views/components/drive.folder.vue:
|
||||
rename: "Renombrar"
|
||||
rename-folder: "Renombrar carpeta"
|
||||
input-new-folder-name: "Escribe el nombre nuevo"
|
||||
else-folders: "Otros"
|
||||
desktop/views/components/drive.vue:
|
||||
search: "Buscar"
|
||||
empty-draghover: "¡Saluda!"
|
||||
@ -959,12 +971,18 @@ admin/views/drive.vue:
|
||||
mark-as-sensitive: "Marcar como 'sensible'"
|
||||
unmark-as-sensitive: "Desmarcar como 'sensible'"
|
||||
admin/views/users.vue:
|
||||
username: "Usuario"
|
||||
host: "Host"
|
||||
users:
|
||||
state:
|
||||
all: "Todo"
|
||||
moderator: "Moderadores"
|
||||
origin:
|
||||
local: "Local"
|
||||
admin/views/moderators.vue:
|
||||
logs:
|
||||
title: "Registros"
|
||||
moderator: "Moderadores"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
add: "Agregar"
|
||||
@ -1076,6 +1094,8 @@ mobile/views/pages/games/reversi.vue:
|
||||
reversi: "Reversi"
|
||||
mobile/views/pages/search.vue:
|
||||
search: "Buscar"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notificaciones"
|
||||
mobile/views/pages/user/home.vue:
|
||||
activity: "Actividad"
|
||||
mobile/views/pages/user/home.photos.vue:
|
||||
@ -1091,6 +1111,7 @@ deck:
|
||||
deck/deck.user-column.vue:
|
||||
activity: "Actividad"
|
||||
pages:
|
||||
pin-this-page: "Fijar en el perfil"
|
||||
like: "Me gusta"
|
||||
blocks:
|
||||
post: "Formulario"
|
||||
@ -1114,3 +1135,8 @@ pages:
|
||||
arg1: "Listas"
|
||||
types:
|
||||
array: "Listas"
|
||||
room:
|
||||
save: "Guardar"
|
||||
furnitures:
|
||||
moon: "Luna"
|
||||
bin: "Papelera"
|
||||
|
@ -34,6 +34,20 @@ common:
|
||||
signup: "S'enregistrer"
|
||||
signout: "Se déconnecter"
|
||||
reload-to-apply-the-setting: "Le rechargement de la page est nécessaire pour appliquer ces paramètres. Désirez-vous la recharger maintenant ?"
|
||||
unfollow-confirm: "Désirez-vous vous désabonner de {name} ?"
|
||||
delete-confirm: "Supprimer cette publication ?"
|
||||
signin-required: "Veuillez vous connecter"
|
||||
notification-type: "Type de notification"
|
||||
notification-types:
|
||||
all: "Tout"
|
||||
pollVote: "Sondage"
|
||||
follow: "Abonnements"
|
||||
receiveFollowRequest: "Demandes d’abonnements"
|
||||
reply: "Répondre"
|
||||
quote: "Cité par"
|
||||
renote: "Republier"
|
||||
mention: "Mentions"
|
||||
reaction: "Réaction"
|
||||
got-it: "J’ai compris !"
|
||||
customization-tips:
|
||||
title: "Conseils de personnalisation"
|
||||
@ -117,6 +131,7 @@ common:
|
||||
add-visible-user: "Ajouter un utilisateur"
|
||||
cw-placeholder: "Commenter le contenu (optionnel)"
|
||||
username-prompt: "Saisir un nom d'utilisateur"
|
||||
enter-file-name: "Éditer le nom du fichier"
|
||||
weekday-short:
|
||||
sunday: "D"
|
||||
monday: "L"
|
||||
@ -183,6 +198,7 @@ common:
|
||||
remember-note-visibility: "Se souvenir du mode de visibilité de la publication"
|
||||
web-search-engine: "Moteur de recherche Web"
|
||||
web-search-engine-desc: "Exemple : https://www.google.com/?#q={{query}}"
|
||||
paste: "Coller"
|
||||
keep-cw: "Maintenir l'avertissement de contenu"
|
||||
keep-cw-desc: "Lorsque vous répondez à un message, le même avertissement de contenu est reprit par défaut dans la réponse, le même que celui qui a été défini dans le message original."
|
||||
i-like-sushi: "Je préfère les sushis plutôt que le pudding"
|
||||
@ -266,6 +282,8 @@ common:
|
||||
load-raw-images: "Afficher les photos jointes dans leur qualité originale"
|
||||
load-remote-media: "Afficher les médias depuis le serveur distant"
|
||||
sync: "Synchroniser"
|
||||
save: "Enregistrer"
|
||||
saved: "enregistré"
|
||||
search: "Recherche"
|
||||
delete: "Supprimer"
|
||||
loading: "Chargement en cours …"
|
||||
@ -387,6 +405,7 @@ common/views/components/games/reversi/reversi.room.vue:
|
||||
black-or-white: "Noirs/Blancs"
|
||||
black-is: "{} Noirs"
|
||||
rules: "Règles"
|
||||
is-llotheo: "Celui ou celle qui a le moins de pièces gagne (Llotheo)"
|
||||
looped-map: "Carte en boucle"
|
||||
can-put-everywhere: "Peut poser partout"
|
||||
settings-of-the-bot: "Configuration du bot"
|
||||
@ -514,6 +533,7 @@ common/views/components/note-menu.vue:
|
||||
unpin: "Désépingler"
|
||||
delete: "Supprimer"
|
||||
delete-confirm: "Supprimer cette publication ?"
|
||||
delete-and-edit: "Effacer et éditer"
|
||||
remote: "Afficher la note originale"
|
||||
common/views/components/user-menu.vue:
|
||||
mention: "Mention"
|
||||
@ -532,8 +552,10 @@ common/views/components/user-menu.vue:
|
||||
report-abuse-reported: "Transmit à l’administrateur. Merci de votre collaboration."
|
||||
silence: "Mettre en sourdine"
|
||||
unsilence: "Enlever la sourdine"
|
||||
silence-confirm: "Êtes-vous surs de vouloir mettre cet·te utilisateur·rice en sourdine ?"
|
||||
suspend: "Suspendre"
|
||||
unsuspend: "Ne plus suspendre"
|
||||
suspend-confirm: "Êtes-vous surs de vouloir suspendre cet·te utilisateur·rice ?"
|
||||
common/views/components/poll.vue:
|
||||
vote-to: "Voter pour '{}'"
|
||||
vote-count: "{} votes"
|
||||
@ -552,6 +574,7 @@ common/views/components/poll-editor.vue:
|
||||
remove: "Supprimer ce choix"
|
||||
add: "+ Ajouter un choix"
|
||||
destroy: "Annuler ce sondage"
|
||||
multiple: "Autoriser le multi-choix"
|
||||
expiration: "Valide jusqu'à"
|
||||
infinite: "Illimité"
|
||||
at: "Choisir une date et une durée"
|
||||
@ -577,6 +600,14 @@ common/views/components/emoji-picker.vue:
|
||||
objects: "Objets"
|
||||
symbols: "Symboles"
|
||||
flags: "Drapeaux"
|
||||
common/views/components/settings/app-type.vue:
|
||||
title: "Mode"
|
||||
intro: "Vous pouvez choisir, si vous voulez utiliser la disposition de bureau ou mobile."
|
||||
choices:
|
||||
auto: "Choisir la disposition automatiquement"
|
||||
desktop: "Toujours utiliser la disposition de bureau"
|
||||
mobile: "Toujours utiliser la disposition mobile"
|
||||
info: "Le rechargement de la page est requis afin d'appliquer les modifications."
|
||||
common/views/components/signin.vue:
|
||||
username: "Nom d'utilisateur·rice"
|
||||
password: "Mot de passe"
|
||||
@ -587,6 +618,8 @@ common/views/components/signin.vue:
|
||||
signin-with-github: "Se connecter avec GitHub"
|
||||
signin-with-discord: "Se connecter avec Discord"
|
||||
login-failed: "Échec d’authentification. Veuillez vérifier que votre nom d’utilisateur et mot de passe sont corrects."
|
||||
tap-key: "Cliquez sur la clé de sécurité pour vous connecter"
|
||||
enter-2fa-code: "Entrez votre code de vérification"
|
||||
common/views/components/signup.vue:
|
||||
invitation-code: "Code d’invitation"
|
||||
invitation-info: "Si vous n’avez pas de code d’invitation, contactez un <a href=\"{}\">administrateur</a>."
|
||||
@ -691,6 +724,9 @@ common/views/components/profile-editor.vue:
|
||||
saved: "Profil mis à jour avec succès"
|
||||
uploading: "En cours d’envoi …"
|
||||
upload-failed: "Échec de l'envoi"
|
||||
unable-to-process: "L'opération n'a pas pu être complétée"
|
||||
avatar-not-an-image: "Le fichier sélectionné pour votre avatar n'est pas une image"
|
||||
banner-not-an-image: "Le fichier sélectionné pour votre bannière n'est pas une image"
|
||||
email: "Paramètres de messagerie"
|
||||
email-address: "Adresse de courrier électronique"
|
||||
email-verified: "L’adresse du courrier électronique a été vérifiée."
|
||||
@ -710,6 +746,9 @@ common/views/components/profile-editor.vue:
|
||||
danger-zone: "Zone de danger"
|
||||
delete-account: "Supprimer le compte"
|
||||
account-deleted: "Le compte a été supprimé. Cela peut prendre un certain temps avant que toutes les données disparaissent."
|
||||
profile-metadata: "Métadonnées du profil"
|
||||
metadata-label: "Étiquette"
|
||||
metadata-content: "Contenu"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "Utilisateur·rice"
|
||||
rename: "Renommer la liste"
|
||||
@ -723,6 +762,7 @@ common/views/components/user-group-editor.vue:
|
||||
rename: "Renommer le groupe"
|
||||
delete: "Supprimer le groupe"
|
||||
transfer: "Transférer de groupe"
|
||||
transfer-are-you-sure: "Êtes vous surs de vouloir ajouter @$2 au groupe $1 ?"
|
||||
transferred: "Groupe transféré"
|
||||
remove-user: "Enlever un utilisateur de ce groupe"
|
||||
delete-are-you-sure: "Désirez-vous vraiment supprimer le groupe $1 ?"
|
||||
@ -817,6 +857,7 @@ desktop:
|
||||
uploading-avatar: "Téléversement du nouvel avatar"
|
||||
avatar-updated: "Mise à jour de l’avatar avec succès"
|
||||
choose-avatar: "Choisir un avatar"
|
||||
unable-to-process: "L'opération n'a pas pu être complétée"
|
||||
invalid-filetype: "Ce format de fichier n’est pas pris en charge"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Noirs ... Total"
|
||||
@ -857,7 +898,7 @@ desktop/views/components/drive.file.vue:
|
||||
unmark-as-sensitive: "Ne pas marquer comme sensible"
|
||||
copy-url: "Copier l’URL"
|
||||
download: "Télécharger"
|
||||
else-files: "Autres..."
|
||||
else-files: "Avancé"
|
||||
set-as-avatar: "Utiliser en tant qu'avatar"
|
||||
set-as-banner: "Utiliser en tant que bannière"
|
||||
open-in-app: "Ouvrir dans l'application"
|
||||
@ -867,15 +908,20 @@ desktop/views/components/drive.file.vue:
|
||||
copied: "Copié"
|
||||
copied-url-to-clipboard: "L'URL a été copiée dans le presse-papier"
|
||||
desktop/views/components/drive.folder.vue:
|
||||
upload-folder: "Emplacement de téléversement par défaut"
|
||||
unable-to-process: "L'opération n'a pas pu être complétée"
|
||||
circular-reference-detected: "Le dossier de destination est un sous-dossier du dossier que vous souhaitez déplacer."
|
||||
unhandled-error: "Erreur inconnue"
|
||||
unable-to-delete: "Ne peut pas être supprimé"
|
||||
has-child-files-or-folders: "Ce dossier n'est pas vide, il ne peut pas être supprimé"
|
||||
contextmenu:
|
||||
move-to-this-folder: "Déplacer dans ce dossier"
|
||||
show-in-new-window: "Ouvrir dans une nouvelle fenêtre"
|
||||
rename: "Renommer"
|
||||
rename-folder: "Renommer le dossier"
|
||||
input-new-folder-name: "Entrer un nouveau nom"
|
||||
else-folders: "Avancé"
|
||||
set-as-upload-folder: "Spécifier en tant que dossier de téléversement par défaut"
|
||||
desktop/views/components/drive.vue:
|
||||
search: "Rechercher"
|
||||
empty-draghover: "Drop Welcome!"
|
||||
@ -981,6 +1027,14 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "Sauvegarde des paramètres avec succès !"
|
||||
failed: "L’opération a échoué. Veuillez vous assurer que le jeton a été saisi correctement."
|
||||
info: "À partir de maintenant, à chaque fois que vous vous connectez entrez votre mot de passe ainsi que le jeton généré sur votre appareil."
|
||||
totp-header: "Application d'authentification"
|
||||
security-key-header: "Clé de sécurité"
|
||||
last-used: "Dernière utilisation :"
|
||||
activate-key: "Cliquez pour activer la clé de sécurité"
|
||||
security-key-name: "Nom de la clé"
|
||||
something-went-wrong: "Oula ! Il y a eu un problème lors de l’enregistrement de la clé."
|
||||
key-unregistered: "La clé a été supprimée"
|
||||
use-password-less-login: "Utiliser une connexion sans mot de passe"
|
||||
common/views/components/media-image.vue:
|
||||
sensitive: "Contenu sensible"
|
||||
click-to-show: "Cliquer pour afficher"
|
||||
@ -1005,6 +1059,9 @@ common/views/components/drive-settings.vue:
|
||||
max: "Maximale"
|
||||
in-use: "utilisé"
|
||||
stats: "Statistiques"
|
||||
default-upload-folder: "Emplacement par défaut du dossier de transfert"
|
||||
default-upload-folder-name: "Dossier·s"
|
||||
change-default-upload-folder: "Changer de dossier"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "Silencés / Bloqués"
|
||||
mute: "Mettre en sourdine"
|
||||
@ -1111,11 +1168,13 @@ admin/views/queue.vue:
|
||||
deliver: "Délivrées"
|
||||
inbox: "Reçues"
|
||||
db: "Base de données"
|
||||
objectStorage: "Stockage d'objets"
|
||||
state: "État"
|
||||
states:
|
||||
active: "en cours"
|
||||
delayed: "Programmé"
|
||||
waiting: "En file d'attente"
|
||||
result-is-truncated: "Le résultat est tronqué"
|
||||
other-queues: "Autres files d’attente"
|
||||
admin/views/logs.vue:
|
||||
logs: "Journaux"
|
||||
@ -1148,12 +1207,14 @@ admin/views/instance.vue:
|
||||
languages-desc: "Vous pouvez en définir plus d’une, séparées par des espaces."
|
||||
tos-url: "URL des conditions d'utilisation"
|
||||
repository-url: "URL du dépôt"
|
||||
feedback-url: "URL pour les commentaires"
|
||||
maintainer-config: "Informations de l’administrateur"
|
||||
maintainer-name: "Nom de l’administrateur"
|
||||
maintainer-email: "Contact administratif"
|
||||
advanced-config: "Autres réglages"
|
||||
note-and-tl: "Notes et fils"
|
||||
drive-config: "Paramètres du lecteur"
|
||||
use-object-storage: "Utiliser le stockage d'objets"
|
||||
object-storage-base-url: "URL"
|
||||
object-storage-prefix: "Préfixe"
|
||||
object-storage-endpoint: "Point de terminaison"
|
||||
@ -1294,6 +1355,9 @@ admin/views/users.vue:
|
||||
update-remote-user: "Mettre à jour les informations de l’utilisateur·rice distant·e"
|
||||
remote-user-updated: "Les informations de l’utilisateur·rice distant·e ont étés mis à jour"
|
||||
delete-all-files: "Supprimer tous les fichiers"
|
||||
delete-all-files-confirm: "Êtes vous surs de vouloir supprimer tous les fichiers ?"
|
||||
username: "Nom d'utilisateur·rice"
|
||||
host: "Hôte"
|
||||
users:
|
||||
title: "Utilisateur·rice·s"
|
||||
sort:
|
||||
@ -1324,6 +1388,12 @@ admin/views/moderators.vue:
|
||||
added: "Ajouté en tant que modérateur"
|
||||
remove: "Révoquer"
|
||||
removed: "Le modérateur a été révoqué"
|
||||
logs:
|
||||
title: "Journaux"
|
||||
moderator: "Modérateurs"
|
||||
type: "Actions"
|
||||
at: "Date de modification"
|
||||
info: "Informations"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
title: "Ajouter un émoji"
|
||||
@ -1600,6 +1670,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "Aucune publication trouvée pour « {q} »."
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Choisissez un fichier"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notifications"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Connecté·e en tant que {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@ -1658,6 +1730,7 @@ deck/deck.user-column.vue:
|
||||
activity: "Activité"
|
||||
timeline: "Fil d’actualité"
|
||||
pinned-notes: "Notes épinglées"
|
||||
pinned-page: "Page épinglée"
|
||||
docs:
|
||||
edit-this-page-on-github: "Vous avez trouvé une erreur ou vous voulez contribuer à la documentation ?"
|
||||
edit-this-page-on-github-link: "Éditez cette page sur GitHub !"
|
||||
@ -1692,6 +1765,8 @@ pages:
|
||||
are-you-sure-delete: "Confirmez-vous la suppression de cette page ?"
|
||||
page-deleted: "La page a bien été supprimée."
|
||||
edit-this-page: "Éditer cette page"
|
||||
pin-this-page: "Épingler sur votre profil"
|
||||
unpin-this-page: "Désépingler"
|
||||
view-source: "Afficher la source"
|
||||
view-page: "Afficher la page"
|
||||
like: "Bien"
|
||||
@ -1705,6 +1780,7 @@ pages:
|
||||
url: "URL de page"
|
||||
summary: "Résumé de page"
|
||||
align-center: "Centrée"
|
||||
hide-title-when-pinned: "Masquer le titre de la page lorsque celle-ci est épinglée au profil"
|
||||
font: "Police de caractères"
|
||||
fontSerif: "Serif"
|
||||
fontSansSerif: "Sans Serif"
|
||||
@ -1757,12 +1833,24 @@ pages:
|
||||
inc: "Augmenter le chiffre"
|
||||
_button:
|
||||
text: "Titre"
|
||||
colored: "Couleur"
|
||||
action: "L'opération lorsque le bouton sera pressé"
|
||||
_action:
|
||||
dialog: "Afficher une fenêtre de dialogue"
|
||||
_dialog:
|
||||
content: "Contenu"
|
||||
resetRandom: "Réinitialiser le nombre aléatoire"
|
||||
pushEvent: "Envoyer un évènement"
|
||||
_pushEvent:
|
||||
event: "Nom de l'évènement"
|
||||
message: "Message à afficher lorsque appuyé"
|
||||
variable: "Variable à envoyer"
|
||||
no-variable: "Aucune"
|
||||
radioButton: "Choix"
|
||||
_radioButton:
|
||||
name: "Nom de la variable"
|
||||
title: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
script:
|
||||
categories:
|
||||
flow: "Contrôle"
|
||||
@ -1911,3 +1999,9 @@ pages:
|
||||
emptySlot: "Slot vide"
|
||||
enviromentVariables: "Variables d'environnement"
|
||||
pageVariables: "Élément de page"
|
||||
room:
|
||||
translate: "Déplacer"
|
||||
save: "Enregistrer"
|
||||
furnitures:
|
||||
moon: "L"
|
||||
bin: "Corbeille"
|
||||
|
@ -37,6 +37,19 @@ common:
|
||||
reload-to-apply-the-setting: "この設定を反映するにはページをリロードする必要があります。今すぐリロードしますか?"
|
||||
fetching-as-ap-object: "連合に照会中"
|
||||
unfollow-confirm: "{name}さんをフォロー解除しますか?"
|
||||
delete-confirm: "この投稿を削除しますか?"
|
||||
signin-required: "ログインしてください"
|
||||
notification-type: "通知の種類"
|
||||
notification-types:
|
||||
all: "すべて"
|
||||
pollVote: "投票"
|
||||
follow: "フォロー"
|
||||
receiveFollowRequest: "フォローリクエスト"
|
||||
reply: "返信"
|
||||
quote: "引用"
|
||||
renote: "Renote"
|
||||
mention: "言及"
|
||||
reaction: "リアクション"
|
||||
|
||||
got-it: "わかった"
|
||||
customization-tips:
|
||||
@ -129,6 +142,7 @@ common:
|
||||
add-visible-user: "ユーザーを追加"
|
||||
cw-placeholder: "内容への注釈 (オプション)"
|
||||
username-prompt: "ユーザー名を入力してください"
|
||||
enter-file-name: "ファイル名を編集"
|
||||
|
||||
weekday-short:
|
||||
sunday: "日"
|
||||
@ -201,6 +215,11 @@ common:
|
||||
remember-note-visibility: "投稿の公開範囲を記憶する"
|
||||
web-search-engine: "ウェブ検索エンジン"
|
||||
web-search-engine-desc: "例: https://www.google.com/?#q={{query}}"
|
||||
paste: "ペースト"
|
||||
pasted-file-name: "ペーストされたファイル名のテンプレート"
|
||||
pasted-file-name-desc: "例: \"yyyy-MM-dd HH-mm-ss [{{number}}]\" → \"2018-03-20 21-30-24 1\""
|
||||
paste-dialog: "ペースト時にファイル名を編集"
|
||||
paste-dialog-desc: "ペースト時にファイル名を編集するダイアログを表示するようにします。"
|
||||
keep-cw: "CW保持"
|
||||
keep-cw-desc: "投稿にリプライする際、リプライ元の投稿にCWが設定されていたとき、デフォルトで同じCWを設定するようにします。"
|
||||
i-like-sushi: "私は(プリンよりむしろ)寿司が好き"
|
||||
@ -208,6 +227,7 @@ common:
|
||||
use-avatar-reversi-stones: "リバーシの石にアバターを使う"
|
||||
disable-animated-mfm: "投稿内の動きのあるテキストを無効にする"
|
||||
disable-showing-animated-images: "アニメーション画像を再生しない"
|
||||
enable-quick-notification-view: "通知のクイックビューを有効にする"
|
||||
suggest-recent-hashtags: "最近のハッシュタグを投稿フォームに表示する"
|
||||
always-show-nsfw: "常に閲覧注意のメディアを表示する"
|
||||
always-mark-nsfw: "常にメディアを閲覧注意として投稿"
|
||||
@ -284,8 +304,20 @@ common:
|
||||
load-raw-images: "添付された画像を高画質で表示する"
|
||||
load-remote-media: "リモートサーバーのメディアを表示する"
|
||||
sync: "同期"
|
||||
save: "保存"
|
||||
saved: "保存しました"
|
||||
home-profile: "ホームのプロファイル"
|
||||
deck-profile: "デッキのプロファイル"
|
||||
room: "ルーム"
|
||||
_room:
|
||||
graphicsQuality: "グラフィックの品質"
|
||||
_graphicsQuality:
|
||||
ultra: "最高"
|
||||
high: "高"
|
||||
medium: "中"
|
||||
low: "低"
|
||||
cheep: "最低"
|
||||
useOrthographicCamera: "平行投影カメラを使用"
|
||||
|
||||
search: "検索"
|
||||
delete: "削除"
|
||||
@ -561,6 +593,8 @@ common/views/components/note-menu.vue:
|
||||
unpin: "ピン留め解除"
|
||||
delete: "削除"
|
||||
delete-confirm: "この投稿を削除しますか?"
|
||||
delete-and-edit: "削除して編集"
|
||||
delete-and-edit-confirm: "この投稿を削除してもう一度編集しますか?この投稿へのリアクション、Renote、返信も全て削除されます。"
|
||||
remote: "投稿元で見る"
|
||||
pin-limit-exceeded: "これ以上ピン留めできません。"
|
||||
|
||||
@ -637,6 +671,15 @@ common/views/components/emoji-picker.vue:
|
||||
symbols: "記号"
|
||||
flags: "旗"
|
||||
|
||||
common/views/components/settings/app-type.vue:
|
||||
title: "モード"
|
||||
intro: "デスクトップ版とモバイル版のどちらを使うかを指定できます。"
|
||||
choices:
|
||||
auto: "自動で選択"
|
||||
desktop: "デスクトップ版に固定"
|
||||
mobile: "モバイル版に固定"
|
||||
info: "変更はページの再度読み込み後に反映されます。"
|
||||
|
||||
common/views/components/signin.vue:
|
||||
username: "ユーザー名"
|
||||
password: "パスワード"
|
||||
@ -765,6 +808,9 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存しました"
|
||||
uploading: "アップロード中"
|
||||
upload-failed: "アップロードに失敗しました"
|
||||
unable-to-process: "操作を完了できません"
|
||||
avatar-not-an-image: "アイコンとして指定したファイルは画像ではありません"
|
||||
banner-not-an-image: "バナーとして指定したファイルは画像ではありません"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "メールアドレスが確認されました"
|
||||
@ -784,6 +830,9 @@ common/views/components/profile-editor.vue:
|
||||
danger-zone: "危険な設定"
|
||||
delete-account: "アカウントを削除"
|
||||
account-deleted: "アカウントが削除されました。データが消えるまで時間がかかる場合があります。"
|
||||
profile-metadata: "プロフィール補足情報"
|
||||
metadata-label: "ラベル"
|
||||
metadata-content: "内容"
|
||||
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "ユーザー"
|
||||
@ -910,6 +959,7 @@ desktop:
|
||||
uploading-avatar: "新しいアバターをアップロードしています"
|
||||
avatar-updated: "アバターを更新しました"
|
||||
choose-avatar: "アバターにする画像を選択"
|
||||
unable-to-process: "操作を完了できません"
|
||||
invalid-filetype: "この形式のファイルはサポートされていません"
|
||||
|
||||
desktop/views/components/activity.chart.vue:
|
||||
@ -958,7 +1008,7 @@ desktop/views/components/drive.file.vue:
|
||||
unmark-as-sensitive: "閲覧注意を解除"
|
||||
copy-url: "URLをコピー"
|
||||
download: "ダウンロード"
|
||||
else-files: "その他..."
|
||||
else-files: "その他"
|
||||
set-as-avatar: "アイコンに設定"
|
||||
set-as-banner: "バナーに設定"
|
||||
open-in-app: "アプリで開く"
|
||||
@ -969,15 +1019,20 @@ desktop/views/components/drive.file.vue:
|
||||
copied-url-to-clipboard: "URLをクリップボードにコピーしました"
|
||||
|
||||
desktop/views/components/drive.folder.vue:
|
||||
upload-folder: "既定アップロード先"
|
||||
unable-to-process: "操作を完了できません"
|
||||
circular-reference-detected: "移動先のフォルダーは、移動するフォルダーのサブフォルダーです。"
|
||||
unhandled-error: "不明なエラー"
|
||||
unable-to-delete: "削除できません"
|
||||
has-child-files-or-folders: "このフォルダは空でないため、削除できません。"
|
||||
contextmenu:
|
||||
move-to-this-folder: "このフォルダへ移動"
|
||||
show-in-new-window: "新しいウィンドウで表示"
|
||||
rename: "名前を変更"
|
||||
rename-folder: "フォルダ名の変更"
|
||||
input-new-folder-name: "新しいフォルダ名を入力してください"
|
||||
else-folders: "その他"
|
||||
set-as-upload-folder: "既定アップロード先に設定"
|
||||
|
||||
desktop/views/components/drive.vue:
|
||||
search: "検索"
|
||||
@ -1112,6 +1167,7 @@ desktop/views/components/settings.2fa.vue:
|
||||
register-security-key: "キーの登録を完了"
|
||||
something-went-wrong: "わー! キーを登録する際に問題が発生しました:"
|
||||
key-unregistered: "キーが削除されました"
|
||||
use-password-less-login: "パスワードなしのログインを使用"
|
||||
|
||||
common/views/components/media-image.vue:
|
||||
sensitive: "閲覧注意"
|
||||
@ -1140,6 +1196,9 @@ common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使用中"
|
||||
stats: "統計"
|
||||
default-upload-folder: "既定のアップロード先フォルダ"
|
||||
default-upload-folder-name: "フォルダ"
|
||||
change-default-upload-folder: "フォルダを変更"
|
||||
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
@ -1201,6 +1260,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
groups: "グループ"
|
||||
follow-requests: "フォロー申請"
|
||||
admin: "管理"
|
||||
room: "ルーム"
|
||||
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "ゲーム"
|
||||
@ -1472,6 +1532,8 @@ admin/views/users.vue:
|
||||
remote-user-updated: "リモートユーザー情報を更新しました"
|
||||
delete-all-files: "すべてのファイルを削除"
|
||||
delete-all-files-confirm: "すべてのファイルを削除しますか?"
|
||||
username: "ユーザー名"
|
||||
host: "ホスト"
|
||||
users:
|
||||
title: "ユーザー"
|
||||
sort:
|
||||
@ -1503,6 +1565,12 @@ admin/views/moderators.vue:
|
||||
added: "モデレーターを登録しました"
|
||||
remove: "解除"
|
||||
removed: "モデレーター登録を解除しました"
|
||||
logs:
|
||||
title: "ログ"
|
||||
moderator: "モデレーター"
|
||||
type: "操作"
|
||||
at: "日時"
|
||||
info: "情報"
|
||||
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
@ -1842,6 +1910,9 @@ mobile/views/pages/search.vue:
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "ファイルを選択"
|
||||
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "通知"
|
||||
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "{}としてサインイン中"
|
||||
|
||||
@ -1906,6 +1977,7 @@ deck/deck.user-column.vue:
|
||||
activity: "アクティビティ"
|
||||
timeline: "タイムライン"
|
||||
pinned-notes: "ピン留めされた投稿"
|
||||
pinned-page: "ピン留めされたページ"
|
||||
|
||||
docs:
|
||||
edit-this-page-on-github: "間違いや改善点を見つけましたか?"
|
||||
@ -1945,6 +2017,8 @@ pages:
|
||||
are-you-sure-delete: "このページを削除しますか?"
|
||||
page-deleted: "ページを削除しました"
|
||||
edit-this-page: "このページを編集"
|
||||
pin-this-page: "ピン留め"
|
||||
unpin-this-page: "ピン留め解除"
|
||||
view-source: "ソースを表示"
|
||||
view-page: "ページを見る"
|
||||
like: "いいね"
|
||||
@ -1963,6 +2037,7 @@ pages:
|
||||
url: "ページURL"
|
||||
summary: "ページの要約"
|
||||
align-center: "中央寄せ"
|
||||
hide-title-when-pinned: "ピン留めされているときにタイトルを非表示"
|
||||
font: "フォント"
|
||||
fontSerif: "セリフ"
|
||||
fontSansSerif: "サンセリフ"
|
||||
@ -2024,12 +2099,26 @@ pages:
|
||||
|
||||
_button:
|
||||
text: "タイトル"
|
||||
colored: "色付き"
|
||||
action: "ボタンを押したときの動作"
|
||||
_action:
|
||||
dialog: "ダイアログを表示する"
|
||||
_dialog:
|
||||
content: "内容"
|
||||
resetRandom: "乱数をリセット"
|
||||
pushEvent: "イベントを送信させる"
|
||||
_pushEvent:
|
||||
event: "イベント名"
|
||||
message: "押したときに表示するメッセージ"
|
||||
variable: "送信する変数"
|
||||
no-variable: "なし"
|
||||
|
||||
radioButton: "選択肢"
|
||||
_radioButton:
|
||||
name: "変数名"
|
||||
title: "タイトル"
|
||||
values: "改行で区切った選択肢"
|
||||
default: "デフォルト値"
|
||||
|
||||
script:
|
||||
categories:
|
||||
@ -2203,3 +2292,58 @@ pages:
|
||||
enviromentVariables: "環境変数"
|
||||
pageVariables: "ページ要素"
|
||||
argVariables: "入力スロット"
|
||||
|
||||
room:
|
||||
add-furniture: "家具を置く"
|
||||
translate: "移動"
|
||||
rotate: "回転"
|
||||
exit: "戻る"
|
||||
remove: "しまう"
|
||||
save: "保存"
|
||||
chooseImage: "画像を選択"
|
||||
room-type: "部屋のタイプ"
|
||||
carpet-color: "床の色"
|
||||
rooms:
|
||||
default: "デフォルト"
|
||||
washitsu: "和室"
|
||||
furnitures:
|
||||
milk: "牛乳パック"
|
||||
bed: "ベッド"
|
||||
low-table: "ローテーブル"
|
||||
desk: "デスク"
|
||||
chair: "チェア"
|
||||
chair2: "チェア2"
|
||||
fan: "換気扇"
|
||||
pc: "パソコン"
|
||||
plant: "観葉植物"
|
||||
plant2: "観葉植物2"
|
||||
eraser: "消しゴム"
|
||||
pencil: "鉛筆"
|
||||
pudding: "プリン"
|
||||
cardboard-box: "段ボール箱"
|
||||
cardboard-box2: "段ボール箱2"
|
||||
cardboard-box3: "段ボール箱3"
|
||||
book: "本"
|
||||
book2: "本2"
|
||||
piano: "ピアノ"
|
||||
facial-tissue: "ティッシュボックス"
|
||||
server: "サーバー"
|
||||
moon: "月"
|
||||
corkboard: "コルクボード"
|
||||
mousepad: "マウスパッド"
|
||||
monitor: "モニター"
|
||||
keyboard: "キーボード"
|
||||
carpet-stripe: "カーペット(縞)"
|
||||
mat: "マット"
|
||||
color-box: "カラーボックス"
|
||||
wall-clock: "壁掛け時計"
|
||||
photoframe: "額縁"
|
||||
cube: "キューブ"
|
||||
tv: "テレビ"
|
||||
pinguin: "ピンギン"
|
||||
rubik-cube: "ルービックキューブ"
|
||||
poster-h: "ポスター(横長)"
|
||||
poster-v: "ポスター(縦長)"
|
||||
sofa: "ソファ"
|
||||
spiral: "螺旋階段"
|
||||
bin: "ゴミ箱"
|
||||
|
@ -27,6 +27,13 @@ common:
|
||||
load-more: "もっとあらへんのか!"
|
||||
enter-password: "パスワードを入れてや"
|
||||
2fa: "二段階認証"
|
||||
delete-confirm: "この投稿を削除してもええか?"
|
||||
notification-types:
|
||||
all: "すべて"
|
||||
follow: "フォロー"
|
||||
reply: "返す"
|
||||
renote: "Renote"
|
||||
reaction: "リアクション"
|
||||
got-it: "ほい"
|
||||
customization-tips:
|
||||
title: "カスタマイズのヒント"
|
||||
@ -123,6 +130,8 @@ common:
|
||||
password: "パスワード"
|
||||
other: "その他"
|
||||
timeline: "タイムライン"
|
||||
save: "保存"
|
||||
saved: "保存したで!"
|
||||
search: "検索"
|
||||
delete: "削除"
|
||||
loading: "読み込み中"
|
||||
@ -363,6 +372,8 @@ common/views/components/emoji-picker.vue:
|
||||
objects: "物"
|
||||
symbols: "記号"
|
||||
flags: "旗"
|
||||
common/views/components/settings/app-type.vue:
|
||||
info: "ページもっぺん読み込んだら反映したるで。"
|
||||
common/views/components/signin.vue:
|
||||
username: "ユーザー名"
|
||||
password: "パスワード"
|
||||
@ -468,6 +479,7 @@ common/views/components/profile-editor.vue:
|
||||
saved: "プロフィールを保存したで"
|
||||
uploading: "アップロードしとります"
|
||||
upload-failed: "これアップロードでけへんわ"
|
||||
unable-to-process: "あかん、無理やわ"
|
||||
email: "メール設定"
|
||||
email-address: "メールアドレス"
|
||||
email-verified: "このメールアドレスOKや!"
|
||||
@ -561,6 +573,7 @@ desktop:
|
||||
uploading-avatar: "新しいアバターをアップロードしとるで"
|
||||
avatar-updated: "アバターを更新したで"
|
||||
choose-avatar: "アバターにする画像選んでや"
|
||||
unable-to-process: "あかん、無理やわ"
|
||||
invalid-filetype: "この形式のファイル無理やねん"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "黒いの ... 全部"
|
||||
@ -601,7 +614,7 @@ desktop/views/components/drive.file.vue:
|
||||
unmark-as-sensitive: "やっぱ見せたるわ"
|
||||
copy-url: "URLをコピー"
|
||||
download: "ダウンロード"
|
||||
else-files: "まだあんで..."
|
||||
else-files: "その他"
|
||||
set-as-avatar: "アイコンにする"
|
||||
set-as-banner: "バナーにする"
|
||||
open-in-app: "アプリで開く"
|
||||
@ -620,6 +633,7 @@ desktop/views/components/drive.folder.vue:
|
||||
rename: "名前を変えるで"
|
||||
rename-folder: "フォルダ名を変えるで"
|
||||
input-new-folder-name: "新しいフォルダ名を入力してや"
|
||||
else-folders: "その他"
|
||||
desktop/views/components/drive.vue:
|
||||
search: "検索"
|
||||
empty-draghover: "ドロップするにゃ!お魚以外なら何でもいいにゃ!"
|
||||
@ -747,6 +761,7 @@ common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "使うとる"
|
||||
stats: "統計"
|
||||
default-upload-folder-name: "フォルダ"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "ミュートとブロック"
|
||||
mute: "ミュート"
|
||||
@ -938,6 +953,8 @@ admin/views/users.vue:
|
||||
reset-password: "パスワードをリセット"
|
||||
password-updated: "パスワードは現在「{password} 」やで"
|
||||
suspend: "凍結"
|
||||
username: "ユーザー名"
|
||||
host: "ホスト"
|
||||
users:
|
||||
title: "ユーザー"
|
||||
state:
|
||||
@ -945,6 +962,11 @@ admin/views/users.vue:
|
||||
moderator: "モデレーター"
|
||||
origin:
|
||||
local: "ローカル"
|
||||
admin/views/moderators.vue:
|
||||
logs:
|
||||
moderator: "モデレーター"
|
||||
type: "操作"
|
||||
info: "情報"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
add: "増やす"
|
||||
@ -1161,6 +1183,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "ワイは「{q}」なんて投稿知らんわ、無いんちゃう?知らんけど。"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "ファイル選んでや"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "通知"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "あんたは橋の下で拾った{}や!"
|
||||
mobile/views/pages/user.vue:
|
||||
@ -1234,6 +1258,8 @@ dev/views/new-app.vue:
|
||||
authority-desc: "ここにチェックした機能しかAPIからアクセスできひんから気ぃつけてな"
|
||||
authority-warning: "アプリ作った後でも変えれるけど、新しいやつ追加したらそん時関連付いてるユーザーキーは全部ほかされるで。"
|
||||
pages:
|
||||
pin-this-page: "ピン留め"
|
||||
unpin-this-page: "ピン留めやめる"
|
||||
like: "ええやん"
|
||||
blocks:
|
||||
image: "画像"
|
||||
@ -1258,3 +1284,9 @@ pages:
|
||||
arg1: "リスト"
|
||||
types:
|
||||
array: "リスト"
|
||||
room:
|
||||
translate: "移動"
|
||||
save: "保存"
|
||||
furnitures:
|
||||
moon: "月"
|
||||
bin: "ゴミ箱"
|
||||
|
@ -36,6 +36,19 @@ common:
|
||||
reload-to-apply-the-setting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
|
||||
fetching-as-ap-object: "연합에서 조회 중"
|
||||
unfollow-confirm: "{name} 님을 팔로우 해제하시겠습니까?"
|
||||
delete-confirm: "이 글을 삭제하시겠습니까?"
|
||||
signin-required: "로그인 해주세요"
|
||||
notification-type: "알림의 종류"
|
||||
notification-types:
|
||||
all: "모두"
|
||||
pollVote: "투표"
|
||||
follow: "팔로잉"
|
||||
receiveFollowRequest: "팔로우 요청"
|
||||
reply: "답글 달기"
|
||||
quote: "인용"
|
||||
renote: "리노트"
|
||||
mention: "멘션"
|
||||
reaction: "리액션"
|
||||
got-it: "알겠습니다"
|
||||
customization-tips:
|
||||
title: "커스터마이징 도움말"
|
||||
@ -88,7 +101,7 @@ common:
|
||||
"read:mutes": "뮤트 보기"
|
||||
"write:mutes": "뮤트 수정"
|
||||
"write:notes": "글 작성, 삭제"
|
||||
"read:notifications": "글 보기"
|
||||
"read:notifications": "알림 보기"
|
||||
"write:notifications": "알림 수정"
|
||||
"read:reactions": "리액션 보기"
|
||||
"write:reactions": "리액션 수정"
|
||||
@ -123,6 +136,7 @@ common:
|
||||
add-visible-user: "사용자 추가"
|
||||
cw-placeholder: "내용에 대한 주석 (옵션)"
|
||||
username-prompt: "사용자명을 입력해주세요"
|
||||
enter-file-name: "파일 이름 수정"
|
||||
weekday-short:
|
||||
sunday: "일"
|
||||
monday: "월"
|
||||
@ -189,6 +203,11 @@ common:
|
||||
remember-note-visibility: "글의 공개 범위를 기억하기"
|
||||
web-search-engine: "웹 검색엔진"
|
||||
web-search-engine-desc: "예: https://www.google.com/?#q={{query}}"
|
||||
paste: "붙여넣기"
|
||||
pasted-file-name: "붙여넣은 파일의 이름 템플릿"
|
||||
pasted-file-name-desc: "예시: \"yyyy-MM-dd HH-mm-ss [{{number}}]\" → \"2018-03-20 21-30-24 1\""
|
||||
paste-dialog: "붙여넣기 시 파일 이름을 편집"
|
||||
paste-dialog-desc: "붙여넣기 시 파일 이름을 편집할 수 있도록 대화 상자를 표시합니다."
|
||||
keep-cw: "CW 유지"
|
||||
keep-cw-desc: "글에 답글을 달 때, 답글할 글에 CW가 설정되어 있는 경우 기본값으로 동일한 CW를 설정하도록 합니다."
|
||||
i-like-sushi: "저는 (푸딩보다 차라리) 초밥이 좋아요"
|
||||
@ -196,6 +215,7 @@ common:
|
||||
use-avatar-reversi-stones: "리버시의 돌로 아바타를 사용"
|
||||
disable-animated-mfm: "글의 문자 애니메이션을 비활성화"
|
||||
disable-showing-animated-images: "움직이는 이미지를 자동으로 재생하지 않음"
|
||||
enable-quick-notification-view: "알림의 빠른 보기를 사용합니다"
|
||||
suggest-recent-hashtags: "최근 해시태그를 글 작성란에 표시"
|
||||
always-show-nsfw: "항상 열람주의 미디어를 표시"
|
||||
always-mark-nsfw: "항상 미디어를 열람주의로 설정하여 게시"
|
||||
@ -272,8 +292,20 @@ common:
|
||||
load-raw-images: "첨부 이미지를 고품질로 표시"
|
||||
load-remote-media: "원격 서버의 미디어를 표시"
|
||||
sync: "동기화"
|
||||
save: "저장"
|
||||
saved: "저장하였습니다"
|
||||
home-profile: "홈 프로필"
|
||||
deck-profile: "덱 프로필"
|
||||
room: "룸"
|
||||
_room:
|
||||
graphicsQuality: "그래픽 품질"
|
||||
_graphicsQuality:
|
||||
ultra: "최고"
|
||||
high: "높음"
|
||||
medium: "보통"
|
||||
low: "낮음"
|
||||
cheep: "최저"
|
||||
useOrthographicCamera: "평행 투시 카메라를 사용"
|
||||
search: "검색"
|
||||
delete: "삭제"
|
||||
loading: "로드 중"
|
||||
@ -290,7 +322,7 @@ common:
|
||||
is-remote-user: "이 사용자 정보는 정확하지 않을 수 있습니다."
|
||||
is-remote-post: "이 글 정보는 복사본입니다."
|
||||
view-on-remote: "정확한 정보 보기"
|
||||
renoted-by: "{user}님이 리노트"
|
||||
renoted-by: "{user} 님이 리노트"
|
||||
no-notes: "글이 없습니다"
|
||||
turn-on-darkmode: "어둠에 삼켜져라"
|
||||
turn-off-darkmode: "빛이 있으라"
|
||||
@ -523,6 +555,8 @@ common/views/components/note-menu.vue:
|
||||
unpin: "프로필에서 고정 해제"
|
||||
delete: "삭제"
|
||||
delete-confirm: "이 글을 삭제하시겠습니까?"
|
||||
delete-and-edit: "삭제 후 편집"
|
||||
delete-and-edit-confirm: "이 글을 삭제한 뒤 다시 편집하시겠습니까? 이 글에 대한 리액션, 리노트, 답글 또한 모두 삭제됩니다."
|
||||
remote: "글 원본 보기"
|
||||
pin-limit-exceeded: "더 이상 고정할 수 없습니다."
|
||||
common/views/components/user-menu.vue:
|
||||
@ -593,6 +627,14 @@ common/views/components/emoji-picker.vue:
|
||||
objects: "사물"
|
||||
symbols: "기호"
|
||||
flags: "깃발"
|
||||
common/views/components/settings/app-type.vue:
|
||||
title: "모드"
|
||||
intro: "데스크톱과 모바일 중 어떤 레이아웃을 사용할 지 지정할 수 있습니다."
|
||||
choices:
|
||||
auto: "자동으로 선택"
|
||||
desktop: "데스크톱 레이아웃으로 고정"
|
||||
mobile: "모바일 레이아웃으로 고정"
|
||||
info: "변경사항은 페이지를 새로고침한 뒤에 반영됩니다."
|
||||
common/views/components/signin.vue:
|
||||
username: "사용자명"
|
||||
password: "비밀번호"
|
||||
@ -709,6 +751,9 @@ common/views/components/profile-editor.vue:
|
||||
saved: "프로필을 저장하였습니다"
|
||||
uploading: "업로드 중"
|
||||
upload-failed: "업로드에 실패하였습니다"
|
||||
unable-to-process: "작업을 완료할 수 없습니다"
|
||||
avatar-not-an-image: "아바타로 지정한 파일이 이미지 형식이 아닙니다"
|
||||
banner-not-an-image: "배너로 지정한 파일이 이미지 형식이 아닙니다"
|
||||
email: "메일 설정"
|
||||
email-address: "메일 주소"
|
||||
email-verified: "매일 주소가 확인되었습니다"
|
||||
@ -728,6 +773,9 @@ common/views/components/profile-editor.vue:
|
||||
danger-zone: "위험한 설정"
|
||||
delete-account: "계정 삭제"
|
||||
account-deleted: "계정이 삭제되었습니다. 데이터가 사라질 때까지 시간이 걸릴 수 있습니다."
|
||||
profile-metadata: "프로필 추가 정보"
|
||||
metadata-label: "라벨"
|
||||
metadata-content: "내용"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "사용자"
|
||||
rename: "리스트 이름 바꾸기"
|
||||
@ -837,6 +885,7 @@ desktop:
|
||||
uploading-avatar: "새로운 아바타를 업로드하고 있습니다"
|
||||
avatar-updated: "아바타가 변경되었습니다"
|
||||
choose-avatar: "아바타 이미지를 선택"
|
||||
unable-to-process: "작업을 완료할 수 없습니다"
|
||||
invalid-filetype: "이 형식의 파일은 지원되지 않습니다"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "검은색 ... 전체"
|
||||
@ -877,8 +926,8 @@ desktop/views/components/drive.file.vue:
|
||||
unmark-as-sensitive: "열람주의 해제"
|
||||
copy-url: "URL 복사"
|
||||
download: "다운로드"
|
||||
else-files: "기타..."
|
||||
set-as-avatar: "아이콘으로 설정"
|
||||
else-files: "기타"
|
||||
set-as-avatar: "아바타로 설정"
|
||||
set-as-banner: "배너로 설정"
|
||||
open-in-app: "앱에서 열기"
|
||||
add-app: "앱 추가"
|
||||
@ -887,15 +936,20 @@ desktop/views/components/drive.file.vue:
|
||||
copied: "복사 완료"
|
||||
copied-url-to-clipboard: "URL을 클립보드에 복사하였습니다"
|
||||
desktop/views/components/drive.folder.vue:
|
||||
upload-folder: "기본 업로드 위치"
|
||||
unable-to-process: "작업을 완료할 수 없습니다"
|
||||
circular-reference-detected: "대상 폴더가 이동할 폴더의 하위 폴더입니다."
|
||||
unhandled-error: "알 수 없는 오류"
|
||||
unable-to-delete: "삭제할 수 없습니다"
|
||||
has-child-files-or-folders: "이 폴더는 비어있지 않기 때문에 삭제할 수 없습니다."
|
||||
contextmenu:
|
||||
move-to-this-folder: "이 폴더로 이동"
|
||||
show-in-new-window: "새 창으로 보기"
|
||||
rename: "이름 변경"
|
||||
rename-folder: "폴더 이름 변경"
|
||||
input-new-folder-name: "새 폴더 이름을 입력하여 주십시오"
|
||||
else-folders: "기타"
|
||||
set-as-upload-folder: "기본 업로드 위치로 설정"
|
||||
desktop/views/components/drive.vue:
|
||||
search: "검색"
|
||||
empty-draghover: "끌어놓으신 거 맞나요? 괜찮아요, 저는 귀여우니까요"
|
||||
@ -1010,6 +1064,7 @@ desktop/views/components/settings.2fa.vue:
|
||||
register-security-key: "키 등록 완료"
|
||||
something-went-wrong: "으악! 키를 등록하는 도중 문제가 발생하였습니다:"
|
||||
key-unregistered: "키가 등록되어 있지 않습니다"
|
||||
use-password-less-login: "비밀번호 없는 로그인 사용"
|
||||
common/views/components/media-image.vue:
|
||||
sensitive: "열람주의"
|
||||
click-to-show: "클릭하여 보기"
|
||||
@ -1034,6 +1089,9 @@ common/views/components/drive-settings.vue:
|
||||
max: "최대 용량"
|
||||
in-use: "사용중"
|
||||
stats: "통계"
|
||||
default-upload-folder: "기본 업로드 폴더 위치"
|
||||
default-upload-folder-name: "폴더"
|
||||
change-default-upload-folder: "폴더 변경"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "뮤트 및 차단"
|
||||
mute: "뮤트"
|
||||
@ -1087,6 +1145,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
groups: "그룹"
|
||||
follow-requests: "팔로우 요청"
|
||||
admin: "관리"
|
||||
room: "룸"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "게임"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@ -1340,6 +1399,8 @@ admin/views/users.vue:
|
||||
remote-user-updated: "원격 사용자 정보를 갱신하였습니다"
|
||||
delete-all-files: "모든 파일 삭제"
|
||||
delete-all-files-confirm: "모든 파일을 삭제하시겠습니까?"
|
||||
username: "사용자명"
|
||||
host: "관리자"
|
||||
users:
|
||||
title: "사용자"
|
||||
sort:
|
||||
@ -1370,6 +1431,12 @@ admin/views/moderators.vue:
|
||||
added: "모더레이터를 등록하였습니다"
|
||||
remove: "해제"
|
||||
removed: "모더레이터 등록을 해제했습니다"
|
||||
logs:
|
||||
title: "로그"
|
||||
moderator: "모더레이터"
|
||||
type: "작업"
|
||||
at: "일시"
|
||||
info: "정보"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
title: "이모지 등록"
|
||||
@ -1658,6 +1725,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "\"{q}\" 와 일치하는 글을 찾을 수 없습니다."
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "파일 선택"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "알림"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "{}(으)로 로그인"
|
||||
mobile/views/pages/user.vue:
|
||||
@ -1716,6 +1785,7 @@ deck/deck.user-column.vue:
|
||||
activity: "활동"
|
||||
timeline: "타임라인"
|
||||
pinned-notes: "고정해놓은 글"
|
||||
pinned-page: "고정해놓은 페이지"
|
||||
docs:
|
||||
edit-this-page-on-github: "틀린 점이나 개선할 점을 찾으셨나요?"
|
||||
edit-this-page-on-github-link: "이 페이지를 GitHub에서 편집"
|
||||
@ -1750,6 +1820,8 @@ pages:
|
||||
are-you-sure-delete: "이 페이지를 삭제하시겠습니까?"
|
||||
page-deleted: "페이지가 삭제되었습니다"
|
||||
edit-this-page: "이 페이지를 편집"
|
||||
pin-this-page: "프로필에 고정"
|
||||
unpin-this-page: "프로필에서 고정 해제"
|
||||
view-source: "소스 보기"
|
||||
view-page: "페이지 보기"
|
||||
like: "좋아요"
|
||||
@ -1768,6 +1840,7 @@ pages:
|
||||
url: "페이지 URL"
|
||||
summary: "페이지 요약"
|
||||
align-center: "가운데 정렬"
|
||||
hide-title-when-pinned: "프로필에 고정해놓은 경우 타이틀을 표시하지 않음"
|
||||
font: "글꼴"
|
||||
fontSerif: "세리프"
|
||||
fontSansSerif: "산 세리프"
|
||||
@ -1821,12 +1894,25 @@ pages:
|
||||
inc: "증가치"
|
||||
_button:
|
||||
text: "제목"
|
||||
colored: "색상"
|
||||
action: "버튼을 눌렀을 때의 동작"
|
||||
_action:
|
||||
dialog: "대화상자를 표시"
|
||||
_dialog:
|
||||
content: "내용"
|
||||
resetRandom: "난수를 초기화"
|
||||
pushEvent: "이벤트 보내기"
|
||||
_pushEvent:
|
||||
event: "이벤트 이름"
|
||||
message: "눌렀을 때 표시할 메시지"
|
||||
variable: "보낼 변수"
|
||||
no-variable: "없음"
|
||||
radioButton: "선택지"
|
||||
_radioButton:
|
||||
name: "변수명"
|
||||
title: "제목"
|
||||
values: "줄바꿈으로 구분된 선택지"
|
||||
default: "기본값"
|
||||
script:
|
||||
categories:
|
||||
flow: "흐름 제어"
|
||||
@ -1999,3 +2085,55 @@ pages:
|
||||
enviromentVariables: "환경 변수"
|
||||
pageVariables: "페이지 요소"
|
||||
argVariables: "입력 슬롯"
|
||||
room:
|
||||
add-furniture: "가구를 배치"
|
||||
translate: "이동"
|
||||
rotate: "회전"
|
||||
exit: "선택 해제"
|
||||
remove: "치우기"
|
||||
save: "저장"
|
||||
chooseImage: "이미지 선택"
|
||||
room-type: "룸 종류"
|
||||
carpet-color: "바닥 색상"
|
||||
rooms:
|
||||
default: "기본"
|
||||
washitsu: "일본식"
|
||||
furnitures:
|
||||
milk: "우유 팩"
|
||||
bed: "침대"
|
||||
low-table: "낮은 테이블"
|
||||
desk: "책상"
|
||||
chair: "의자"
|
||||
chair2: "의자 2"
|
||||
fan: "환기구"
|
||||
pc: "컴퓨터"
|
||||
plant: "관엽식물"
|
||||
plant2: "관엽식물 2"
|
||||
eraser: "지우개"
|
||||
pencil: "연필"
|
||||
pudding: "푸딩"
|
||||
cardboard-box: "골판지 상자"
|
||||
cardboard-box2: "골판지 상자 2"
|
||||
cardboard-box3: "골판지 상자 3"
|
||||
book: "책"
|
||||
book2: "책 2"
|
||||
piano: "피아노"
|
||||
facial-tissue: "휴지 상자"
|
||||
server: "서버"
|
||||
moon: "월"
|
||||
corkboard: "게시판"
|
||||
mousepad: "마우스 패드"
|
||||
monitor: "모니터"
|
||||
keyboard: "키보드"
|
||||
carpet-stripe: "카페트 (줄무늬)"
|
||||
mat: "매트"
|
||||
color-box: "책장"
|
||||
wall-clock: "벽걸이 시계"
|
||||
photoframe: "액자"
|
||||
cube: "큐브"
|
||||
tv: "TV"
|
||||
pinguin: "펭귄"
|
||||
rubik-cube: "루빅스 큐브"
|
||||
poster-h: "포스터 (가로)"
|
||||
poster-v: "포스터 (세로)"
|
||||
bin: "휴지통"
|
||||
|
@ -8,6 +8,11 @@ common:
|
||||
reaction: "Reactie"
|
||||
close: "Sluiten"
|
||||
enter-password: "Voer het wachtwoord in"
|
||||
notification-types:
|
||||
all: "Alle"
|
||||
follow: "Volgend"
|
||||
reply: "Beantwoorden"
|
||||
reaction: "Reactie"
|
||||
time:
|
||||
unknown: "onbekend"
|
||||
future: "toekomstig"
|
||||
@ -199,6 +204,7 @@ common/views/components/profile-editor.vue:
|
||||
name: "Naam"
|
||||
avatar: "Gebruikersafbeelding"
|
||||
banner: "Omslagfoto"
|
||||
unable-to-process: "De operatie kan niet worden voltooid."
|
||||
export-targets:
|
||||
following-list: "Volgend"
|
||||
user-lists: "Lijsten"
|
||||
@ -226,6 +232,7 @@ common/views/pages/follow.vue:
|
||||
follow: "Volgend"
|
||||
desktop:
|
||||
banner: "Omslagfoto"
|
||||
unable-to-process: "De operatie kan niet worden voltooid."
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Zwart ... totaal"
|
||||
notes: "Blauw ... notities"
|
||||
@ -260,7 +267,6 @@ desktop/views/components/drive.file.vue:
|
||||
rename: "Naam wijzigen"
|
||||
copy-url: "URL kopiëren"
|
||||
download: "Downloaden"
|
||||
else-files: "Overig"
|
||||
set-as-avatar: "Instellen als gebruikersafbeelding"
|
||||
set-as-banner: "Instellen als omslagfoto"
|
||||
open-in-app: "Openen in app"
|
||||
@ -377,6 +383,7 @@ common/views/components/api-settings.vue:
|
||||
common/views/components/drive-settings.vue:
|
||||
in-use: "gebruikt"
|
||||
stats: "Statistieken"
|
||||
default-upload-folder-name: "Map(pen)"
|
||||
desktop/views/components/sub-note-content.vue:
|
||||
private: "(dit bericht is privé)"
|
||||
poll: "Peilingen"
|
||||
@ -425,6 +432,7 @@ admin/views/drive.vue:
|
||||
local: "Lokaal"
|
||||
delete: "Verwijderen"
|
||||
admin/views/users.vue:
|
||||
username: "Gebruikersnaam"
|
||||
users:
|
||||
title: "Gebruiker"
|
||||
state:
|
||||
@ -566,6 +574,8 @@ mobile/views/pages/search.vue:
|
||||
search: "Zoeken"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Kies een bestand"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Meldingen"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Ingelogd als {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@ -606,6 +616,7 @@ docs:
|
||||
edit-this-page-on-github: "Heb je een fout ontdekt of wil je bijdragen aan de documentatie? "
|
||||
edit-this-page-on-github-link: "Bewerk deze pagina op GitHub!"
|
||||
pages:
|
||||
pin-this-page: "Vastmaken aan profielpagina"
|
||||
like: "Leuk"
|
||||
blocks:
|
||||
image: "Afbeeldingen"
|
||||
@ -627,3 +638,7 @@ pages:
|
||||
arg1: "Lijsten"
|
||||
types:
|
||||
array: "Lijsten"
|
||||
room:
|
||||
translate: "Verplaatsen"
|
||||
furnitures:
|
||||
moon: "M"
|
||||
|
@ -11,6 +11,10 @@ common:
|
||||
rich-contents: "Innlegg"
|
||||
drive: "Disk"
|
||||
close: "Lukk"
|
||||
notification-types:
|
||||
all: "Alle"
|
||||
follow: "Følger"
|
||||
reply: "Svar"
|
||||
got-it: "Skjønner!"
|
||||
notification:
|
||||
file-uploaded: "Filen ble lastet opp!"
|
||||
@ -78,6 +82,7 @@ common:
|
||||
_settings:
|
||||
notification: "Notifikasjon"
|
||||
password: "Passord"
|
||||
save: "Lagre"
|
||||
search: "Søk"
|
||||
delete: "Slett"
|
||||
loading: "Laster inn..."
|
||||
@ -286,6 +291,7 @@ common/views/components/api-settings.vue:
|
||||
common/views/components/drive-settings.vue:
|
||||
in-use: "brukt"
|
||||
stats: "Statistikk"
|
||||
default-upload-folder-name: "Mappe(r)"
|
||||
common/views/components/mute-and-block.vue:
|
||||
save: "Lagre"
|
||||
desktop/views/components/settings.tags.vue:
|
||||
@ -344,12 +350,16 @@ admin/views/drive.vue:
|
||||
local: "Lokalt"
|
||||
delete: "Slett"
|
||||
admin/views/users.vue:
|
||||
username: "Brukernavn"
|
||||
users:
|
||||
title: "Bruker"
|
||||
state:
|
||||
all: "Alle"
|
||||
origin:
|
||||
local: "Lokalt"
|
||||
admin/views/moderators.vue:
|
||||
logs:
|
||||
info: "Informasjon"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
add: "Legg til"
|
||||
@ -461,6 +471,8 @@ mobile/views/pages/games/reversi.vue:
|
||||
reversi: "Reversi"
|
||||
mobile/views/pages/search.vue:
|
||||
search: "Søk"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Notifikasjon"
|
||||
mobile/views/pages/user.vue:
|
||||
following: "Følger"
|
||||
followers: "Følgere"
|
||||
@ -484,6 +496,7 @@ deck/deck.user-column.vue:
|
||||
followers: "Følgere"
|
||||
images: "Bilder"
|
||||
pages:
|
||||
pin-this-page: "Fest til profilen din"
|
||||
like: "Lik"
|
||||
blocks:
|
||||
image: "Bilder"
|
||||
@ -507,3 +520,9 @@ pages:
|
||||
arg1: "Lister"
|
||||
types:
|
||||
array: "Lister"
|
||||
room:
|
||||
translate: "Flytt"
|
||||
save: "Lagre"
|
||||
furnitures:
|
||||
moon: "Måne"
|
||||
bin: "Papirkurv"
|
||||
|
@ -28,6 +28,18 @@ common:
|
||||
signin: "Zaloguj się"
|
||||
signup: "Rejestracja"
|
||||
signout: "Wyloguj się"
|
||||
delete-confirm: "Czy na pewno chcesz usunąć ten wpis?"
|
||||
notification-type: "Typy powiadomień"
|
||||
notification-types:
|
||||
all: "Wszyscy"
|
||||
pollVote: "Głosy"
|
||||
follow: "Śledzeni"
|
||||
receiveFollowRequest: "Prośby o śledzenie"
|
||||
reply: "Odpowiedzi"
|
||||
quote: "Cytat"
|
||||
renote: "Udostępnij"
|
||||
mention: "Wzmianki"
|
||||
reaction: "Reakcje"
|
||||
got-it: "Rozumiem!"
|
||||
customization-tips:
|
||||
title: "Wskazówki o dostosowywaniu"
|
||||
@ -178,6 +190,8 @@ common:
|
||||
version: "Wersja:"
|
||||
do-update: "Sprawdź dostępność nowych aktualizacji"
|
||||
navbar-position-left: "Z lewej"
|
||||
save: "Zapisz"
|
||||
saved: "Zapisano"
|
||||
search: "Szukaj"
|
||||
delete: "Usuń"
|
||||
loading: "Ładowanie"
|
||||
@ -414,6 +428,8 @@ common/views/components/emoji-picker.vue:
|
||||
objects: "Rzeczy"
|
||||
symbols: "Symbole"
|
||||
flags: "Flagi"
|
||||
common/views/components/settings/app-type.vue:
|
||||
info: "Musisz odświeżyć stronę, aby zmiany zostały uwzględnione."
|
||||
common/views/components/signin.vue:
|
||||
username: "Nazwa użytkownika"
|
||||
password: "Hasło"
|
||||
@ -514,6 +530,7 @@ common/views/components/profile-editor.vue:
|
||||
saved: "Pomyślnie zaktualizowano profil"
|
||||
uploading: "Wysyłanie"
|
||||
upload-failed: "Wysyłanie nie powiodło się"
|
||||
unable-to-process: "Nie udało się ukończyć działania."
|
||||
email: "Ustawienia e-mail"
|
||||
email-address: "Adres e-mail"
|
||||
email-verified: "Twój adres e-mail został zweryfikowany."
|
||||
@ -608,6 +625,7 @@ desktop:
|
||||
uploading-avatar: "Wysyłanie awatara"
|
||||
avatar-updated: "Wysłano awatar"
|
||||
choose-avatar: "Wybierz awatar"
|
||||
unable-to-process: "Nie udało się ukończyć działania."
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "Czarny … Łącznie"
|
||||
notes: "Niebieski … Wpisy"
|
||||
@ -666,6 +684,7 @@ desktop/views/components/drive.folder.vue:
|
||||
rename: "Zmień nazwę"
|
||||
rename-folder: "Zmień nazwę katalogu"
|
||||
input-new-folder-name: "Wprowadź nową nazwę"
|
||||
else-folders: "Inne"
|
||||
desktop/views/components/drive.vue:
|
||||
search: "Szukaj"
|
||||
empty-draghover: "Przeciągnij tutaj!"
|
||||
@ -785,6 +804,7 @@ common/views/components/drive-settings.vue:
|
||||
max: "Max"
|
||||
in-use: "użyto"
|
||||
stats: "Statystyki"
|
||||
default-upload-folder-name: "Katalog(i)"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "Wycisz / Zablokuj"
|
||||
mute: "Wycisz"
|
||||
@ -904,6 +924,7 @@ admin/views/drive.vue:
|
||||
unmark-as-sensitive: "Cofnij oznaczenie jako zawartość wrażliwą"
|
||||
admin/views/users.vue:
|
||||
user-not-found: "Nie znaleziono użytkownika"
|
||||
username: "Nazwa użytkownika"
|
||||
users:
|
||||
title: "Użytkownicy"
|
||||
sort:
|
||||
@ -919,6 +940,9 @@ admin/views/users.vue:
|
||||
admin/views/moderators.vue:
|
||||
add-moderator:
|
||||
add: "Zarejestruj się"
|
||||
logs:
|
||||
moderator: "Moderatorzy"
|
||||
info: "Informacje"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
name: "Nazwa Emoji"
|
||||
@ -1125,6 +1149,8 @@ mobile/views/pages/search.vue:
|
||||
search: "Szukaj"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "Wybierz plik"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "Powiadomienia"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "Zalogowany jako {}"
|
||||
mobile/views/pages/user.vue:
|
||||
@ -1189,6 +1215,8 @@ dev/views/new-app.vue:
|
||||
app-name: "Nazwa Aplikacji"
|
||||
authority: "Uprawnienia"
|
||||
pages:
|
||||
pin-this-page: "Przypnij do profilu"
|
||||
unpin-this-page: "Odepnij"
|
||||
like: "Lubię"
|
||||
title: "Tytuł"
|
||||
blocks:
|
||||
@ -1206,6 +1234,8 @@ pages:
|
||||
text: "Tytuł"
|
||||
_button:
|
||||
text: "Tytuł"
|
||||
_radioButton:
|
||||
title: "Tytuł"
|
||||
script:
|
||||
categories:
|
||||
random: "Losowy"
|
||||
@ -1226,3 +1256,9 @@ pages:
|
||||
arg1: "Listy"
|
||||
types:
|
||||
array: "Listy"
|
||||
room:
|
||||
translate: "Przenieś"
|
||||
save: "Zapisz"
|
||||
furnitures:
|
||||
moon: "Pn"
|
||||
bin: "Kosz"
|
||||
|
@ -18,6 +18,8 @@ common:
|
||||
application-authorization: "Aplicativos autorizados"
|
||||
close: "Fechar"
|
||||
do-not-copy-paste: "Por favor, não digite ou copie o código aqui. A conta pode ser comprometida."
|
||||
notification-types:
|
||||
follow: "Seguindo"
|
||||
got-it: "Entendi!"
|
||||
customization-tips:
|
||||
title: "Dicas de personalização"
|
||||
@ -282,3 +284,7 @@ pages:
|
||||
blocks:
|
||||
image: "Imagens"
|
||||
post: "Formulário de publicação"
|
||||
room:
|
||||
furnitures:
|
||||
moon: "Lua"
|
||||
bin: "Lixo"
|
||||
|
@ -28,6 +28,7 @@ common:
|
||||
enter-password: "Пожалуйста, введите ваш пароль"
|
||||
2fa: "Двухфакторная аутентификация"
|
||||
customize-home: "Настройка домашней страницы"
|
||||
featured-notes: "Рекомендуемые"
|
||||
dark-mode: "Тёмная тема"
|
||||
signin: "Войти"
|
||||
signup: "Регистрация"
|
||||
@ -167,3 +168,7 @@ pages:
|
||||
random: "Случайно"
|
||||
blocks:
|
||||
random: "Случайно"
|
||||
room:
|
||||
furnitures:
|
||||
moon: "Луна"
|
||||
bin: "Мусорное ведро"
|
||||
|
@ -35,6 +35,20 @@ common:
|
||||
signout: "退出"
|
||||
reload-to-apply-the-setting: "必须重新加载页面以应用此设置。 确实要立即重新加载吗?"
|
||||
fetching-as-ap-object: "联合查询"
|
||||
unfollow-confirm: "取消对{name}的关注?"
|
||||
delete-confirm: "确定删除这个投稿吗?"
|
||||
signin-required: "请先登录"
|
||||
notification-type: "通知类型"
|
||||
notification-types:
|
||||
all: "所有"
|
||||
pollVote: "投票"
|
||||
follow: "关注中"
|
||||
receiveFollowRequest: "关注请求"
|
||||
reply: "回复"
|
||||
quote: "引用"
|
||||
renote: "转推"
|
||||
mention: "提及"
|
||||
reaction: "回应"
|
||||
got-it: "知道了"
|
||||
customization-tips:
|
||||
title: "自定义提示"
|
||||
@ -122,6 +136,7 @@ common:
|
||||
add-visible-user: "添加用户"
|
||||
cw-placeholder: "评论帖子(可选)"
|
||||
username-prompt: "输入用户名"
|
||||
enter-file-name: "编辑文件名"
|
||||
weekday-short:
|
||||
sunday: "日"
|
||||
monday: "一"
|
||||
@ -188,6 +203,11 @@ common:
|
||||
remember-note-visibility: "记住帖子可见性"
|
||||
web-search-engine: "搜索引擎"
|
||||
web-search-engine-desc: "例如: https://www.google.com/?#q={{query}}"
|
||||
paste: "粘贴"
|
||||
pasted-file-name: "粘贴的文件名模板"
|
||||
pasted-file-name-desc: "例: \"yyyy-MM-dd HH-mm-ss [{{number}}]\" → \"2018-03-20 21-30-24 1\""
|
||||
paste-dialog: "粘贴时编辑文件名"
|
||||
paste-dialog-desc: "粘贴时显示编辑文件名的对话框"
|
||||
keep-cw: "保留内容警告"
|
||||
keep-cw-desc: "在回复帖子时,如果原帖设置了内容警告,默认情况下回帖也会设置相同的内容警告。"
|
||||
i-like-sushi: "相比于布丁来说, 我更喜欢寿司。"
|
||||
@ -195,6 +215,7 @@ common:
|
||||
use-avatar-reversi-stones: "用头像作为黑白棋的棋子"
|
||||
disable-animated-mfm: "在帖子中禁用动画文本"
|
||||
disable-showing-animated-images: "不播放动画"
|
||||
enable-quick-notification-view: "启用通知快速查看"
|
||||
suggest-recent-hashtags: "在帖子表单上显示最近流行的哈希标签"
|
||||
always-show-nsfw: "总是显示 NSFW 的内容"
|
||||
always-mark-nsfw: "总是用 NSFW 来标记附件"
|
||||
@ -271,8 +292,20 @@ common:
|
||||
load-raw-images: "以原始质量显示附加图像"
|
||||
load-remote-media: "显示来自远程服务器的媒体"
|
||||
sync: "同步"
|
||||
save: "保存"
|
||||
saved: "已保存"
|
||||
home-profile: "定制首页数据"
|
||||
deck-profile: "定制Deck数据"
|
||||
room: "房间"
|
||||
_room:
|
||||
graphicsQuality: "图形质量"
|
||||
_graphicsQuality:
|
||||
ultra: "最高"
|
||||
high: "高"
|
||||
medium: "中"
|
||||
low: "低"
|
||||
cheep: "最低"
|
||||
useOrthographicCamera: "使用正交相机"
|
||||
search: "搜索"
|
||||
delete: "删除"
|
||||
loading: "正在加载中"
|
||||
@ -522,7 +555,10 @@ common/views/components/note-menu.vue:
|
||||
unpin: "取消置顶"
|
||||
delete: "删除"
|
||||
delete-confirm: "确定删除这个投稿吗?"
|
||||
delete-and-edit: "删除和编辑"
|
||||
delete-and-edit-confirm: "要删除此帖并再次编辑吗?对此帖的所有回应,转推和回复也将被删除。"
|
||||
remote: "显示原始投稿"
|
||||
pin-limit-exceeded: "无法置顶更多了。"
|
||||
common/views/components/user-menu.vue:
|
||||
mention: "提到"
|
||||
mute: "屏蔽"
|
||||
@ -591,6 +627,14 @@ common/views/components/emoji-picker.vue:
|
||||
objects: "物品"
|
||||
symbols: "符号"
|
||||
flags: "旗帜"
|
||||
common/views/components/settings/app-type.vue:
|
||||
title: "模式"
|
||||
intro: "您可以指定使用桌面版或移动版。"
|
||||
choices:
|
||||
auto: "自动选择"
|
||||
desktop: "固定为桌面版"
|
||||
mobile: "固定为移动版"
|
||||
info: "更改将在刷新页面后生效。"
|
||||
common/views/components/signin.vue:
|
||||
username: "用户名"
|
||||
password: "密码"
|
||||
@ -601,6 +645,8 @@ common/views/components/signin.vue:
|
||||
signin-with-github: "用 GitHub 登录"
|
||||
signin-with-discord: "用 Discord 登录"
|
||||
login-failed: "登录失败。请检查用户名和密码。"
|
||||
tap-key: "点击安全密钥登录"
|
||||
enter-2fa-code: "输入验证码"
|
||||
common/views/components/signup.vue:
|
||||
invitation-code: "邀请码"
|
||||
invitation-info: "如果您没有邀请码,请联系<a href=\"{}\">管理员</a>。"
|
||||
@ -705,6 +751,9 @@ common/views/components/profile-editor.vue:
|
||||
saved: "您的个人资料已保存"
|
||||
uploading: "正在上传"
|
||||
upload-failed: "上传失败"
|
||||
unable-to-process: "无法完成操作"
|
||||
avatar-not-an-image: "选择的头像文件不是图片格式"
|
||||
banner-not-an-image: "选择的横幅背景不是图片格式"
|
||||
email: "邮件设置"
|
||||
email-address: "电子邮件地址"
|
||||
email-verified: "电子邮件地址已验证"
|
||||
@ -724,6 +773,9 @@ common/views/components/profile-editor.vue:
|
||||
danger-zone: "危险选项"
|
||||
delete-account: "删除帐户"
|
||||
account-deleted: "帐户已被删除。 数据会在一段时间之后清除。"
|
||||
profile-metadata: "个人资料补充信息"
|
||||
metadata-label: "标签"
|
||||
metadata-content: "内容"
|
||||
common/views/components/user-list-editor.vue:
|
||||
users: "用户"
|
||||
rename: "重命名列表"
|
||||
@ -833,6 +885,7 @@ desktop:
|
||||
uploading-avatar: "上传一个新的头像"
|
||||
avatar-updated: "成功上传头像"
|
||||
choose-avatar: "选择作为头像的图片"
|
||||
unable-to-process: "无法完成操作"
|
||||
invalid-filetype: "不接受此文件类型"
|
||||
desktop/views/components/activity.chart.vue:
|
||||
total: "黑 ... 总计"
|
||||
@ -873,7 +926,7 @@ desktop/views/components/drive.file.vue:
|
||||
unmark-as-sensitive: "取消标记为“敏感”"
|
||||
copy-url: "复制链接"
|
||||
download: "下载"
|
||||
else-files: "其他..."
|
||||
else-files: "其他"
|
||||
set-as-avatar: "设置为头像"
|
||||
set-as-banner: "设置为背景"
|
||||
open-in-app: "在应用程序中打开"
|
||||
@ -883,15 +936,20 @@ desktop/views/components/drive.file.vue:
|
||||
copied: "已复制"
|
||||
copied-url-to-clipboard: "已复制链接到剪贴板"
|
||||
desktop/views/components/drive.folder.vue:
|
||||
upload-folder: "默认上传文件夹"
|
||||
unable-to-process: "无法完成操作"
|
||||
circular-reference-detected: "目标文件夹是您要移动的文件夹的子文件夹。"
|
||||
unhandled-error: "未知错误"
|
||||
unable-to-delete: "无法删除"
|
||||
has-child-files-or-folders: "此文件夹不为空,无法删除。"
|
||||
contextmenu:
|
||||
move-to-this-folder: "移动到此文件夹"
|
||||
show-in-new-window: "在新窗口打开"
|
||||
rename: "重命名"
|
||||
rename-folder: "重命名文件夹"
|
||||
input-new-folder-name: "请输入新文件名"
|
||||
else-folders: "其他"
|
||||
set-as-upload-folder: "设置为默认上传文件夹"
|
||||
desktop/views/components/drive.vue:
|
||||
search: "搜索"
|
||||
empty-draghover: "放在这里!因为你知道我很可爱,对吗?"
|
||||
@ -997,6 +1055,16 @@ desktop/views/components/settings.2fa.vue:
|
||||
success: "设置完成"
|
||||
failed: "设置失败, 请确保您的密钥是正确的。"
|
||||
info: "从下次登录Misskey时,您的设备上显示的令牌以及密码也是必需的。"
|
||||
totp-header: "身份验证 App"
|
||||
security-key-header: "安全密钥"
|
||||
security-key: "为了增强安全性,您可以使用支持FIDO2的硬件安全密钥登录您的帐户。 登录时,您将需要注册安全密钥或身份验证应用。"
|
||||
last-used: "最后使用:"
|
||||
activate-key: "单击以激活您的安全密钥"
|
||||
security-key-name: "密钥名称"
|
||||
register-security-key: "安全密钥注册完成"
|
||||
something-went-wrong: "糟糕!安全密钥注册出现问题:"
|
||||
key-unregistered: "安全密钥已被删除。"
|
||||
use-password-less-login: "使用免密码登录"
|
||||
common/views/components/media-image.vue:
|
||||
sensitive: "阅读注意"
|
||||
click-to-show: "点击查看"
|
||||
@ -1021,6 +1089,9 @@ common/views/components/drive-settings.vue:
|
||||
max: "容量"
|
||||
in-use: "已使用"
|
||||
stats: "统计"
|
||||
default-upload-folder: "默认上传文件夹"
|
||||
default-upload-folder-name: "文件夹"
|
||||
change-default-upload-folder: "更改文件夹"
|
||||
common/views/components/mute-and-block.vue:
|
||||
mute-and-block: "屏蔽/拉黑"
|
||||
mute: "屏蔽"
|
||||
@ -1074,6 +1145,7 @@ desktop/views/components/ui.header.account.vue:
|
||||
groups: "群组"
|
||||
follow-requests: "关注申请"
|
||||
admin: "管理"
|
||||
room: "房间"
|
||||
desktop/views/components/ui.header.nav.vue:
|
||||
game: "游戏"
|
||||
desktop/views/components/ui.header.notifications.vue:
|
||||
@ -1110,6 +1182,9 @@ admin/views/index.vue:
|
||||
back-to-misskey: "返回 Misskey"
|
||||
admin/views/db.vue:
|
||||
tables: "表格"
|
||||
vacuum: "VACUUM"
|
||||
vacuum-info: "清理数据库。 保持数据完整并减少磁盘使用量。 此操作通常会自动定期执行。"
|
||||
vacuum-exclamation: "运行VACUUM之后,数据库上的负载可能会持续一段时间,并且可能不响应用户操作。"
|
||||
admin/views/dashboard.vue:
|
||||
dashboard: "Dashboard"
|
||||
accounts: "账户"
|
||||
@ -1324,6 +1399,8 @@ admin/views/users.vue:
|
||||
remote-user-updated: "远程用户信息已更新"
|
||||
delete-all-files: "删除所有文件"
|
||||
delete-all-files-confirm: "删除所有文件吗?"
|
||||
username: "用户名"
|
||||
host: "主机名"
|
||||
users:
|
||||
title: "用户"
|
||||
sort:
|
||||
@ -1354,6 +1431,12 @@ admin/views/moderators.vue:
|
||||
added: "已注册版主。"
|
||||
remove: "取消"
|
||||
removed: "取消注册版主"
|
||||
logs:
|
||||
title: "登录"
|
||||
moderator: "版主"
|
||||
type: "操作"
|
||||
at: "日期和时间"
|
||||
info: "信息"
|
||||
admin/views/emoji.vue:
|
||||
add-emoji:
|
||||
title: "添加emoji"
|
||||
@ -1642,6 +1725,8 @@ mobile/views/pages/search.vue:
|
||||
not-found: "没有找到有关于“{q}”的帖子"
|
||||
mobile/views/pages/selectdrive.vue:
|
||||
select-file: "选择文件"
|
||||
mobile/views/pages/notifications.vue:
|
||||
notifications: "通知"
|
||||
mobile/views/pages/settings.vue:
|
||||
signed-in-as: "以{}登录"
|
||||
mobile/views/pages/user.vue:
|
||||
@ -1700,6 +1785,7 @@ deck/deck.user-column.vue:
|
||||
activity: "活动"
|
||||
timeline: "时间线"
|
||||
pinned-notes: "置顶帖"
|
||||
pinned-page: "已置顶的页面"
|
||||
docs:
|
||||
edit-this-page-on-github: "发现错误或想要为文档做出贡献?"
|
||||
edit-this-page-on-github-link: "在GitHub上编辑这个页面。"
|
||||
@ -1734,6 +1820,8 @@ pages:
|
||||
are-you-sure-delete: "是否删除此页面?"
|
||||
page-deleted: "该页面已被删除。"
|
||||
edit-this-page: "编辑此页面"
|
||||
pin-this-page: "置顶"
|
||||
unpin-this-page: "取消置顶"
|
||||
view-source: "查看源代码"
|
||||
view-page: "查看页面"
|
||||
like: "赞"
|
||||
@ -1752,6 +1840,7 @@ pages:
|
||||
url: "页面URL"
|
||||
summary: "页面摘要"
|
||||
align-center: "居中"
|
||||
hide-title-when-pinned: "置顶时隐藏标题"
|
||||
font: "字体"
|
||||
fontSerif: "衬线字体"
|
||||
fontSansSerif: "无衬线字体"
|
||||
@ -1805,12 +1894,25 @@ pages:
|
||||
inc: "增加值"
|
||||
_button:
|
||||
text: "标题"
|
||||
colored: "彩色"
|
||||
action: "按下按钮时的行为"
|
||||
_action:
|
||||
dialog: "显示对话框"
|
||||
_dialog:
|
||||
content: "内容"
|
||||
resetRandom: "随机值重置"
|
||||
pushEvent: "发送事件"
|
||||
_pushEvent:
|
||||
event: "事件名称"
|
||||
message: "按下时显示的消息"
|
||||
variable: "发送的变量"
|
||||
no-variable: "空"
|
||||
radioButton: "选择项"
|
||||
_radioButton:
|
||||
name: "变量名"
|
||||
title: "标题"
|
||||
values: "使用换行区分的选择项"
|
||||
default: "默认值"
|
||||
script:
|
||||
categories:
|
||||
flow: "控制"
|
||||
@ -1983,3 +2085,55 @@ pages:
|
||||
enviromentVariables: "环境变量"
|
||||
pageVariables: "页面元素"
|
||||
argVariables: "输入槽函数"
|
||||
room:
|
||||
add-furniture: "放置家具"
|
||||
translate: "移动"
|
||||
rotate: "旋转"
|
||||
exit: "返回"
|
||||
remove: "移除"
|
||||
save: "保存"
|
||||
chooseImage: "选择图片"
|
||||
room-type: "房间类型"
|
||||
carpet-color: "地板颜色"
|
||||
rooms:
|
||||
default: "默认"
|
||||
washitsu: "和式房间"
|
||||
furnitures:
|
||||
milk: "牛奶纸箱"
|
||||
bed: "床"
|
||||
low-table: "矮桌"
|
||||
desk: "书桌"
|
||||
chair: "椅子"
|
||||
chair2: "椅子2"
|
||||
fan: "换气扇"
|
||||
pc: "电脑"
|
||||
plant: "观叶植物"
|
||||
plant2: "观叶植物2"
|
||||
eraser: "橡皮擦"
|
||||
pencil: "铅笔"
|
||||
pudding: "布丁"
|
||||
cardboard-box: "纸板箱"
|
||||
cardboard-box2: "纸板箱2"
|
||||
cardboard-box3: "纸板箱3"
|
||||
book: "书"
|
||||
book2: "书2"
|
||||
piano: "钢琴"
|
||||
facial-tissue: "纸巾盒"
|
||||
server: "服务器"
|
||||
moon: "一"
|
||||
corkboard: "软木板"
|
||||
mousepad: "鼠标垫"
|
||||
monitor: "显示器"
|
||||
keyboard: "键盘"
|
||||
carpet-stripe: "地毯(条纹)"
|
||||
mat: "垫子"
|
||||
color-box: "彩盒"
|
||||
wall-clock: "挂钟"
|
||||
photoframe: "相框"
|
||||
cube: "立方体"
|
||||
tv: "电视"
|
||||
pinguin: "企鹅君"
|
||||
rubik-cube: "魔方"
|
||||
poster-h: "海报(横向)"
|
||||
poster-v: "海报(纵向)"
|
||||
bin: "垃圾箱"
|
||||
|
@ -88,3 +88,6 @@ admin/views/charts.vue:
|
||||
drive: "雲端硬碟"
|
||||
pages:
|
||||
like: "贊"
|
||||
room:
|
||||
furnitures:
|
||||
moon: "月"
|
||||
|
13
migration/1562422242907-PasswordLessLogin.ts
Normal file
13
migration/1562422242907-PasswordLessLogin.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class PasswordLessLogin1562422242907 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "usePasswordLessLogin" boolean DEFAULT false NOT NULL`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "usePasswordLessLogin"`);
|
||||
}
|
||||
|
||||
}
|
17
migration/1562444565093-PinnedPage.ts
Normal file
17
migration/1562444565093-PinnedPage.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class PinnedPage1562444565093 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD "pinnedPageId" character varying(32)`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "UQ_6dc44f1ceb65b1e72bacef2ca27" UNIQUE ("pinnedPageId")`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD CONSTRAINT "FK_6dc44f1ceb65b1e72bacef2ca27" FOREIGN KEY ("pinnedPageId") REFERENCES "page"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "FK_6dc44f1ceb65b1e72bacef2ca27"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP CONSTRAINT "UQ_6dc44f1ceb65b1e72bacef2ca27"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "pinnedPageId"`);
|
||||
}
|
||||
|
||||
}
|
13
migration/1562448332510-PageTitleHideOption.ts
Normal file
13
migration/1562448332510-PageTitleHideOption.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class PageTitleHideOption1562448332510 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "page" ADD "hideTitleWhenPinned" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "page" DROP COLUMN "hideTitleWhenPinned"`);
|
||||
}
|
||||
|
||||
}
|
17
migration/1562869971568-ModerationLog.ts
Normal file
17
migration/1562869971568-ModerationLog.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class ModerationLog1562869971568 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`CREATE TABLE "moderation_log" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userId" character varying(32) NOT NULL, "type" character varying(128) NOT NULL, "info" jsonb NOT NULL, CONSTRAINT "PK_d0adca6ecfd068db83e4526cc26" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_a08ad074601d204e0f69da9a95" ON "moderation_log" ("userId") `);
|
||||
await queryRunner.query(`ALTER TABLE "moderation_log" ADD CONSTRAINT "FK_a08ad074601d204e0f69da9a954" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "moderation_log" DROP CONSTRAINT "FK_a08ad074601d204e0f69da9a954"`);
|
||||
await queryRunner.query(`DROP INDEX "IDX_a08ad074601d204e0f69da9a95"`);
|
||||
await queryRunner.query(`DROP TABLE "moderation_log"`);
|
||||
}
|
||||
|
||||
}
|
13
migration/1563757595828-UsedUsername.ts
Normal file
13
migration/1563757595828-UsedUsername.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class UsedUsername1563757595828 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`CREATE TABLE "used_username" ("username" character varying(128) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, CONSTRAINT "PK_78fd79d2d24c6ac2f4cc9a31a5d" PRIMARY KEY ("username"))`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`DROP TABLE "used_username"`);
|
||||
}
|
||||
|
||||
}
|
13
migration/1565634203341-room.ts
Normal file
13
migration/1565634203341-room.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class room1565634203341 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD "room" jsonb NOT NULL DEFAULT '{}'`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "room"`);
|
||||
}
|
||||
|
||||
}
|
86
package.json
86
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "11.24.0",
|
||||
"version": "11.29.0",
|
||||
"codename": "daybreak",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -27,17 +27,16 @@
|
||||
},
|
||||
"resolutions": {
|
||||
"gulp-cssnano/cssnano/postcss-svgo/svgo/js-yaml": "^3.13.1",
|
||||
"video-thumbnail-generator/lodash": "^4.17.11"
|
||||
"lodash": "^4.17.13"
|
||||
},
|
||||
"dependencies": {
|
||||
"@elastic/elasticsearch": "7.1.0",
|
||||
"@elastic/elasticsearch": "7.3.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.19",
|
||||
"@fortawesome/free-brands-svg-icons": "5.9.0",
|
||||
"@fortawesome/free-regular-svg-icons": "5.9.0",
|
||||
"@fortawesome/free-solid-svg-icons": "5.9.0",
|
||||
"@fortawesome/vue-fontawesome": "0.1.6",
|
||||
"@koa/cors": "3.0.0",
|
||||
"@typescript-eslint/parser": "1.11.0",
|
||||
"@types/bcryptjs": "2.4.2",
|
||||
"@types/bull": "3.5.15",
|
||||
"@types/cbor": "2.0.0",
|
||||
@ -50,7 +49,6 @@
|
||||
"@types/gulp-replace": "0.0.31",
|
||||
"@types/gulp-uglify": "3.0.6",
|
||||
"@types/gulp-util": "3.0.34",
|
||||
"@types/is-root": "2.1.2",
|
||||
"@types/is-url": "1.2.28",
|
||||
"@types/js-yaml": "3.12.1",
|
||||
"@types/jsdom": "12.2.4",
|
||||
@ -68,7 +66,6 @@
|
||||
"@types/koa-views": "2.0.3",
|
||||
"@types/koa__cors": "2.2.3",
|
||||
"@types/lolex": "3.1.1",
|
||||
"@types/minio": "7.0.2",
|
||||
"@types/mocha": "5.2.7",
|
||||
"@types/node": "12.0.10",
|
||||
"@types/nodemailer": "6.2.0",
|
||||
@ -83,7 +80,7 @@
|
||||
"@types/ratelimiter": "2.1.28",
|
||||
"@types/redis": "2.8.13",
|
||||
"@types/rename": "1.0.1",
|
||||
"@types/request": "2.48.1",
|
||||
"@types/request": "2.48.2",
|
||||
"@types/request-promise-native": "1.0.16",
|
||||
"@types/request-stats": "3.0.0",
|
||||
"@types/rimraf": "2.0.2",
|
||||
@ -100,35 +97,39 @@
|
||||
"@types/webpack-stream": "3.2.10",
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/ws": "6.0.1",
|
||||
"animejs": "3.0.1",
|
||||
"apexcharts": "3.8.1",
|
||||
"@typescript-eslint/parser": "1.11.0",
|
||||
"agentkeepalive": "4.0.2",
|
||||
"animejs": "3.1.0",
|
||||
"apexcharts": "3.8.4",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"aws-sdk": "2.512.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"bootstrap": "4.3.1",
|
||||
"bootstrap-vue": "2.0.0-rc.13",
|
||||
"bull": "3.10.0",
|
||||
"cafy": "15.1.1",
|
||||
"cbor": "4.1.5",
|
||||
"cbor": "4.2.1",
|
||||
"chai": "4.2.0",
|
||||
"chalk": "2.4.2",
|
||||
"cli-highlight": "2.1.1",
|
||||
"commander": "2.20.0",
|
||||
"content-disposition": "0.5.3",
|
||||
"crc-32": "1.2.0",
|
||||
"css-loader": "3.0.0",
|
||||
"css-loader": "3.2.0",
|
||||
"cssnano": "4.1.10",
|
||||
"dateformat": "3.0.3",
|
||||
"deep-equal": "1.0.1",
|
||||
"diskusage": "1.1.2",
|
||||
"diskusage": "1.1.3",
|
||||
"double-ended-queue": "2.1.0-0",
|
||||
"emojilib": "2.4.0",
|
||||
"eslint": "6.0.1",
|
||||
"eslint": "6.1.0",
|
||||
"eslint-plugin-vue": "5.2.3",
|
||||
"eventemitter3": "4.0.0",
|
||||
"feed": "3.0.0",
|
||||
"file-type": "12.0.0",
|
||||
"file-type": "12.1.0",
|
||||
"fluent-ffmpeg": "2.1.2",
|
||||
"fuckadblock": "3.2.1",
|
||||
"gulp": "4.0.2",
|
||||
"gulp-cssnano": "2.1.3",
|
||||
@ -145,7 +146,8 @@
|
||||
"hard-source-webpack-plugin": "0.13.1",
|
||||
"html-minifier": "4.0.0",
|
||||
"http-signature": "1.2.0",
|
||||
"insert-text-at-cursor": "0.2.0",
|
||||
"https-proxy-agent": "2.2.2",
|
||||
"insert-text-at-cursor": "0.3.0",
|
||||
"is-root": "2.1.0",
|
||||
"is-svg": "4.2.0",
|
||||
"js-yaml": "3.13.1",
|
||||
@ -153,13 +155,13 @@
|
||||
"json5": "2.1.0",
|
||||
"json5-loader": "3.0.0",
|
||||
"jsrsasign": "8.0.12",
|
||||
"katex": "0.10.2",
|
||||
"katex": "0.11.0",
|
||||
"koa": "2.7.0",
|
||||
"koa-bodyparser": "4.2.1",
|
||||
"koa-compress": "3.0.0",
|
||||
"koa-favicon": "2.0.1",
|
||||
"koa-json-body": "5.3.0",
|
||||
"koa-logger": "3.2.0",
|
||||
"koa-logger": "3.2.1",
|
||||
"koa-mount": "4.0.0",
|
||||
"koa-multer": "1.0.2",
|
||||
"koa-router": "7.4.0",
|
||||
@ -168,36 +170,33 @@
|
||||
"koa-views": "6.2.0",
|
||||
"langmap": "0.0.16",
|
||||
"loader-utils": "1.2.3",
|
||||
"lolex": "4.1.0",
|
||||
"lolex": "4.2.0",
|
||||
"lookup-dns-cache": "2.1.0",
|
||||
"minio": "7.0.10",
|
||||
"mocha": "6.1.4",
|
||||
"mocha": "6.2.0",
|
||||
"moji": "0.5.1",
|
||||
"moment": "2.24.0",
|
||||
"ms": "2.1.2",
|
||||
"nested-property": "1.0.1",
|
||||
"node-fetch": "2.6.0",
|
||||
"nodemailer": "6.2.1",
|
||||
"nodemailer": "6.3.0",
|
||||
"nprogress": "0.2.0",
|
||||
"object-assign-deep": "0.4.0",
|
||||
"os-utils": "0.0.14",
|
||||
"parse5": "5.1.0",
|
||||
"parsimmon": "1.12.0",
|
||||
"pg": "7.11.0",
|
||||
"parsimmon": "1.13.0",
|
||||
"pg": "7.12.1",
|
||||
"portscanner": "2.2.0",
|
||||
"postcss-loader": "3.0.0",
|
||||
"prismjs": "1.16.0",
|
||||
"prismjs": "1.17.1",
|
||||
"progress-bar-webpack-plugin": "1.12.1",
|
||||
"promise-any": "0.2.0",
|
||||
"promise-limit": "2.7.0",
|
||||
"promise-sequential": "1.1.1",
|
||||
"pug": "2.0.4",
|
||||
"punycode": "2.1.1",
|
||||
"pureimage": "0.1.6",
|
||||
"qrcode": "1.3.3",
|
||||
"qrcode": "1.4.1",
|
||||
"random-seed": "0.3.0",
|
||||
"randomcolor": "0.5.4",
|
||||
"ratelimiter": "3.3.0",
|
||||
"ratelimiter": "3.3.1",
|
||||
"recaptcha-promise": "0.1.3",
|
||||
"reconnecting-websocket": "4.1.10",
|
||||
"redis": "2.8.0",
|
||||
@ -210,8 +209,8 @@
|
||||
"rimraf": "2.6.3",
|
||||
"rndstr": "1.0.0",
|
||||
"s-age": "1.1.2",
|
||||
"seedrandom": "3.0.1",
|
||||
"sharp": "0.22.1",
|
||||
"seedrandom": "3.0.3",
|
||||
"sharp": "0.23.0",
|
||||
"showdown": "1.9.0",
|
||||
"showdown-highlightjs-extension": "0.1.2",
|
||||
"speakeasy": "2.0.0",
|
||||
@ -220,10 +219,11 @@
|
||||
"stylus": "0.54.5",
|
||||
"stylus-loader": "3.0.2",
|
||||
"summaly": "2.3.0",
|
||||
"systeminformation": "4.13.1",
|
||||
"systeminformation": "4.14.4",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"terser-webpack-plugin": "1.3.0",
|
||||
"terser-webpack-plugin": "1.4.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.107.0",
|
||||
"tinycolor2": "1.4.1",
|
||||
"tmp": "0.1.0",
|
||||
"ts-loader": "5.3.3",
|
||||
@ -231,25 +231,24 @@
|
||||
"tslint": "5.18.0",
|
||||
"tslint-sonarts": "1.9.0",
|
||||
"typeorm": "0.2.18",
|
||||
"typescript": "3.5.2",
|
||||
"typescript": "3.5.3",
|
||||
"uglify-es": "3.3.9",
|
||||
"ulid": "2.3.0",
|
||||
"url-loader": "2.0.1",
|
||||
"url-loader": "2.1.0",
|
||||
"uuid": "3.3.2",
|
||||
"v-animate-css": "0.0.3",
|
||||
"v-debounce": "0.1.2",
|
||||
"video-thumbnail-generator": "1.1.3",
|
||||
"vue": "2.6.10",
|
||||
"vue-color": "2.7.0",
|
||||
"vue-content-loading": "1.6.0",
|
||||
"vue-cropperjs": "4.0.0",
|
||||
"vue-i18n": "8.11.2",
|
||||
"vue-i18n": "8.14.0",
|
||||
"vue-js-modal": "1.3.31",
|
||||
"vue-json-pretty": "1.6.0",
|
||||
"vue-loader": "15.7.0",
|
||||
"vue-loader": "15.7.1",
|
||||
"vue-marquee-text-component": "1.1.1",
|
||||
"vue-prism-component": "1.1.1",
|
||||
"vue-router": "3.0.6",
|
||||
"vue-router": "3.1.2",
|
||||
"vue-sequential-entrance": "1.1.3",
|
||||
"vue-style-loader": "4.1.2",
|
||||
"vue-svg-inline-loader": "1.2.16",
|
||||
@ -259,10 +258,13 @@
|
||||
"vuex": "3.1.1",
|
||||
"vuex-persistedstate": "2.5.4",
|
||||
"web-push": "3.3.5",
|
||||
"webpack": "4.35.2",
|
||||
"webpack-cli": "3.3.5",
|
||||
"websocket": "1.0.28",
|
||||
"ws": "7.0.1",
|
||||
"webpack": "4.39.2",
|
||||
"webpack-cli": "3.3.7",
|
||||
"websocket": "1.0.29",
|
||||
"ws": "7.1.2",
|
||||
"xev": "2.0.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/fluent-ffmpeg": "2.1.10"
|
||||
}
|
||||
}
|
||||
|
20
src/@types/lookup-dns-cache.d.ts
vendored
20
src/@types/lookup-dns-cache.d.ts
vendored
@ -1,17 +1,9 @@
|
||||
declare module 'lookup-dns-cache' {
|
||||
type IPv4 = 4;
|
||||
import { LookupOneOptions, LookupAllOptions, LookupOptions, LookupAddress } from 'dns';
|
||||
|
||||
type IPv6 = 6;
|
||||
|
||||
type Family = IPv4 | IPv6 | undefined;
|
||||
|
||||
interface IRunOptions {
|
||||
family?: Family;
|
||||
all?: boolean;
|
||||
}
|
||||
|
||||
type RunCallback = (error: Error | null, address?: string | string[], family?: Family) => void;
|
||||
|
||||
export function lookup(hostname: string, options: IRunOptions | Family, callback: RunCallback): {} | undefined;
|
||||
export function lookup(hostname: string, callback: RunCallback): {} | undefined;
|
||||
function lookup(hostname: string, family: number, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
|
||||
function lookup(hostname: string, options: LookupOneOptions, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
|
||||
function lookup(hostname: string, options: LookupAllOptions, callback: (err: NodeJS.ErrnoException | null, addresses: LookupAddress[]) => void): void;
|
||||
function lookup(hostname: string, options: LookupOptions, callback: (err: NodeJS.ErrnoException | null, address: string | LookupAddress[], family: number) => void): void;
|
||||
function lookup(hostname: string, callback: (err: NodeJS.ErrnoException | null, address: string, family: number) => void): void;
|
||||
}
|
||||
|
7
src/@types/promise-any.d.ts
vendored
7
src/@types/promise-any.d.ts
vendored
@ -1,7 +0,0 @@
|
||||
declare module 'promise-any' {
|
||||
function promiseAny<T>(iterable: Iterable<T | PromiseLike<T>>): Promise<T>;
|
||||
|
||||
namespace promiseAny {} // Hack
|
||||
|
||||
export = promiseAny;
|
||||
}
|
@ -12,6 +12,31 @@
|
||||
</ui-horizon-group>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title>{{ $t('logs.title') }}</template>
|
||||
<section class="fit-top">
|
||||
<sequential-entrance animation="entranceFromTop" delay="25">
|
||||
<div v-for="log in logs" :key="log.id" class="">
|
||||
<ui-horizon-group inputs>
|
||||
<ui-input :value="log.user | acct" type="text" readonly>
|
||||
<span>{{ $t('logs.moderator') }}</span>
|
||||
</ui-input>
|
||||
<ui-input :value="log.type" type="text" readonly>
|
||||
<span>{{ $t('logs.type') }}</span>
|
||||
</ui-input>
|
||||
<ui-input :value="log.createdAt | date" type="text" readonly>
|
||||
<span>{{ $t('logs.at') }}</span>
|
||||
</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-textarea :value="JSON.stringify(log.info, null, 4)" readonly>
|
||||
<span>{{ $t('logs.info') }}</span>
|
||||
</ui-textarea>
|
||||
</div>
|
||||
</sequential-entrance>
|
||||
<ui-button v-if="existMoreLogs" @click="fetchLogs">{{ $t('@.load-more') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -26,10 +51,17 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
username: '',
|
||||
changing: false
|
||||
changing: false,
|
||||
logs: [],
|
||||
untilLogId: null,
|
||||
existMoreLogs: false
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.fetchLogs();
|
||||
},
|
||||
|
||||
methods: {
|
||||
async add() {
|
||||
this.changing = true;
|
||||
@ -74,6 +106,22 @@ export default Vue.extend({
|
||||
|
||||
this.changing = false;
|
||||
},
|
||||
|
||||
fetchLogs() {
|
||||
this.$root.api('admin/show-moderation-logs', {
|
||||
untilId: this.untilId,
|
||||
limit: 10 + 1
|
||||
}).then(logs => {
|
||||
if (logs.length == 10 + 1) {
|
||||
logs.pop();
|
||||
this.existMoreLogs = true;
|
||||
} else {
|
||||
this.existMoreLogs = false;
|
||||
}
|
||||
this.logs = this.logs.concat(logs);
|
||||
this.untilLogId = this.logs[this.logs.length - 1].id;
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -5,7 +5,7 @@
|
||||
<mk-avatar class="avatar" :user="user" :disable-link="true"/>
|
||||
</a>
|
||||
</div>
|
||||
<div>
|
||||
<div @click="click(user.id)">
|
||||
<header>
|
||||
<b><mk-user-name :user="user"/></b>
|
||||
<span class="username">@{{ user | acct }}</span>
|
||||
@ -32,7 +32,7 @@ import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('admin/views/users.vue'),
|
||||
props: ['user'],
|
||||
props: ['user', 'click'],
|
||||
data() {
|
||||
return {
|
||||
faSnowflake, faMicrophoneSlash
|
||||
@ -44,7 +44,7 @@ export default Vue.extend({
|
||||
<style lang="stylus" scoped>
|
||||
.kofvwchc
|
||||
display flex
|
||||
padding 16px 0
|
||||
padding 16px
|
||||
border-top solid 1px var(--faceDivider)
|
||||
|
||||
> div:first-child
|
||||
@ -55,6 +55,7 @@ export default Vue.extend({
|
||||
|
||||
> div:last-child
|
||||
flex 1
|
||||
cursor pointer
|
||||
padding-left 16px
|
||||
|
||||
@media (max-width 500px)
|
||||
@ -80,4 +81,15 @@ export default Vue.extend({
|
||||
> .is-suspended
|
||||
margin 0 0 0 .5em
|
||||
color #4dabf7
|
||||
|
||||
&:hover
|
||||
color var(--primaryForeground)
|
||||
background var(--primary)
|
||||
text-decoration none
|
||||
border-radius 3px
|
||||
|
||||
&:active
|
||||
color var(--primaryForeground)
|
||||
background var(--primaryDarken10)
|
||||
border-radius 3px
|
||||
</style>
|
||||
|
@ -8,7 +8,7 @@
|
||||
</ui-input>
|
||||
<ui-button @click="showUser"><fa :icon="faSearch"/> {{ $t('lookup') }}</ui-button>
|
||||
|
||||
<div class="user" v-if="user">
|
||||
<div ref="user" class="user" v-if="user" :key="user.id">
|
||||
<x-user :user="user"/>
|
||||
<div class="actions">
|
||||
<ui-button v-if="user.host != null" @click="updateRemoteUser"><fa :icon="faSync"/> {{ $t('update-remote-user') }}</ui-button>
|
||||
@ -54,8 +54,16 @@
|
||||
<option value="remote">{{ $t('users.origin.remote') }}</option>
|
||||
</ui-select>
|
||||
</ui-horizon-group>
|
||||
<ui-horizon-group searchboxes>
|
||||
<ui-input v-model="searchUsername" type="text" spellcheck="false" @input="fetchUsers(true)">
|
||||
<span>{{ $t('username') }}</span>
|
||||
</ui-input>
|
||||
<ui-input v-model="searchHost" type="text" spellcheck="false" @input="fetchUsers(true)" :disabled="origin === 'local'">
|
||||
<span>{{ $t('host') }}</span>
|
||||
</ui-input>
|
||||
</ui-horizon-group>
|
||||
<sequential-entrance animation="entranceFromTop" delay="25">
|
||||
<x-user v-for="user in users" :user='user' :key="user.id"/>
|
||||
<x-user v-for="user in users" :key="user.id" :user='user' :click="showUserOnClick"/>
|
||||
</sequential-entrance>
|
||||
<ui-button v-if="existMore" @click="fetchUsers">{{ $t('@.load-more') }}</ui-button>
|
||||
</section>
|
||||
@ -85,6 +93,8 @@ export default Vue.extend({
|
||||
sort: '+createdAt',
|
||||
state: 'all',
|
||||
origin: 'local',
|
||||
searchUsername: '',
|
||||
searchHost: '',
|
||||
limit: 10,
|
||||
offset: 0,
|
||||
users: [],
|
||||
@ -107,6 +117,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
origin() {
|
||||
if (this.origin === 'local') this.searchHost = '';
|
||||
this.users = [];
|
||||
this.offset = 0;
|
||||
this.fetchUsers();
|
||||
@ -157,6 +168,15 @@ export default Vue.extend({
|
||||
this.target = '';
|
||||
},
|
||||
|
||||
async showUserOnClick(userId: string) {
|
||||
this.$root.api('admin/show-user', { userId: userId }).then(info => {
|
||||
this.user = info;
|
||||
this.$nextTick(() => {
|
||||
this.$refs.user.scrollIntoView();
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/** 処理対象ユーザーの情報を更新する */
|
||||
async refreshUser() {
|
||||
this.$root.api('admin/show-user', { userId: this.user.id }).then(info => {
|
||||
@ -308,13 +328,16 @@ export default Vue.extend({
|
||||
return !confirm.canceled;
|
||||
},
|
||||
|
||||
fetchUsers() {
|
||||
fetchUsers(truncate?: boolean) {
|
||||
if (truncate) this.offset = 0;
|
||||
this.$root.api('admin/show-users', {
|
||||
state: this.state,
|
||||
origin: this.origin,
|
||||
sort: this.sort,
|
||||
offset: this.offset,
|
||||
limit: this.limit + 1
|
||||
limit: this.limit + 1,
|
||||
username: this.searchUsername,
|
||||
hostname: this.searchHost
|
||||
}).then(users => {
|
||||
if (users.length == this.limit + 1) {
|
||||
users.pop();
|
||||
@ -322,7 +345,7 @@ export default Vue.extend({
|
||||
} else {
|
||||
this.existMore = false;
|
||||
}
|
||||
this.users = this.users.concat(users);
|
||||
this.users = truncate ? users : this.users.concat(users);
|
||||
this.offset += this.limit;
|
||||
});
|
||||
}
|
||||
|
@ -84,7 +84,14 @@
|
||||
|
||||
// Detect the user agent
|
||||
const ua = navigator.userAgent.toLowerCase();
|
||||
const isMobile = /mobile|iphone|ipad|android/.test(ua);
|
||||
let isMobile = /mobile|iphone|ipad|android/.test(ua) || window.innerWidth < 576;
|
||||
if (settings && settings.device.appTypeForce) {
|
||||
if (settings.device.appTypeForce === 'mobile') {
|
||||
isMobile = true;
|
||||
} else if (settings.device.appTypeForce === 'desktop') {
|
||||
isMobile = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Get the <head> element
|
||||
const head = document.getElementsByTagName('head')[0];
|
||||
|
@ -32,6 +32,12 @@ export function collectPageVars(content) {
|
||||
type: 'number',
|
||||
value: 0
|
||||
});
|
||||
} else if (x.type === 'radioButton') {
|
||||
pageVars.push({
|
||||
name: x.name,
|
||||
type: 'string',
|
||||
value: x.default || ''
|
||||
});
|
||||
} else if (x.children) {
|
||||
collect(x.children);
|
||||
}
|
||||
|
@ -4,7 +4,8 @@ const faces = [
|
||||
'🐡( \'-\' 🐡 )フグパンチ!!!!',
|
||||
'✌️(´・_・`)✌️',
|
||||
'(。>﹏<。)',
|
||||
'(Δ・x・Δ)'
|
||||
'(Δ・x・Δ)',
|
||||
'(コ`・ヘ・´ケ)'
|
||||
];
|
||||
|
||||
export default () => faces[Math.floor(Math.random() * faces.length)];
|
||||
|
@ -3,6 +3,8 @@ import { sum, unique } from '../../../../prelude/array';
|
||||
import shouldMuteNote from './should-mute-note';
|
||||
import MkNoteMenu from '../views/components/note-menu.vue';
|
||||
import MkReactionPicker from '../views/components/reaction-picker.vue';
|
||||
import pleaseLogin from './please-login';
|
||||
import i18n from '../../i18n';
|
||||
|
||||
function focus(el, fn) {
|
||||
const target = fn(el);
|
||||
@ -20,6 +22,8 @@ type Opts = {
|
||||
};
|
||||
|
||||
export default (opts: Opts = {}) => ({
|
||||
i18n: i18n(),
|
||||
|
||||
data() {
|
||||
return {
|
||||
showContent: false,
|
||||
@ -108,6 +112,7 @@ export default (opts: Opts = {}) => ({
|
||||
|
||||
methods: {
|
||||
reply(viaKeyboard = false) {
|
||||
pleaseLogin(this.$root);
|
||||
this.$root.$post({
|
||||
reply: this.appearNote,
|
||||
animation: !viaKeyboard,
|
||||
@ -118,6 +123,7 @@ export default (opts: Opts = {}) => ({
|
||||
},
|
||||
|
||||
renote(viaKeyboard = false) {
|
||||
pleaseLogin(this.$root);
|
||||
this.$root.$post({
|
||||
renote: this.appearNote,
|
||||
animation: !viaKeyboard,
|
||||
@ -134,6 +140,7 @@ export default (opts: Opts = {}) => ({
|
||||
},
|
||||
|
||||
react(viaKeyboard = false) {
|
||||
pleaseLogin(this.$root);
|
||||
this.blur();
|
||||
this.$root.new(MkReactionPicker, {
|
||||
source: this.$refs.reactButton,
|
||||
@ -159,6 +166,7 @@ export default (opts: Opts = {}) => ({
|
||||
},
|
||||
|
||||
favorite() {
|
||||
pleaseLogin(this.$root);
|
||||
this.$root.api('notes/favorites/create', {
|
||||
noteId: this.appearNote.id
|
||||
}).then(() => {
|
||||
@ -170,8 +178,16 @@ export default (opts: Opts = {}) => ({
|
||||
},
|
||||
|
||||
del() {
|
||||
this.$root.api('notes/delete', {
|
||||
noteId: this.appearNote.id
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('@.delete-confirm'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
|
||||
this.$root.api('notes/delete', {
|
||||
noteId: this.appearNote.id
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
@ -44,13 +44,21 @@ export default (opts) => ({
|
||||
return window.scrollY <= 8;
|
||||
};
|
||||
|
||||
window.addEventListener('scroll', this.onWindowScroll, { passive: true });
|
||||
window.addEventListener('scroll', this.onScroll, { passive: true });
|
||||
} else if (opts.isContainer) {
|
||||
this.isScrollTop = () => {
|
||||
return this.$el.scrollTop <= 8;
|
||||
};
|
||||
|
||||
this.$el.addEventListener('scroll', this.onScroll, { passive: true });
|
||||
}
|
||||
},
|
||||
|
||||
beforeDestroy() {
|
||||
if (opts.captureWindowScroll) {
|
||||
window.removeEventListener('scroll', this.onWindowScroll);
|
||||
window.removeEventListener('scroll', this.onScroll);
|
||||
} else if (opts.isContainer) {
|
||||
this.$el.removeEventListener('scroll', this.onScroll);
|
||||
}
|
||||
},
|
||||
|
||||
@ -67,6 +75,7 @@ export default (opts) => ({
|
||||
|
||||
async init() {
|
||||
this.fetching = true;
|
||||
if (opts.beforeInit) opts.beforeInit(this);
|
||||
let params = typeof this.pagination.params === 'function' ? this.pagination.params(true) : this.pagination.params;
|
||||
if (params && params.then) params = await params;
|
||||
await this.$root.api(this.pagination.endpoint, {
|
||||
@ -151,7 +160,7 @@ export default (opts) => ({
|
||||
this.queue = [];
|
||||
},
|
||||
|
||||
onWindowScroll() {
|
||||
onScroll() {
|
||||
if (this.isScrollTop()) {
|
||||
this.onTop();
|
||||
}
|
||||
@ -162,8 +171,10 @@ export default (opts) => ({
|
||||
// http://d.hatena.ne.jp/favril/20091105/1257403319
|
||||
if (this.$el.offsetHeight == 0) return;
|
||||
|
||||
const current = window.scrollY + window.innerHeight;
|
||||
if (current > document.body.offsetHeight - 8) this.onBottom();
|
||||
const bottomPosition = opts.isContainer ? this.$el.scrollHeight : document.body.offsetHeight;
|
||||
|
||||
const currentBottomPosition = opts.isContainer ? this.$el.scrollTop + this.$el.clientHeight : window.scrollY + window.innerHeight;
|
||||
if (currentBottomPosition > (bottomPosition - 8)) this.onBottom();
|
||||
}
|
||||
},
|
||||
|
||||
|
10
src/client/app/common/scripts/please-login.ts
Normal file
10
src/client/app/common/scripts/please-login.ts
Normal file
@ -0,0 +1,10 @@
|
||||
export default ($root: any) => {
|
||||
if ($root.$store.getters.isSignedIn) return;
|
||||
|
||||
$root.dialog({
|
||||
title: $root.$t('@.signin-required'),
|
||||
text: null
|
||||
});
|
||||
|
||||
throw new Error('signin required');
|
||||
};
|
@ -8,6 +8,7 @@ import { host, url } from '../../config';
|
||||
import i18n from '../../i18n';
|
||||
import { erase, unique } from '../../../../prelude/array';
|
||||
import extractMentions from '../../../../misc/extract-mentions';
|
||||
import { formatTimeString } from '../../../../misc/format-time-string';
|
||||
|
||||
export default (opts) => ({
|
||||
i18n: i18n(),
|
||||
@ -34,6 +35,10 @@ export default (opts) => ({
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
initialNote: {
|
||||
type: Object,
|
||||
required: false
|
||||
},
|
||||
instant: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
@ -151,9 +156,16 @@ export default (opts) => ({
|
||||
// 公開以外へのリプライ時は元の公開範囲を引き継ぐ
|
||||
if (this.reply && ['home', 'followers', 'specified'].includes(this.reply.visibility)) {
|
||||
this.visibility = this.reply.visibility;
|
||||
if (this.reply.visibility === 'specified') {
|
||||
this.$root.api('users/show', {
|
||||
userIds: this.reply.visibleUserIds.filter(uid => uid !== this.$store.state.i.id && uid !== this.reply.userId)
|
||||
}).then(users => {
|
||||
this.visibleUsers.push(...users);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (this.reply) {
|
||||
if (this.reply && this.reply.userId !== this.$store.state.i.id) {
|
||||
this.$root.api('users/show', { userId: this.reply.userId }).then(user => {
|
||||
this.visibleUsers.push(user);
|
||||
});
|
||||
@ -187,6 +199,28 @@ export default (opts) => ({
|
||||
this.$emit('change-attached-files', this.files);
|
||||
}
|
||||
}
|
||||
if (this.initialNote) {
|
||||
// 削除して編集
|
||||
const init = this.initialNote;
|
||||
this.text = init.text ? init.text : '';
|
||||
this.files = init.files;
|
||||
this.cw = init.cw;
|
||||
this.useCw = init.cw != null;
|
||||
if (init.poll) {
|
||||
this.poll = true;
|
||||
this.$nextTick(() => {
|
||||
(this.$refs.poll as any).set({
|
||||
choices: init.poll.choices.map(c => c.text),
|
||||
multiple: init.poll.multiple
|
||||
});
|
||||
});
|
||||
}
|
||||
// hack 位置情報投稿が動くようになったら適用する
|
||||
this.geo = null;
|
||||
this.visibility = init.visibility;
|
||||
this.localOnly = init.localOnly;
|
||||
this.quoteId = init.renote ? init.renote.id : null;
|
||||
}
|
||||
|
||||
this.$nextTick(() => this.watch());
|
||||
});
|
||||
@ -237,8 +271,8 @@ export default (opts) => ({
|
||||
for (const x of Array.from((this.$refs.file as any).files)) this.upload(x);
|
||||
},
|
||||
|
||||
upload(file) {
|
||||
(this.$refs.uploader as any).upload(file);
|
||||
upload(file: File, name?: string) {
|
||||
(this.$refs.uploader as any).upload(file, this.$store.state.settings.uploadFolder, name);
|
||||
},
|
||||
|
||||
onChangeUploadings(uploads) {
|
||||
@ -320,6 +354,7 @@ export default (opts) => ({
|
||||
this.text = '';
|
||||
this.files = [];
|
||||
this.poll = false;
|
||||
this.quoteId = null;
|
||||
this.$emit('change-attached-files', this.files);
|
||||
},
|
||||
|
||||
@ -327,16 +362,29 @@ export default (opts) => ({
|
||||
if ((e.which == 10 || e.which == 13) && (e.ctrlKey || e.metaKey) && this.canPost) this.post();
|
||||
},
|
||||
|
||||
async onPaste(e) {
|
||||
for (const item of Array.from(e.clipboardData.items)) {
|
||||
async onPaste(e: ClipboardEvent) {
|
||||
for (const { item, i } of Array.from(e.clipboardData.items).map((item, i) => ({item, i}))) {
|
||||
if (item.kind == 'file') {
|
||||
this.upload(item.getAsFile());
|
||||
const file = item.getAsFile();
|
||||
const lio = file.name.lastIndexOf('.');
|
||||
const ext = lio >= 0 ? file.name.slice(lio) : '';
|
||||
const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.settings.pastedFileName).replace(/{{number}}/g, `${i + 1}`)}${ext}`;
|
||||
const name = this.$store.state.settings.pasteDialog
|
||||
? await this.$root.dialog({
|
||||
title: this.$t('@.post-form.enter-file-name'),
|
||||
input: {
|
||||
default: formatted
|
||||
},
|
||||
allowEmpty: false
|
||||
}).then(({ canceled, result }) => canceled ? false : result)
|
||||
: formatted;
|
||||
if (name) this.upload(file, name);
|
||||
}
|
||||
}
|
||||
|
||||
const paste = e.clipboardData.getData('text');
|
||||
|
||||
if (paste.startsWith(url + '/notes/')) {
|
||||
if (!this.renote && !this.quoteId && paste.startsWith(url + '/notes/')) {
|
||||
e.preventDefault();
|
||||
|
||||
this.$root.dialog({
|
||||
|
21
src/client/app/common/scripts/room/furniture.ts
Normal file
21
src/client/app/common/scripts/room/furniture.ts
Normal file
@ -0,0 +1,21 @@
|
||||
export type RoomInfo = {
|
||||
roomType: string;
|
||||
carpetColor: string;
|
||||
furnitures: Furniture[];
|
||||
};
|
||||
|
||||
export type Furniture = {
|
||||
id: string; // 同じ家具が複数ある場合にそれぞれを識別するためのIDであり、家具IDではない
|
||||
type: string; // こっちが家具ID(chairとか)
|
||||
position: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
rotation: {
|
||||
x: number;
|
||||
y: number;
|
||||
z: number;
|
||||
};
|
||||
props?: Record<string, any>;
|
||||
};
|
354
src/client/app/common/scripts/room/furnitures.json5
Normal file
354
src/client/app/common/scripts/room/furnitures.json5
Normal file
@ -0,0 +1,354 @@
|
||||
// 家具メタデータ
|
||||
|
||||
// 家具にはユーザーが設定できるプロパティを設定可能です:
|
||||
//
|
||||
// props: {
|
||||
// <propname>: <proptype>
|
||||
// }
|
||||
//
|
||||
// proptype一覧:
|
||||
// * image ... 画像選択ダイアログを出し、その画像のURLが格納されます
|
||||
// * color ... 色選択コントロールを出し、選択された色が格納されます
|
||||
|
||||
// 家具にカスタムテクスチャを適用できるようにするには、textureプロパティに以下の追加の情報を含めます:
|
||||
// 便宜上そのUVのどの部分にカスタムテクスチャを貼り合わせるかのエリアをテクスチャエリアと呼びます。
|
||||
// UVは1024*1024だと仮定します。
|
||||
//
|
||||
// <key>: {
|
||||
// prop: <プロパティ名>,
|
||||
// uv: {
|
||||
// x: <テクスチャエリアX座標>,
|
||||
// y: <テクスチャエリアY座標>,
|
||||
// width: <テクスチャエリアの幅>,
|
||||
// height: <テクスチャエリアの高さ>,
|
||||
// },
|
||||
// }
|
||||
//
|
||||
// <key>には、カスタムテクスチャを適用したいメッシュ名を指定します
|
||||
// <プロパティ名>には、カスタムテクスチャとして使用する画像を格納するプロパティ(前述)名を指定します
|
||||
|
||||
// 家具にカスタムカラーを適用できるようにするには、colorプロパティに以下の追加の情報を含めます:
|
||||
//
|
||||
// <key>: <プロパティ名>
|
||||
//
|
||||
// <key>には、カスタムカラーを適用したいマテリアル名を指定します
|
||||
// <プロパティ名>には、カスタムカラーとして使用する色を格納するプロパティ(前述)名を指定します
|
||||
|
||||
[
|
||||
{
|
||||
id: "milk",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "bed",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "low-table",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Table: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "desk",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Board: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "chair",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Chair: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "chair2",
|
||||
place: "floor",
|
||||
props: {
|
||||
color1: 'color',
|
||||
color2: 'color'
|
||||
},
|
||||
color: {
|
||||
Cushion: 'color1',
|
||||
Leg: 'color2'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "fan",
|
||||
place: "wall"
|
||||
},
|
||||
{
|
||||
id: "pc",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "plant",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "plant2",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "eraser",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "pencil",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "pudding",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "cardboard-box",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "cardboard-box2",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "cardboard-box3",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "book",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Cover: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "book2",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "piano",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "facial-tissue",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "server",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "moon",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "corkboard",
|
||||
place: "wall"
|
||||
},
|
||||
{
|
||||
id: "mousepad",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Pad: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "monitor",
|
||||
place: "floor",
|
||||
props: {
|
||||
screen: 'image'
|
||||
},
|
||||
texture: {
|
||||
Screen: {
|
||||
prop: 'screen',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 434,
|
||||
width: 1024,
|
||||
height: 588,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "tv",
|
||||
place: "floor",
|
||||
props: {
|
||||
screen: 'image'
|
||||
},
|
||||
texture: {
|
||||
Screen: {
|
||||
prop: 'screen',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 434,
|
||||
width: 1024,
|
||||
height: 588,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "keyboard",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "carpet-stripe",
|
||||
place: "floor",
|
||||
props: {
|
||||
color1: 'color',
|
||||
color2: 'color'
|
||||
},
|
||||
color: {
|
||||
CarpetAreaA: 'color1',
|
||||
CarpetAreaB: 'color2'
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "mat",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Mat: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "color-box",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
main: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "wall-clock",
|
||||
place: "wall"
|
||||
},
|
||||
{
|
||||
id: "cube",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Cube: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "photoframe",
|
||||
place: "wall",
|
||||
props: {
|
||||
photo: 'image',
|
||||
color: 'color'
|
||||
},
|
||||
texture: {
|
||||
Photo: {
|
||||
prop: 'photo',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 342,
|
||||
width: 1024,
|
||||
height: 683,
|
||||
},
|
||||
},
|
||||
},
|
||||
color: {
|
||||
Frame: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "pinguin",
|
||||
place: "floor"
|
||||
},
|
||||
{
|
||||
id: "rubik-cube",
|
||||
place: "floor",
|
||||
},
|
||||
{
|
||||
id: "poster-h",
|
||||
place: "wall",
|
||||
props: {
|
||||
picture: 'image'
|
||||
},
|
||||
texture: {
|
||||
Poster: {
|
||||
prop: 'picture',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 277,
|
||||
width: 1024,
|
||||
height: 745,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "poster-v",
|
||||
place: "wall",
|
||||
props: {
|
||||
picture: 'image'
|
||||
},
|
||||
texture: {
|
||||
Poster: {
|
||||
prop: 'picture',
|
||||
uv: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 745,
|
||||
height: 1024,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "sofa",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Sofa: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "spiral",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Step: 'color'
|
||||
}
|
||||
},
|
||||
{
|
||||
id: "bin",
|
||||
place: "floor",
|
||||
props: {
|
||||
color: 'color'
|
||||
},
|
||||
color: {
|
||||
Bin: 'color'
|
||||
}
|
||||
},
|
||||
]
|
712
src/client/app/common/scripts/room/room.ts
Normal file
712
src/client/app/common/scripts/room/room.ts
Normal file
@ -0,0 +1,712 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import * as THREE from 'three';
|
||||
import { GLTFLoader, GLTF } from 'three/examples/jsm/loaders/GLTFLoader';
|
||||
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
||||
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js';
|
||||
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js';
|
||||
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js';
|
||||
import { BloomPass } from 'three/examples/jsm/postprocessing/BloomPass.js';
|
||||
import { FXAAShader } from 'three/examples/jsm/shaders/FXAAShader.js';
|
||||
import { TransformControls } from 'three/examples/jsm/controls/TransformControls.js';
|
||||
import { Furniture, RoomInfo } from './furniture';
|
||||
import { query as urlQuery } from '../../../../../prelude/url';
|
||||
const furnitureDefs = require('./furnitures.json5');
|
||||
|
||||
THREE.ImageUtils.crossOrigin = '';
|
||||
|
||||
type Options = {
|
||||
graphicsQuality: Room['graphicsQuality'];
|
||||
onChangeSelect: Room['onChangeSelect'];
|
||||
useOrthographicCamera: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* MisskeyRoom Core Engine
|
||||
*/
|
||||
export class Room {
|
||||
private clock: THREE.Clock;
|
||||
private scene: THREE.Scene;
|
||||
private renderer: THREE.WebGLRenderer;
|
||||
private camera: THREE.PerspectiveCamera | THREE.OrthographicCamera;
|
||||
private controls: OrbitControls;
|
||||
private composer: EffectComposer;
|
||||
private mixers: THREE.AnimationMixer[] = [];
|
||||
private furnitureControl: TransformControls;
|
||||
private roomInfo: RoomInfo;
|
||||
private graphicsQuality: 'cheep' | 'low' | 'medium' | 'high' | 'ultra';
|
||||
private roomObj: THREE.Object3D;
|
||||
private objects: THREE.Object3D[] = [];
|
||||
private selectedObject: THREE.Object3D = null;
|
||||
private onChangeSelect: Function;
|
||||
private isTransformMode = false;
|
||||
private renderFrameRequestId: number;
|
||||
|
||||
private get canvas(): HTMLCanvasElement {
|
||||
return this.renderer.domElement;
|
||||
}
|
||||
|
||||
private get furnitures(): Furniture[] {
|
||||
return this.roomInfo.furnitures;
|
||||
}
|
||||
|
||||
private set furnitures(furnitures: Furniture[]) {
|
||||
this.roomInfo.furnitures = furnitures;
|
||||
}
|
||||
|
||||
private get enableShadow() {
|
||||
return this.graphicsQuality != 'cheep';
|
||||
}
|
||||
|
||||
private get usePostFXs() {
|
||||
return this.graphicsQuality !== 'cheep' && this.graphicsQuality !== 'low';
|
||||
}
|
||||
|
||||
private get shadowQuality() {
|
||||
return (
|
||||
this.graphicsQuality === 'ultra' ? 16384 :
|
||||
this.graphicsQuality === 'high' ? 8192 :
|
||||
this.graphicsQuality === 'medium' ? 4096 :
|
||||
this.graphicsQuality === 'low' ? 1024 :
|
||||
0); // cheep
|
||||
}
|
||||
|
||||
constructor(user, isMyRoom, roomInfo: RoomInfo, container, options: Options) {
|
||||
this.roomInfo = roomInfo;
|
||||
this.graphicsQuality = options.graphicsQuality;
|
||||
this.onChangeSelect = options.onChangeSelect;
|
||||
|
||||
this.clock = new THREE.Clock(true);
|
||||
|
||||
//#region Init a scene
|
||||
this.scene = new THREE.Scene();
|
||||
|
||||
const width = window.innerWidth;
|
||||
const height = window.innerHeight;
|
||||
|
||||
//#region Init a renderer
|
||||
this.renderer = new THREE.WebGLRenderer({
|
||||
antialias: false,
|
||||
stencil: false,
|
||||
alpha: false,
|
||||
powerPreference:
|
||||
this.graphicsQuality === 'ultra' ? 'high-performance' :
|
||||
this.graphicsQuality === 'high' ? 'high-performance' :
|
||||
this.graphicsQuality === 'medium' ? 'default' :
|
||||
this.graphicsQuality === 'low' ? 'low-power' :
|
||||
'low-power' // cheep
|
||||
});
|
||||
|
||||
this.renderer.setPixelRatio(window.devicePixelRatio);
|
||||
this.renderer.setSize(width, height);
|
||||
this.renderer.autoClear = false;
|
||||
this.renderer.setClearColor(new THREE.Color(0x051f2d));
|
||||
this.renderer.shadowMap.enabled = this.enableShadow;
|
||||
this.renderer.gammaOutput = true;
|
||||
this.renderer.shadowMap.type =
|
||||
this.graphicsQuality === 'ultra' ? THREE.PCFSoftShadowMap :
|
||||
this.graphicsQuality === 'high' ? THREE.PCFSoftShadowMap :
|
||||
this.graphicsQuality === 'medium' ? THREE.PCFShadowMap :
|
||||
this.graphicsQuality === 'low' ? THREE.BasicShadowMap :
|
||||
THREE.BasicShadowMap; // cheep
|
||||
|
||||
container.appendChild(this.canvas);
|
||||
//#endregion
|
||||
|
||||
//#region Init a camera
|
||||
this.camera = options.useOrthographicCamera
|
||||
? new THREE.OrthographicCamera(
|
||||
width / - 2, width / 2, height / 2, height / - 2, -10, 10)
|
||||
: new THREE.PerspectiveCamera(45, width / height);
|
||||
|
||||
if (options.useOrthographicCamera) {
|
||||
this.camera.position.x = 2;
|
||||
this.camera.position.y = 2;
|
||||
this.camera.position.z = 2;
|
||||
this.camera.zoom = 100;
|
||||
this.camera.updateProjectionMatrix();
|
||||
} else {
|
||||
this.camera.position.x = 5;
|
||||
this.camera.position.y = 2;
|
||||
this.camera.position.z = 5;
|
||||
}
|
||||
|
||||
this.scene.add(this.camera);
|
||||
//#endregion
|
||||
|
||||
//#region AmbientLight
|
||||
const ambientLight = new THREE.AmbientLight(0xffffff, 1);
|
||||
this.scene.add(ambientLight);
|
||||
//#endregion
|
||||
|
||||
if (this.graphicsQuality !== 'cheep') {
|
||||
//#region Room light
|
||||
const roomLight = new THREE.SpotLight(0xffffff, 0.1);
|
||||
|
||||
roomLight.position.set(0, 8, 0);
|
||||
roomLight.castShadow = this.enableShadow;
|
||||
roomLight.shadow.bias = -0.0001;
|
||||
roomLight.shadow.mapSize.width = this.shadowQuality;
|
||||
roomLight.shadow.mapSize.height = this.shadowQuality;
|
||||
roomLight.shadow.camera.near = 0.1;
|
||||
roomLight.shadow.camera.far = 9;
|
||||
roomLight.shadow.camera.fov = 45;
|
||||
|
||||
this.scene.add(roomLight);
|
||||
//#endregion
|
||||
}
|
||||
|
||||
//#region Out light
|
||||
const outLight1 = new THREE.SpotLight(0xffffff, 0.4);
|
||||
outLight1.position.set(9, 3, -2);
|
||||
outLight1.castShadow = this.enableShadow;
|
||||
outLight1.shadow.bias = -0.001; // アクネ、アーチファクト対策 その代わりピーターパンが発生する可能性がある
|
||||
outLight1.shadow.mapSize.width = this.shadowQuality;
|
||||
outLight1.shadow.mapSize.height = this.shadowQuality;
|
||||
outLight1.shadow.camera.near = 6;
|
||||
outLight1.shadow.camera.far = 15;
|
||||
outLight1.shadow.camera.fov = 45;
|
||||
this.scene.add(outLight1);
|
||||
|
||||
const outLight2 = new THREE.SpotLight(0xffffff, 0.2);
|
||||
outLight2.position.set(-2, 3, 9);
|
||||
outLight2.castShadow = false;
|
||||
outLight2.shadow.bias = -0.001; // アクネ、アーチファクト対策 その代わりピーターパンが発生する可能性がある
|
||||
outLight2.shadow.camera.near = 6;
|
||||
outLight2.shadow.camera.far = 15;
|
||||
outLight2.shadow.camera.fov = 45;
|
||||
this.scene.add(outLight2);
|
||||
//#endregion
|
||||
|
||||
//#region Init a controller
|
||||
this.controls = new OrbitControls(this.camera, this.canvas);
|
||||
|
||||
this.controls.target.set(0, 1, 0);
|
||||
this.controls.enableZoom = true;
|
||||
this.controls.enablePan = isMyRoom;
|
||||
this.controls.minPolarAngle = 0;
|
||||
this.controls.maxPolarAngle = Math.PI / 2;
|
||||
this.controls.minAzimuthAngle = 0;
|
||||
this.controls.maxAzimuthAngle = Math.PI / 2;
|
||||
this.controls.enableDamping = true;
|
||||
this.controls.dampingFactor = 0.2;
|
||||
this.controls.mouseButtons.LEFT = 1;
|
||||
this.controls.mouseButtons.MIDDLE = 2;
|
||||
this.controls.mouseButtons.RIGHT = 0;
|
||||
//#endregion
|
||||
|
||||
//#region POST FXs
|
||||
if (!this.usePostFXs) {
|
||||
this.composer = null;
|
||||
} else {
|
||||
const renderTarget = new THREE.WebGLRenderTarget(width, height, {
|
||||
minFilter: THREE.LinearFilter,
|
||||
magFilter: THREE.LinearFilter,
|
||||
format: THREE.RGBFormat,
|
||||
stencilBuffer: false,
|
||||
});
|
||||
|
||||
const fxaa = new ShaderPass(FXAAShader);
|
||||
fxaa.uniforms['resolution'].value = new THREE.Vector2(1 / width, 1 / height);
|
||||
fxaa.renderToScreen = true;
|
||||
|
||||
this.composer = new EffectComposer(this.renderer, renderTarget);
|
||||
this.composer.addPass(new RenderPass(this.scene, this.camera));
|
||||
if (this.graphicsQuality === 'ultra') {
|
||||
this.composer.addPass(new BloomPass(0.25, 30, 128.0, 512));
|
||||
}
|
||||
this.composer.addPass(fxaa);
|
||||
}
|
||||
//#endregion
|
||||
//#endregion
|
||||
|
||||
//#region Label
|
||||
//#region Avatar
|
||||
const avatarUrl = `/proxy/?${urlQuery({ url: user.avatarUrl })}`;
|
||||
|
||||
const textureLoader = new THREE.TextureLoader();
|
||||
textureLoader.crossOrigin = 'anonymous';
|
||||
|
||||
const iconTexture = textureLoader.load(avatarUrl);
|
||||
iconTexture.wrapS = THREE.RepeatWrapping;
|
||||
iconTexture.wrapT = THREE.RepeatWrapping;
|
||||
iconTexture.anisotropy = 16;
|
||||
|
||||
const avatarMaterial = new THREE.MeshBasicMaterial({
|
||||
map: iconTexture,
|
||||
side: THREE.DoubleSide,
|
||||
alphaTest: 0.5
|
||||
});
|
||||
|
||||
const iconGeometry = new THREE.PlaneGeometry(1, 1);
|
||||
|
||||
const avatarObject = new THREE.Mesh(iconGeometry, avatarMaterial);
|
||||
avatarObject.position.set(-3, 2.5, 2);
|
||||
avatarObject.rotation.y = Math.PI / 2;
|
||||
avatarObject.castShadow = false;
|
||||
|
||||
this.scene.add(avatarObject);
|
||||
//#endregion
|
||||
|
||||
//#region Username
|
||||
const name = user.username;
|
||||
|
||||
new THREE.FontLoader().load('/assets/fonts/helvetiker_regular.typeface.json', font => {
|
||||
const nameGeometry = new THREE.TextGeometry(name, {
|
||||
size: 0.5,
|
||||
height: 0,
|
||||
curveSegments: 8,
|
||||
font: font,
|
||||
bevelThickness: 0,
|
||||
bevelSize: 0,
|
||||
bevelEnabled: false
|
||||
});
|
||||
|
||||
const nameMaterial = new THREE.MeshLambertMaterial({
|
||||
color: 0xffffff
|
||||
});
|
||||
|
||||
const nameObject = new THREE.Mesh(nameGeometry, nameMaterial);
|
||||
nameObject.position.set(-3, 2.25, 1.25);
|
||||
nameObject.rotation.y = Math.PI / 2;
|
||||
nameObject.castShadow = false;
|
||||
|
||||
this.scene.add(nameObject);
|
||||
});
|
||||
//#endregion
|
||||
//#endregion
|
||||
|
||||
//#region Interaction
|
||||
if (isMyRoom) {
|
||||
this.furnitureControl = new TransformControls(this.camera, this.canvas);
|
||||
this.scene.add(this.furnitureControl);
|
||||
|
||||
// Hover highlight
|
||||
this.canvas.onmousemove = this.onmousemove;
|
||||
|
||||
// Click
|
||||
this.canvas.onmousedown = this.onmousedown;
|
||||
}
|
||||
//#endregion
|
||||
|
||||
//#region Init room
|
||||
this.loadRoom();
|
||||
//#endregion
|
||||
|
||||
//#region Load furnitures
|
||||
for (const furniture of this.furnitures) {
|
||||
this.loadFurniture(furniture).then(obj => {
|
||||
this.scene.add(obj.scene);
|
||||
this.objects.push(obj.scene);
|
||||
});
|
||||
}
|
||||
//#endregion
|
||||
|
||||
// Start render
|
||||
if (this.usePostFXs) {
|
||||
this.renderWithPostFXs();
|
||||
} else {
|
||||
this.renderWithoutPostFXs();
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
private renderWithoutPostFXs() {
|
||||
this.renderFrameRequestId =
|
||||
window.requestAnimationFrame(this.renderWithoutPostFXs);
|
||||
|
||||
// Update animations
|
||||
const clock = this.clock.getDelta();
|
||||
for (const mixer of this.mixers) {
|
||||
mixer.update(clock);
|
||||
}
|
||||
|
||||
this.controls.update();
|
||||
this.renderer.render(this.scene, this.camera);
|
||||
}
|
||||
|
||||
@autobind
|
||||
private renderWithPostFXs() {
|
||||
this.renderFrameRequestId =
|
||||
window.requestAnimationFrame(this.renderWithPostFXs);
|
||||
|
||||
// Update animations
|
||||
const clock = this.clock.getDelta();
|
||||
for (const mixer of this.mixers) {
|
||||
mixer.update(clock);
|
||||
}
|
||||
|
||||
this.controls.update();
|
||||
this.renderer.clear();
|
||||
this.composer.render();
|
||||
}
|
||||
|
||||
@autobind
|
||||
private loadRoom() {
|
||||
new GLTFLoader().load(`/assets/room/rooms/${this.roomInfo.roomType}/${this.roomInfo.roomType}.glb`, gltf => {
|
||||
gltf.scene.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
|
||||
child.receiveShadow = this.enableShadow;
|
||||
|
||||
child.material = new THREE.MeshLambertMaterial({
|
||||
color: (child.material as THREE.MeshStandardMaterial).color,
|
||||
map: (child.material as THREE.MeshStandardMaterial).map,
|
||||
name: (child.material as THREE.MeshStandardMaterial).name,
|
||||
});
|
||||
|
||||
// 異方性フィルタリング
|
||||
if ((child.material as THREE.MeshLambertMaterial).map && this.graphicsQuality !== 'cheep') {
|
||||
(child.material as THREE.MeshLambertMaterial).map.minFilter = THREE.LinearMipMapLinearFilter;
|
||||
(child.material as THREE.MeshLambertMaterial).map.magFilter = THREE.LinearMipMapLinearFilter;
|
||||
(child.material as THREE.MeshLambertMaterial).map.anisotropy = 8;
|
||||
}
|
||||
});
|
||||
|
||||
gltf.scene.position.set(0, 0, 0);
|
||||
|
||||
this.scene.add(gltf.scene);
|
||||
this.roomObj = gltf.scene;
|
||||
if (this.roomInfo.roomType === 'default') {
|
||||
this.applyCarpetColor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private loadFurniture(furniture: Furniture) {
|
||||
const def = furnitureDefs.find(d => d.id === furniture.type);
|
||||
return new Promise<GLTF>((res, rej) => {
|
||||
const loader = new GLTFLoader();
|
||||
loader.load(`/assets/room/furnitures/${furniture.type}/${furniture.type}.glb`, gltf => {
|
||||
const model = gltf.scene;
|
||||
|
||||
// Load animation
|
||||
if (gltf.animations.length > 0) {
|
||||
const mixer = new THREE.AnimationMixer(model);
|
||||
this.mixers.push(mixer);
|
||||
for (const clip of gltf.animations) {
|
||||
mixer.clipAction(clip).play();
|
||||
}
|
||||
}
|
||||
|
||||
model.name = furniture.id;
|
||||
model.position.x = furniture.position.x;
|
||||
model.position.y = furniture.position.y;
|
||||
model.position.z = furniture.position.z;
|
||||
model.rotation.x = furniture.rotation.x;
|
||||
model.rotation.y = furniture.rotation.y;
|
||||
model.rotation.z = furniture.rotation.z;
|
||||
|
||||
model.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
child.castShadow = this.enableShadow;
|
||||
child.receiveShadow = this.enableShadow;
|
||||
(child.material as THREE.MeshStandardMaterial).metalness = 0;
|
||||
|
||||
// 異方性フィルタリング
|
||||
if ((child.material as THREE.MeshStandardMaterial).map && this.graphicsQuality !== 'cheep') {
|
||||
(child.material as THREE.MeshStandardMaterial).map.minFilter = THREE.LinearMipMapLinearFilter;
|
||||
(child.material as THREE.MeshStandardMaterial).map.magFilter = THREE.LinearMipMapLinearFilter;
|
||||
(child.material as THREE.MeshStandardMaterial).map.anisotropy = 8;
|
||||
}
|
||||
});
|
||||
|
||||
if (def.color) { // カスタムカラー
|
||||
this.applyCustomColor(model);
|
||||
}
|
||||
|
||||
if (def.texture) { // カスタムテクスチャ
|
||||
this.applyCustomTexture(model);
|
||||
}
|
||||
|
||||
res(gltf);
|
||||
}, null, rej);
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private applyCarpetColor() {
|
||||
this.roomObj.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
if (child.material && (child.material as THREE.MeshStandardMaterial).name && (child.material as THREE.MeshStandardMaterial).name === 'Carpet') {
|
||||
(child.material as THREE.MeshStandardMaterial).color.setHex(parseInt(this.roomInfo.carpetColor.substr(1), 16));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
public applyCustomColor(model: THREE.Object3D) {
|
||||
const furniture = this.furnitures.find(furniture => furniture.id === model.name);
|
||||
const def = furnitureDefs.find(d => d.id === furniture.type);
|
||||
if (def.color == null) return;
|
||||
model.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
for (const t of Object.keys(def.color)) {
|
||||
if (!child.material || !(child.material as THREE.MeshStandardMaterial).name || (child.material as THREE.MeshStandardMaterial).name !== t) continue;
|
||||
|
||||
const prop = def.color[t];
|
||||
const val = furniture.props ? furniture.props[prop] : undefined;
|
||||
|
||||
if (val == null) continue;
|
||||
|
||||
(child.material as THREE.MeshStandardMaterial).color.setHex(parseInt(val.substr(1), 16));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
public applyCustomTexture(model: THREE.Object3D) {
|
||||
const furniture = this.furnitures.find(furniture => furniture.id === model.name);
|
||||
const def = furnitureDefs.find(d => d.id === furniture.type);
|
||||
if (def.texture == null) return;
|
||||
|
||||
model.traverse(child => {
|
||||
if (!(child instanceof THREE.Mesh)) return;
|
||||
for (const t of Object.keys(def.texture)) {
|
||||
if (child.name !== t) continue;
|
||||
|
||||
const prop = def.texture[t].prop;
|
||||
const val = furniture.props ? furniture.props[prop] : undefined;
|
||||
|
||||
if (val == null) continue;
|
||||
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.height = 1024;
|
||||
canvas.width = 1024;
|
||||
|
||||
child.material = new THREE.MeshLambertMaterial({
|
||||
emissive: 0x111111,
|
||||
side: THREE.DoubleSide,
|
||||
alphaTest: 0.5,
|
||||
});
|
||||
|
||||
const img = new Image();
|
||||
img.crossOrigin = 'anonymous';
|
||||
img.onload = () => {
|
||||
const uvInfo = def.texture[t].uv;
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height, uvInfo.x, uvInfo.y, uvInfo.width, uvInfo.height);
|
||||
|
||||
const texture = new THREE.Texture(canvas);
|
||||
texture.wrapS = THREE.RepeatWrapping;
|
||||
texture.wrapT = THREE.RepeatWrapping;
|
||||
texture.anisotropy = 16;
|
||||
texture.flipY = false;
|
||||
|
||||
(child.material as THREE.MeshLambertMaterial).map = texture;
|
||||
(child.material as THREE.MeshLambertMaterial).needsUpdate = true;
|
||||
(child.material as THREE.MeshLambertMaterial).map.needsUpdate = true;
|
||||
};
|
||||
img.src = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
private onmousemove(ev: MouseEvent) {
|
||||
if (this.isTransformMode) return;
|
||||
|
||||
const rect = (ev.target as HTMLElement).getBoundingClientRect();
|
||||
const x = (((ev.clientX * window.devicePixelRatio) - rect.left) / this.canvas.width) * 2 - 1;
|
||||
const y = -(((ev.clientY * window.devicePixelRatio) - rect.top) / this.canvas.height) * 2 + 1;
|
||||
const pos = new THREE.Vector2(x, y);
|
||||
|
||||
this.camera.updateMatrixWorld();
|
||||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera(pos, this.camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects(this.objects, true);
|
||||
|
||||
for (const object of this.objects) {
|
||||
if (this.isSelectedObject(object)) continue;
|
||||
object.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
(child.material as THREE.MeshStandardMaterial).emissive.setHex(0x000000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const intersected = this.getRoot(intersects[0].object);
|
||||
if (this.isSelectedObject(intersected)) return;
|
||||
intersected.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
(child.material as THREE.MeshStandardMaterial).emissive.setHex(0x191919);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
private onmousedown(ev: MouseEvent) {
|
||||
if (this.isTransformMode) return;
|
||||
if (ev.target !== this.canvas || ev.button !== 0) return;
|
||||
|
||||
const rect = (ev.target as HTMLElement).getBoundingClientRect();
|
||||
const x = (((ev.clientX * window.devicePixelRatio) - rect.left) / this.canvas.width) * 2 - 1;
|
||||
const y = -(((ev.clientY * window.devicePixelRatio) - rect.top) / this.canvas.height) * 2 + 1;
|
||||
const pos = new THREE.Vector2(x, y);
|
||||
|
||||
this.camera.updateMatrixWorld();
|
||||
|
||||
const raycaster = new THREE.Raycaster();
|
||||
raycaster.setFromCamera(pos, this.camera);
|
||||
|
||||
const intersects = raycaster.intersectObjects(this.objects, true);
|
||||
|
||||
for (const object of this.objects) {
|
||||
object.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
(child.material as THREE.MeshStandardMaterial).emissive.setHex(0x000000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (intersects.length > 0) {
|
||||
const selectedObj = this.getRoot(intersects[0].object);
|
||||
this.selectFurniture(selectedObj);
|
||||
} else {
|
||||
this.selectedObject = null;
|
||||
this.onChangeSelect(null);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
private getRoot(obj: THREE.Object3D): THREE.Object3D {
|
||||
let found = false;
|
||||
let x = obj.parent;
|
||||
while (!found) {
|
||||
if (x.parent.parent == null) {
|
||||
found = true;
|
||||
} else {
|
||||
x = x.parent;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
@autobind
|
||||
private isSelectedObject(obj: THREE.Object3D): boolean {
|
||||
if (this.selectedObject == null) {
|
||||
return false;
|
||||
} else {
|
||||
return obj.name === this.selectedObject.name;
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
private selectFurniture(obj: THREE.Object3D) {
|
||||
this.selectedObject = obj;
|
||||
this.onChangeSelect(obj);
|
||||
obj.traverse(child => {
|
||||
if (child instanceof THREE.Mesh) {
|
||||
(child.material as THREE.MeshStandardMaterial).emissive.setHex(0xff0000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
public enterTransformMode(type: 'translate' | 'rotate') {
|
||||
this.isTransformMode = true;
|
||||
this.furnitureControl.setMode(type);
|
||||
this.furnitureControl.attach(this.selectedObject);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public exitTransformMode() {
|
||||
this.isTransformMode = false;
|
||||
this.furnitureControl.detach();
|
||||
}
|
||||
|
||||
@autobind
|
||||
public updateProp(key: string, value: any) {
|
||||
const furniture = this.furnitures.find(furniture => furniture.id === this.selectedObject.name);
|
||||
if (furniture.props == null) furniture.props = {};
|
||||
furniture.props[key] = value;
|
||||
this.applyCustomColor(this.selectedObject);
|
||||
this.applyCustomTexture(this.selectedObject);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public addFurniture(type: string) {
|
||||
const furniture = {
|
||||
id: uuid(),
|
||||
type: type,
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
},
|
||||
rotation: {
|
||||
x: 0,
|
||||
y: 0,
|
||||
z: 0,
|
||||
},
|
||||
};
|
||||
|
||||
this.furnitures.push(furniture);
|
||||
|
||||
this.loadFurniture(furniture).then(obj => {
|
||||
this.scene.add(obj.scene);
|
||||
this.objects.push(obj.scene);
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
public removeFurniture() {
|
||||
this.exitTransformMode();
|
||||
const obj = this.selectedObject;
|
||||
this.scene.remove(obj);
|
||||
this.objects = this.objects.filter(object => object.name !== obj.name);
|
||||
this.furnitures = this.furnitures.filter(furniture => furniture.id !== obj.name);
|
||||
this.selectedObject = null;
|
||||
this.onChangeSelect(null);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public updateCarpetColor(color: string) {
|
||||
this.roomInfo.carpetColor = color;
|
||||
this.applyCarpetColor();
|
||||
}
|
||||
|
||||
@autobind
|
||||
public changeRoomType(type: string) {
|
||||
this.roomInfo.roomType = type;
|
||||
this.scene.remove(this.roomObj);
|
||||
this.loadRoom();
|
||||
}
|
||||
|
||||
@autobind
|
||||
public getRoomInfo() {
|
||||
for (const obj of this.objects) {
|
||||
const furniture = this.furnitures.find(f => f.id === obj.name);
|
||||
furniture.position.x = obj.position.x;
|
||||
furniture.position.y = obj.position.y;
|
||||
furniture.position.z = obj.position.z;
|
||||
furniture.rotation.x = obj.rotation.x;
|
||||
furniture.rotation.y = obj.rotation.y;
|
||||
furniture.rotation.z = obj.rotation.z;
|
||||
}
|
||||
|
||||
return this.roomInfo;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public getSelectedObject() {
|
||||
return this.selectedObject;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public findFurnitureById(id: string) {
|
||||
return this.furnitures.find(furniture => furniture.id === id);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public destroy() {
|
||||
// Stop render loop
|
||||
window.cancelAnimationFrame(this.renderFrameRequestId);
|
||||
}
|
||||
}
|
@ -72,6 +72,9 @@ export default Vue.extend({
|
||||
columnWidth: '80%'
|
||||
}
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
grid: {
|
||||
clipMarkers: false,
|
||||
padding: {
|
||||
|
@ -98,7 +98,7 @@ export default Vue.extend({
|
||||
return {
|
||||
inputValue: this.input && this.input.default ? this.input.default : null,
|
||||
userInputValue: null,
|
||||
selectedValue: this.select ? this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null,
|
||||
selectedValue: this.select ? this.select.default ? this.select.default : this.select.items ? this.select.items[0].value : this.select.groupedItems[0].items[0].value : null,
|
||||
canOk: true,
|
||||
faTimesCircle, faQuestionCircle
|
||||
};
|
||||
|
@ -55,6 +55,20 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
customEmojis() {
|
||||
if (this.name) {
|
||||
const customEmoji = this.customEmojis.find(x => x.name == this.name);
|
||||
if (customEmoji) {
|
||||
this.customEmoji = customEmoji;
|
||||
this.url = this.$store.state.device.disableShowingAnimatedImages
|
||||
? getStaticImageUrl(customEmoji.url)
|
||||
: customEmoji.url;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.name) {
|
||||
const customEmoji = this.customEmojis.find(x => x.name == this.name);
|
||||
@ -80,7 +94,7 @@ export default Vue.extend({
|
||||
|
||||
this.url = `${twemojiBase}/2/svg/${codes.join('-')}.svg`;
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
|
@ -5,17 +5,17 @@
|
||||
|
||||
<div style="overflow: hidden; line-height: 28px;">
|
||||
<p class="turn" v-if="!iAmPlayer && !game.isEnded">
|
||||
<mfm :key="'turn:' + $options.filters.userName(turnUser)" :text="$t('@.reversi.turn-of', { name: $options.filters.userName(turnUser) })" :should-break="false" :plain-text="true" :custom-emojis="turnUser.emojis"/>
|
||||
<mfm :key="'turn:' + $options.filters.userName(turnUser)" :text="$t('@.reversi.turn-of', { name: $options.filters.userName(turnUser) })" :plain="true" :custom-emojis="turnUser.emojis"/>
|
||||
<mk-ellipsis/>
|
||||
</p>
|
||||
<p class="turn" v-if="logPos != logs.length">
|
||||
<mfm :key="'past-turn-of:' + $options.filters.userName(turnUser)" :text="$t('@.reversi.past-turn-of', { name: $options.filters.userName(turnUser) })" :should-break="false" :plain-text="true" :custom-emojis="turnUser.emojis"/>
|
||||
<mfm :key="'past-turn-of:' + $options.filters.userName(turnUser)" :text="$t('@.reversi.past-turn-of', { name: $options.filters.userName(turnUser) })" :plain="true" :custom-emojis="turnUser.emojis"/>
|
||||
</p>
|
||||
<p class="turn1" v-if="iAmPlayer && !game.isEnded && !isMyTurn">{{ $t('@.reversi.opponent-turn') }}<mk-ellipsis/></p>
|
||||
<p class="turn2" v-if="iAmPlayer && !game.isEnded && isMyTurn" v-animate-css="{ classes: 'tada', iteration: 'infinite' }">{{ $t('@.reversi.my-turn') }}</p>
|
||||
<p class="result" v-if="game.isEnded && logPos == logs.length">
|
||||
<template v-if="game.winner">
|
||||
<mfm :key="'won'" :text="$t('@.reversi.won', { name: $options.filters.userName(game.winner) })" :should-break="false" :plain-text="true" :custom-emojis="game.winner.emojis"/>
|
||||
<mfm :key="'won'" :text="$t('@.reversi.won', { name: $options.filters.userName(game.winner) })" :plain="true" :custom-emojis="game.winner.emojis"/>
|
||||
<span v-if="game.surrendered != null"> ({{ $t('surrendered') }})</span>
|
||||
</template>
|
||||
<template v-else>{{ $t('@.reversi.drawn') }}</template>
|
||||
|
@ -41,7 +41,6 @@ export default Vue.extend({
|
||||
padding 10px
|
||||
width 100%
|
||||
height 40px
|
||||
font-family sans-serif
|
||||
font-size 16px
|
||||
color var(--googleSearchFg)
|
||||
background var(--googleSearchBg)
|
||||
|
@ -1,11 +1,17 @@
|
||||
<template>
|
||||
<router-link class="ldlomzub" :to="`/${ canonical }`" v-user-preview="canonical">
|
||||
<router-link class="ldlomzub" :to="url" v-user-preview="canonical" v-if="url.startsWith('/')">
|
||||
<span class="me" v-if="isMe">{{ $t('@.you') }}</span>
|
||||
<span class="main">
|
||||
<span class="username">@{{ username }}</span>
|
||||
<span class="host" :class="{ fade: $store.state.settings.contrastedAcct }" v-if="(host != localHost) || $store.state.settings.showFullAcct">@{{ toUnicode(host) }}</span>
|
||||
</span>
|
||||
</router-link>
|
||||
<a class="ldlomzub" :href="url" target="_blank" rel="noopener" v-else>
|
||||
<span class="main">
|
||||
<span class="username">@{{ username }}</span>
|
||||
<span class="host" :class="{ fade: $store.state.settings.contrastedAcct }">@{{ toUnicode(host) }}</span>
|
||||
</span>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -32,6 +38,15 @@ export default Vue.extend({
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
url(): string {
|
||||
switch (this.host) {
|
||||
case 'twitter.com':
|
||||
case 'github.com':
|
||||
return `https://${this.host}/${this.username}`;
|
||||
default:
|
||||
return `/${this.canonical}`;
|
||||
}
|
||||
},
|
||||
canonical(): string {
|
||||
return this.host === localHost ? `@${this.username}` : `@${this.username}@${toUnicode(this.host)}`;
|
||||
},
|
||||
|
@ -30,6 +30,7 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../i18n';
|
||||
import * as autosize from 'autosize';
|
||||
import { formatTimeString } from '../../../../../misc/format-time-string';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/messaging-room.form.vue'),
|
||||
@ -84,13 +85,26 @@ export default Vue.extend({
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
onPaste(e) {
|
||||
async onPaste(e: ClipboardEvent) {
|
||||
const data = e.clipboardData;
|
||||
const items = data.items;
|
||||
|
||||
if (items.length == 1) {
|
||||
if (items[0].kind == 'file') {
|
||||
this.upload(items[0].getAsFile());
|
||||
const file = items[0].getAsFile();
|
||||
const lio = file.name.lastIndexOf('.');
|
||||
const ext = lio >= 0 ? file.name.slice(lio) : '';
|
||||
const formatted = `${formatTimeString(new Date(file.lastModified), this.$store.state.settings.pastedFileName).replace(/{{number}}/g, '1')}${ext}`;
|
||||
const name = this.$store.state.settings.pasteDialog
|
||||
? await this.$root.dialog({
|
||||
title: this.$t('@.post-form.enter-file-name'),
|
||||
input: {
|
||||
default: formatted
|
||||
},
|
||||
allowEmpty: false
|
||||
}).then(({ canceled, result }) => canceled ? false : result)
|
||||
: formatted;
|
||||
if (name) this.upload(file, name);
|
||||
}
|
||||
} else {
|
||||
if (items[0].kind == 'file') {
|
||||
@ -157,8 +171,8 @@ export default Vue.extend({
|
||||
this.upload((this.$refs.file as any).files[0]);
|
||||
},
|
||||
|
||||
upload(file) {
|
||||
(this.$refs.uploader as any).upload(file);
|
||||
upload(file: File, name?: string) {
|
||||
(this.$refs.uploader as any).upload(file, this.$store.state.settings.uploadFolder, name);
|
||||
},
|
||||
|
||||
onUploaded(file) {
|
||||
|
@ -22,11 +22,11 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
shouldBreak: {
|
||||
plain: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
default: false
|
||||
},
|
||||
plainText: {
|
||||
nowrap: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
@ -50,7 +50,7 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
render(createElement) {
|
||||
if (this.text == null || this.text == '') return;
|
||||
|
||||
const ast = (this.plainText ? parsePlain : parse)(this.text);
|
||||
const ast = (this.plain ? parsePlain : parse)(this.text);
|
||||
|
||||
let bigCount = 0;
|
||||
let motionCount = 0;
|
||||
@ -60,7 +60,7 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
case 'text': {
|
||||
const text = token.node.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
||||
|
||||
if (this.shouldBreak) {
|
||||
if (!this.plain) {
|
||||
const x = text.split('\n')
|
||||
.map(t => t == '' ? [createElement('br')] : [createElement('span', t), createElement('br')]);
|
||||
x[x.length - 1].pop();
|
||||
@ -270,7 +270,7 @@ export default Vue.component('misskey-flavored-markdown', {
|
||||
},
|
||||
props: {
|
||||
customEmojis: this.customEmojis || customEmojis,
|
||||
normal: this.plainText
|
||||
normal: this.plain
|
||||
}
|
||||
})];
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<mfm-core v-bind="$attrs" class="havbbuyv" v-once/>
|
||||
<mfm-core v-bind="$attrs" class="havbbuyv" :class="{ nowrap: $attrs['nowrap'] }" v-once/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -17,6 +17,9 @@ export default Vue.extend({
|
||||
.havbbuyv
|
||||
white-space pre-wrap
|
||||
|
||||
&.nowrap
|
||||
white-space pre
|
||||
|
||||
>>> .title
|
||||
display block
|
||||
margin-bottom 4px
|
||||
@ -27,6 +30,7 @@ export default Vue.extend({
|
||||
border-radius 4px
|
||||
|
||||
>>> .quote
|
||||
display block
|
||||
margin 8px
|
||||
padding 6px 0 6px 12px
|
||||
color var(--mfmQuote)
|
||||
|
@ -22,66 +22,95 @@ export default Vue.extend({
|
||||
},
|
||||
computed: {
|
||||
items(): any[] {
|
||||
return [{
|
||||
icon: 'at',
|
||||
text: this.$t('mention'),
|
||||
action: this.mention
|
||||
}, null, {
|
||||
icon: 'info-circle',
|
||||
text: this.$t('detail'),
|
||||
action: this.detail
|
||||
}, {
|
||||
icon: faCopy,
|
||||
text: this.$t('copy-content'),
|
||||
action: this.copyContent
|
||||
}, {
|
||||
icon: 'link',
|
||||
text: this.$t('copy-link'),
|
||||
action: this.copyLink
|
||||
}, this.note.uri ? {
|
||||
icon: 'external-link-square-alt',
|
||||
text: this.$t('remote'),
|
||||
action: () => {
|
||||
window.open(this.note.uri, '_blank');
|
||||
}
|
||||
} : undefined,
|
||||
null,
|
||||
this.isFavorited ? {
|
||||
icon: 'star',
|
||||
text: this.$t('unfavorite'),
|
||||
action: () => this.toggleFavorite(false)
|
||||
} : {
|
||||
icon: 'star',
|
||||
text: this.$t('favorite'),
|
||||
action: () => this.toggleFavorite(true)
|
||||
},
|
||||
this.note.userId != this.$store.state.i.id ? this.isWatching ? {
|
||||
icon: faEyeSlash,
|
||||
text: this.$t('unwatch'),
|
||||
action: () => this.toggleWatch(false)
|
||||
} : {
|
||||
icon: faEye,
|
||||
text: this.$t('watch'),
|
||||
action: () => this.toggleWatch(true)
|
||||
} : undefined,
|
||||
this.note.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.note.id) ? {
|
||||
icon: 'thumbtack',
|
||||
text: this.$t('unpin'),
|
||||
action: () => this.togglePin(false)
|
||||
} : {
|
||||
icon: 'thumbtack',
|
||||
text: this.$t('pin'),
|
||||
action: () => this.togglePin(true)
|
||||
} : undefined,
|
||||
...(this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [
|
||||
null, {
|
||||
icon: ['far', 'trash-alt'],
|
||||
text: this.$t('delete'),
|
||||
action: this.del
|
||||
}]
|
||||
: []
|
||||
)]
|
||||
.filter(x => x !== undefined)
|
||||
if (this.$store.getters.isSignedIn) {
|
||||
return [{
|
||||
icon: 'at',
|
||||
text: this.$t('mention'),
|
||||
action: this.mention
|
||||
}, null, {
|
||||
icon: 'info-circle',
|
||||
text: this.$t('detail'),
|
||||
action: this.detail
|
||||
}, {
|
||||
icon: faCopy,
|
||||
text: this.$t('copy-content'),
|
||||
action: this.copyContent
|
||||
}, {
|
||||
icon: 'link',
|
||||
text: this.$t('copy-link'),
|
||||
action: this.copyLink
|
||||
}, this.note.uri ? {
|
||||
icon: 'external-link-square-alt',
|
||||
text: this.$t('remote'),
|
||||
action: () => {
|
||||
window.open(this.note.uri, '_blank');
|
||||
}
|
||||
} : undefined,
|
||||
null,
|
||||
this.isFavorited ? {
|
||||
icon: 'star',
|
||||
text: this.$t('unfavorite'),
|
||||
action: () => this.toggleFavorite(false)
|
||||
} : {
|
||||
icon: 'star',
|
||||
text: this.$t('favorite'),
|
||||
action: () => this.toggleFavorite(true)
|
||||
},
|
||||
this.note.userId != this.$store.state.i.id ? this.isWatching ? {
|
||||
icon: faEyeSlash,
|
||||
text: this.$t('unwatch'),
|
||||
action: () => this.toggleWatch(false)
|
||||
} : {
|
||||
icon: faEye,
|
||||
text: this.$t('watch'),
|
||||
action: () => this.toggleWatch(true)
|
||||
} : undefined,
|
||||
this.note.userId == this.$store.state.i.id ? (this.$store.state.i.pinnedNoteIds || []).includes(this.note.id) ? {
|
||||
icon: 'thumbtack',
|
||||
text: this.$t('unpin'),
|
||||
action: () => this.togglePin(false)
|
||||
} : {
|
||||
icon: 'thumbtack',
|
||||
text: this.$t('pin'),
|
||||
action: () => this.togglePin(true)
|
||||
} : undefined,
|
||||
...(this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin || this.$store.state.i.isModerator ? [
|
||||
null,
|
||||
this.note.userId == this.$store.state.i.id ? {
|
||||
icon: 'undo-alt',
|
||||
text: this.$t('delete-and-edit'),
|
||||
action: this.deleteAndEdit
|
||||
} : undefined,
|
||||
{
|
||||
icon: ['far', 'trash-alt'],
|
||||
text: this.$t('delete'),
|
||||
action: this.del
|
||||
}]
|
||||
: []
|
||||
)]
|
||||
.filter(x => x !== undefined);
|
||||
} else {
|
||||
return [{
|
||||
icon: 'info-circle',
|
||||
text: this.$t('detail'),
|
||||
action: this.detail
|
||||
}, {
|
||||
icon: faCopy,
|
||||
text: this.$t('copy-content'),
|
||||
action: this.copyContent
|
||||
}, {
|
||||
icon: 'link',
|
||||
text: this.$t('copy-link'),
|
||||
action: this.copyLink
|
||||
}, this.note.uri ? {
|
||||
icon: 'external-link-square-alt',
|
||||
text: this.$t('remote'),
|
||||
action: () => {
|
||||
window.open(this.note.uri, '_blank');
|
||||
}
|
||||
} : undefined]
|
||||
.filter(x => x !== undefined);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@ -154,6 +183,25 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
deleteAndEdit() {
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('delete-and-edit-confirm'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (canceled) return;
|
||||
this.$root.api('notes/delete', {
|
||||
noteId: this.note.id
|
||||
}).then(() => {
|
||||
this.destroyDom();
|
||||
});
|
||||
this.$post({
|
||||
initialNote: this.note,
|
||||
reply: this.note.reply,
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
toggleFavorite(favorite: boolean) {
|
||||
this.$root.api(favorite ? 'notes/favorites/create' : 'notes/favorites/delete', {
|
||||
noteId: this.note.id
|
||||
|
@ -16,10 +16,11 @@ import XIf from './page.if.vue';
|
||||
import XTextarea from './page.textarea.vue';
|
||||
import XPost from './page.post.vue';
|
||||
import XCounter from './page.counter.vue';
|
||||
import XRadioButton from './page.radio-button.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter
|
||||
XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter, XRadioButton
|
||||
},
|
||||
|
||||
props: {
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-button class="kudkigyw" @click="click()">{{ script.interpolate(value.text) }}</ui-button>
|
||||
<ui-button class="kudkigyw" @click="click()" :primary="value.primary">{{ script.interpolate(value.text) }}</ui-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -27,6 +27,19 @@ export default Vue.extend({
|
||||
} else if (this.value.action === 'resetRandom') {
|
||||
this.script.aiScript.updateRandomSeed(Math.random());
|
||||
this.script.eval();
|
||||
} else if (this.value.action === 'pushEvent') {
|
||||
this.$root.api('page-push', {
|
||||
pageId: this.script.page.id,
|
||||
event: this.value.event,
|
||||
...(this.value.var ? {
|
||||
var: this.script.vars[this.value.var]
|
||||
} : {})
|
||||
});
|
||||
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
text: this.script.interpolate(this.value.message)
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,7 +49,7 @@ export default Vue.extend({
|
||||
<style lang="stylus" scoped>
|
||||
.kudkigyw
|
||||
display inline-block
|
||||
min-width 300px
|
||||
min-width 200px
|
||||
max-width 450px
|
||||
margin 8px 0
|
||||
</style>
|
@ -0,0 +1,37 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>{{ script.interpolate(value.title) }}</div>
|
||||
<ui-radio v-for="x in value.values" v-model="v" :value="x" :key="x">{{ x }}</ui-radio>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
script: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
v: this.value.default,
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div v-if="page" class="iroscrza" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners, center: page.alignCenter }" :style="{ fontFamily: page.font }" :key="path">
|
||||
<header>
|
||||
<div class="iroscrza" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners, center: page.alignCenter, serif: page.font === 'serif' }">
|
||||
<header v-if="showTitle">
|
||||
<div class="title">{{ page.title }}</div>
|
||||
</header>
|
||||
|
||||
@ -8,9 +8,13 @@
|
||||
<x-block v-for="child in page.content" :value="child" @input="v => updateBlock(v)" :page="page" :script="script" :key="child.id" :h="2"/>
|
||||
</div>
|
||||
|
||||
<footer>
|
||||
<footer v-if="showFooter">
|
||||
<small>@{{ page.user.username }}</small>
|
||||
<router-link v-if="$store.getters.isSignedIn && $store.state.i.id === page.userId" :to="`/i/pages/edit/${page.id}`">{{ $t('edit-this-page') }}</router-link>
|
||||
<template v-if="$store.getters.isSignedIn && $store.state.i.id === page.userId">
|
||||
<router-link :to="`/i/pages/edit/${page.id}`">{{ $t('edit-this-page') }}</router-link>
|
||||
<a v-if="$store.state.i.pinnedPageId === page.id" @click="pin(false)">{{ $t('unpin-this-page') }}</a>
|
||||
<a v-else @click="pin(true)">{{ $t('pin-this-page') }}</a>
|
||||
</template>
|
||||
<router-link :to="`./${page.name}/view-source`">{{ $t('view-source') }}</router-link>
|
||||
<div class="like">
|
||||
<button @click="unlike()" v-if="page.isLiked" :title="$t('unlike')"><fa :icon="faHeartS"/></button>
|
||||
@ -25,7 +29,7 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { faHeart as faHeartS } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHeart, faStickyNote } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faHeart } from '@fortawesome/free-regular-svg-icons';
|
||||
import XBlock from './page.block.vue';
|
||||
import { ASEvaluator } from '../../../../../../misc/aiscript/evaluator';
|
||||
import { collectPageVars } from '../../../scripts/collect-page-vars';
|
||||
@ -35,8 +39,10 @@ class Script {
|
||||
public aiScript: ASEvaluator;
|
||||
private onError: any;
|
||||
public vars: Record<string, any>;
|
||||
public page: Record<string, any>;
|
||||
|
||||
constructor(aiScript, onError) {
|
||||
constructor(page, aiScript, onError) {
|
||||
this.page = page;
|
||||
this.aiScript = aiScript;
|
||||
this.onError = onError;
|
||||
this.eval();
|
||||
@ -67,64 +73,43 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
props: {
|
||||
pageName: {
|
||||
type: String,
|
||||
page: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
username: {
|
||||
type: String,
|
||||
required: true
|
||||
showTitle: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
},
|
||||
showFooter: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
page: null,
|
||||
script: null,
|
||||
faHeartS, faHeart
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
path(): string {
|
||||
return this.username + '/' + this.pageName;
|
||||
}
|
||||
},
|
||||
|
||||
watch: {
|
||||
path() {
|
||||
this.fetch();
|
||||
}
|
||||
},
|
||||
|
||||
created() {
|
||||
this.fetch();
|
||||
const pageVars = this.getPageVars();
|
||||
this.script = new Script(this.page, new ASEvaluator(this.page.variables, pageVars, {
|
||||
randomSeed: Math.random(),
|
||||
user: this.page.user,
|
||||
visitor: this.$store.state.i,
|
||||
page: this.page,
|
||||
url: url
|
||||
}), e => {
|
||||
console.dir(e);
|
||||
});
|
||||
},
|
||||
|
||||
methods: {
|
||||
fetch() {
|
||||
this.$root.api('pages/show', {
|
||||
name: this.pageName,
|
||||
username: this.username,
|
||||
}).then(page => {
|
||||
this.page = page;
|
||||
this.$emit('init', {
|
||||
title: this.page.title,
|
||||
icon: faStickyNote
|
||||
});
|
||||
const pageVars = this.getPageVars();
|
||||
this.script = new Script(new ASEvaluator(this.page.variables, pageVars, {
|
||||
randomSeed: Math.random(),
|
||||
user: page.user,
|
||||
visitor: this.$store.state.i,
|
||||
page: page,
|
||||
url: url
|
||||
}), e => {
|
||||
console.dir(e);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
getPageVars() {
|
||||
return collectPageVars(this.page.content);
|
||||
},
|
||||
@ -145,6 +130,17 @@ export default Vue.extend({
|
||||
this.page.isLiked = false;
|
||||
this.page.likedCount--;
|
||||
});
|
||||
},
|
||||
|
||||
pin(pin) {
|
||||
this.$root.api('i/update', {
|
||||
pinnedPageId: pin ? this.page.id : null,
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -154,6 +150,10 @@ export default Vue.extend({
|
||||
.iroscrza
|
||||
overflow hidden
|
||||
background var(--face)
|
||||
|
||||
&.serif
|
||||
> div
|
||||
font-family serif
|
||||
|
||||
&.center
|
||||
text-align center
|
@ -26,13 +26,19 @@
|
||||
<option value="after">{{ $t('after') }}</option>
|
||||
</ui-select>
|
||||
<section v-if="expiration === 'at'">
|
||||
<ui-input v-model="atDate" type="date">{{ $t('deadline-date') }}</ui-input>
|
||||
<ui-input v-model="atTime" type="time">{{ $t('deadline-time') }}</ui-input>
|
||||
<ui-input v-model="atDate" type="date">
|
||||
<template #title>{{ $t('deadline-date') }}</template>
|
||||
</ui-input>
|
||||
<ui-input v-model="atTime" type="time">
|
||||
<template #title>{{ $t('deadline-time') }}</template>
|
||||
</ui-input>
|
||||
</section>
|
||||
<section v-if="expiration === 'after'">
|
||||
<ui-input v-model="after" type="number">{{ $t('interval') }}</ui-input>
|
||||
<ui-input v-model="after" type="number">
|
||||
<template #title>{{ $t('interval') }}</template>
|
||||
</ui-input>
|
||||
<ui-select v-model="unit">
|
||||
<template #label>{{ $t('unit') }}</template>
|
||||
<template #title>{{ $t('unit') }}</template>
|
||||
<option value="second">{{ $t('second') }}</option>
|
||||
<option value="minute">{{ $t('minute') }}</option>
|
||||
<option value="hour">{{ $t('hour') }}</option>
|
||||
@ -46,9 +52,11 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as moment from 'moment';
|
||||
import i18n from '../../../i18n';
|
||||
import { erase } from '../../../../../prelude/array';
|
||||
import { addTimespan } from '../../../../../prelude/time';
|
||||
import { formatDateTimeString } from '../../../../../misc/format-time-string';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/poll-editor.vue'),
|
||||
data() {
|
||||
@ -56,7 +64,7 @@ export default Vue.extend({
|
||||
choices: ['', ''],
|
||||
multiple: false,
|
||||
expiration: 'infinite',
|
||||
atDate: moment().add(1, 'day').toISOString().split('T')[0],
|
||||
atDate: formatDateTimeString(addTimespan(new Date(), 1, 'days'), 'yyyy-MM-dd'),
|
||||
atTime: '00:00',
|
||||
after: 0,
|
||||
unit: 'second'
|
||||
@ -89,7 +97,7 @@ export default Vue.extend({
|
||||
|
||||
get() {
|
||||
const at = () => {
|
||||
return moment(`${this.atDate} ${this.atTime}`).valueOf();
|
||||
return new Date(`${this.atDate} ${this.atTime}`).getTime();
|
||||
};
|
||||
|
||||
const after = () => {
|
||||
|
@ -5,7 +5,7 @@
|
||||
<div class="backdrop" :style="{ 'width': `${showResult ? (choice.votes / total * 100) : 0}%` }"></div>
|
||||
<span>
|
||||
<template v-if="choice.isVoted"><fa icon="check"/></template>
|
||||
<mfm :text="choice.text" :should-break="false" :plain-text="true" :custom-emojis="note.emojis"/>
|
||||
<mfm :text="choice.text" :plain="true" :custom-emojis="note.emojis"/>
|
||||
<span class="votes" v-if="showResult">({{ $t('vote-count').replace('{}', choice.votes) }})</span>
|
||||
</span>
|
||||
</li>
|
||||
|
@ -36,7 +36,7 @@ export default Vue.extend({
|
||||
required: true
|
||||
},
|
||||
detachMediaFn: {
|
||||
type: Object,
|
||||
type: Function,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
@ -15,9 +15,14 @@ export default Vue.extend({
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
customEmojis: (this.$root.getMetaSync() || { emojis: [] }).emojis || []
|
||||
customEmojis: []
|
||||
};
|
||||
},
|
||||
created() {
|
||||
this.$root.getMeta().then(meta => {
|
||||
if (meta && meta.emojis) this.customEmojis = meta.emojis;
|
||||
});
|
||||
},
|
||||
computed: {
|
||||
str(): any {
|
||||
switch (this.reaction) {
|
||||
|
@ -276,6 +276,7 @@ export default Vue.extend({
|
||||
font-size 14px
|
||||
color var(--popupFg)
|
||||
border-bottom solid var(--lineWidth) var(--faceDivider)
|
||||
line-height 20px
|
||||
|
||||
> .buttons
|
||||
padding 4px 4px 8px 4px
|
||||
|
@ -28,6 +28,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ui-switch v-model="usePasswordLessLogin" @change="updatePasswordLessLogin" v-if="$store.state.i.securityKeysList.length > 0">
|
||||
{{ $t('use-password-less-login') }}
|
||||
</ui-switch>
|
||||
|
||||
<ui-info warn v-if="registration && registration.error">{{ $t('something-went-wrong') }} {{ registration.error }}</ui-info>
|
||||
<ui-button v-if="!registration || registration.error" @click="addSecurityKey">{{ $t('register') }}</ui-button>
|
||||
|
||||
@ -80,6 +84,7 @@ export default Vue.extend({
|
||||
return {
|
||||
data: null,
|
||||
supportsCredentials: !!navigator.credentials,
|
||||
usePasswordLessLogin: this.$store.state.i.usePasswordLessLogin,
|
||||
registration: null,
|
||||
keyName: '',
|
||||
token: null
|
||||
@ -112,6 +117,9 @@ export default Vue.extend({
|
||||
if (canceled) return;
|
||||
this.$root.api('i/2fa/unregister', {
|
||||
password: password
|
||||
}).then(() => {
|
||||
this.usePasswordLessLogin = false;
|
||||
this.updatePasswordLessLogin();
|
||||
}).then(() => {
|
||||
this.$notify(this.$t('unregistered'));
|
||||
this.$store.state.i.twoFactorEnabled = false;
|
||||
@ -157,6 +165,9 @@ export default Vue.extend({
|
||||
return this.$root.api('i/2fa/remove-key', {
|
||||
password,
|
||||
credentialId: key.id
|
||||
}).then(() => {
|
||||
this.usePasswordLessLogin = false;
|
||||
this.updatePasswordLessLogin();
|
||||
}).then(() => {
|
||||
this.$notify(this.$t('key-unregistered'));
|
||||
});
|
||||
@ -213,6 +224,11 @@ export default Vue.extend({
|
||||
this.registration.stage = -1;
|
||||
});
|
||||
});
|
||||
},
|
||||
updatePasswordLessLogin() {
|
||||
this.$root.api('i/2fa/password-less', {
|
||||
value: !!this.usePasswordLessLogin
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
53
src/client/app/common/views/components/settings/app-type.vue
Normal file
53
src/client/app/common/views/components/settings/app-type.vue
Normal file
@ -0,0 +1,53 @@
|
||||
<template>
|
||||
<ui-card>
|
||||
<template #title><fa :icon="faMobileAlt"/> {{ $t('title') }}</template>
|
||||
|
||||
<section class="fit-top">
|
||||
<p>{{ $t('intro') }}</p>
|
||||
<ui-select v-model="appTypeForce" :placeholder="$t('intro')">
|
||||
<option v-for="x in ['auto', 'desktop', 'mobile']" :value="x" :key="x">{{ $t(`choices.${x}`) }}</option>
|
||||
</ui-select>
|
||||
<ui-info warn>{{ $t('info') }}</ui-info>
|
||||
</section>
|
||||
</ui-card>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { faMobileAlt } from '@fortawesome/free-solid-svg-icons'
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('common/views/components/settings/app-type.vue'),
|
||||
|
||||
data() {
|
||||
return {
|
||||
faMobileAlt
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
appTypeForce: {
|
||||
get() { return this.$store.state.device.appTypeForce; },
|
||||
set(value) {
|
||||
this.$store.commit('device/set', { key: 'appTypeForce', value });
|
||||
this.reload();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
reload() {
|
||||
this.$root.dialog({
|
||||
type: 'warning',
|
||||
text: this.$t('@.reload-to-apply-the-setting'),
|
||||
showCancelButton: true
|
||||
}).then(({ canceled }) => {
|
||||
if (!canceled) {
|
||||
location.reload();
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
@ -11,6 +11,12 @@
|
||||
<header>{{ $t('stats') }}</header>
|
||||
<div ref="chart" style="margin-bottom: -16px; margin-left: -8px; color: #000;"></div>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>{{ $t('default-upload-folder') }}</header>
|
||||
<ui-input v-model="uploadFolderName" readonly>{{ $t('default-upload-folder-name') }}</ui-input>
|
||||
<ui-button @click="chooseUploadFolder()">{{ $t('change-default-upload-folder') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
</template>
|
||||
|
||||
@ -26,7 +32,8 @@ export default Vue.extend({
|
||||
return {
|
||||
fetching: true,
|
||||
usage: null,
|
||||
capacity: null
|
||||
capacity: null,
|
||||
uploadFolderName: null
|
||||
};
|
||||
},
|
||||
|
||||
@ -40,10 +47,25 @@ export default Vue.extend({
|
||||
l: 0.5
|
||||
})
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
uploadFolder: {
|
||||
get() { return this.$store.state.settings.uploadFolder; },
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'uploadFolder', value }); }
|
||||
},
|
||||
},
|
||||
|
||||
mounted() {
|
||||
if (this.uploadFolder == null) {
|
||||
this.uploadFolderName = this.$t('@._settings.root');
|
||||
} else {
|
||||
this.$root.api('drive/folders/show', {
|
||||
folderId: this.uploadFolder
|
||||
}).then(folder => {
|
||||
this.uploadFolderName = folder.name;
|
||||
});
|
||||
}
|
||||
|
||||
this.$root.api('drive').then(info => {
|
||||
this.capacity = info.capacity;
|
||||
this.usage = info.usage;
|
||||
@ -89,6 +111,9 @@ export default Vue.extend({
|
||||
height: 150,
|
||||
zoom: {
|
||||
enabled: false
|
||||
},
|
||||
toolbar: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
plotOptions: {
|
||||
@ -152,6 +177,13 @@ export default Vue.extend({
|
||||
|
||||
chart.render();
|
||||
});
|
||||
},
|
||||
|
||||
chooseUploadFolder() {
|
||||
this.$chooseDriveFolder().then(folder => {
|
||||
this.uploadFolder = folder ? folder.id : null;
|
||||
this.uploadFolderName = folder ? folder.name : this.$t('@._settings.root');
|
||||
})
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -51,6 +51,26 @@
|
||||
<template #desc v-if="bannerUploading">{{ $t('uploading') }}<mk-ellipsis/></template>
|
||||
</ui-input>
|
||||
|
||||
<div class="fields">
|
||||
<header>{{ $t('profile-metadata') }}</header>
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="fieldName0">{{ $t('metadata-label') }}</ui-input>
|
||||
<ui-input v-model="fieldValue0">{{ $t('metadata-content') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="fieldName1">{{ $t('metadata-label') }}</ui-input>
|
||||
<ui-input v-model="fieldValue1">{{ $t('metadata-content') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="fieldName2">{{ $t('metadata-label') }}</ui-input>
|
||||
<ui-input v-model="fieldValue2">{{ $t('metadata-content') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
<ui-horizon-group>
|
||||
<ui-input v-model="fieldName3">{{ $t('metadata-label') }}</ui-input>
|
||||
<ui-input v-model="fieldValue3">{{ $t('metadata-content') }}</ui-input>
|
||||
</ui-horizon-group>
|
||||
</div>
|
||||
|
||||
<ui-button @click="save(true)"><fa :icon="faSave"/> {{ $t('save') }}</ui-button>
|
||||
</ui-form>
|
||||
</section>
|
||||
@ -139,6 +159,14 @@ export default Vue.extend({
|
||||
username: null,
|
||||
location: null,
|
||||
description: null,
|
||||
fieldName0: null,
|
||||
fieldValue0: null,
|
||||
fieldName1: null,
|
||||
fieldValue1: null,
|
||||
fieldName2: null,
|
||||
fieldValue2: null,
|
||||
fieldName3: null,
|
||||
fieldValue3: null,
|
||||
lang: null,
|
||||
birthday: null,
|
||||
avatarId: null,
|
||||
@ -189,6 +217,15 @@ export default Vue.extend({
|
||||
this.isLocked = this.$store.state.i.isLocked;
|
||||
this.carefulBot = this.$store.state.i.carefulBot;
|
||||
this.autoAcceptFollowed = this.$store.state.i.autoAcceptFollowed;
|
||||
|
||||
this.fieldName0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].name : null;
|
||||
this.fieldValue0 = this.$store.state.i.fields[0] ? this.$store.state.i.fields[0].value : null;
|
||||
this.fieldName1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].name : null;
|
||||
this.fieldValue1 = this.$store.state.i.fields[1] ? this.$store.state.i.fields[1].value : null;
|
||||
this.fieldName2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].name : null;
|
||||
this.fieldValue2 = this.$store.state.i.fields[2] ? this.$store.state.i.fields[2].value : null;
|
||||
this.fieldName3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].name : null;
|
||||
this.fieldValue3 = this.$store.state.i.fields[3] ? this.$store.state.i.fields[3].value : null;
|
||||
},
|
||||
|
||||
methods: {
|
||||
@ -237,6 +274,13 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
save(notify) {
|
||||
const fields = [
|
||||
{ name: this.fieldName0, value: this.fieldValue0 },
|
||||
{ name: this.fieldName1, value: this.fieldValue1 },
|
||||
{ name: this.fieldName2, value: this.fieldValue2 },
|
||||
{ name: this.fieldName3, value: this.fieldValue3 },
|
||||
];
|
||||
|
||||
this.saving = true;
|
||||
|
||||
this.$root.api('i/update', {
|
||||
@ -247,6 +291,7 @@ export default Vue.extend({
|
||||
birthday: this.birthday || null,
|
||||
avatarId: this.avatarId || undefined,
|
||||
bannerId: this.bannerId || undefined,
|
||||
fields,
|
||||
isCat: !!this.isCat,
|
||||
isBot: !!this.isBot,
|
||||
isLocked: !!this.isLocked,
|
||||
@ -265,6 +310,29 @@ export default Vue.extend({
|
||||
text: this.$t('saved')
|
||||
});
|
||||
}
|
||||
}).catch(err => {
|
||||
this.saving = false;
|
||||
switch(err.id) {
|
||||
case 'f419f9f8-2f4d-46b1-9fb4-49d3a2fd7191':
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
title: this.$t('unable-to-process'),
|
||||
text: this.$t('avatar-not-an-image')
|
||||
});
|
||||
break;
|
||||
case '75aedb19-2afd-4e6d-87fc-67941256fa60':
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
title: this.$t('unable-to-process'),
|
||||
text: this.$t('banner-not-an-image')
|
||||
});
|
||||
break;
|
||||
default:
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('unable-to-process')
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
@ -366,4 +434,9 @@ export default Vue.extend({
|
||||
height 72px
|
||||
margin auto
|
||||
|
||||
.fields
|
||||
> header
|
||||
padding 8px 0px
|
||||
font-weight bold
|
||||
|
||||
</style>
|
||||
|
@ -47,6 +47,7 @@
|
||||
<ui-switch v-model="disableAnimatedMfm">{{ $t('@._settings.disable-animated-mfm') }}</ui-switch>
|
||||
<ui-switch v-model="disableShowingAnimatedImages">{{ $t('@._settings.disable-showing-animated-images') }}</ui-switch>
|
||||
<ui-switch v-model="remainDeletedNote">{{ $t('@._settings.remain-deleted-note') }}</ui-switch>
|
||||
<ui-switch v-model="enableMobileQuickNotificationView">{{ $t('@._settings.enable-quick-notification-view') }}</ui-switch>
|
||||
</section>
|
||||
<section>
|
||||
<header>{{ $t('@._settings.line-width') }}</header>
|
||||
@ -140,7 +141,36 @@
|
||||
|
||||
<section>
|
||||
<header>{{ $t('@._settings.web-search-engine') }}</header>
|
||||
<ui-input v-model="webSearchEngine">{{ $t('@._settings.web-search-engine') }}<template #desc>{{ $t('@._settings.web-search-engine-desc') }}</template></ui-input>
|
||||
<ui-input v-model="webSearchEngine">{{ $t('@._settings.web-search-engine') }}
|
||||
<template #desc>{{ $t('@._settings.web-search-engine-desc') }}</template>
|
||||
</ui-input>
|
||||
<ui-button @click="save('webSearchEngine', webSearchEngine)"><fa :icon="faSave"/> {{ $t('@._settings.save') }}</ui-button>
|
||||
</section>
|
||||
|
||||
<section v-if="!$root.isMobile">
|
||||
<header>{{ $t('@._settings.paste') }}</header>
|
||||
<ui-input v-model="pastedFileName">{{ $t('@._settings.pasted-file-name') }}
|
||||
<template v-if="pastedFileName === this.$store.state.settings.pastedFileName" #desc>{{ $t('@._settings.pasted-file-name-desc') }}</template>
|
||||
<template v-else #desc>{{ pastedFileNamePreview() }}</template>
|
||||
</ui-input>
|
||||
<ui-button @click="save('pastedFileName', pastedFileName)"><fa :icon="faSave"/> {{ $t('@._settings.save') }}</ui-button>
|
||||
|
||||
<ui-switch v-model="pasteDialog">{{ $t('@._settings.paste-dialog') }}
|
||||
<template #desc>{{ $t('@._settings.paste-dialog-desc') }}</template>
|
||||
</ui-switch>
|
||||
</section>
|
||||
|
||||
<section>
|
||||
<header>{{ $t('@._settings.room') }}</header>
|
||||
<ui-select v-model="roomGraphicsQuality">
|
||||
<template #label>{{ $t('@._settings._room.graphicsQuality') }}</template>
|
||||
<option value="ultra">{{ $t('@._settings._room._graphicsQuality.ultra') }}</option>
|
||||
<option value="high">{{ $t('@._settings._room._graphicsQuality.high') }}</option>
|
||||
<option value="medium">{{ $t('@._settings._room._graphicsQuality.medium') }}</option>
|
||||
<option value="low">{{ $t('@._settings._room._graphicsQuality.low') }}</option>
|
||||
<option value="cheep">{{ $t('@._settings._room._graphicsQuality.cheep') }}</option>
|
||||
</ui-select>
|
||||
<ui-switch v-model="roomUseOrthographicCamera">{{ $t('@._settings._room.useOrthographicCamera') }}</ui-switch>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
@ -163,6 +193,7 @@
|
||||
</ui-card>
|
||||
|
||||
<x-language/>
|
||||
<x-app-type/>
|
||||
</template>
|
||||
|
||||
<template v-if="page == null || page == 'notification'">
|
||||
@ -271,10 +302,13 @@ import XPassword from './password.vue';
|
||||
import XProfile from './profile.vue';
|
||||
import XApi from './api.vue';
|
||||
import XLanguage from './language.vue';
|
||||
import XAppType from './app-type.vue';
|
||||
import XNotification from './notification.vue';
|
||||
|
||||
import { url, version } from '../../../../config';
|
||||
import checkForUpdate from '../../../scripts/check-for-update';
|
||||
import { formatTimeString } from '../../../../../../misc/format-time-string';
|
||||
import { faSave } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n(),
|
||||
@ -291,6 +325,7 @@ export default Vue.extend({
|
||||
XProfile,
|
||||
XApi,
|
||||
XLanguage,
|
||||
XAppType,
|
||||
XNotification,
|
||||
},
|
||||
props: {
|
||||
@ -304,8 +339,11 @@ export default Vue.extend({
|
||||
return {
|
||||
meta: null,
|
||||
version,
|
||||
webSearchEngine: this.$store.state.settings.webSearchEngine,
|
||||
pastedFileName : this.$store.state.settings.pastedFileName,
|
||||
latestVersion: undefined,
|
||||
checkingForUpdate: false
|
||||
checkingForUpdate: false,
|
||||
faSave
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -404,9 +442,9 @@ export default Vue.extend({
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'defaultNoteVisibility', value }); }
|
||||
},
|
||||
|
||||
webSearchEngine: {
|
||||
get() { return this.$store.state.settings.webSearchEngine; },
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'webSearchEngine', value }); }
|
||||
pasteDialog: {
|
||||
get() { return this.$store.state.settings.pasteDialog; },
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'pasteDialog', value }); }
|
||||
},
|
||||
|
||||
showReplyTarget: {
|
||||
@ -478,6 +516,16 @@ export default Vue.extend({
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'iLikeSushi', value }); }
|
||||
},
|
||||
|
||||
roomUseOrthographicCamera: {
|
||||
get() { return this.$store.state.device.roomUseOrthographicCamera; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'roomUseOrthographicCamera', value }); }
|
||||
},
|
||||
|
||||
roomGraphicsQuality: {
|
||||
get() { return this.$store.state.device.roomGraphicsQuality; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'roomGraphicsQuality', value }); }
|
||||
},
|
||||
|
||||
games_reversi_showBoardLabels: {
|
||||
get() { return this.$store.state.settings.gamesReversiShowBoardLabels; },
|
||||
set(value) { this.$store.dispatch('settings/set', { key: 'gamesReversiShowBoardLabels', value }); }
|
||||
@ -508,6 +556,11 @@ export default Vue.extend({
|
||||
set(value) { this.$store.commit('device/set', { key: 'mobileNotificationPosition', value }); }
|
||||
},
|
||||
|
||||
enableMobileQuickNotificationView: {
|
||||
get() { return this.$store.state.device.enableMobileQuickNotificationView; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'enableMobileQuickNotificationView', value }); }
|
||||
},
|
||||
|
||||
homeProfile: {
|
||||
get() { return this.$store.state.device.homeProfile; },
|
||||
set(value) { this.$store.commit('device/set', { key: 'homeProfile', value }); }
|
||||
@ -540,6 +593,17 @@ export default Vue.extend({
|
||||
}
|
||||
});
|
||||
},
|
||||
save(key, value) {
|
||||
this.$store.dispatch('settings/set', {
|
||||
key,
|
||||
value
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
text: this.$t('@._settings.saved')
|
||||
})
|
||||
});
|
||||
},
|
||||
customizeHome() {
|
||||
location.href = '/?customize';
|
||||
},
|
||||
@ -575,7 +639,10 @@ export default Vue.extend({
|
||||
const sound = new Audio(`${url}/assets/message.mp3`);
|
||||
sound.volume = this.$store.state.device.soundVolume;
|
||||
sound.play();
|
||||
}
|
||||
},
|
||||
pastedFileNamePreview() {
|
||||
return `${formatTimeString(new Date(), this.pastedFileName).replace(/{{number}}/g, `1`)}.png`
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -16,7 +16,7 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('desktop/views/components/settings.tags.vue'),
|
||||
|
@ -125,7 +125,7 @@ import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { lightTheme, darkTheme, builtinThemes, applyTheme, Theme } from '../../../../theme';
|
||||
import { Chrome } from 'vue-color';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
import * as JSON5 from 'json5';
|
||||
import { faMoon, faSun } from '@fortawesome/free-regular-svg-icons';
|
||||
|
@ -7,7 +7,7 @@
|
||||
<template #prefix>@</template>
|
||||
<template #suffix>@{{ host }}</template>
|
||||
</ui-input>
|
||||
<ui-input v-model="password" type="password" :with-password-toggle="true" required>
|
||||
<ui-input v-model="password" type="password" :with-password-toggle="true" v-if="!user || user && !user.usePasswordLessLogin" required>
|
||||
<span>{{ $t('password') }}</span>
|
||||
<template #prefix><fa icon="lock"/></template>
|
||||
</ui-input>
|
||||
@ -28,6 +28,10 @@
|
||||
</div>
|
||||
<div class="twofa-group totp-group">
|
||||
<p style="margin-bottom:0;">{{ $t('enter-2fa-code') }}</p>
|
||||
<ui-input v-model="password" type="password" :with-password-toggle="true" v-if="user && user.usePasswordLessLogin" required>
|
||||
<span>{{ $t('password') }}</span>
|
||||
<template #prefix><fa icon="lock"/></template>
|
||||
</ui-input>
|
||||
<ui-input v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
|
||||
<span>{{ $t('@.2fa') }}</span>
|
||||
<template #prefix><fa icon="gavel"/></template>
|
||||
@ -107,9 +111,8 @@ export default Vue.extend({
|
||||
})),
|
||||
timeout: 60 * 1000
|
||||
}
|
||||
}).catch(err => {
|
||||
}).catch(() => {
|
||||
this.queryingKey = false;
|
||||
console.warn(err);
|
||||
return Promise.reject(null);
|
||||
}).then(credential => {
|
||||
this.queryingKey = false;
|
||||
@ -127,8 +130,7 @@ export default Vue.extend({
|
||||
localStorage.setItem('i', res.i);
|
||||
location.reload();
|
||||
}).catch(err => {
|
||||
if(err === null) return;
|
||||
console.error(err);
|
||||
if (err === null) return;
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('login-failed')
|
||||
@ -142,7 +144,7 @@ export default Vue.extend({
|
||||
|
||||
if (!this.totpLogin && this.user && this.user.twoFactorEnabled) {
|
||||
if (window.PublicKeyCredential && this.user.securityKeys) {
|
||||
this.$root.api('i/2fa/getkeys', {
|
||||
this.$root.api('signin', {
|
||||
username: this.username,
|
||||
password: this.password
|
||||
}).then(res => {
|
||||
@ -150,6 +152,14 @@ export default Vue.extend({
|
||||
this.signing = false;
|
||||
this.challengeData = res;
|
||||
return this.queryKey();
|
||||
}).catch(() => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('login-failed')
|
||||
});
|
||||
this.challengeData = null;
|
||||
this.totpLogin = false;
|
||||
this.signing = false;
|
||||
});
|
||||
} else {
|
||||
this.totpLogin = true;
|
||||
|
@ -43,7 +43,7 @@
|
||||
</i18n>
|
||||
</ui-switch>
|
||||
<div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div>
|
||||
<ui-button type="submit" :disabled="!(meta.ToSUrl ? ToSAgreement : true) || passwordRetypeState == 'not-match'">{{ $t('create') }}</ui-button>
|
||||
<ui-button type="submit" :disabled=" submitting || !(meta.ToSUrl ? ToSAgreement : true) || passwordRetypeState == 'not-match'">{{ $t('create') }}</ui-button>
|
||||
</template>
|
||||
</form>
|
||||
</template>
|
||||
@ -70,6 +70,7 @@ export default Vue.extend({
|
||||
passwordStrength: '',
|
||||
passwordRetypeState: null,
|
||||
meta: {},
|
||||
submitting: false,
|
||||
ToSAgreement: false
|
||||
}
|
||||
},
|
||||
@ -145,6 +146,9 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
onSubmit() {
|
||||
if (this.submitting) return;
|
||||
this.submitting = true;
|
||||
|
||||
this.$root.api('signup', {
|
||||
username: this.username,
|
||||
password: this.password,
|
||||
@ -159,6 +163,8 @@ export default Vue.extend({
|
||||
location.href = '/';
|
||||
});
|
||||
}).catch(() => {
|
||||
this.submitting = false;
|
||||
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: this.$t('some-error')
|
||||
|
@ -31,7 +31,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as uuid from 'uuid';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
|
@ -46,7 +46,7 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
upload(file: File, folder: any) {
|
||||
upload(file: File, folder: any, name?: string) {
|
||||
if (folder && typeof folder == 'object') folder = folder.id;
|
||||
|
||||
const id = Math.random();
|
||||
@ -61,7 +61,7 @@ export default Vue.extend({
|
||||
|
||||
const ctx = {
|
||||
id: id,
|
||||
name: file.name || 'untitled',
|
||||
name: name || file.name || 'untitled',
|
||||
progress: undefined,
|
||||
img: window.URL.createObjectURL(file)
|
||||
};
|
||||
@ -75,6 +75,7 @@ export default Vue.extend({
|
||||
data.append('file', file);
|
||||
|
||||
if (folder) data.append('folderId', folder);
|
||||
if (name) data.append('name', name);
|
||||
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open('POST', apiUrl + '/drive/files/create', true);
|
||||
|
@ -63,8 +63,10 @@ export default Vue.extend({
|
||||
data() {
|
||||
const isSelf = this.url.startsWith(local);
|
||||
const hasRoute =
|
||||
(this.url.substr(local.length) === '/') ||
|
||||
this.url.substr(local.length).startsWith('/@') ||
|
||||
this.url.substr(local.length).startsWith('/notes/') ||
|
||||
this.url.substr(local.length).startsWith('/tags/') ||
|
||||
this.url.substr(local.length).startsWith('/pages/');
|
||||
return {
|
||||
local,
|
||||
|
@ -28,6 +28,7 @@ export default Vue.extend({
|
||||
(this.url.substr(local.length) === '/') ||
|
||||
this.url.substr(local.length).startsWith('/@') ||
|
||||
this.url.substr(local.length).startsWith('/notes/') ||
|
||||
this.url.substr(local.length).startsWith('/tags/') ||
|
||||
this.url.substr(local.length).startsWith('/pages/'));
|
||||
return {
|
||||
local,
|
||||
|
@ -16,7 +16,7 @@
|
||||
<p class="username">@{{ user | acct }}</p>
|
||||
</div>
|
||||
<div class="description" v-if="user.description" :title="user.description">
|
||||
<mfm :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis" :should-break="false" :plain-text="true"/>
|
||||
<mfm :text="user.description" :is-note="false" :author="user" :i="$store.state.i" :custom-emojis="user.emojis" :plain="true" :nowrap="true"/>
|
||||
</div>
|
||||
<mk-follow-button class="follow-button" v-if="$store.getters.isSignedIn && user.id != $store.state.i.id" :user="user" mini/>
|
||||
</div>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<mfm :text="user.name || user.username" :should-break="false" :plain-text="true" :custom-emojis="user.emojis"/>
|
||||
<mfm :text="user.name || user.username" :plain="true" :nowrap="nowrap" :custom-emojis="user.emojis"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
@ -10,7 +10,11 @@ export default Vue.extend({
|
||||
user: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
nowrap: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
@ -14,7 +14,7 @@
|
||||
<template v-if="active"><fa icon="angle-up"/></template>
|
||||
<template v-else><fa icon="angle-down"/></template>
|
||||
</button>
|
||||
<span><slot name="header"></slot></span>
|
||||
<span class="header"><slot name="header"></slot></span>
|
||||
<span class="count" v-if="count > 0">({{ count }})</span>
|
||||
<button v-if="!isTemporaryColumn" class="menu" ref="menu" @click.stop="showMenu"><fa icon="caret-down"/></button>
|
||||
<button v-else class="close" @click.stop="close"><fa icon="times"/></button>
|
||||
@ -395,13 +395,22 @@ export default Vue.extend({
|
||||
&.indicate
|
||||
box-shadow 0 3px 0 0 var(--primary)
|
||||
|
||||
> span
|
||||
> .header
|
||||
display inline-block
|
||||
align-items center
|
||||
overflow hidden
|
||||
text-overflow ellipsis
|
||||
white-space nowrap
|
||||
|
||||
[data-icon]
|
||||
margin-right 8px
|
||||
|
||||
> .count
|
||||
margin-left 4px
|
||||
opacity 0.5
|
||||
|
||||
> span:only-of-type
|
||||
width 100%
|
||||
|
||||
> .toggleActive
|
||||
> .menu
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user