Compare commits
	
		
			82 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 338793d891 | ||
|   | 78ac41a613 | ||
|   | be23ccf23b | ||
|   | 1ba5e433e5 | ||
|   | 5bf4f569e4 | ||
|   | 5a11844eff | ||
|   | 14795b68f2 | ||
|   | bea42f5804 | ||
|   | fbbce302bc | ||
|   | 4b9c605477 | ||
|   | a499ad6879 | ||
|   | 76c5dc8999 | ||
|   | b985e14b13 | ||
|   | 61de9cdbd4 | ||
|   | 9208825975 | ||
|   | 72a49f334a | ||
|   | b35ca3b739 | ||
|   | 85950f17fa | ||
|   | 78f95b5910 | ||
|   | 909a09a0c0 | ||
|   | bb9fc56cc1 | ||
|   | 9f3b4ccd14 | ||
|   | 90bf976fe2 | ||
|   | 388de9dc96 | ||
|   | 8a0a46b1c9 | ||
|   | 186163ec3f | ||
|   | 91171c559a | ||
|   | 36b483d04d | ||
|   | 54e0a7f8a8 | ||
|   | 6d4e96dea2 | ||
|   | a6958da091 | ||
|   | 502bde5567 | ||
|   | d5702f9d51 | ||
|   | 5141afe476 | ||
|   | 4422219be7 | ||
|   | 4b2afec108 | ||
|   | 07bb8067ae | ||
|   | 4f63eb0c7e | ||
|   | 4ea7b2dd63 | ||
|   | e8189f7c69 | ||
|   | 23c054caec | ||
|   | a084c8b344 | ||
|   | 361069314f | ||
|   | d252514a39 | ||
|   | 31d1edc0fb | ||
|   | 987474726c | ||
|   | 4beea63d3f | ||
|   | 53f3b779bf | ||
|   | f59f424795 | ||
|   | c63ba5470a | ||
|   | 935d6473ed | ||
|   | 0faa4470fb | ||
|   | 0281bdd90c | ||
|   | 608ff73907 | ||
|   | c82ce9233b | ||
|   | cc75620976 | ||
|   | 1cd8bfaded | ||
|   | 65d9c304df | ||
|   | 4b48ba4e8c | ||
|   | bf7bacceab | ||
|   | a56f461895 | ||
|   | e21ff916b0 | ||
|   | da20675ada | ||
|   | 7364fb84ef | ||
|   | 48f46333f8 | ||
|   | 8407702b38 | ||
|   | e1a8b158e0 | ||
|   | 7e30910ab8 | ||
|   | 7fbee704ec | ||
|   | b1f94c7bc6 | ||
|   | 90411967d6 | ||
|   | 5719c623a4 | ||
|   | e210ec782a | ||
|   | 229780f6ff | ||
|   | 6490a9829b | ||
|   | 6384d0708d | ||
|   | db1fde2956 | ||
|   | c86720a331 | ||
|   | 2af158be26 | ||
|   | 7115bd46ff | ||
|   | f84483896e | ||
|   | fe7bc174fb | 
| @@ -157,3 +157,10 @@ id: 'aid' | ||||
|  | ||||
| # Sign to ActivityPub GET request (default: false) | ||||
| #signToActivityPubGet: true | ||||
|  | ||||
| #allowedPrivateNetworks: [ | ||||
| #  '127.0.0.1/32' | ||||
| #] | ||||
|  | ||||
| # Upload or download file size limits (bytes) | ||||
| #maxFileSize: 262144000 | ||||
|   | ||||
| @@ -1,8 +1,8 @@ | ||||
| .autogen | ||||
| .git | ||||
| .github | ||||
| .travis | ||||
| .vscode | ||||
| .config | ||||
| Dockerfile | ||||
| build/ | ||||
| built/ | ||||
| @@ -12,3 +12,4 @@ elasticsearch/ | ||||
| node_modules/ | ||||
| redis/ | ||||
| files/ | ||||
| misskey-assets/ | ||||
|   | ||||
							
								
								
									
										28
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										28
									
								
								.github/PULL_REQUEST_TEMPLATE.md
									
									
									
									
										vendored
									
									
								
							| @@ -1,30 +1,10 @@ | ||||
| <!-- ℹ お読みください | ||||
| PRありがとうございます! PRを作成する前に、以下をご確認ください: | ||||
| - 可能であればタイトルに、以下で示すようなPRの種類が分かるキーワードをプリフィクスしてください。 | ||||
|   - fix / refactor / feat / enhance / perf / chore | ||||
|   - また、PRの粒度が適切であることを確認してください。ひとつのPRに複数の種類の変更や関心を含めることは避けてください。 | ||||
| - このPRによって解決されるIssueがある場合は、そのIssueへの参照を本文内に含めてください。 | ||||
| - CHANGELOG.mdに変更点を追記してください。リファクタリングなど、利用者に影響を与えない変更についてはこの限りではありません。 | ||||
| - この変更により新たに作成、もしくは更新すべきドキュメントがないか確認してください。 | ||||
| - 機能追加やバグ修正をした場合は、可能であればテストケースを追加してください。 | ||||
| - テスト、Lintが通っていることを予め確認してください。 | ||||
|   - `npm run test`、`npm run lint`でぞれぞれ実施可能です | ||||
| - UIに変更がある場合はスクリーンショットを本文内に添付してください。 | ||||
| ご協力ありがとうございます🤗 | ||||
| PRありがとうございます! PRを作成する前に、コントリビューションガイドをご確認ください: | ||||
| https://github.com/misskey-dev/misskey/blob/develop/CONTRIBUTING.md | ||||
| --> | ||||
| <!-- ℹ README | ||||
| Thank you for your PR! Before creating a PR, please check the following: | ||||
| - If possible, prefix the title with a keyword that identifies the type of this PR, as shown below. | ||||
|   - fix / refactor / feat / enhance / perf / chore | ||||
|   - Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR. | ||||
| - If there is an Issue which will be resolved by this PR, please include a reference to the Issue in the text. | ||||
| - Please add the summary of the changes to CHANGELOG.md. However, this is not necessary for changes that do not affect the users, such as refactoring. | ||||
| - Check if there are any documents that need to be created or updated due to this change. | ||||
| - If you have added a feature or fixed a bug, please add a test case if possible. | ||||
| - Please make sure that tests and Lint are passed in advance. | ||||
|   - You can run it with `npm run test` and `npm run lint`. | ||||
| - If this PR includes UI changes, please attach a screenshot in the text. | ||||
| Thanks for your cooperation 🤗 | ||||
| Thank you for your PR! Before creating a PR, please check the contribution guide: | ||||
| https://github.com/misskey-dev/misskey/blob/develop/docs/CONTRIBUTING.en.md | ||||
| --> | ||||
|  | ||||
| # What | ||||
|   | ||||
							
								
								
									
										32
									
								
								.github/workflows/docker.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								.github/workflows/docker.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| name: Publish Docker image | ||||
|  | ||||
| on: | ||||
|   release: | ||||
|     types: [published] | ||||
|   workflow_dispatch: | ||||
|  | ||||
| jobs: | ||||
|   push_to_registry: | ||||
|     name: Push Docker image to Docker Hub | ||||
|     runs-on: ubuntu-latest | ||||
|  | ||||
|     steps: | ||||
|       - name: Check out the repo | ||||
|         uses: actions/checkout@v2 | ||||
|       - name: Docker meta | ||||
|         id: meta | ||||
|         uses: docker/metadata-action@v3 | ||||
|         with: | ||||
|           images: misskey/misskey | ||||
|       - name: Log in to Docker Hub | ||||
|         uses: docker/login-action@v1 | ||||
|         with: | ||||
|           username: ${{ secrets.DOCKER_USERNAME }} | ||||
|           password: ${{ secrets.DOCKER_PASSWORD }} | ||||
|       - name: Build and Push to Docker Hub | ||||
|         uses: docker/build-push-action@v2 | ||||
|         with: | ||||
|           context: . | ||||
|           push: true | ||||
|           tags: ${{ steps.meta.outputs.tags }} | ||||
|           labels: ${{ steps.meta.outputs.labels }} | ||||
							
								
								
									
										4
									
								
								.github/workflows/nodejs.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.github/workflows/nodejs.yml
									
									
									
									
										vendored
									
									
								
							| @@ -29,6 +29,8 @@ jobs: | ||||
|  | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|       with: | ||||
|         submodules: true | ||||
|     - name: Use Node.js ${{ matrix.node-version }} | ||||
|       uses: actions/setup-node@v1 | ||||
|       with: | ||||
| @@ -48,6 +50,8 @@ jobs: | ||||
|     runs-on: ubuntu-latest | ||||
|     steps: | ||||
|     - uses: actions/checkout@v2 | ||||
|       with: | ||||
|         submodules: true | ||||
|     - uses: actions/setup-node@v1 | ||||
|       with: | ||||
|         node-version: 12.x | ||||
|   | ||||
							
								
								
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -9,6 +9,10 @@ | ||||
| /node_modules | ||||
| report.*.json | ||||
|  | ||||
| # Cypress | ||||
| cypress/screenshots | ||||
| cypress/videos | ||||
|  | ||||
| # config | ||||
| /.config/* | ||||
| !/.config/example.yml | ||||
|   | ||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| [submodule "misskey-assets"] | ||||
| 	path = misskey-assets | ||||
| 	url = https://github.com/misskey-dev/assets.git | ||||
							
								
								
									
										51
									
								
								CHANGELOG.md
									
									
									
									
									
								
							
							
						
						
									
										51
									
								
								CHANGELOG.md
									
									
									
									
									
								
							| @@ -7,6 +7,57 @@ | ||||
|  | ||||
| --> | ||||
|  | ||||
| ## 12.91.0 (2021/09/22) | ||||
|  | ||||
| ### Improvements | ||||
| - ActivityPub: リモートユーザーのDeleteアクティビティに対応 | ||||
| - ActivityPub: add resolver check for blocked instance | ||||
| - ActivityPub: deliverキューのメモリ使用量を削減 | ||||
| - API: 管理者用アカウント削除APIを実装(/admin/accounts/delete) | ||||
| 	- リモートユーザーの削除も可能に | ||||
| - アカウントが凍結された場合に、凍結された旨を表示してからログアウトするように | ||||
| - 凍結されたアカウントにログインしようとしたときに、凍結されている旨を表示するように | ||||
| - リスト、アンテナタイムラインを個別ページとして分割 | ||||
| - UIの改善 | ||||
| - MFMにsparklesエフェクトを追加 | ||||
| - 非ログイン自は更新ダイアログを出さないように | ||||
| - クライアント起動時、アップデートが利用可能な場合エラー表示およびダイアログ表示しないように | ||||
|  | ||||
| ### Bugfixes | ||||
| - アカウントデータのエクスポート/インポート処理ができない問題を修正 | ||||
| - アンテナの既読が付かない問題を修正 | ||||
| - popupで設定ページを表示すると、アカウントの削除ページにアクセスすることができない問題を修正 | ||||
| - "問題が発生しました"ウィンドウを開くと☓ボタンがなくて閉じれない問題を修正 | ||||
|  | ||||
| ## 12.90.1 (2021/09/05) | ||||
|  | ||||
| ### Bugfixes | ||||
| - Dockerfileを修正 | ||||
| - ノート翻訳時に公開範囲が考慮されていない問題を修正 | ||||
|  | ||||
| ## 12.90.0 (2021/09/04) | ||||
|  | ||||
| ### Improvements | ||||
| - 藍モード、および藍ウィジェット | ||||
| 	- クライアントに藍ちゃんを召喚することができるようになりました。 | ||||
| - URLからのアップロード, APの添付ファイル, 外部ファイルのプロキシ等では、Privateアドレス等へのリクエストは拒否されるようになりました。 | ||||
| 	- developmentで動作している場合は、この制限は適用されません。 | ||||
| 	- Proxy使用時には、この制限は適用されません。 | ||||
| 		Proxy使用時に同等の制限を行いたい場合は、Proxy側で設定を行う必要があります。 | ||||
| 	- `default.yml`にて`allowedPrivateNetworks`にCIDRを追加することにより、宛先ネットワークを指定してこの制限から除外することが出来ます。 | ||||
| - アップロード, ダウンロード出来るファイルサイズにハードリミットが適用されるようになりました。(約250MB) | ||||
| 	- `default.yml`にて`maxFileSize`を変更することにより、制限値を変更することが出来ます。 | ||||
|  | ||||
| ### Bugfixes | ||||
| - 管理者が最初にサインアップするページでログインされないのを修正 | ||||
| - CWを維持する設定を復活 | ||||
| - クライアントの表示を修正 | ||||
|  | ||||
| ## 12.89.2 (2021/08/24) | ||||
|  | ||||
| ### Bugfixes | ||||
| - カスタムCSSを有効にしているとエラーになる問題を修正 | ||||
|  | ||||
| ## 12.89.1 (2021/08/24) | ||||
|  | ||||
| ### Improvements | ||||
|   | ||||
							
								
								
									
										168
									
								
								CONTRIBUTING.md
									
									
									
									
									
								
							
							
						
						
									
										168
									
								
								CONTRIBUTING.md
									
									
									
									
									
								
							| @@ -1,22 +1,43 @@ | ||||
| # Contribution guide | ||||
| :v: Thanks for your contributions :v: | ||||
| We're glad you're interested in contributing Misskey! In this document you will find the information you need to contribute to the project. | ||||
|  | ||||
| ## When you contribute... | ||||
| - 任意のIssueについて、せっかく実装してくださっても、実装方法や設計の認識が揃ってないとマージできない/しないことになりかねないので、初めにそのIssue上で着手することを宣言し、必要に応じて他メンバーと実装方法や設計のすり合わせを行ってください。宣言することは作業が他の人と被るのを防止する効果もあります。 | ||||
|   - 設計に迷った時はプロジェクトリーダーの判断を仰いでください。 | ||||
| - 時間や優先度の都合上、提出してくださったPRが長期間放置されることもありますがご理解ください。 | ||||
|   - 温度感高めで見てほしいものは責付いてください。 | ||||
| **ℹ️ Important:** This project uses Japanese as its major language, **but you do not need to translate and write the Issues/PRs in Japanese.** | ||||
| Also, you might receive comments on your Issue/PR in Japanese, but you do not need to reply to them in Japanese as well.\ | ||||
| The accuracy of translation into Japanese is not high, so it will be easier for us to understand if you write it in the original language. | ||||
| It will also allow the reader to use the translation tool of their preference if necessary. | ||||
|  | ||||
| ## Issues | ||||
| Feature suggestions and bug reports are filed in https://github.com/misskey-dev/misskey/issues . | ||||
| Before creating an issue, please check the following: | ||||
| - To avoid duplication, please search for similar issues before creating a new issue. | ||||
| - Do not use Issues as a question. | ||||
| 	- Issues should only be used to feature requests, suggestions, and report problems. | ||||
| 	- Please ask questions in the [Misskey Forum](https://forum.misskey.io/) or [Discord](https://discord.gg/Wp8gVStHW3). | ||||
|  | ||||
| * 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. | ||||
| ## Before implementation | ||||
| When you want to add a feature or fix a bug, **first have the design and policy reviewed in an Issue** (if it is not there, please make one). Without this step, there is a high possibility that the PR will not be merged even if it is implemented. | ||||
|  | ||||
| ## 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. | ||||
| Also, when you start implementation, assign yourself to the Issue (if you cannot do it yourself, ask another member to assign you). By expressing your intention to work the Issue, you can prevent conflicts in the work. | ||||
|  | ||||
| ## Well-known branches | ||||
| - **`master`** branch is tracking the latest release and used for production purposes. | ||||
| - **`develop`** branch is where we work for the next release. | ||||
| 	- When you create a PR, basically target it to this branch. | ||||
| - **`l10n_develop`** branch is reserved for localization management. | ||||
|  | ||||
| ## Creating a PR | ||||
| Thank you for your PR! Before creating a PR, please check the following: | ||||
| - If possible, prefix the title with a keyword that identifies the type of this PR, as shown below. | ||||
|   - `fix` / `refactor` / `feat` / `enhance` / `perf` / `chore` etc | ||||
|   - Also, make sure that the granularity of this PR is appropriate. Please do not include more than one type of change or interest in a single PR. | ||||
| - If there is an Issue which will be resolved by this PR, please include a reference to the Issue in the text. | ||||
| - Please add the summary of the changes to [`CHANGELOG.md`](/CHANGELOG.md). However, this is not necessary for changes that do not affect the users, such as refactoring. | ||||
| - Check if there are any documents that need to be created or updated due to this change. | ||||
| - If you have added a feature or fixed a bug, please add a test case if possible. | ||||
| - Please make sure that tests and Lint are passed in advance. | ||||
|   - You can run it with `npm run test` and `npm run lint`. [See more info](#testing) | ||||
| - If this PR includes UI changes, please attach a screenshot in the text. | ||||
|  | ||||
| Thanks for your cooperation 🤗 | ||||
|  | ||||
| ## Localization (l10n) | ||||
| Misskey uses [Crowdin](https://crowdin.com/project/misskey) for localization management. | ||||
| @@ -32,14 +53,22 @@ If your language is not listed in Crowdin, please open an issue. | ||||
| * Documents for instance admins are located in [`/docs`](/docs). | ||||
| * Documents for end users are located in [`/src/docs`](/src/docs). | ||||
|  | ||||
| ## Test | ||||
| * Test codes are located in [`/test`](/test). | ||||
| ## Testing | ||||
| - Test codes are located in [`/test`](/test). | ||||
|  | ||||
| ### Run specify test | ||||
| ### Run test | ||||
| ``` | ||||
| npm run test | ||||
| ``` | ||||
|  | ||||
| #### Run specify test | ||||
| ``` | ||||
| npx cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT="./test/tsconfig.json" npx mocha test/foo.ts --require ts-node/register | ||||
| ``` | ||||
|  | ||||
| ### e2e tests | ||||
| TODO | ||||
|  | ||||
| ## Continuous integration | ||||
| Misskey uses GitHub Actions for executing automated tests. | ||||
| Configuration files are located in [`/.github/workflows`](/.github/workflows). | ||||
| @@ -55,116 +84,11 @@ Configuration files are located in [`/.github/workflows`](/.github/workflows). | ||||
| 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 | ||||
| ## Notes | ||||
| ### How to resolve conflictions occurred at yarn.lock? | ||||
|  | ||||
| Just execute `yarn` to fix it. | ||||
|  | ||||
| ## Glossary | ||||
| ### AP | ||||
| Stands for _**A**ctivity**P**ub_. | ||||
|  | ||||
| ### MFM | ||||
| Stands for _**M**isskey **F**lavored **M**arkdown_. | ||||
|  | ||||
| ### Mk | ||||
| Stands for _**M**iss**k**ey_. | ||||
|  | ||||
| ### SW | ||||
| Stands for _**S**ervice**W**orker_. | ||||
|  | ||||
| ### Nyaize | ||||
| Convert な(na) to にゃ(nya) | ||||
|  | ||||
| #### Denyaize | ||||
| Revert Nyaize | ||||
|  | ||||
| ## 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) | ||||
| 	bar; | ||||
| else | ||||
| 	baz; | ||||
| ``` | ||||
|  | ||||
| Good: | ||||
| ``` ts | ||||
| if (foo) { | ||||
| 	bar; | ||||
| } else { | ||||
| 	baz; | ||||
| } | ||||
| ``` | ||||
|  | ||||
| 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; | ||||
| ``` | ||||
|  | ||||
| Make sure that the condition and the body statement are on the same line. | ||||
|  | ||||
| ### 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 | ||||
| export default function(foo: string): string { | ||||
| ``` | ||||
|  | ||||
| Good: | ||||
| ``` ts | ||||
| export function something(foo: string): string { | ||||
| ``` | ||||
|  | ||||
| ## Directory structure | ||||
| ``` | ||||
| src ... Source code | ||||
| 	@types ... Type definitions | ||||
| 	prelude ... Independence utils for coding JavaScript without side effects | ||||
| 	misc ... Independence utils for Misskey without side effects | ||||
| 	service ... Common functions with side effects | ||||
| 	queue ... Job queues and Jobs | ||||
| 	server ... Web Server | ||||
| 	client ... Client | ||||
| 	mfm ... MFM | ||||
|  | ||||
| test ... Test code | ||||
|  | ||||
| ``` | ||||
|  | ||||
| ## Notes | ||||
| ### placeholder | ||||
| SQLをクエリビルダで組み立てる際、使用するプレースホルダは重複してはならない | ||||
| 例えば | ||||
|   | ||||
							
								
								
									
										26
									
								
								Dockerfile
									
									
									
									
									
								
							
							
						
						
									
										26
									
								
								Dockerfile
									
									
									
									
									
								
							| @@ -4,26 +4,17 @@ ENV NODE_ENV=production | ||||
|  | ||||
| WORKDIR /misskey | ||||
|  | ||||
| ENV BUILD_DEPS autoconf automake file g++ gcc libc-dev libtool make nasm pkgconfig python3 zlib-dev git | ||||
|  | ||||
| FROM base AS builder | ||||
|  | ||||
| RUN apk add --no-cache \ | ||||
|     autoconf \ | ||||
|     automake \ | ||||
|     file \ | ||||
|     g++ \ | ||||
|     gcc \ | ||||
|     libc-dev \ | ||||
|     libtool \ | ||||
|     make \ | ||||
|     nasm \ | ||||
|     pkgconfig \ | ||||
|     python3 \ | ||||
|     zlib-dev | ||||
|  | ||||
| COPY package.json yarn.lock .yarnrc ./ | ||||
| RUN yarn install | ||||
| COPY . ./ | ||||
| RUN yarn build | ||||
|  | ||||
| RUN apk add --no-cache $BUILD_DEPS && \ | ||||
|     git submodule update --init && \ | ||||
|     yarn install && \ | ||||
|     yarn build && \ | ||||
|     rm -rf .git | ||||
|  | ||||
| FROM base AS runner | ||||
|  | ||||
| @@ -38,3 +29,4 @@ COPY --from=builder /misskey/built ./built | ||||
| COPY . ./ | ||||
|  | ||||
| CMD ["npm", "run", "migrateandstart"] | ||||
|  | ||||
|   | ||||
| @@ -104,6 +104,12 @@ Related projects | ||||
| - [misskey.js](https://github.com/misskey-dev/misskey.js) - Misskey SDK for JavaScript | ||||
| - [mfm.js](https://github.com/misskey-dev/mfm.js) - MFM parser | ||||
|  | ||||
| Sponsors | ||||
| ---------------------------------------------------------------- | ||||
| <div align="center"> | ||||
| 	<a class="rss3" title="RSS3" href="https://rss3.io/" target="_blank" style="display: inline-block;"><img src="https://rss3.io/assets/images/Logo.svg" alt="RSS3" style="display: inline-block; height: 60px;"></a> | ||||
| </div> | ||||
|  | ||||
| :heart: Backers | ||||
| ---------------------------------------------------------------- | ||||
| <!-- PATREON_START --> | ||||
|   | ||||
							
								
								
									
										
											BIN
										
									
								
								assets/client/sparkle-spritesheet.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								assets/client/sparkle-spritesheet.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 185 B | 
| @@ -1,10 +1,14 @@ | ||||
| describe('Basic', () => { | ||||
| 	before(() => { | ||||
| 		cy.request('POST', '/api/reset-db'); | ||||
| describe('Before setup instance', () => { | ||||
| 	beforeEach(() => { | ||||
| 		cy.request('POST', '/api/reset-db').as('reset'); | ||||
| 		cy.get('@reset').its('status').should('equal', 204); | ||||
| 		cy.reload(true); | ||||
| 	}); | ||||
|  | ||||
| 	beforeEach(() => { | ||||
| 		cy.reload(true); | ||||
| 	afterEach(() => { | ||||
| 		// テスト終了直前にページ遷移するようなテストケース(例えばアカウント作成)だと、たぶんCypressのバグでブラウザの内容が次のテストケースに引き継がれてしまう(例えばアカウントが作成し終わった段階からテストが始まる)。 | ||||
| 		// waitを入れることでそれを防止できる | ||||
| 		cy.wait(1000); | ||||
| 	}); | ||||
|  | ||||
|   it('successfully loads', () => { | ||||
| @@ -14,56 +18,172 @@ describe('Basic', () => { | ||||
| 	it('setup instance', () => { | ||||
|     cy.visit('/'); | ||||
|  | ||||
| 		cy.intercept('POST', '/api/admin/accounts/create').as('signup'); | ||||
| 	 | ||||
| 		cy.get('[data-cy-admin-username] input').type('admin'); | ||||
|  | ||||
| 		cy.get('[data-cy-admin-password] input').type('admin1234'); | ||||
|  | ||||
| 		cy.get('[data-cy-admin-ok]').click(); | ||||
|  | ||||
| 		// なぜか動かない | ||||
| 		//cy.wait('@signup').should('have.property', 'response.statusCode'); | ||||
| 		cy.wait('@signup'); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| describe('After setup instance', () => { | ||||
| 	beforeEach(() => { | ||||
| 		cy.request('POST', '/api/reset-db').as('reset'); | ||||
| 		cy.get('@reset').its('status').should('equal', 204); | ||||
| 		cy.reload(true); | ||||
|  | ||||
| 		// インスタンス初期セットアップ | ||||
| 		cy.request('POST', '/api/admin/accounts/create', { | ||||
| 			username: 'admin', | ||||
| 			password: 'pass', | ||||
| 		}).its('body').as('admin'); | ||||
|  | ||||
| 		cy.get('@admin'); | ||||
| 	}); | ||||
|  | ||||
| 	afterEach(() => { | ||||
| 		// テスト終了直前にページ遷移するようなテストケース(例えばアカウント作成)だと、たぶんCypressのバグでブラウザの内容が次のテストケースに引き継がれてしまう(例えばアカウントが作成し終わった段階からテストが始まる)。 | ||||
| 		// waitを入れることでそれを防止できる | ||||
| 		cy.wait(1000); | ||||
| 	}); | ||||
|  | ||||
|   it('successfully loads', () => { | ||||
|     cy.visit('/'); | ||||
|   }); | ||||
|  | ||||
| 	it('signup', () => { | ||||
|     cy.visit('/'); | ||||
| 		cy.visit('/'); | ||||
|  | ||||
| 		cy.intercept('POST', '/api/signup').as('signup'); | ||||
|  | ||||
| 		cy.get('[data-cy-signup]').click(); | ||||
|  | ||||
| 		cy.get('[data-cy-signup-username] input').type('alice'); | ||||
|  | ||||
| 		cy.get('[data-cy-signup-password] input').type('alice1234'); | ||||
| 	 | ||||
| 		cy.get('[data-cy-signup-password-retype] input').type('alice1234'); | ||||
|  | ||||
| 		cy.get('[data-cy-signup-submit]').click(); | ||||
|  | ||||
| 		cy.wait('@signup'); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| describe('After user signup', () => { | ||||
| 	beforeEach(() => { | ||||
| 		cy.request('POST', '/api/reset-db').as('reset'); | ||||
| 		cy.get('@reset').its('status').should('equal', 204); | ||||
| 		cy.reload(true); | ||||
|  | ||||
| 		// インスタンス初期セットアップ | ||||
| 		cy.request('POST', '/api/admin/accounts/create', { | ||||
| 			username: 'admin', | ||||
| 			password: 'pass', | ||||
| 		}).its('body').as('admin'); | ||||
|  | ||||
| 		cy.get('@admin').then(() => { | ||||
| 			// ユーザー作成 | ||||
| 			cy.request('POST', '/api/signup', { | ||||
| 				username: 'alice', | ||||
| 				password: 'alice1234', | ||||
| 			}).its('body').as('alice'); | ||||
| 		}); | ||||
|  | ||||
| 		cy.get('@alice'); | ||||
| 	}); | ||||
|  | ||||
| 	afterEach(() => { | ||||
| 		// テスト終了直前にページ遷移するようなテストケース(例えばアカウント作成)だと、たぶんCypressのバグでブラウザの内容が次のテストケースに引き継がれてしまう(例えばアカウントが作成し終わった段階からテストが始まる)。 | ||||
| 		// waitを入れることでそれを防止できる | ||||
| 		cy.wait(1000); | ||||
| 	}); | ||||
|  | ||||
|   it('successfully loads', () => { | ||||
|     cy.visit('/'); | ||||
|   }); | ||||
|  | ||||
| 	it('signin', () => { | ||||
|     cy.visit('/'); | ||||
| 		cy.visit('/'); | ||||
|  | ||||
| 		cy.intercept('POST', '/api/signin').as('signin'); | ||||
|  | ||||
| 		cy.get('[data-cy-signin]').click(); | ||||
|  | ||||
| 		cy.get('[data-cy-signin-username] input').type('alice'); | ||||
|  | ||||
| 		// Enterキーでサインインできるかの確認も兼ねる | ||||
| 		cy.get('[data-cy-signin-password] input').type('alice1234{enter}'); | ||||
|  | ||||
| 		cy.wait('@signin'); | ||||
|   }); | ||||
|  | ||||
| 	it('suspend', function() { | ||||
| 		cy.request('POST', '/api/admin/suspend-user', { | ||||
| 			i: this.admin.token, | ||||
| 			userId: this.alice.id, | ||||
| 		}); | ||||
|  | ||||
| 		cy.visit('/'); | ||||
|  | ||||
| 		cy.get('[data-cy-signin]').click(); | ||||
| 		cy.get('[data-cy-signin-username] input').type('alice'); | ||||
| 		cy.get('[data-cy-signin-password] input').type('alice1234{enter}'); | ||||
|  | ||||
| 		cy.contains('アカウントが凍結されています'); | ||||
| 	}); | ||||
| }); | ||||
|  | ||||
| describe('After user singed in', () => { | ||||
| 	beforeEach(() => { | ||||
| 		cy.request('POST', '/api/reset-db').as('reset'); | ||||
| 		cy.get('@reset').its('status').should('equal', 204); | ||||
| 		cy.reload(true); | ||||
|  | ||||
| 		// インスタンス初期セットアップ | ||||
| 		cy.request('POST', '/api/admin/accounts/create', { | ||||
| 			username: 'admin', | ||||
| 			password: 'pass', | ||||
| 		}).its('body').as('admin'); | ||||
|  | ||||
| 		cy.get('@admin').then(() => { | ||||
| 			// ユーザー作成 | ||||
| 			cy.request('POST', '/api/signup', { | ||||
| 				username: 'alice', | ||||
| 				password: 'alice1234', | ||||
| 			}).its('body').as('alice'); | ||||
| 		}); | ||||
|  | ||||
| 		cy.get('@alice').then(() => { | ||||
| 			cy.visit('/'); | ||||
|  | ||||
| 			cy.intercept('POST', '/api/signin').as('signin'); | ||||
|  | ||||
| 			cy.get('[data-cy-signin]').click(); | ||||
| 			cy.get('[data-cy-signin-username] input').type('alice'); | ||||
| 			cy.get('[data-cy-signin-password] input').type('alice1234{enter}'); | ||||
|  | ||||
| 			cy.wait('@signin').as('signedIn'); | ||||
| 		}); | ||||
|  | ||||
| 		cy.get('@signedIn'); | ||||
| 	}); | ||||
|  | ||||
| 	afterEach(() => { | ||||
| 		// テスト終了直前にページ遷移するようなテストケース(例えばアカウント作成)だと、たぶんCypressのバグでブラウザの内容が次のテストケースに引き継がれてしまう(例えばアカウントが作成し終わった段階からテストが始まる)。 | ||||
| 		// waitを入れることでそれを防止できる | ||||
| 		cy.wait(1000); | ||||
| 	}); | ||||
|  | ||||
|   it('successfully loads', () => { | ||||
|     cy.visit('/'); | ||||
|   }); | ||||
|  | ||||
| 	it('note', () => { | ||||
|     cy.visit('/'); | ||||
|  | ||||
| 		//#region TODO: この辺はUI操作ではなくAPI操作でログインする | ||||
| 		cy.get('[data-cy-signin]').click(); | ||||
|  | ||||
| 		cy.get('[data-cy-signin-username] input').type('alice'); | ||||
|  | ||||
| 		// Enterキーでサインインできるかの確認も兼ねる | ||||
| 		cy.get('[data-cy-signin-password] input').type('alice1234{enter}'); | ||||
| 		//#endregion | ||||
|  | ||||
| 		cy.get('[data-cy-open-post-form]').click(); | ||||
|  | ||||
| 		cy.get('[data-cy-post-form-text]').type('Hello, Misskey!'); | ||||
|  | ||||
| 		cy.get('[data-cy-open-post-form-submit]').click(); | ||||
|  | ||||
| 		// TODO: 投稿した文字列が画面内にあるか(=タイムラインに流れてきたか)のテスト | ||||
| 		cy.contains('Hello, Misskey!'); | ||||
|   }); | ||||
| }); | ||||
|   | ||||
| @@ -15,6 +15,7 @@ services: | ||||
|       - external_network | ||||
|     volumes: | ||||
|       - ./files:/misskey/files | ||||
|       - ./.config:/misskey/.config:ro | ||||
|  | ||||
|   redis: | ||||
|     restart: always | ||||
|   | ||||
| @@ -83,10 +83,11 @@ Just `docker-compose up -d`. GLHF! | ||||
| 1. `git stash` | ||||
| 2. `git checkout master` | ||||
| 3. `git pull` | ||||
| 4. `git stash pop` | ||||
| 5. `docker-compose build` | ||||
| 6. Check [ChangeLog](../CHANGELOG.md) for migration information | ||||
| 7. `docker-compose stop && docker-compose up -d` | ||||
| 4. `git submodule update --init` | ||||
| 5. `git stash pop` | ||||
| 6. `docker-compose build` | ||||
| 7. Check [ChangeLog](../CHANGELOG.md) for migration information | ||||
| 8. `docker-compose stop && docker-compose up -d` | ||||
|  | ||||
| ### How to execute [cli commands](manage.en.md): | ||||
| `docker-compose run --rm web node built/tools/mark-admin @example` | ||||
|   | ||||
| @@ -50,10 +50,11 @@ Utilisez la commande `docker-compose up -d`. GLHF! | ||||
| 1. `git stash` | ||||
| 2. `git checkout master` | ||||
| 3. `git pull` | ||||
| 4. `git stash pop` | ||||
| 5. `docker-compose build` | ||||
| 6. Consultez le [ChangeLog](../CHANGELOG.md) pour avoir les éventuelles informations de migration | ||||
| 7. `docker-compose stop && docker-compose up -d` | ||||
| 4. `git submodule update --init` | ||||
| 5. `git stash pop` | ||||
| 6. `docker-compose build` | ||||
| 7. Consultez le [ChangeLog](../CHANGELOG.md) pour avoir les éventuelles informations de migration | ||||
| 8. `docker-compose stop && docker-compose up -d` | ||||
|  | ||||
| ### Comment exécuter des [commandes](manage.fr.md) | ||||
| `docker-compose run --rm web node built/tools/mark-admin @example` | ||||
|   | ||||
| @@ -83,10 +83,11 @@ docker-compose run --rm web yarn run init | ||||
| 1. `git stash` | ||||
| 2. `git checkout master` | ||||
| 3. `git pull` | ||||
| 4. `git stash pop` | ||||
| 5. `docker-compose build` | ||||
| 6. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する | ||||
| 7. `docker-compose stop && docker-compose up -d` | ||||
| 4. `git submodule update --init` | ||||
| 5. `git stash pop` | ||||
| 6. `docker-compose build` | ||||
| 7. [ChangeLog](../CHANGELOG.md)でマイグレーション情報を確認する | ||||
| 8. `docker-compose stop && docker-compose up -d` | ||||
|  | ||||
| ### cliコマンドを実行する方法: | ||||
|  | ||||
|   | ||||
| @@ -83,10 +83,11 @@ docker-compose run --rm web yarn run init | ||||
| 1. `git stash` | ||||
| 2. `git checkout master` | ||||
| 3. `git pull` | ||||
| 4. `git stash pop` | ||||
| 5. `docker-compose build` | ||||
| 6. 检查 [更新日志](../CHANGELOG.md) 以获取升级迁移信息。 | ||||
| 7. `docker-compose stop && docker-compose up -d` | ||||
| 4. `git submodule update --init` | ||||
| 5. `git stash pop` | ||||
| 6. `docker-compose build` | ||||
| 7. 检查 [更新日志](../CHANGELOG.md) 以获取升级迁移信息。 | ||||
| 8. `docker-compose stop && docker-compose up -d` | ||||
|  | ||||
| ### 如何执行 [控制台指令](manage.zh.md): | ||||
| `docker-compose run --rm web node built/tools/mark-admin @example` | ||||
|   | ||||
							
								
								
									
										28
									
								
								docs/push-docker-hub.ja.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								docs/push-docker-hub.ja.md
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| GitHub Actionsを使用してDocker Hubへpushする方法 | ||||
| ================================================================ | ||||
|  | ||||
| [/.github/workflows/docker.yml](/.github/workflows/docker.yml) に   | ||||
| GitHub ActionによりDocker Hubへpushするワークフローが記述されています。 | ||||
|  | ||||
| オリジナルリポジトリでは、リリースされたタイミングで `latest`, `<リリース名>` それぞれのタグでDocker Hubにpushされます。   | ||||
| ※ Docker Hub に`<ブランチ名>`のようなタグがあるかもしれませんが、こちらは自動push対象ではありません。 | ||||
|  | ||||
| Fork先でこのワークフローを実行すると失敗します。 | ||||
|  | ||||
| 以下では、Fork先で自分のDocker Hubリポジトリにpushするようにする方法を記述します。 | ||||
|  | ||||
| ## 自分のDocker Hubリポジトリにpushするように設定する方法 | ||||
|  | ||||
| 1. Docker Hubでリポジトリを作成します。 | ||||
| 2. ワークフローファイルの [images](https://github.com/misskey-dev/misskey/blob/53f3b779bf16abcda4f6e026c51384f3b8fbcc62/.github/workflows/docker.yml#L20) を作成したリポジトリに置き換えます。 | ||||
| 3. GitHubにて [暗号化されたシークレット](https://docs.github.com/ja/actions/reference/encrypted-secrets#creating-encrypted-secrets-for-a-repository) を作成します。   | ||||
|    作成が必要なのは `DOCKER_USERNAME` と `DOCKER_PASSWORD` で、それぞれDocker Hubのユーザーとパスワードになります。 | ||||
|  | ||||
| ## pushする方法 | ||||
|  | ||||
| 上記設定によりリリース時に自動的にDocker Hubにpushされるようになります。   | ||||
| 具体的には、GitHubのリリース機能でリリースしたタイミングで `latest`, `<リリース名>` それぞれのタグでDocker Hubにpushされます。 | ||||
|  | ||||
| また、GitHub上から手動でpushすることも出来ます。   | ||||
| それを行うには、Actions => Publish Docker image => Run workflow からbranchを選択してワークフローを実行します。   | ||||
| ただし、この場合作成されるタグは`<ブランチ名>`になります。 | ||||
| @@ -131,11 +131,12 @@ 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. `yarn install` | ||||
| 4. `NODE_ENV=production yarn build` | ||||
| 5. `yarn migrate` | ||||
| 6. Restart your Misskey process to apply changes | ||||
| 7. Enjoy | ||||
| 3. `git submodule update --init` | ||||
| 4. `yarn install` | ||||
| 5. `NODE_ENV=production yarn build` | ||||
| 6. `yarn migrate` | ||||
| 7. Restart your Misskey process to apply changes | ||||
| 8. Enjoy | ||||
|  | ||||
| If you encounter any problems with updating, please try the following: | ||||
| 1. `yarn clean` or `yarn cleanall` | ||||
|   | ||||
| @@ -126,9 +126,10 @@ 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. `yarn install` | ||||
| 4. `NODE_ENV=production yarn build` | ||||
| 5. `yarn migrate` | ||||
| 3. `git submodule update --init` | ||||
| 4. `yarn install` | ||||
| 5. `NODE_ENV=production yarn build` | ||||
| 6. `yarn migrate` | ||||
|  | ||||
| ---------------------------------------------------------------- | ||||
|  | ||||
|   | ||||
| @@ -133,9 +133,10 @@ yarn run init | ||||
| ### Misskeyを最新バージョンにアップデートする方法: | ||||
| 1. `git checkout master` | ||||
| 2. `git pull` | ||||
| 3. `yarn install` | ||||
| 4. `NODE_ENV=production yarn build` | ||||
| 5. `yarn migrate` | ||||
| 3. `git submodule update --init` | ||||
| 4. `yarn install` | ||||
| 5. `NODE_ENV=production yarn build` | ||||
| 6. `yarn migrate` | ||||
|  | ||||
| なにか問題が発生した場合は、`yarn clean`または`yarn cleanall`すると直る場合があります。 | ||||
|  | ||||
|   | ||||
| @@ -131,11 +131,12 @@ yarn run init | ||||
| ### 如何将您的 Misskey 服务器升级至最新版本 | ||||
| 1. `git checkout master` | ||||
| 2. `git pull` | ||||
| 3. `yarn install` | ||||
| 4. `NODE_ENV=production yarn build` | ||||
| 5. `yarn migrate` | ||||
| 6. 重启您的 Misskey 进程来应用改变。 | ||||
| 7. 尽情享受吧! | ||||
| 3. `git submodule update --init` | ||||
| 4. `yarn install` | ||||
| 5. `NODE_ENV=production yarn build` | ||||
| 6. `yarn migrate` | ||||
| 7. 重启您的 Misskey 进程来应用改变。 | ||||
| 8. 尽情享受吧! | ||||
|  | ||||
| 如果您在更新时遇到任何问题,请尝试以下操作: | ||||
| 1. `yarn clean` 或是 `yarn cleanall` | ||||
|   | ||||
| @@ -529,6 +529,8 @@ removeAllFollowing: "Allen gefolgten Benutzern entfolgen" | ||||
| removeAllFollowingDescription: "Dies entfolgt allen Benutzerkonten von {host}. Bitte führe dies durch, falls diese Instanz z.B. nicht mehr existiert." | ||||
| userSuspended: "Dieser Benutzer wurde gesperrt." | ||||
| userSilenced: "Dieser Benutzer wurde instanzweit stummgeschaltet." | ||||
| yourAccountSuspendedTitle: "Dieses Benutzerkonto ist gesperrt" | ||||
| yourAccountSuspendedDescription: "Dieses Benutzerkonto wurde gesperrt, da es gegen die Nutzungsbedingungen dieses Servers verstoßen hat. Trete mit dem Betreiber in Kontakt, falls du weitere Details erfahren möchtest. Bitte erstelle kein neues Benutzerkonto." | ||||
| menu: "Menü" | ||||
| divider: "Trenner" | ||||
| addItem: "Element hinzufügen" | ||||
| @@ -748,7 +750,7 @@ switch: "Wechseln" | ||||
| noMaintainerInformationWarning: "Betreiberinformationen sind nicht konfiguriert." | ||||
| noBotProtectionWarning: "Bot-Schutz ist nicht konfiguriert." | ||||
| configure: "Konfigurieren" | ||||
| postToGallery: "Beitrag zu Galerie hinzufügen" | ||||
| postToGallery: "Neuen Galerie-Beitrag erstellen" | ||||
| gallery: "Galerie" | ||||
| recentPosts: "Neue Beiträge" | ||||
| popularPosts: "Beliebte Beiträge" | ||||
| @@ -779,6 +781,12 @@ translate: "Übersetzen" | ||||
| translatedFrom: "Aus {x} übersetzt" | ||||
| accountDeletionInProgress: "Löschung des Benutzerkontos momentan in Bearbeitung" | ||||
| usernameInfo: "Ein Name, durch den dein Benutzerkonto auf diesem Server identifiziert werden kann. Du kannst das Alphabet (a~z, A~Z), Ziffern (0~9) oder Unterstriche (_) verwenden. Benutzernamen können später nicht geändert werden." | ||||
| aiChanMode: "Ai Modus" | ||||
| keepCw: "Inhaltswarnung beibehalten" | ||||
| pubSub: "Pub/Sub Benutzerkonten" | ||||
| lastCommunication: "Letzte Kommunikation" | ||||
| resolved: "Gelöst" | ||||
| unresolved: "Ungelöst" | ||||
| _accountDelete: | ||||
|   accountDelete: "Benutzerkonto löschen" | ||||
|   mayTakeTime: "Da die Löschung eines Benutzerkontos ein aufwendiger Prozess ist, kann dessen Dauer davon abhängen, wie viel Inhalt in diesem erstellt wurde oder wie viele Dateien hochgeladen wurden." | ||||
| @@ -868,19 +876,19 @@ _mfm: | ||||
|   flip: "Spiegelung" | ||||
|   flipDescription: "Inhalt horizontal oder vertikal gespiegelt anzeigen." | ||||
|   jelly: "Animation (Dehnen)" | ||||
|   jellyDescription: "Verleiht eine sich dehnende Animation." | ||||
|   jellyDescription: "Verleiht dem Inhalt eine sich dehnende Animation." | ||||
|   tada: "Animation (Tada)" | ||||
|   tadaDescription: "Verleiht eine Animation mit \"Tada!\"-Gefühl" | ||||
|   jump: "Animation (Sprung)" | ||||
|   jumpDescription: "Verleiht eine springende Animation." | ||||
|   jumpDescription: "Verleiht dem Inhalt eine springende Animation." | ||||
|   bounce: "Animation (Federn)" | ||||
|   bounceDescription: "Verleiht eine federnde Animation." | ||||
|   bounceDescription: "Verleiht dem Inhalt eine federnde Animation." | ||||
|   shake: "Animation (Zittern)" | ||||
|   shakeDescription: "Verleiht eine zitternde Animation." | ||||
|   shakeDescription: "Verleiht dem Inhalt eine zitternde Animation." | ||||
|   twitch: "Animation (Zucken)" | ||||
|   twitchDescription: "Verleiht eine sehr stark zuckende Animation." | ||||
|   twitchDescription: "Verleiht dem Inhalt eine sehr stark zuckende Animation." | ||||
|   spin: "Animation (Rotieren)" | ||||
|   spinDescription: "Verleiht eine rotierende Animation." | ||||
|   spinDescription: "Verleiht dem Inhalt eine rotierende Animation." | ||||
|   x2: "Groß" | ||||
|   x2Description: "Inhalte größer anzeigen." | ||||
|   x3: "Sehr groß" | ||||
| @@ -1119,6 +1127,10 @@ _permissions: | ||||
|   "write:user-groups": "Benutzergruppen bearbeiten oder löschen" | ||||
|   "read:channels": "Kanäle lesen" | ||||
|   "write:channels": "Kanäle bedienen" | ||||
|   "read:gallery": "Beiträge deiner Galerie lesen" | ||||
|   "write:gallery": "Deine Galerie bearbeiten" | ||||
|   "read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge lesen" | ||||
|   "write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge bearbeiten" | ||||
| _auth: | ||||
|   shareAccess: "Möchtest du \"{name}\" authorisieren, auf dieses Benuzerkonto zugreifen zu können?" | ||||
|   shareAccessAsk: "Bist du dir sicher, dass du diese Anwendung authorisieren möchtest, auf dein Benutzerkonto zugreifen zu können?" | ||||
| @@ -1159,6 +1171,7 @@ _widgets: | ||||
|   jobQueue: "Job-Warteschlange" | ||||
|   serverMetric: "Servermetriken" | ||||
|   aiscript: "AiScript-Konsole" | ||||
|   aichan: "Ai" | ||||
| _cw: | ||||
|   hide: "Verbergen" | ||||
|   show: "Inhalt anzeigen" | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| --- | ||||
| _lang_: "English" | ||||
| headlineMisskey: "A network connected by notes" | ||||
| introMisskey: "Welcome! Misskey is an open source, decentralized microblogging service.\nCreate \"notes\" to share what is happening now, or to share it with everyone around you. 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes. 👍\nLet's explore a new world! 🚀" | ||||
| introMisskey: "Welcome! Misskey is an open source, decentralized microblogging service.\nCreate \"notes\" to share what your thoughts with everyone around you. 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes. 👍\nLet's explore a new world! 🚀" | ||||
| monthAndDay: "{month}/{day}" | ||||
| search: "Search" | ||||
| notifications: "Notifications" | ||||
| @@ -92,7 +92,7 @@ unfollow: "Unfollow" | ||||
| followRequestPending: "Pending follow request" | ||||
| enterEmoji: "Enter an emoji" | ||||
| renote: "Renote" | ||||
| unrenote: "Take back Renote" | ||||
| unrenote: "Take back renote" | ||||
| renoted: "Renoted." | ||||
| cantRenote: "This post can't be renoted." | ||||
| cantReRenote: "A renote can't be renoted." | ||||
| @@ -136,7 +136,7 @@ settingGuide: "Recommended settings" | ||||
| cacheRemoteFiles: "Cache remote files" | ||||
| cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded directly from the remote instance. Disabling this will decrease storage usage, but increase traffic, as thumbnails will not be generated." | ||||
| flagAsBot: "Mark this account as as bot" | ||||
| flagAsBotDescription: "Enable this option if this account is controlled by a program. If enabled, it will act as flag for other developers to prevent endless interaction chains with other bots and adjust Misskey's internal systems to treat this account as a bot." | ||||
| flagAsBotDescription: "Enable this option if this account is controlled by a program. If enabled, it will act as a flag for other developers to prevent endless interaction chains with other bots and adjust Misskey's internal systems to treat this account as a bot." | ||||
| flagAsCat: "Mark this account as a cat" | ||||
| flagAsCatDescription: "Enable this option to mark this account as a cat." | ||||
| autoAcceptFollowed: "Automatically approve follow requests from users you're following" | ||||
| @@ -199,7 +199,7 @@ done: "Done" | ||||
| processing: "Processing..." | ||||
| preview: "Preview" | ||||
| default: "Default" | ||||
| noCustomEmojis: "There are no emojis" | ||||
| noCustomEmojis: "There are no emoji" | ||||
| noJobs: "There are no jobs" | ||||
| federating: "Federating" | ||||
| blocked: "Blocked" | ||||
| @@ -213,7 +213,7 @@ instanceFollowers: "Followers of instance" | ||||
| instanceUsers: "Users of this instance" | ||||
| changePassword: "Change password" | ||||
| security: "Security" | ||||
| retypedNotMatch: "The inputs does not match." | ||||
| retypedNotMatch: "The inputs do not match." | ||||
| currentPassword: "Current password" | ||||
| newPassword: "New password" | ||||
| newPasswordRetype: "Retype new password" | ||||
| @@ -429,7 +429,7 @@ invitationCode: "Invitation code" | ||||
| checking: "Checking..." | ||||
| available: "Available" | ||||
| unavailable: "Not available" | ||||
| usernameInvalidFormat: "You can use upper- and lowercase letters, numbers as well as underscores." | ||||
| usernameInvalidFormat: "You can use upper- and lowercase letters, numbers, and underscores." | ||||
| tooShort: "Too short" | ||||
| tooLong: "Too long" | ||||
| weakPassword: "Weak password" | ||||
| @@ -445,7 +445,7 @@ language: "Language" | ||||
| uiLanguage: "User interface language" | ||||
| groupInvited: "You've been invited to a group" | ||||
| aboutX: "About {x}" | ||||
| useOsNativeEmojis: "Use OS native Emojis" | ||||
| useOsNativeEmojis: "Use OS native Emoji" | ||||
| youHaveNoGroups: "You have no groups" | ||||
| joinOrCreateGroup: "Get invited to a group or create your own." | ||||
| noHistory: "No history available" | ||||
| @@ -482,7 +482,7 @@ objectStorageBaseUrlDesc: "URL used as reference. Specify the URL of your CDN or | ||||
| objectStorageBucket: "Bucket" | ||||
| objectStorageBucketDesc: "Please specify the bucket name used at your provider." | ||||
| objectStoragePrefix: "Prefix" | ||||
| objectStoragePrefixDesc: "Files will stored under directories with this prefix." | ||||
| objectStoragePrefixDesc: "Files will be stored under directories with this prefix." | ||||
| objectStorageEndpoint: "Endpoint" | ||||
| objectStorageEndpointDesc: "Leave this empty if you are using AWS S3, otherwise specify the endpoint as '<host>' or '<host>:<port>', depending on the service you are using." | ||||
| objectStorageRegion: "Region" | ||||
| @@ -529,6 +529,8 @@ removeAllFollowing: "Unfollow all followed users" | ||||
| removeAllFollowingDescription: "Executing this unfollows all accounts from {host}. Please run this if the instance e.g. no longer exists." | ||||
| userSuspended: "This user has been suspended." | ||||
| userSilenced: "This user has been silenced." | ||||
| yourAccountSuspendedTitle: "This account is suspended" | ||||
| yourAccountSuspendedDescription: "This account has been suspended due to breaking the server's terms of services or similar. Contact the administrator if you would like to know a more detailed reason. Please do not create a new account." | ||||
| menu: "Menu" | ||||
| divider: "Divider" | ||||
| addItem: "Add Item" | ||||
| @@ -606,7 +608,7 @@ useGlobalSettingDesc: "If turned on, your account's notification settings will b | ||||
| other: "Other" | ||||
| regenerateLoginToken: "Regenerate login token" | ||||
| regenerateLoginTokenDescription: "Regenerate the token used internally during login. Normally this action is not necessary. If regenerated, all devices will be logged out." | ||||
| setMultipleBySeparatingWithSpace: "You can set multiple by separating them with spaces." | ||||
| setMultipleBySeparatingWithSpace: "Separate multiple entries with spaces." | ||||
| fileIdOrUrl: "File-ID or URL" | ||||
| chatOpenBehavior: "Behavior of the chat window when opened" | ||||
| behavior: "Behavior" | ||||
| @@ -649,7 +651,7 @@ pollVotesCount: "Number of sent poll votes" | ||||
| pollVotedCount: "Number of received poll votes" | ||||
| yes: "Yes" | ||||
| no: "No" | ||||
| driveFilesCount: "Number of drive files" | ||||
| driveFilesCount: "Number of Drive files" | ||||
| driveUsage: "Drive space usage" | ||||
| noCrawle: "Reject crawler indexing" | ||||
| noCrawleDescription: "Ask search engines to not index your profile page, notes, Pages, etc." | ||||
| @@ -657,7 +659,7 @@ lockedAccountInfo: "Unless you set your note visiblity to \"Followers only\", yo | ||||
| alwaysMarkSensitive: "Mark as NSFW by default" | ||||
| loadRawImages: "Load original images instead of showing thumbnails" | ||||
| disableShowingAnimatedImages: "Don't play animated images" | ||||
| verificationEmailSent: "A verification email has been sent. Please access the included link to complete verification." | ||||
| verificationEmailSent: "A verification email has been sent. Please follow the included link to complete verification." | ||||
| notSet: "Not set" | ||||
| emailVerified: "Email has been verified" | ||||
| noteFavoritesCount: "Number of favorite notes" | ||||
| @@ -688,7 +690,7 @@ sendErrorReportsDescription: "When turned on, detailed error information will be | ||||
| myTheme: "My theme" | ||||
| backgroundColor: "Background color" | ||||
| accentColor: "Accent color" | ||||
| textColor: "Textfarbe" | ||||
| textColor: "Text color" | ||||
| saveAs: "Save as..." | ||||
| advanced: "Advanced" | ||||
| value: "Value" | ||||
| @@ -724,7 +726,7 @@ fullView: "Full view" | ||||
| quitFullView: "Exit full view" | ||||
| addDescription: "Add description" | ||||
| userPagePinTip: "You can display notes here by selecting \"Pin to profile\" from the menu of individual notes." | ||||
| notSpecifiedMentionWarning: "This note contains mentions of users not included as recipient" | ||||
| notSpecifiedMentionWarning: "This note contains mentions of users not included as recipients" | ||||
| info: "About" | ||||
| userInfo: "User information" | ||||
| unknown: "Unknown" | ||||
| @@ -748,7 +750,7 @@ switch: "Switch" | ||||
| noMaintainerInformationWarning: "Maintainer information is not configured." | ||||
| noBotProtectionWarning: "Bot protection is not configured." | ||||
| configure: "Configure" | ||||
| postToGallery: "Post to Gallery" | ||||
| postToGallery: "Create new gallery post" | ||||
| gallery: "Gallery" | ||||
| recentPosts: "Recent posts" | ||||
| popularPosts: "Popular posts" | ||||
| @@ -778,7 +780,13 @@ whatIsNew: "Show changes" | ||||
| translate: "Translate" | ||||
| translatedFrom: "Translated from {x}" | ||||
| accountDeletionInProgress: "Account deletion is currently in progress" | ||||
| usernameInfo: "A name that identifies your account from others on this server.  You can use the alphabet (a~z, A~Z), digits (0~9) or underscores (_). Usernames can not be changed later." | ||||
| usernameInfo: "A name that identifies your account from others on this server.  You can use the alphabet (a~z, A~Z), digits (0~9) or underscores (_). Usernames cannot be changed later." | ||||
| aiChanMode: "Ai Mode" | ||||
| keepCw: "Keep Content Warning" | ||||
| pubSub: "Pub/Sub Accounts" | ||||
| lastCommunication: "Last communication" | ||||
| resolved: "Resolved" | ||||
| unresolved: "Unresolved" | ||||
| _accountDelete: | ||||
|   accountDelete: "Delete Account" | ||||
|   mayTakeTime: "As account deletion is a resource-heavy process, it may take some time to complete depending on how much content you have created and how many files you have uploaded." | ||||
| @@ -804,7 +812,7 @@ _gallery: | ||||
|   my: "My Gallery" | ||||
|   liked: "Liked Posts" | ||||
|   like: "Like" | ||||
|   unlike: "Undo like" | ||||
|   unlike: "Remove like" | ||||
| _email: | ||||
|   _follow: | ||||
|     title: "You've got a new follower" | ||||
| @@ -844,11 +852,11 @@ _mfm: | ||||
|   url: "URL" | ||||
|   urlDescription: "URLs can be displayed." | ||||
|   link: "Link" | ||||
|   linkDescription: "Specific parts of text can be displayed as an URL." | ||||
|   linkDescription: "Specific parts of text can be displayed as a URL." | ||||
|   bold: "Bold" | ||||
|   boldDescription: "Highlights letters by making them thicker." | ||||
|   small: "Small" | ||||
|   smallDescription: "Displays contents small and thin." | ||||
|   smallDescription: "Displays content small and thin." | ||||
|   center: "Center" | ||||
|   centerDescription: "Displays content centered." | ||||
|   inlineCode: "Code (In-line)" | ||||
| @@ -860,7 +868,7 @@ _mfm: | ||||
|   blockMath: "Math (Block)" | ||||
|   blockMathDescription: "Display multi-line Math formulas (KaTeX) in a block" | ||||
|   quote: "Quote" | ||||
|   quoteDescription: "Displays content as quote." | ||||
|   quoteDescription: "Displays content as a quote." | ||||
|   emoji: "Custom Emoji" | ||||
|   emojiDescription: "By surrounding a custom emoji name with colons, custom emoji can be displayed." | ||||
|   search: "Search" | ||||
| @@ -890,9 +898,11 @@ _mfm: | ||||
|   blur: "Blur" | ||||
|   blurDescription: "Content can be blurred via this effect. It will be displayed clearly when hovered over." | ||||
|   font: "Font" | ||||
|   fontDescription: "Sets the font to display contents in." | ||||
|   fontDescription: "Sets the font to display content in." | ||||
|   rainbow: "Rainbow" | ||||
|   rainbowDescription: "Makes the content appear in rainbow colors." | ||||
|   sparkle: "Sparkle" | ||||
|   sparkleDescription: "Gives content a sparkling particle effect." | ||||
| _reversi: | ||||
|   reversi: "Reversi" | ||||
|   gameSettings: "Game settings" | ||||
| @@ -952,8 +962,8 @@ _menuDisplay: | ||||
| _wordMute: | ||||
|   muteWords: "Muted words" | ||||
|   muteWordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition." | ||||
|   muteWordsDescription2: "Surround keywords by slashes to use regular expressions." | ||||
|   softDescription: "Hides notes fulfilling the set conditions from the timeline." | ||||
|   muteWordsDescription2: "Surround keywords with slashes to use regular expressions." | ||||
|   softDescription: "Hide notes that fulfil the set conditions from the timeline." | ||||
|   hardDescription: "Prevents notes fulfilling the set conditions from being added to the timeline. In addition, these notes will not be added to the timeline even if the conditions are changed." | ||||
|   soft: "Soft" | ||||
|   hard: "Hard" | ||||
| @@ -1011,7 +1021,7 @@ _theme: | ||||
|     divider: "Divider" | ||||
|     scrollbarHandle: "Scrollbar handle" | ||||
|     scrollbarHandleHover: "Scrollbar handle (Hover)" | ||||
|     dateLabelFg: "Text of date labels" | ||||
|     dateLabelFg: "Date label text" | ||||
|     infoBg: "Information background" | ||||
|     infoFg: "Information text" | ||||
|     infoWarnBg: "Warning background" | ||||
| @@ -1084,19 +1094,19 @@ _tutorial: | ||||
| _2fa: | ||||
|   alreadyRegistered: "You have already registered a 2-factor authentication device." | ||||
|   registerDevice: "Register a new device" | ||||
|   registerKey: "Register a new Security Key" | ||||
|   registerKey: "Register a security key" | ||||
|   step1: "First, install an authentication app (such as {a} or {b}) on your device." | ||||
|   step2: "Then, scan the QR code displayed on this screen." | ||||
|   step3: "Enter the token provided by your app to finish setup." | ||||
|   step4: "From now, any future login attempts will ask for such a login token." | ||||
|   securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup authentication via hardware security keys that support FIDO2 to further secure your login process." | ||||
|   step4: "From now on, any future login attempts will ask for such a login token." | ||||
|   securityKeyInfo: "Besides fingerprint or PIN authentication, you can also setup authentication via hardware security keys that support FIDO2 to further secure your account." | ||||
| _permissions: | ||||
|   "read:account": "View your account information" | ||||
|   "write:account": "Edit your account information" | ||||
|   "read:blocks": "View the list of people you blocked" | ||||
|   "read:blocks": "View your list of blocked users" | ||||
|   "write:blocks": "Edit your list of blocked users" | ||||
|   "read:drive": "Access your drive files and folders" | ||||
|   "write:drive": "Edit or delete your drive files and folders" | ||||
|   "read:drive": "Access your Drive files and folders" | ||||
|   "write:drive": "Edit or delete your Drive files and folders" | ||||
|   "read:favorites": "View your list of favorites" | ||||
|   "write:favorites": "Edit your list of favorites" | ||||
|   "read:following": "View information on who you follow" | ||||
| @@ -1119,6 +1129,10 @@ _permissions: | ||||
|   "write:user-groups": "Edit or delete your user groups" | ||||
|   "read:channels": "Read your channels" | ||||
|   "write:channels": "Modify your channels" | ||||
|   "read:gallery": "View your gallery" | ||||
|   "write:gallery": "Edit your gallery" | ||||
|   "read:gallery-likes": "View list of liked gallery posts" | ||||
|   "write:gallery-likes": "Edit list of liked gallery posts" | ||||
| _auth: | ||||
|   shareAccess: "Would you like to authorize \"{name}\" to access this account?" | ||||
|   shareAccessAsk: "Are you sure you want to authorize this application to access your account?" | ||||
| @@ -1159,6 +1173,7 @@ _widgets: | ||||
|   jobQueue: "Job Queue" | ||||
|   serverMetric: "Server metrics" | ||||
|   aiscript: "AiScript console" | ||||
|   aichan: "Ai" | ||||
| _cw: | ||||
|   hide: "Hide" | ||||
|   show: "Show content" | ||||
| @@ -1320,23 +1335,23 @@ _rooms: | ||||
|     doll-ai: "Ai doll" | ||||
|     banknote: "Pile of money" | ||||
| _pages: | ||||
|   newPage: "Create a page" | ||||
|   editPage: "Edit this page" | ||||
|   newPage: "Create a new Page" | ||||
|   editPage: "Edit this Page" | ||||
|   readPage: "Source view activated" | ||||
|   created: "Page successfully created" | ||||
|   updated: "Page successfully edited" | ||||
|   deleted: "Page successfully deleted" | ||||
|   pageSetting: "Page settings" | ||||
|   nameAlreadyExists: "The specified page URL already exists" | ||||
|   invalidNameTitle: "The specified page URL is invalid" | ||||
|   invalidNameText: "Make sure the page title is not empty" | ||||
|   editThisPage: "Edit this page" | ||||
|   nameAlreadyExists: "The specified Page URL already exists" | ||||
|   invalidNameTitle: "The specified Page URL is invalid" | ||||
|   invalidNameText: "Make sure the Page title is not empty" | ||||
|   editThisPage: "Edit this Page" | ||||
|   viewSource: "View source" | ||||
|   viewPage: "View your pages" | ||||
|   viewPage: "View your Pages" | ||||
|   like: "Like" | ||||
|   unlike: "Undo like" | ||||
|   my: "My pages" | ||||
|   liked: "Liked pages" | ||||
|   unlike: "Remove like" | ||||
|   my: "My Pages" | ||||
|   liked: "Liked Pages" | ||||
|   featured: "Featured" | ||||
|   inspector: "Inspector" | ||||
|   contents: "Contents" | ||||
| @@ -1346,10 +1361,10 @@ _pages: | ||||
|   url: "Page URL" | ||||
|   summary: "Page summary" | ||||
|   alignCenter: "Center elements" | ||||
|   hideTitleWhenPinned: "Hide page title when pinned to profile" | ||||
|   hideTitleWhenPinned: "Hide Page title when pinned to profile" | ||||
|   font: "Font" | ||||
|   fontSerif: "Serif" | ||||
|   fontSansSerif: "Sans serif" | ||||
|   fontSansSerif: "Sans Serif" | ||||
|   eyeCatchingImageSet: "Set thumbnail" | ||||
|   eyeCatchingImageRemove: "Delete thumbnail" | ||||
|   chooseBlock: "Add a block" | ||||
| @@ -1564,7 +1579,7 @@ _pages: | ||||
|       seedRandomPick: "Randomly choose from list (with seed)" | ||||
|       _seedRandomPick: | ||||
|         arg1: "Seed" | ||||
|         arg2: "Liste" | ||||
|         arg2: "List" | ||||
|       DRPWPM: "Randomly choose from weighted list (Changes once a day for each user)" | ||||
|       _DRPWPM: | ||||
|         arg1: "Text list" | ||||
| @@ -1644,8 +1659,8 @@ _deck: | ||||
|   columnMargin: "Margin between columns" | ||||
|   columnHeaderHeight: "Column header height" | ||||
|   addColumn: "Add column" | ||||
|   swapLeft: "Swap to left" | ||||
|   swapRight: "Swap to right" | ||||
|   swapLeft: "Swap left" | ||||
|   swapRight: "Swap right" | ||||
|   swapUp: "Swap with above" | ||||
|   swapDown: "Swap with below" | ||||
|   stackLeft: "Stack on left column" | ||||
|   | ||||
| @@ -1,102 +1,107 @@ | ||||
| --- | ||||
| _lang_: "Esperanto" | ||||
| headlineMisskey: "Reto ligata per notoj" | ||||
| introMisskey: "Bonvenon! Misskey estas malfermitkoda malcentraliza mikrobloga servo.\nKreu \"noto\"n por diskonigu tion ke nun okazas, aŭ por dissendu pri vi. 📡\nPer la funkcio \"reago\", vi ankaŭ povas rapide esprimi vian senton pri ĉies noto. 👍\nOni esploru novan mondon. 🚀" | ||||
| monthAndDay: "{day}-a/{month}" | ||||
| headlineMisskey: "Jen la reto konektita de notoj" | ||||
| introMisskey: "Bonvenon! Misskey estas malfermitkoda malcentraliza mikrobloga servo.\nKreu \"noto\"n por diskonigu tion kio nun okazas, aŭ por parolu pri vi. 📡\nUzu la funkcion \"reago\" por esprimu rapide vian senton pri ies noto. 👍\nBonvole esploru novan mondon. 🚀" | ||||
| monthAndDay: "{day}a/{month}" | ||||
| search: "Serĉi" | ||||
| notifications: "Sciigoj" | ||||
| username: "Uzantnomo" | ||||
| password: "Pasvorto" | ||||
| forgotPassword: "Ĉu vi forgesis pasvorton?" | ||||
| fetchingAsApObject: "Informpetado de Fediverso..." | ||||
| fetchingAsApObject: "Informpetado de kunfederaĵo…" | ||||
| ok: "Akcepteble" | ||||
| gotIt: "Mi komprenas" | ||||
| cancel: "Nuligi" | ||||
| enterUsername: "Entajpu uzantnomon" | ||||
| renotedBy: "Renoto farita de {user}" | ||||
| renotedBy: "Noto plusendita de {user}" | ||||
| noNotes: "Neniu noto!" | ||||
| noNotifications: "Vi ne havas sciigojn." | ||||
| instance: "Ekzemplo" | ||||
| instance: "Nodo" | ||||
| settings: "Agordoj" | ||||
| basicSettings: "Ĝeneralaj agordoj" | ||||
| otherSettings: "Aliaj agordoj" | ||||
| openInWindow: "Malfermi en nova fenestro" | ||||
| profile: "Profilo" | ||||
| timeline: "Templinio" | ||||
| noAccountDescription: "Tiu uzanto ankoraŭ ne skribis biografieton" | ||||
| noAccountDescription: "Ĉi tiu uzanto ne skribis vivpriskribon." | ||||
| login: "Ensaluti" | ||||
| loggingIn: "Ensalutado..." | ||||
| loggingIn: "Ensalutado…" | ||||
| logout: "Elsaluti" | ||||
| signup: "Krei konton" | ||||
| uploading: "Alŝutado..." | ||||
| signup: "Registriĝi" | ||||
| uploading: "Alŝutado…" | ||||
| save: "Konservi" | ||||
| users: "Uzanto" | ||||
| users: "Uzantoj" | ||||
| addUser: "Aldoni uzanton" | ||||
| favorite: "Preferi" | ||||
| favorites: "Preferataj" | ||||
| favorites: "Preferataĵoj" | ||||
| unfavorite: "Malpreferi" | ||||
| favorited: "Aldonita al preferatoj" | ||||
| alreadyFavorited: "Jame aldonita al preferatoj" | ||||
| cantFavorite: "Ne aldonita al preferatoj" | ||||
| pin: "Alpingli sur la profilo" | ||||
| favorited: "Aldonita al preferataĵoj" | ||||
| alreadyFavorited: "Ankoraŭ aldonita al via listo de preferaĵoj." | ||||
| cantFavorite: "Ne aldonita al preferataĵoj" | ||||
| pin: "Alpingli al la profilo" | ||||
| unpin: "Depingli" | ||||
| copyContent: "Kopii enhavon" | ||||
| copyLink: "Kopii ligilon" | ||||
| delete: "Forviŝi" | ||||
| deleteAndEdit: "Forigi kaj redakti" | ||||
| deleteAndEditConfirm: "Ĉu vi certas, ke vi volas forigi kaj redakti la noton? Ankaŭ ĉiuj reagoj, renotoj, kaj respondoj al ĝi foriĝos." | ||||
| addToList: "Aldoni al la listo" | ||||
| deleteAndEdit: "Forviŝi kaj redakti" | ||||
| deleteAndEditConfirm: "Ĉu vi certas, ke vi volas forigi kaj redakti la noton? Kun tio foriĝos reagoj, plusendaĵoj, kaj respondoj ĉiuj de ĝi." | ||||
| addToList: "Aldoni al listo" | ||||
| sendMessage: "Sendi mesaĝon" | ||||
| copyUsername: "Kopii uzantnomon" | ||||
| searchUser: "Serĉi uzanton" | ||||
| reply: "Respondi" | ||||
| loadMore: "Vidu pli" | ||||
| showMore: "Vidi pli" | ||||
| youGotNewFollower: "sksekvis vin" | ||||
| youGotNewFollower: "eksekvis vin" | ||||
| receiveFollowRequest: "Peto de sekvado estas ricevita" | ||||
| followRequestAccepted: "La peto de sekvado akceptita" | ||||
| mention: "Mencioj" | ||||
| mentions: "Al vi" | ||||
| directNotes: "Rektaj notoj" | ||||
| directNotes: "Notoj rektaj" | ||||
| importAndExport: "Importi/eksporti" | ||||
| import: "Importi" | ||||
| export: "Eksporti" | ||||
| files: "Dosieroj" | ||||
| download: "Elŝuti" | ||||
| driveFileDeleteConfirm: "Ĉu vi certas, ke vi volas forviŝi la dosieron \"{name}\"? Ankaŭ notoj kiu enhavas ĝin forviŝiĝos." | ||||
| driveFileDeleteConfirm: "Ĉu vi certas, ke vi volas forviŝi la dosieron \"{name}\"? Pro tio forviŝiĝos ankaŭ la notoj kiuj enhavas ĝin." | ||||
| unfollowConfirm: "Ĉu vi certas, ke vi volas ne plu sekvi {name}'(o)n?" | ||||
| lists: "Listoj" | ||||
| noLists: "Neniu listo" | ||||
| note: "Elsendi noto" | ||||
| note: "Notoj" | ||||
| notes: "Notoj" | ||||
| following: "Sekvatoj" | ||||
| followers: "Sekvantoj" | ||||
| followsYou: "Sekvas vin" | ||||
| createList: "Kreii liston" | ||||
| createList: "Krei liston" | ||||
| manageLists: "Administri liston" | ||||
| error: "Eraro" | ||||
| somethingHappened: "Problemo okazis." | ||||
| retry: "Reprovi" | ||||
| somethingHappened: "Problemo okazis" | ||||
| retry: "Provi denove" | ||||
| enterListName: "Entajpu nomon de la listo" | ||||
| privacy: "Privateco" | ||||
| defaultNoteVisibility: "Implicitaĵo de videbleco" | ||||
| follow: "Sekvi" | ||||
| followRequest: "Peti de sekvado" | ||||
| followRequests: "Petoj de sekvado" | ||||
| unfollow: "Malsekvi" | ||||
| unfollow: "Ne plu sekvi" | ||||
| enterEmoji: "Entajpu emoĵion" | ||||
| renote: "Fari renoton" | ||||
| unrenote: "Malfari renoton" | ||||
| renoted: "Renoton fariĝis." | ||||
| cantRenote: "Tiu noto ne estas renototebla." | ||||
| cantReRenote: "Oni ne povas fari renoton kiu enhavas renoto." | ||||
| renote: "Plusendi la noton" | ||||
| unrenote: "Malfari plusendadon" | ||||
| renoted: "Sukcese plusendita" | ||||
| cantRenote: "Oni ne povas plusendi la noton." | ||||
| cantReRenote: "Plusendado ne estas plusendebla." | ||||
| quote: "Citi" | ||||
| pinnedNote: "Pinglita noto" | ||||
| pinned: "Alpingli sur la profilo" | ||||
| pinnedNote: "Alpinglita noto" | ||||
| pinned: "Alpingli al la profilo" | ||||
| you: "Vi" | ||||
| clickToShow: "Klaku por malkaŝu" | ||||
| sensitive: "Enhavo ne estas deca por laborejo (NSFW)" | ||||
| add: "Aldoni" | ||||
| reaction: "Reagoj" | ||||
| rememberNoteVisibility: "Rememoru la videblecon de la noto laste sendita" | ||||
| attachCancel: "Deigi aldonaĵon" | ||||
| markAsSensitive: "Troviĝi NSFW" | ||||
| unmarkAsSensitive: "Ne troviĝi NSFW" | ||||
| enterFileName: "Entajpu nomon de dosiero" | ||||
| mute: "Silentigi" | ||||
| unmute: "Malsilentigi" | ||||
| @@ -119,12 +124,12 @@ emojis: "Emoĵio" | ||||
| emojiName: "Nomo de emoĵio" | ||||
| emojiUrl: "URL de la emoĵio" | ||||
| addEmoji: "Aldoni emoĵion" | ||||
| settingGuide: "Rekomendaj agordoj" | ||||
| cacheRemoteFiles: "Havi staplon de transaj dosieroj" | ||||
| flagAsBot: "Tiu uzanto estas roboto" | ||||
| flagAsCat: "Tiu uzanto estas kato" | ||||
| settingGuide: "Agordaj rekomendoj" | ||||
| cacheRemoteFiles: "Stapli transajn dosierojn" | ||||
| flagAsBot: "Agordo por robota uzanto" | ||||
| flagAsCat: "Agordo de katiĝa uzanto" | ||||
| addAccount: "Aldoni konton" | ||||
| showOnRemote: "Vidi sur la fora ekzemplo" | ||||
| showOnRemote: "Vidi ĉe la surloka nodo" | ||||
| general: "Ĝenerala" | ||||
| wallpaper: "Ekranfonoj" | ||||
| setWallpaper: "Apliki ekranfonon" | ||||
| @@ -132,45 +137,60 @@ removeWallpaper: "Forviŝi ekranfonon. " | ||||
| searchWith: "Serĉi: {q}" | ||||
| youHaveNoLists: "Vi ne havas listojn." | ||||
| followConfirm: "Ĉu vi certas ke vi volas sekvi {name}'(o)n?" | ||||
| host: "Gastiganto" | ||||
| selectUser: "Elekti uzanton" | ||||
| recipient: "Ricevonto" | ||||
| annotation: "Komentarioj" | ||||
| federation: "Kunfederaĵo" | ||||
| instances: "Ekzemplo" | ||||
| instances: "Nodoj" | ||||
| latestRequestSentAt: "Lastatempa sendo" | ||||
| latestRequestReceivedAt: "Lastatempa ricevo" | ||||
| latestStatus: "Laŭstato" | ||||
| perHour: "Po horo" | ||||
| perDay: "Po tago" | ||||
| blockThisInstance: "Bloki tiu ekzemplo" | ||||
| blockThisInstance: "Bloki la nodon" | ||||
| operations: "Agoj" | ||||
| software: "Programaro" | ||||
| version: "Versio" | ||||
| metadata: "Metadatumoj" | ||||
| withNFiles: "{n} dosiero(j)" | ||||
| monitor: "Monitoro" | ||||
| network: "Reto" | ||||
| disk: "Diskilo" | ||||
| instanceInfo: "Informo pri la ekzemplo" | ||||
| instanceInfo: "Informoj pri la nodo" | ||||
| statistics: "Statistikoj" | ||||
| clearCachedFiles: "Malplenigi la staplon" | ||||
| clearCachedFilesConfirm: "Ĉu vi certas, ke vi volas forviŝi ĉiujn transajn dosierojn en la staplo?" | ||||
| blockedInstances: "Blokataj ekzemploj" | ||||
| muteAndBlock: "Silentigatoj kaj blokatoj" | ||||
| mutedUsers: "Silentigataj uzantoj" | ||||
| blockedUsers: "Blokataj uzantoj" | ||||
| blockedInstances: "Blokitaj nodoj" | ||||
| muteAndBlock: "Silentigitoj kaj blokitoj" | ||||
| mutedUsers: "Silentigitaj uzantoj" | ||||
| blockedUsers: "Blokitaj uzantoj" | ||||
| noUsers: "Sen uzantoj" | ||||
| editProfile: "Redakti profilon" | ||||
| noteDeleteConfirm: "Ĉu vi certas ke vi volas forviŝi la noton?" | ||||
| pinLimitExceeded: "Vi povas alpingli ne pli noton." | ||||
| processing: "Traktado..." | ||||
| done: "Fini" | ||||
| processing: "Prilaborado…" | ||||
| preview: "Antaŭmontro" | ||||
| noCustomEmojis: "Neniu emoĵio" | ||||
| federating: "Kunfederado" | ||||
| blocked: "Blokata" | ||||
| federating: "Nun kunfederanta" | ||||
| blocked: "Blokita" | ||||
| suspended: "Suspendita" | ||||
| all: "Ĉiuj" | ||||
| subscribing: "Abonita" | ||||
| publishing: "Dissendado" | ||||
| subscribing: "Abonata" | ||||
| publishing: "Al kiu dissendas" | ||||
| notResponding: "Alvokato ne disponeblas" | ||||
| instanceFollowing: "Sekvatoj el la ekzemplo" | ||||
| instanceFollowers: "Sekvantoj el la ekzemplo" | ||||
| instanceUsers: "Uzantoj de la ekzemplo" | ||||
| instanceFollowing: "Sekvatoj en la nodo" | ||||
| instanceFollowers: "Sekvantoj el la nodo" | ||||
| instanceUsers: "Uzantoj de tiu ĉi nodo" | ||||
| changePassword: "Ŝanĝi pasvorton" | ||||
| security: "Sekureco" | ||||
| retypedNotMatch: "Enigitoj ne estas konformaj." | ||||
| currentPassword: "Aktuala pasvorto" | ||||
| newPassword: "Nova pasvorto" | ||||
| newPasswordRetype: "Reentajpu la novan pasvorton" | ||||
| attachFile: "Aldoni dosieron" | ||||
| more: "Plu !" | ||||
| more: "Plu!" | ||||
| featured: "Maksimumi" | ||||
| usernameOrUserId: "Uzantnomo aŭ identigilo de uzanto" | ||||
| noSuchUser: "Neniuj uzantoj trovitaj." | ||||
| @@ -187,7 +207,7 @@ upload: "Alŝuti" | ||||
| fromDrive: "De la disko" | ||||
| fromUrl: "De URL" | ||||
| uploadFromUrl: "Alŝuti de URL" | ||||
| uploadFromUrlDescription: "URL de la dosiero kiun vi volas alŝuti" | ||||
| uploadFromUrlDescription: "URL de dosiero kiun vi volas alŝuti" | ||||
| explore: "Esplori" | ||||
| games: "Miskiaj Ludoj" | ||||
| messageRead: "Legita" | ||||
| @@ -195,8 +215,8 @@ startMessaging: "Komenci babiladon" | ||||
| nUsersRead: "Legita de {n} homoj" | ||||
| tos: "Kondiĉoj de uzado" | ||||
| start: "Komenciĝi" | ||||
| home: "Hejmo" | ||||
| remoteUserCaution: "Ĉi tiu Infomoj estas ne tute ekzakta pro distanca uzanto." | ||||
| home: "Hejma" | ||||
| remoteUserCaution: "Ĉi tiuj infomoj ne estas tute ekzaktaj pro transa uzanto." | ||||
| activity: "Aktiveco" | ||||
| images: "Bildoj" | ||||
| birthday: "Naskiĝdato" | ||||
| @@ -233,14 +253,15 @@ watch: "Observi" | ||||
| unwatch: "Malobservi" | ||||
| accept: "Permesi" | ||||
| normal: "Normala" | ||||
| instanceName: "Nomo de la ekzemplo" | ||||
| instanceName: "Nomo de la nodo" | ||||
| instanceDescription: "Mempriskribo de la nodo " | ||||
| maintainerName: "Nomo de la administranto" | ||||
| maintainerEmail: "Retpoŝto de la administranto" | ||||
| tosUrl: "URL de kondiĉoj de uzado" | ||||
| thisYear: "Ĉi-jare" | ||||
| thisMonth: "Ĉi-monate" | ||||
| today: "Hodiaŭ" | ||||
| dayX: "{day}-a" | ||||
| dayX: "{day}a" | ||||
| monthX: "{month}" | ||||
| yearX: "La jaro {year}" | ||||
| pages: "Paĝoj" | ||||
| @@ -249,6 +270,8 @@ disconnectService: "Farkonektiĝi" | ||||
| enableLocalTimeline: "Ebligi lokan templinion" | ||||
| enableGlobalTimeline: "Ebligi mallokan templinion" | ||||
| registration: "Registri" | ||||
| enableRegistration: "Ebligi novan uzanton registriĝon" | ||||
| invite: "Inviti" | ||||
| driveCapacityPerLocalAccount: "Volumo de disko po unu loka uzanto" | ||||
| driveCapacityPerRemoteAccount: "Volumo de disko po unu transa uzanto" | ||||
| iconUrl: "URL de la ikono (retpaĝsimbolo, ktp)" | ||||
| @@ -257,26 +280,40 @@ backgroundImageUrl: "URL de fona bildo" | ||||
| basicInfo: "Baza informo" | ||||
| pinnedUsers: "Alpinglita uzanto" | ||||
| pinnedPages: "Alpinglitaj paĝoj" | ||||
| pinnedNotes: "Pinglita noto" | ||||
| pinnedNotes: "Alpinglita noto" | ||||
| hcaptchaSiteKey: "Reteja ŝlosilo" | ||||
| hcaptchaSecretKey: "Sekreta ŝlosilo" | ||||
| recaptcha: "reCAPTCHA" | ||||
| enableRecaptcha: "Ebligi reCAPTCHA'on" | ||||
| recaptchaSiteKey: "Reteja ŝlosilo" | ||||
| recaptchaSecretKey: "Sekreta ŝlosilo" | ||||
| antennas: "Antenoj" | ||||
| manageAntennas: "Administri antenojn" | ||||
| name: "Nomo" | ||||
| notifyAntenna: "Oni sciigos novajn notojn" | ||||
| withFileAntenna: "Nur kun aldonaĵo" | ||||
| withReplies: "Inkluzive respondoj" | ||||
| connectedTo: "Sekva konto estas konektita" | ||||
| notesAndReplies: "Kun respondoj" | ||||
| withFiles: "Kun aldonaĵo" | ||||
| silence: "Mutigi" | ||||
| silenceConfirm: "Ĉu vi certas ke vi volas mutigi la uzanton?" | ||||
| unsilence: "Malmutigi" | ||||
| unsilenceConfirm: "Ĉu vi certas ke vi volas malmutigi la uzanton?" | ||||
| recentlyUpdatedUsers: "Uzantoj kiu lastatempe faris noton" | ||||
| recentlyRegisteredUsers: "Nove aniĝintaj uzantoj" | ||||
| popularUsers: "Popularaj uzantoj" | ||||
| recentlyUpdatedUsers: "Uzantoj kiuj lastatempe sendis noton" | ||||
| recentlyRegisteredUsers: "Novaliĝintaj uzantoj" | ||||
| recentlyDiscoveredUsers: "Lastatempe trovitaj uzantoj" | ||||
| exploreUsersCount: "Tio estas {count} uzantoj" | ||||
| exploreFediverse: "Esplori la Fediverson" | ||||
| popularTags: "Popularaj kradvortoj" | ||||
| userList: "Listoj" | ||||
| about: "Informoj" | ||||
| aboutMisskey: "Pri Misskey" | ||||
| administrator: "Administranto" | ||||
| moderator: "Moderigisto" | ||||
| twoStepAuthentication: "Dua-faktora aŭtentiko" | ||||
| moderator: "Kontrolisto" | ||||
| nUsersMentioned: "{n} uzanto(j) menciis" | ||||
| securityKey: "Sekureca ŝlosilo" | ||||
| securityKeyName: "Nomo de la ŝlosilo" | ||||
| lastUsed: "Plej malnove uzita" | ||||
| @@ -292,29 +329,40 @@ close: "Fermi" | ||||
| group: "Grupo" | ||||
| groups: "Grupoj" | ||||
| createGroup: "Krei grupon" | ||||
| invites: "Inviti" | ||||
| groupName: "Grupa nomo" | ||||
| members: "Membroj" | ||||
| messagingWithUser: "Mesaĝado kun uzanto" | ||||
| messagingWithGroup: "Mesaĝado kun grupo" | ||||
| messagingWithUser: "Babili private" | ||||
| messagingWithGroup: "Babili grupe" | ||||
| title: "Titolo" | ||||
| text: "Teksto" | ||||
| enable: "Ebligi" | ||||
| next: "Sekve" | ||||
| retype: "Retajpu" | ||||
| noteOf: "Noto de {user}" | ||||
| noMessagesYet: "Neniu mesaĝo" | ||||
| quoteAttached: "Kun citaĵo" | ||||
| quoteQuestion: "Ĉu vi aldonas citaĵon?" | ||||
| noMessagesYet: "Ankoraŭ neniu mesaĝo" | ||||
| newMessageExists: "Vi ricevis novan mesaĝon." | ||||
| onlyOneFileCanBeAttached: "Vi povas aldoni nur unu dosieron po unu mesaĝo." | ||||
| signinRequired: "Bonvolu ensaluti" | ||||
| invitations: "Inviti" | ||||
| invitationCode: "Kodo de invito" | ||||
| unavailable: "Ne disponebla" | ||||
| passwordMatched: "Konforma" | ||||
| passwordNotMatched: "Nekonforma" | ||||
| or: "Aŭ" | ||||
| language: "Lingvo" | ||||
| uiLanguage: "Lingvo de la fasado" | ||||
| uiLanguage: "Lingvo de fasado" | ||||
| aboutX: "Pri {x}" | ||||
| useOsNativeEmojis: "Oni uzas la emoĵioj de la denaska sistemo" | ||||
| youHaveNoGroups: "Neniuj grupoj" | ||||
| doing: "Traktado..." | ||||
| category: "Kategorio" | ||||
| tags: "Etikedoj" | ||||
| createAccount: "Krei konton" | ||||
| existingAccount: "Ekzista konto" | ||||
| regenerate: "Regeneri" | ||||
| fontSize: "Tipara grando" | ||||
| noFollowRequests: "Vi ne havas peto de sekvado" | ||||
| openImageInNewTab: "Fermi la bildon en nova tablo" | ||||
| @@ -322,81 +370,124 @@ dashboard: "Stirpanelo" | ||||
| local: "Loka" | ||||
| remote: "Transa" | ||||
| total: "Entute" | ||||
| appearance: "Eksteraĵo" | ||||
| clientSettings: "Agordoj de kliento" | ||||
| accountSettings: "Agordoj de Konto" | ||||
| accountSettings: "Agordoj de konto" | ||||
| numberOfDays: "Nombro de tagoj" | ||||
| hideThisNote: "Kaŝi tiun noton" | ||||
| hideThisNote: "Kaŝi la noton" | ||||
| objectStorageBaseUrl: "Baza URL" | ||||
| objectStorageRegion: "Regiono" | ||||
| objectStorageUseSSL: "Oni uzas SSL" | ||||
| serverLogs: "Servila protokolo" | ||||
| deleteAll: "Forviŝi ĉiujn" | ||||
| newNoteRecived: "Jen estas novaj notoj" | ||||
| sounds: "Sonoj" | ||||
| listen: "Aŭdi" | ||||
| none: "Neniu" | ||||
| showInPage: "Vidi en paĝo" | ||||
| popout: "Superigi" | ||||
| volume: "Laŭteco" | ||||
| masterVolume: "Baza laŭteco" | ||||
| chooseEmoji: "Elekti emoĵion" | ||||
| recentUsed: "Lastatempaj uzitaj" | ||||
| install: "Instali" | ||||
| uninstall: "Malinstali" | ||||
| installedApps: "Instalita programo" | ||||
| nothing: "Neniu" | ||||
| installedDate: "Dato de instalado" | ||||
| lastUsedDate: "Lastfoje uzita je" | ||||
| state: "Stato" | ||||
| sort: "Ordigado" | ||||
| output: "Elmeto" | ||||
| script: "Skripto" | ||||
| disablePagesScript: "Malebligi AiScripto en la paĝoj" | ||||
| deleteAllFiles: "Forviŝi ĉiujn dosierojn" | ||||
| deleteAllFilesConfirm: "Ĉu vi certas, ke vi volas forviŝi ĉiujn viajn dosierojn?" | ||||
| userSilenced: "Tiu uzanto estas mutigata." | ||||
| deleteAllFilesConfirm: "Ĉu vi certas, ke vi volas forviŝi ĉiujn dosierojn?" | ||||
| userSuspended: "Ĉi tiu uzanto estas flostigita." | ||||
| userSilenced: "Ĉi tiu uzanto estas mutigita." | ||||
| menu: "Menuo" | ||||
| addItem: "Aldoni novaĵon" | ||||
| rooms: "Ĉambro" | ||||
| deletedNote: "Forviŝita noto" | ||||
| invisibleNote: "Malpublika noto" | ||||
| visibility: "Videbleco" | ||||
| poll: "Balotujo" | ||||
| useCw: "Kaŝi enhavo" | ||||
| enablePlayer: "Vidi videon" | ||||
| disablePlayer: "Fermi videon" | ||||
| expandTweet: "Disvolvi pepon" | ||||
| themeEditor: "Redaktilo de koloraroj" | ||||
| description: "Priskribe" | ||||
| describeFile: "Priskribi la bildon" | ||||
| enterFileDescription: "Priskribu" | ||||
| author: "Aŭtoro" | ||||
| manage: "Administro" | ||||
| plugins: "Kromaĵoj" | ||||
| deck: "Kartaro" | ||||
| width: "Larĝeco" | ||||
| height: "Alteco" | ||||
| medium: "Meza" | ||||
| small: "Malgranda" | ||||
| edit: "Redakti" | ||||
| emailServer: "Retpoŝta servilo" | ||||
| email: "Retpoŝto" | ||||
| emailAddress: "Retpoŝta adreso" | ||||
| smtpConfig: "Agordoj de la servilo SMTP" | ||||
| smtpConfig: "Agordoj de SMTP servilo" | ||||
| smtpHost: "Gastiganto" | ||||
| smtpPort: "Pordo" | ||||
| smtpUser: "Uzantnomo" | ||||
| smtpPass: "Pasvorto" | ||||
| wordMute: "Silentigo de vortoj" | ||||
| wordMute: "Silentigi specifajn vortojn" | ||||
| userSaysSomething: "{name} parolis ion" | ||||
| makeActive: "Aktivigi" | ||||
| display: "Vidi" | ||||
| copy: "Kopii" | ||||
| overview: "Resumo" | ||||
| database: "Datumbazo" | ||||
| channel: "Kanalo" | ||||
| create: "Krei" | ||||
| notificationSetting: "Agordoj de sciigoj" | ||||
| useGlobalSetting: "Oni uzas malloka agordo" | ||||
| fileIdOrUrl: "Dosiera identigilo aŭ URL" | ||||
| sample: "Ekzemplo" | ||||
| abuseReports: "Signaloj" | ||||
| reportAbuse: "Signalo" | ||||
| reportAbuseOf: "Signali kontraŭ {name}'(o)" | ||||
| send: "Sendi" | ||||
| openInNewTab: "Malfermi en nova langeto" | ||||
| editTheseSettingsMayBreakAccount: "Redakti tiujn agordojn estas eble damaĝi konton." | ||||
| editTheseSettingsMayBreakAccount: "Redakti ĉi tiujn agordojn povas damaĝi vian konton." | ||||
| instanceTicker: "Informoj pri la nodo kiu dissendas la noton" | ||||
| random: "Hazarde" | ||||
| system: "Sistemo" | ||||
| desktop: "Labortablo" | ||||
| createNew: "Krei novan" | ||||
| optional: "Opciaj" | ||||
| public: "Publika" | ||||
| i18nInfo: "Misskey estas tradukata en diversaj lingvoj far volontuloj. Oni povas kontribui por la tradukado ĉe {link}." | ||||
| accountInfo: "Kontaj Informoj" | ||||
| notesCount: "Numero de notoj" | ||||
| repliesCount: "Numero de respondoj senditaj" | ||||
| renotesCount: "Numero de renotoj kiun vi sendis" | ||||
| repliedCount: "Numero de respondoj ricevitaj" | ||||
| renotedCount: "Numero de renotoj kiun vi ricevis" | ||||
| followingCount: "Numero de sekvatoj" | ||||
| followersCount: "Numero de sekvantoj" | ||||
| sentReactionsCount: "Numero de sentitaj reagoj" | ||||
| receivedReactionsCount: "Numero de ricevitaj reagoj" | ||||
| notesCount: "La nombro de notoj" | ||||
| repliesCount: "La nombro de respondoj senditaj" | ||||
| renotesCount: "La nombro de notoj kiujn la uzanto plusendis" | ||||
| repliedCount: "La nombro de respondoj ricevitaj" | ||||
| renotedCount: "La nombro de uzantulaj notoj plusenditaj" | ||||
| followingCount: "La nombro de sekvatoj" | ||||
| followersCount: "La nombro de sekvantoj" | ||||
| sentReactionsCount: "La nombro de la reagoj senditaj" | ||||
| receivedReactionsCount: "La nombro de la reagoj ricevitaj" | ||||
| yes: "Jes" | ||||
| no: "Ne" | ||||
| driveFilesCount: "Numero de dosieroj sur la disko" | ||||
| driveFilesCount: "La nombro de la dosieroj ĉe la disko" | ||||
| notSet: "Ne elektita" | ||||
| noteFavoritesCount: "Numero de la preferataj notoj" | ||||
| emailVerified: "Via retpoŝto estis kontrolita." | ||||
| noteFavoritesCount: "La nombro de notoj preferataj" | ||||
| pageLikesCount: "La nombro de paĝoj kiun la uzanto preferas" | ||||
| pageLikedCount: "La nombro de uzantoj kiuj preferas la paĝon" | ||||
| contact: "Kontakto" | ||||
| makeExplorable: "Videbligi konton sur la paĝo \"Esplori\"" | ||||
| duplicate: "Duobligi" | ||||
| left: "Maldekstra" | ||||
| center: "Centra" | ||||
| showTitlebar: "Montri titola stango" | ||||
| showTitlebar: "Videbligi titolan stangon" | ||||
| clearCache: "Malplenigi staplon" | ||||
| onlineUsersCount: "{n} uzanto(j) estas surlinea" | ||||
| nUsers: "{n} uzanto(j)" | ||||
| @@ -417,19 +508,25 @@ newVersionOfClientAvailable: "Nova versio de via kliento estas disponebla." | ||||
| inUse: "Uzata" | ||||
| editCode: "Redakti kodon" | ||||
| emailNotification: "Sciigoj per retpoŝto" | ||||
| publish: "Publikigi" | ||||
| inChannelSearch: "Serĉi en kanalo" | ||||
| useReactionPickerForContextMenu: "Oni malfermas reago-elektilon per dekstro-kliki" | ||||
| typingUsers: "{users} estas entajpanta(j)..." | ||||
| typingUsers: "{users} nun entajpas…" | ||||
| clear: "Vakigi" | ||||
| goBack: "Reiri antaŭ" | ||||
| addDescription: "Priskribi" | ||||
| info: "Informoj" | ||||
| userInfo: "La informoj de uzanto" | ||||
| unknown: "Nekonata" | ||||
| online: "Surkonektita" | ||||
| offline: "Forkonektita" | ||||
| instanceBlocking: "Blokado de ekzemplo" | ||||
| instanceBlocking: "Bloki specifajn nodojn" | ||||
| selectAccount: "Elekti konton" | ||||
| user: "Uzanto" | ||||
| user: "Uzantoj" | ||||
| administration: "Administro" | ||||
| accounts: "Kontoj" | ||||
| shareWithNote: "Kundividi en noto" | ||||
| ads: "Reklamaĵo" | ||||
| memo: "Memorigilo" | ||||
| high: "Alta" | ||||
| middle: "Meza" | ||||
| low: "Malalta" | ||||
| @@ -439,6 +536,7 @@ sent: "Sendi" | ||||
| received: "Ricevita" | ||||
| searchResult: "Serĉorezultoj" | ||||
| hashtags: "Kradvorto" | ||||
| troubleshooting: "Problemsolvi" | ||||
| learnMore: "Lernu pli" | ||||
| translate: "Traduki" | ||||
| translatedFrom: "Tradukita el {x}" | ||||
| @@ -446,6 +544,8 @@ _docs: | ||||
|   continueReading: "Legi plu" | ||||
|   features: "Funkcioj" | ||||
|   admin: "Administro" | ||||
| _ad: | ||||
|   back: "Nuligi" | ||||
| _gallery: | ||||
|   liked: "Ŝatitaj notoj" | ||||
|   like: "Ŝati" | ||||
| @@ -465,12 +565,12 @@ _registry: | ||||
| _aboutMisskey: | ||||
|   about: "Misskey estas malfermitkoda programo evoluigata de syuilo ekde la 2014." | ||||
|   contributors: "Precipaj kontribuantoj" | ||||
|   allContributors: "Ĉiuj kontribuintoj" | ||||
|   allContributors: "Ĉiuj kontribuantoj" | ||||
|   source: "Fontkodo" | ||||
|   translation: "Traduki Misskey'on" | ||||
|   patrons: "Mecenatoj" | ||||
| _mfm: | ||||
|   dummy: "Misskey vastigas la mondon de Fediverso" | ||||
|   dummy: "Misskey evoluigas la mondon de Fediverso" | ||||
|   mention: "Mencioj" | ||||
|   hashtag: "Kradvorto" | ||||
|   url: "URL" | ||||
| @@ -489,6 +589,7 @@ _mfm: | ||||
|   x2: "Granda" | ||||
|   x3: "Grandega" | ||||
|   x4: "Pli grandega" | ||||
|   font: "Presliteraro" | ||||
| _reversi: | ||||
|   total: "Entute" | ||||
| _instanceTicker: | ||||
| @@ -498,27 +599,37 @@ _instanceTicker: | ||||
| _channel: | ||||
|   create: "Krei kanalon" | ||||
|   edit: "Redakti kanalon" | ||||
|   setBanner: "Apliki standardan bildon" | ||||
|   removeBanner: "Forviŝi la standardan bildon" | ||||
|   owned: "Posedaĵo" | ||||
|   following: "Sekvante" | ||||
|   usersCount: "{n} partoprenanto(j)" | ||||
| _menuDisplay: | ||||
|   top: "Supro" | ||||
|   hide: "Kaŝi" | ||||
| _wordMute: | ||||
|   muteWords: "Kaŝigitaj vortoj" | ||||
|   mutedNotes: "Silentigataj notoj" | ||||
|   muteWords: "Silentigitaj vortoj" | ||||
|   soft: "En kliento" | ||||
|   hard: "En servilo" | ||||
|   mutedNotes: "Silentigitaj notoj" | ||||
| _theme: | ||||
|   manage: "Administri kolorarojn" | ||||
|   code: "Kodo de koloraro" | ||||
|   description: "Priskribe" | ||||
|   color: "Koloro" | ||||
|   darken: "Malbrileco" | ||||
|   lighten: "Brileco" | ||||
|   keys: | ||||
|     bg: "Fono" | ||||
|     navBg: "Fono de flanka stango" | ||||
|     link: "Ligilo" | ||||
|     hashtag: "Kradvorto" | ||||
|     mention: "Mencioj" | ||||
|     renote: "Fari renoton" | ||||
|     mentionMe: "Mencio al vi" | ||||
|     renote: "Noto plusendita" | ||||
|     buttonBg: "Fono de butono" | ||||
|     driveFolderBg: "Fono de dosierujo de la disko" | ||||
|     messageBg: "Fono de retbabilejo" | ||||
| _sfx: | ||||
|   note: "Nova noto" | ||||
|   noteMy: "Mia noto" | ||||
| @@ -530,7 +641,7 @@ _sfx: | ||||
| _ago: | ||||
|   future: "Futuro" | ||||
|   justNow: "Ĵus" | ||||
|   secondsAgo: "Antaŭ {n} sekundoj" | ||||
|   secondsAgo: "Antaŭ {n} sekundo(j)" | ||||
|   minutesAgo: "Antaŭ {n} minutoj" | ||||
|   hoursAgo: "Antaŭ {n} horo(j)" | ||||
|   daysAgo: "Antaŭ {n} tagoj" | ||||
| @@ -547,27 +658,33 @@ _tutorial: | ||||
|   step1_1: "Bonvenon." | ||||
|   step7_2: "Se vi volas scii pli pri Misskey, rigardu la fakon {help}." | ||||
|   step7_3: "Do, bonvolu amuziĝi Misskey'on🚀" | ||||
| _2fa: | ||||
|   registerKey: "Nove registri ŝlosilon" | ||||
| _permissions: | ||||
|   "read:blocks": "Vidi la liston de uzantoj kiun vi blokas" | ||||
|   "write:blocks": "Redakti vian liston de blokataj uzantoj" | ||||
|   "read:drive": "Operacio por legi la informon de dosiero en via disko de Misskey" | ||||
|   "read:account": "Legado de la informoj pri via konto" | ||||
|   "write:account": "Redatado de la informoj de via konto" | ||||
|   "read:blocks": "Vidi vian liston de uzantoj blokitaj" | ||||
|   "write:blocks": "Redakti vian liston de blokitoj" | ||||
|   "read:drive": "Legi vian diskon" | ||||
|   "write:drive": "Ĉia operacio por skribi, forviŝi, aŭ alimaniere ŝanĝi la informon de dosiero en via disko de Misskey" | ||||
|   "read:favorites": "Vidi vian liston de preferatoj" | ||||
|   "read:following": "Vidi tiun kiun vi sekvas" | ||||
|   "read:favorites": "Vidi vian liston de preferataĵoj" | ||||
|   "write:favorites": "Redakti vian liston de preferataĵoj." | ||||
|   "read:following": "Vidi la infomaciojn pri tio, kion vi sekvas" | ||||
|   "write:following": "Sekvi aŭ malsekvi alian uzanton" | ||||
|   "read:messaging": "Vidi vian retbabiladon" | ||||
|   "read:mutes": "Vidi vian liston de silentigoj" | ||||
|   "write:mutes": "Redakti vian liston de silentigoj" | ||||
|   "write:messaging": "Retbabilejo" | ||||
|   "read:mutes": "Vidi vian liston de silentigitoj" | ||||
|   "write:mutes": "Redakti vian liston de silentigitoj" | ||||
|   "write:notes": "Krei / Forviŝi noton" | ||||
|   "read:notifications": "Vidi sciigojn" | ||||
|   "write:notifications": "Manipulado por viaj sciigoj" | ||||
|   "read:reactions": "Vidi reagojn" | ||||
|   "write:reactions": "Redakti viajn reagojn" | ||||
|   "read:pages": "Vidi via paĝojn" | ||||
|   "read:page-likes": "Vidi ŝatojn de paĝo" | ||||
|   "read:channels": "Vidi kanalojn" | ||||
| _antennaSources: | ||||
|   all: "Ĉiuj notoj" | ||||
|   homeTimeline: "Notoj far uzantoj, kiujn vi sekvas" | ||||
|   homeTimeline: "Notoj far uzantoj kiujn vi sekvas" | ||||
| _weekday: | ||||
|   sunday: "dimanĉo" | ||||
|   monday: "lundo" | ||||
| @@ -585,6 +702,7 @@ _widgets: | ||||
|   slideshow: "Bildoprezento" | ||||
|   button: "Butono" | ||||
|   onlineUsers: "Surkonektita uzanto" | ||||
|   aichan: "Ai" | ||||
| _cw: | ||||
|   show: "Vidu pli" | ||||
|   files: "{count} dosiero(j)" | ||||
| @@ -597,59 +715,90 @@ _poll: | ||||
|   vote: "Baloti" | ||||
|   closed: "Oni jam balotis ĝin" | ||||
| _visibility: | ||||
|   publicDescription: "Via noto aperiĝos sur la konfederacia templinio" | ||||
|   home: "Hejmo" | ||||
|   homeDescription: "Elsendi nur sur la hejmtemplinio" | ||||
|   publicDescription: "Via noto estos videbla de ĉiuj uzantoj" | ||||
|   home: "Hejma" | ||||
|   homeDescription: "Dissendi nur sur hejma templinio" | ||||
|   followers: "Sekvantoj" | ||||
|   followersDescription: "Nur al sekvantoj al mi" | ||||
|   specified: "Rekta" | ||||
|   specifiedDescription: "Publikigi nur al specifaj uzantoj" | ||||
|   localOnly: "Nur loka" | ||||
|   localOnlyDescription: "Ne montri al transaj uzantoj" | ||||
| _postForm: | ||||
|   replyPlaceholder: "Respondi al tiu noto..." | ||||
|   quotePlaceholder: "Citado tiun noton..." | ||||
|   channelPlaceholder: "Sendi sur la kanalo" | ||||
|   replyPlaceholder: "Respondi tiun noton…" | ||||
|   quotePlaceholder: "Citi tiun noton…" | ||||
|   channelPlaceholder: "Mencii en kanalo…" | ||||
| _profile: | ||||
|   name: "Nomo" | ||||
|   username: "Uzantnomo" | ||||
|   description: "Pri mi" | ||||
|   metadata: "Kromaj informoj" | ||||
|   metadataEdit: "Redakti kromaj informoj" | ||||
|   changeAvatar: "Ŝanĝi profilbildon" | ||||
|   changeBanner: "Ŝanĝi standardon" | ||||
| _exportOrImport: | ||||
|   allNotes: "Ĉiuj notoj" | ||||
|   followingList: "Sekvataj" | ||||
|   followingList: "Sekvataj uzantoj" | ||||
|   muteList: "Silentigoj" | ||||
|   blockingList: "Blokado" | ||||
|   blockingList: "Blokitoj" | ||||
|   userLists: "Listoj" | ||||
| _charts: | ||||
|   federationInstancesTotal: "Tuta numero de kunfederantaj ekzemploj" | ||||
|   filesTotal: "Tuta numero de dosieroj" | ||||
|   federationInstancesTotal: "La totala nombro de nodoj kunfederantaj" | ||||
|   usersTotal: "La totala nombro de la uzantoj" | ||||
|   activeUsers: "La nombro de la uzantoj aktivaj" | ||||
|   notesTotal: "La totala nombro de la notoj" | ||||
|   filesTotal: "La totala nombro de la dosieroj" | ||||
| _timelines: | ||||
|   home: "HEJMO" | ||||
|   local: "LOKA" | ||||
|   social: "SOCIALA" | ||||
|   global: "MALLOKA" | ||||
|   home: "Hejma" | ||||
|   local: "Loka" | ||||
|   social: "Sociala" | ||||
|   global: "Malloka" | ||||
| _rooms: | ||||
|   translate: "Movi" | ||||
|   chooseImage: "Elekti bildon" | ||||
|   _furnitures: | ||||
|     bed: "Lito" | ||||
|     low-table: "Malaltotablo" | ||||
|     desk: "Skribotablo" | ||||
|     chair: "Seĝo" | ||||
|     chair2: "Seĝo 2" | ||||
|     pc: "Komputilo" | ||||
|     eraser: "Skrapileto" | ||||
|     pencil: "Krajono" | ||||
|     pudding: "Flaŭno" | ||||
|     book: "Libro" | ||||
|     book2: "Libro 2" | ||||
|     piano: "Piano" | ||||
|     facial-tissue: "Tualetpaperejo" | ||||
|     server: "Servilo" | ||||
|     moon: "La luno" | ||||
|     moon: "Luno" | ||||
|     monitor: "Monitoro" | ||||
|     keyboard: "Klavaro" | ||||
|     doll-ai: "Pupa Ai" | ||||
| _pages: | ||||
|   newPage: "Krei novan paĝon" | ||||
|   editPage: "Redakti paĝon" | ||||
|   deleted: "La paĝo estas forigita." | ||||
|   deleted: "Oni forviŝis la paĝon." | ||||
|   editThisPage: "Redakti la paĝon" | ||||
|   viewPage: "Vidi via paĝojn" | ||||
|   viewPage: "Vidi viajn paĝojn" | ||||
|   my: "Miaj paĝoj" | ||||
|   featured: "Ravaĵoj" | ||||
|   content: "Blokado de paĝo" | ||||
|   contents: "Enhavo" | ||||
|   content: "Paĝo en bloko" | ||||
|   url: "URL de paĝo" | ||||
|   alignCenter: "Centrigi" | ||||
|   chooseBlock: "Aldoni blokon" | ||||
|   contentBlocks: "Enhavo" | ||||
|   blocks: | ||||
|     text: "Teksto" | ||||
|     textarea: "Areo de teksto" | ||||
|     image: "Bildo" | ||||
|     button: "Butono" | ||||
|     _post: | ||||
|       canvasId: "Kanvasa identigilo" | ||||
|     textInput: "Enmeto el teksto" | ||||
|     textareaInput: "Enmeto el teksto en multaj linioj" | ||||
|     numberInput: "Nombra enmeto" | ||||
|     _numberInput: | ||||
|       text: "Titolo" | ||||
|     _canvas: | ||||
| @@ -665,8 +814,20 @@ _pages: | ||||
|           event: "Nomo de la evento" | ||||
|   script: | ||||
|     categories: | ||||
|       text: "Manipulo de teksto" | ||||
|       list: "Listoj" | ||||
|     blocks: | ||||
|       text: "Teksto" | ||||
|       multiLineText: "Teksto (multaj linioj)" | ||||
|       textList: "List de teksto" | ||||
|       _strLen: | ||||
|         arg1: "Teksto" | ||||
|       _strPick: | ||||
|         arg1: "Teksto" | ||||
|       _strReplace: | ||||
|         arg1: "Teksto" | ||||
|       _strReverse: | ||||
|         arg1: "Teksto" | ||||
|       _join: | ||||
|         arg1: "Listoj" | ||||
|       _randomPick: | ||||
| @@ -675,28 +836,40 @@ _pages: | ||||
|         arg1: "Listoj" | ||||
|       _seedRandomPick: | ||||
|         arg2: "Listoj" | ||||
|       _DRPWPM: | ||||
|         arg1: "List de teksto" | ||||
|       pick: "Elekti de la listo" | ||||
|       _pick: | ||||
|         arg1: "Listoj" | ||||
|       _listLen: | ||||
|         arg1: "Listoj" | ||||
|       _stringToNumber: | ||||
|         arg1: "Teksto" | ||||
|       _splitStrByLine: | ||||
|         arg1: "Teksto" | ||||
|       _fn: | ||||
|         slots: "Juntoj" | ||||
|         arg1: "Elmeto" | ||||
|     thereIsEmptySlot: "La junto {slot} estas malplena!" | ||||
|     types: | ||||
|       string: "Teksto" | ||||
|       array: "Listoj" | ||||
|       stringArray: "List de teksto" | ||||
|     emptySlot: "Malplena junto" | ||||
|     argVariables: "Eniga junto" | ||||
| _notification: | ||||
|   fileUploaded: "La dosiero sukcese alŝutiĝis." | ||||
|   youRenoted: "{name} plusendis" | ||||
|   youGotPoll: "{name} balotis" | ||||
|   youGotMessagingMessageFromUser: "{name} sentis mesaĝon al vi." | ||||
|   youGotMessagingMessageFromGroup: "Retbabilan mesaĝon oni sendis al la grupo {name}" | ||||
|   youWereFollowed: "sksekvis vin" | ||||
|   youWereFollowed: "eksekvis vin" | ||||
|   youReceivedFollowRequest: "Vi ricevis peton de sekvado" | ||||
|   yourFollowRequestAccepted: "Via peto por sekvado estis akceptita." | ||||
|   _types: | ||||
|     follow: "Sekvatoj" | ||||
|     mention: "Mencioj" | ||||
|     renote: "Fari renoton" | ||||
|     renote: "Notoj plusenditaj" | ||||
|     quote: "Citi" | ||||
|     reaction: "Reagoj" | ||||
|     receiveFollowRequest: "Ricevita peton de sekvado" | ||||
| @@ -709,3 +882,4 @@ _deck: | ||||
|     antenna: "Antenoj" | ||||
|     list: "Listoj" | ||||
|     mentions: "Al vi" | ||||
|     direct: "Notoj rektaj" | ||||
|   | ||||
| @@ -429,7 +429,7 @@ invitationCode: "Code d’invitation" | ||||
| checking: "Vérification en cours..." | ||||
| available: "Disponible" | ||||
| unavailable: "Non disponible" | ||||
| usernameInvalidFormat: "Le nom d'utilisateur peut contenir uniquement des lettres, des chiffres et des _" | ||||
| usernameInvalidFormat: "Le nom d'utilisateur peut contenir uniquement des lettres (minuscules et/ou majuscules), des chiffres et des _" | ||||
| tooShort: "Trop court" | ||||
| tooLong: "Trop long" | ||||
| weakPassword: "Mot de passe faible" | ||||
| @@ -529,6 +529,7 @@ removeAllFollowing: "Retenir tous les abonnements" | ||||
| removeAllFollowingDescription: "Se désabonner de tous les comptes de {host}. Veuillez lancer cette action uniquement si l’instance n’existe plus." | ||||
| userSuspended: "Cet·te utilisateur·rice a été suspendu·e." | ||||
| userSilenced: "Cette utilisateur·trice a été mis·e en sourdine." | ||||
| yourAccountSuspendedTitle: "Ce compte est suspendu" | ||||
| menu: "Menu" | ||||
| divider: "Séparateur" | ||||
| addItem: "Ajouter un élément" | ||||
| @@ -767,6 +768,7 @@ customCssWarn: "Utilisez cette fonctionnalité uniquement si vous savez exacteme | ||||
| global: "Global" | ||||
| squareAvatars: "Avatars carrés" | ||||
| sent: "Envoyer" | ||||
| received: "Reçu" | ||||
| searchResult: "Résultats de la recherche" | ||||
| hashtags: "Hashtags" | ||||
| troubleshooting: "Résolution de problèmes" | ||||
| @@ -776,6 +778,21 @@ misskeyUpdated: "Misskey a été mis à jour !" | ||||
| whatIsNew: "Voir les derniers changements" | ||||
| translate: "Traduire" | ||||
| translatedFrom: "Traduit depuis {x}" | ||||
| accountDeletionInProgress: "La suppression de votre compte est en cours" | ||||
| usernameInfo: "C'est un nom qui identifie votre compte sur l'instance de manière unique. Vous pouvez utiliser des lettres de l'alphabet (minuscules et majuscules), des chiffres (de 0 à 9), ou bien le tiret « _ ». Vous ne pourrez pas modifier votre nom d'utilisateur·rice par la suite." | ||||
| aiChanMode: "Mode Ai" | ||||
| keepCw: "Garder le CW" | ||||
| pubSub: "Comptes Pub/Sub" | ||||
| lastCommunication: "Dernière communication" | ||||
| resolved: "Résolu" | ||||
| unresolved: "En attente" | ||||
| _accountDelete: | ||||
|   accountDelete: "Supprimer le compte" | ||||
|   mayTakeTime: "La suppression de compte nécessitant beaucoup de ressources, l'exécution du processus peut prendre du temps, en fonction de la quantité de contenus que vous avez créés et du nombre de fichiers que vous avez téléversés." | ||||
|   sendEmail: "Une fois la suppression de votre compte effectuée, un courriel sera envoyé à l'adresse que vous aviez enregistrée." | ||||
|   requestAccountDelete: "Demander la suppression de votre compte" | ||||
|   started: "La procédure de suppression a commencé." | ||||
|   inProgress: "Suppression en cours" | ||||
| _docs: | ||||
|   continueReading: "Lire plus" | ||||
|   features: "Fonctionnalités" | ||||
| @@ -1109,6 +1126,10 @@ _permissions: | ||||
|   "write:user-groups": "Éditer les groupes des utilisateur·rice·s" | ||||
|   "read:channels": "Lire les canaux" | ||||
|   "write:channels": "Gérer les canaux" | ||||
|   "read:gallery": "Voir la galerie" | ||||
|   "write:gallery": "Éditer la galerie" | ||||
|   "read:gallery-likes": "Voir les mentions « J'aime » dans la galerie" | ||||
|   "write:gallery-likes": "Gérer les mentions « J'aime » dans la galerie" | ||||
| _auth: | ||||
|   shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?" | ||||
|   shareAccessAsk: "Voulez-vous vraiment autoriser cette application à accéder à votre compte?" | ||||
| @@ -1149,6 +1170,7 @@ _widgets: | ||||
|   jobQueue: "File d’attente" | ||||
|   serverMetric: "Statistiques du serveur" | ||||
|   aiscript: "Console AiScript" | ||||
|   aichan: "Ai" | ||||
| _cw: | ||||
|   hide: "Masquer" | ||||
|   show: "Afficher plus …" | ||||
|   | ||||
| @@ -775,6 +775,18 @@ useBlurEffect: "Gunakan efek blur pada antarmuka" | ||||
| learnMore: "Pelajari lebih lanjut" | ||||
| misskeyUpdated: "Misskey telah dimutakhirkan!" | ||||
| whatIsNew: "Lihat perubahan pemutakhiran" | ||||
| translate: "Terjemahkan" | ||||
| translatedFrom: "Terjemahkan dari {x}" | ||||
| accountDeletionInProgress: "Penghapusan akun sedang dalam proses" | ||||
| usernameInfo: "Nama yang mengidentifikasikan akun kamu dari yang lain pada server ini. Kamu dapat menggunakan alfabet (a~z, A~Z), digit (0~9) atau garis bawah (_). Username tidak dapat diubah setelahnya." | ||||
| keepCw: "Biarkan Peringatan Konten" | ||||
| _accountDelete: | ||||
|   accountDelete: "Hapus akun" | ||||
|   mayTakeTime: "Karena penghapusan akun merupakan proses yang berat dan intensif, kemungkinan dapat membutuhkan waktu untuk menyelesaikan tergantung daripada berapa banyak konten yang kamu buat dan berapa banyak berkas yang telah kamu unggah." | ||||
|   sendEmail: "Setelah penghapusan akun selesai, pemberitahuan akan dikirimkan ke alamat surel yang terdaftarkan pada akun ini." | ||||
|   requestAccountDelete: "Minta penghapusan akun" | ||||
|   started: "Penghapusan telah dimulai" | ||||
|   inProgress: "Penghapusan sedang dalam proses" | ||||
| _docs: | ||||
|   continueReading: "Baca lebih lanjut" | ||||
|   features: "Fitur" | ||||
| @@ -1148,6 +1160,7 @@ _widgets: | ||||
|   jobQueue: "Antrian kerja" | ||||
|   serverMetric: "Statistik server" | ||||
|   aiscript: "Konsol AiScript" | ||||
|   aichan: "Ai" | ||||
| _cw: | ||||
|   hide: "Sembunyikan" | ||||
|   show: "Lihat konten" | ||||
|   | ||||
| @@ -529,6 +529,8 @@ removeAllFollowing: "フォローを全解除" | ||||
| removeAllFollowingDescription: "{host}からのフォローをすべて解除します。そのインスタンスがもう存在しなくなった場合などに実行してください。" | ||||
| userSuspended: "このユーザーは凍結されています。" | ||||
| userSilenced: "このユーザーはサイレンスされています。" | ||||
| yourAccountSuspendedTitle: "アカウントが凍結されています" | ||||
| yourAccountSuspendedDescription: "このアカウントは、サーバーの利用規約に違反したなどの理由により、凍結されています。詳細については管理者までお問い合わせください。新しいアカウントを作らないでください。" | ||||
| menu: "メニュー" | ||||
| divider: "分割線" | ||||
| addItem: "項目を追加" | ||||
| @@ -779,6 +781,12 @@ translate: "翻訳" | ||||
| translatedFrom: "{x}から翻訳" | ||||
| accountDeletionInProgress: "アカウントの削除が進行中です" | ||||
| usernameInfo: "サーバー上であなたのアカウントを一意に識別するための名前。アルファベット(a~z, A~Z)、数字(0~9)、およびアンダーバー(_)が使用できます。ユーザー名は後から変更することは出来ません。" | ||||
| aiChanMode: "藍モード" | ||||
| keepCw: "CWを維持する" | ||||
| pubSub: "Pub/Subのアカウント" | ||||
| lastCommunication: "直近の通信" | ||||
| resolved: "解決済み" | ||||
| unresolved: "未解決" | ||||
|  | ||||
| _accountDelete: | ||||
|   accountDelete: "アカウントの削除" | ||||
| @@ -904,6 +912,8 @@ _mfm: | ||||
|   fontDescription: "内容のフォントを指定することができます。" | ||||
|   rainbow: "レインボー" | ||||
|   rainbowDescription: "内容をレインボーにします。" | ||||
|   sparkle: "キラキラ" | ||||
|   sparkleDescription: "キラキラしたパーティクルのエフェクトを追加します。" | ||||
|  | ||||
| _reversi: | ||||
|   reversi: "リバーシ" | ||||
| @@ -1144,6 +1154,10 @@ _permissions: | ||||
|   "write:user-groups": "ユーザーグループを操作する" | ||||
|   "read:channels": "チャンネルを見る" | ||||
|   "write:channels": "チャンネルを操作する" | ||||
|   "read:gallery": "ギャラリーを見る" | ||||
|   "write:gallery": "ギャラリーを操作する" | ||||
|   "read:gallery-likes": "ギャラリーのいいねを見る" | ||||
|   "write:gallery-likes": "ギャラリーのいいねを操作する" | ||||
|  | ||||
| _auth: | ||||
|   shareAccess: "「{name}」がアカウントにアクセスすることを許可しますか?" | ||||
| @@ -1188,6 +1202,7 @@ _widgets: | ||||
|   jobQueue: "ジョブキュー" | ||||
|   serverMetric: "サーバーメトリクス" | ||||
|   aiscript: "AiScriptコンソール" | ||||
|   aichan: "藍" | ||||
|  | ||||
| _cw: | ||||
|   hide: "隠す" | ||||
|   | ||||
| @@ -245,7 +245,7 @@ messageRead: "もう読んだ" | ||||
| noMoreHistory: "これより過去の履歴はあらへんで" | ||||
| startMessaging: "チャットやるで" | ||||
| nUsersRead: "{n}人が読んでもうた" | ||||
| agreeTo: "{0}はええで" | ||||
| agreeTo: "{0}に同意したで" | ||||
| tos: "利用規約" | ||||
| start: "始める" | ||||
| home: "ホーム" | ||||
| @@ -346,7 +346,7 @@ antennaSource: "受信ソース(このソースは食われへん)" | ||||
| antennaKeywords: "受信キーワード" | ||||
| antennaExcludeKeywords: "除外キーワード" | ||||
| antennaKeywordsDescription: "スペースで区切ったるとAND指定で、改行で区切ったるとOR指定や" | ||||
| notifyAntenna: "新しいノートを追加すんで" | ||||
| notifyAntenna: "新しいノートを通知すんで" | ||||
| withFileAntenna: "なんか添付されたノートだけ" | ||||
| enableServiceworker: "ServiceWorkerをつこて" | ||||
| antennaUsersDescription: "ユーザー名を改行で区切ったってな" | ||||
|   | ||||
| @@ -36,6 +36,7 @@ selectList: "Fren tabdart" | ||||
| youHaveNoLists: "Ulac ɣur-k·m ula d yiwet n tabdart" | ||||
| security: "Taɣellist" | ||||
| remove: "Kkes" | ||||
| connectService: "Qqen" | ||||
| userList: "Tibdarin" | ||||
| securityKey: "Tasarutt n tɣellist" | ||||
| securityKeyName: "Isem n tsarutt" | ||||
|   | ||||
| @@ -529,6 +529,8 @@ removeAllFollowing: "모든 팔로잉 해제" | ||||
| removeAllFollowingDescription: "{host}(으)로부터 모든 팔로잉을 해제합니다. 해당 인스턴스가 더 이상 존재하지 않게 된 경우 등에 실행해 주세요." | ||||
| userSuspended: "이 계정은 정지된 상태입니다." | ||||
| userSilenced: "이 계정은 사일런스된 상태입니다." | ||||
| yourAccountSuspendedTitle: "계정이 정지되었습니다" | ||||
| yourAccountSuspendedDescription: "이 계정은 서버의 이용 약관을 위반하거나, 기타 다른 이유로 인해 정지되었습니다. 자세한 사항은 관리자에게 문의해 주십시오. 계정을 새로 생성하지 마십시오." | ||||
| menu: "메뉴" | ||||
| divider: "구분선" | ||||
| addItem: "항목 추가" | ||||
| @@ -779,6 +781,12 @@ translate: "번역" | ||||
| translatedFrom: "{x}에서 번역" | ||||
| accountDeletionInProgress: "계정 삭제 작업을 진행하고 있습니다" | ||||
| usernameInfo: "서버상에서 계정을 식별하기 위한 이름. 알파벳(a~z, A~Z), 숫자(0~9) 및 언더바(_)를 사용할 수 있습니다. 사용자명은 나중에 변경할 수 없습니다." | ||||
| aiChanMode: "아이 모드" | ||||
| keepCw: "CW 유지하기" | ||||
| pubSub: "Pub/Sub 계정" | ||||
| lastCommunication: "마지막 통신" | ||||
| resolved: "해결됨" | ||||
| unresolved: "해결되지 않음" | ||||
| _accountDelete: | ||||
|   accountDelete: "계정 삭제" | ||||
|   mayTakeTime: "계정 삭제는 서버에 부하를 가하기 때문에, 작성한 콘텐츠나 업로드한 파일의 수가 많으면 완료까지 시간이 걸릴 수 있습니다." | ||||
| @@ -1114,11 +1122,15 @@ _permissions: | ||||
|   "read:pages": "페이지를 봅니다" | ||||
|   "write:pages": "페이지를 수정합니다" | ||||
|   "read:page-likes": "페이지의 좋아요를 확인합니다" | ||||
|   "write:page-likes": "페이지의 좋아요를 추가하거나 삭제합니다" | ||||
|   "write:page-likes": "페이지에 좋아요를 추가하거나 취소합니다" | ||||
|   "read:user-groups": "유저 그룹을 조회합니다" | ||||
|   "write:user-groups": "유저 그룹을 만들거나, 초대하거나, 이름을 변경하거나, 양도하거나, 삭제합니다" | ||||
|   "read:channels": "채널을 보기" | ||||
|   "write:channels": "채널을 변경하기" | ||||
|   "write:channels": "채널을 추가하거나 삭제합니다" | ||||
|   "read:gallery": "갤러리를 봅니다" | ||||
|   "write:gallery": "갤러리를 추가하거나 삭제합니다" | ||||
|   "read:gallery-likes": "갤러리의 좋아요를 확인합니다" | ||||
|   "write:gallery-likes": "갤러리에 좋아요를 추가하거나 취소합니다" | ||||
| _auth: | ||||
|   shareAccess: "\"{name}\" 이 계정에 접근하는 것을 허용하시겠습니까?" | ||||
|   shareAccessAsk: "이 애플리케이션이 계정에 접근하는 것을 허용하시겠습니까?" | ||||
| @@ -1159,6 +1171,7 @@ _widgets: | ||||
|   jobQueue: "작업 대기열" | ||||
|   serverMetric: "서버 통계" | ||||
|   aiscript: "AiScript 콘솔" | ||||
|   aichan: "아이" | ||||
| _cw: | ||||
|   hide: "숨기기" | ||||
|   show: "더 보기" | ||||
|   | ||||
| @@ -1148,6 +1148,7 @@ _widgets: | ||||
|   jobQueue: "Очередь заданий" | ||||
|   serverMetric: "Показатели сервера" | ||||
|   aiscript: "Консоль AiScript" | ||||
|   aichan: "Ай" | ||||
| _cw: | ||||
|   hide: "Спрятать" | ||||
|   show: "Показать еще" | ||||
|   | ||||
| @@ -529,6 +529,8 @@ removeAllFollowing: "取消所有关注" | ||||
| removeAllFollowingDescription: "取消{host}的所有关注者。当实例不存在时执行。" | ||||
| userSuspended: "该用户已被冻结。" | ||||
| userSilenced: "该用户已被禁言。" | ||||
| yourAccountSuspendedTitle: "账户已被冻结" | ||||
| yourAccountSuspendedDescription: "由于违反了服务器的服务条款或其他原因,该账户已被冻结。 您可以与管理员联系以了解更多信息。 请不要创建一个新的帐户。" | ||||
| menu: "菜单" | ||||
| divider: "分割线" | ||||
| addItem: "添加项目" | ||||
| @@ -779,8 +781,18 @@ translate: "翻译" | ||||
| translatedFrom: "从 {x} 翻译" | ||||
| accountDeletionInProgress: "正在删除账户" | ||||
| usernameInfo: "在服务器上唯一标识您的帐户的名称。您可以使用字母 (a ~ z, A ~ Z)、数字 (0 ~ 9) 和下划线 (_)。用户名以后不能更改。" | ||||
| aiChanMode: "小蓝模式" | ||||
| keepCw: "保留CW" | ||||
| pubSub: "Pub/Sub账户" | ||||
| lastCommunication: "最近通信" | ||||
| resolved: "已解决" | ||||
| unresolved: "未解决" | ||||
| _accountDelete: | ||||
|   accountDelete: "删除帐户" | ||||
|   mayTakeTime: "删除账号是一个性能损耗较大的处理,如果账号持有的内容数量和上传的文件数量较多的话,完成需要花费一段时间。" | ||||
|   sendEmail: "账户删除完成后,将向注册的电子邮件地址发送通知。" | ||||
|   requestAccountDelete: "请求删除账户" | ||||
|   started: "账户删除过程已开始。" | ||||
|   inProgress: "正在删除" | ||||
| _docs: | ||||
|   continueReading: "继续阅读" | ||||
| @@ -1115,6 +1127,10 @@ _permissions: | ||||
|   "write:user-groups": "操作用户组" | ||||
|   "read:channels": "查看频道" | ||||
|   "write:channels": "管理频道" | ||||
|   "read:gallery": "浏览图库" | ||||
|   "write:gallery": "操作图库" | ||||
|   "read:gallery-likes": "读取喜欢的图片" | ||||
|   "write:gallery-likes": "操作喜欢的图片" | ||||
| _auth: | ||||
|   shareAccess: "您要授权允许“{name}”访问您的帐户吗?" | ||||
|   shareAccessAsk: "您确定要授权此应用程序访问您的帐户吗?" | ||||
| @@ -1155,6 +1171,7 @@ _widgets: | ||||
|   jobQueue: "作业队列" | ||||
|   serverMetric: "服务器监控" | ||||
|   aiscript: "AiScript控制台" | ||||
|   aichan: "小蓝" | ||||
| _cw: | ||||
|   hide: "隐藏" | ||||
|   show: "查看更多" | ||||
|   | ||||
							
								
								
									
										1
									
								
								misskey-assets
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								misskey-assets
									
									
									
									
									
										Submodule
									
								
							 Submodule misskey-assets added at 0179793ec8
									
								
							
							
								
								
									
										73
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										73
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,7 +1,7 @@ | ||||
| { | ||||
| 	"name": "misskey", | ||||
| 	"author": "syuilo <syuilotan@yahoo.co.jp>", | ||||
| 	"version": "12.89.1", | ||||
| 	"version": "12.91.0", | ||||
| 	"codename": "indigo", | ||||
| 	"repository": { | ||||
| 		"type": "git", | ||||
| @@ -47,7 +47,7 @@ | ||||
| 		"@sinonjs/fake-timers": "7.1.2", | ||||
| 		"@syuilo/aiscript": "0.11.1", | ||||
| 		"@types/bcryptjs": "2.4.2", | ||||
| 		"@types/bull": "3.15.3", | ||||
| 		"@types/bull": "3.15.4", | ||||
| 		"@types/cbor": "6.0.0", | ||||
| 		"@types/dateformat": "3.0.1", | ||||
| 		"@types/escape-regexp": "0.0.0", | ||||
| @@ -55,7 +55,7 @@ | ||||
| 		"@types/gulp": "4.0.9", | ||||
| 		"@types/gulp-rename": "2.0.1", | ||||
| 		"@types/is-url": "1.2.30", | ||||
| 		"@types/js-yaml": "4.0.2", | ||||
| 		"@types/js-yaml": "4.0.3", | ||||
| 		"@types/jsdom": "16.2.13", | ||||
| 		"@types/jsonld": "1.5.6", | ||||
| 		"@types/katex": "0.11.1", | ||||
| @@ -64,16 +64,16 @@ | ||||
| 		"@types/koa-cors": "0.0.2", | ||||
| 		"@types/koa-favicon": "2.0.21", | ||||
| 		"@types/koa-logger": "3.1.1", | ||||
| 		"@types/koa-mount": "4.0.0", | ||||
| 		"@types/koa-mount": "4.0.1", | ||||
| 		"@types/koa-send": "4.1.3", | ||||
| 		"@types/koa-views": "7.0.0", | ||||
| 		"@types/koa__cors": "3.0.3", | ||||
| 		"@types/koa__multer": "2.0.3", | ||||
| 		"@types/koa__router": "8.0.7", | ||||
| 		"@types/markdown-it": "12.0.3", | ||||
| 		"@types/koa__router": "8.0.8", | ||||
| 		"@types/markdown-it": "12.2.1", | ||||
| 		"@types/matter-js": "0.17.5", | ||||
| 		"@types/mocha": "8.2.3", | ||||
| 		"@types/node": "16.6.2", | ||||
| 		"@types/node": "16.9.6", | ||||
| 		"@types/node-fetch": "2.5.12", | ||||
| 		"@types/nodemailer": "6.4.4", | ||||
| 		"@types/nprogress": "0.2.0", | ||||
| @@ -86,13 +86,13 @@ | ||||
| 		"@types/qrcode": "1.4.1", | ||||
| 		"@types/random-seed": "0.3.3", | ||||
| 		"@types/ratelimiter": "3.4.2", | ||||
| 		"@types/redis": "2.8.31", | ||||
| 		"@types/redis": "2.8.32", | ||||
| 		"@types/rename": "1.0.4", | ||||
| 		"@types/request-stats": "3.0.0", | ||||
| 		"@types/rimraf": "3.0.2", | ||||
| 		"@types/seedrandom": "2.4.28", | ||||
| 		"@types/sharp": "0.28.5", | ||||
| 		"@types/sinonjs__fake-timers": "6.0.3", | ||||
| 		"@types/sharp": "0.29.2", | ||||
| 		"@types/sinonjs__fake-timers": "6.0.4", | ||||
| 		"@types/speakeasy": "2.0.6", | ||||
| 		"@types/throttle-debounce": "2.1.0", | ||||
| 		"@types/tinycolor2": "1.4.3", | ||||
| @@ -103,19 +103,19 @@ | ||||
| 		"@types/webpack-stream": "3.2.12", | ||||
| 		"@types/websocket": "1.0.4", | ||||
| 		"@types/ws": "7.4.7", | ||||
| 		"@typescript-eslint/parser": "4.29.2", | ||||
| 		"@vue/compiler-sfc": "3.2.4", | ||||
| 		"@typescript-eslint/parser": "4.31.2", | ||||
| 		"@vue/compiler-sfc": "3.2.13", | ||||
| 		"abort-controller": "3.0.0", | ||||
| 		"apexcharts": "3.27.3", | ||||
| 		"apexcharts": "3.28.3", | ||||
| 		"autobind-decorator": "2.4.0", | ||||
| 		"autosize": "4.0.4", | ||||
| 		"autwh": "0.1.0", | ||||
| 		"aws-sdk": "2.966.0", | ||||
| 		"aws-sdk": "2.992.0", | ||||
| 		"bcryptjs": "2.4.3", | ||||
| 		"blurhash": "1.1.4", | ||||
| 		"broadcast-channel": "4.2.0", | ||||
| 		"bull": "3.28.1", | ||||
| 		"cacheable-lookup": "6.0.0", | ||||
| 		"bull": "3.29.2", | ||||
| 		"cacheable-lookup": "6.0.1", | ||||
| 		"cafy": "15.2.1", | ||||
| 		"cbor": "8.0.0", | ||||
| 		"chalk": "4.1.2", | ||||
| @@ -126,12 +126,12 @@ | ||||
| 		"concurrently": "6.2.1", | ||||
| 		"content-disposition": "0.5.3", | ||||
| 		"crc-32": "1.2.0", | ||||
| 		"css-loader": "6.2.0", | ||||
| 		"css-loader": "6.3.0", | ||||
| 		"cssnano": "5.0.8", | ||||
| 		"dateformat": "4.5.1", | ||||
| 		"escape-regexp": "0.0.1", | ||||
| 		"eslint": "7.32.0", | ||||
| 		"eslint-plugin-vue": "7.16.0", | ||||
| 		"eslint-plugin-vue": "7.18.0", | ||||
| 		"eventemitter3": "4.0.7", | ||||
| 		"feed": "4.2.2", | ||||
| 		"file-type": "16.5.3", | ||||
| @@ -148,6 +148,7 @@ | ||||
| 		"http-signature": "1.3.5", | ||||
| 		"idb-keyval": "5.1.3", | ||||
| 		"insert-text-at-cursor": "0.3.0", | ||||
| 		"ip-cidr": "3.0.4", | ||||
| 		"is-svg": "4.3.1", | ||||
| 		"js-yaml": "4.1.0", | ||||
| 		"jsdom": "16.7.0", | ||||
| @@ -155,7 +156,7 @@ | ||||
| 		"json5-loader": "4.0.1", | ||||
| 		"jsonld": "5.2.0", | ||||
| 		"jsrsasign": "8.0.20", | ||||
| 		"katex": "0.13.13", | ||||
| 		"katex": "0.13.18", | ||||
| 		"koa": "2.13.1", | ||||
| 		"koa-bodyparser": "4.3.0", | ||||
| 		"koa-favicon": "2.1.0", | ||||
| @@ -181,14 +182,15 @@ | ||||
| 		"parse5": "6.0.1", | ||||
| 		"pg": "8.7.1", | ||||
| 		"portscanner": "2.2.0", | ||||
| 		"postcss": "8.3.6", | ||||
| 		"postcss": "8.3.7", | ||||
| 		"postcss-loader": "6.1.1", | ||||
| 		"prismjs": "1.24.1", | ||||
| 		"prismjs": "1.25.0", | ||||
| 		"private-ip": "2.2.1", | ||||
| 		"probe-image-size": "7.2.1", | ||||
| 		"promise-limit": "2.7.0", | ||||
| 		"pug": "3.0.2", | ||||
| 		"punycode": "2.1.1", | ||||
| 		"pureimage": "0.3.2", | ||||
| 		"pureimage": "0.3.5", | ||||
| 		"qrcode": "1.4.4", | ||||
| 		"random-seed": "0.3.0", | ||||
| 		"ratelimiter": "3.4.1", | ||||
| @@ -202,53 +204,54 @@ | ||||
| 		"rimraf": "3.0.2", | ||||
| 		"rndstr": "1.0.0", | ||||
| 		"s-age": "1.1.2", | ||||
| 		"sass": "1.38.0", | ||||
| 		"sass": "1.42.1", | ||||
| 		"sass-loader": "12.1.0", | ||||
| 		"seedrandom": "3.0.5", | ||||
| 		"sharp": "0.29.0", | ||||
| 		"sharp": "0.29.1", | ||||
| 		"speakeasy": "2.0.0", | ||||
| 		"stringz": "2.1.0", | ||||
| 		"style-loader": "3.2.1", | ||||
| 		"style-loader": "3.3.0", | ||||
| 		"summaly": "2.4.1", | ||||
| 		"syslog-pro": "1.0.0", | ||||
| 		"systeminformation": "5.8.0", | ||||
| 		"systeminformation": "5.9.3", | ||||
| 		"syuilo-password-strength": "0.0.1", | ||||
| 		"textarea-caret": "3.1.0", | ||||
| 		"three": "0.117.1", | ||||
| 		"throttle-debounce": "3.0.1", | ||||
| 		"tinycolor2": "1.4.2", | ||||
| 		"tmp": "0.2.1", | ||||
| 		"ts-loader": "9.2.5", | ||||
| 		"ts-loader": "9.2.6", | ||||
| 		"ts-node": "10.2.1", | ||||
| 		"tsc-alias": "1.3.9", | ||||
| 		"tsconfig-paths": "3.10.1", | ||||
| 		"tsconfig-paths": "3.11.0", | ||||
| 		"tslint": "6.1.3", | ||||
| 		"tslint-sonarts": "1.9.0", | ||||
| 		"twemoji-parser": "13.1.0", | ||||
| 		"typeorm": "0.2.37", | ||||
| 		"typescript": "4.3.5", | ||||
| 		"typescript": "4.4.3", | ||||
| 		"ulid": "2.3.0", | ||||
| 		"uuid": "8.3.2", | ||||
| 		"v-debounce": "0.1.2", | ||||
| 		"vue": "3.2.4", | ||||
| 		"vue-loader": "16.5.0", | ||||
| 		"vanilla-tilt": "1.7.2", | ||||
| 		"vue": "3.2.13", | ||||
| 		"vue-loader": "16.7.0", | ||||
| 		"vue-prism-editor": "2.0.0-alpha.2", | ||||
| 		"vue-router": "4.0.5", | ||||
| 		"vue-style-loader": "4.1.3", | ||||
| 		"vue-svg-loader": "0.17.0-beta.2", | ||||
| 		"vuedraggable": "4.0.1", | ||||
| 		"web-push": "3.4.5", | ||||
| 		"webpack": "5.51.0", | ||||
| 		"webpack": "5.53.0", | ||||
| 		"webpack-cli": "4.8.0", | ||||
| 		"websocket": "1.0.34", | ||||
| 		"ws": "8.2.0", | ||||
| 		"ws": "8.2.2", | ||||
| 		"xev": "2.0.1" | ||||
| 	}, | ||||
| 	"devDependencies": { | ||||
| 		"@redocly/openapi-core": "1.0.0-beta.54", | ||||
| 		"@types/fluent-ffmpeg": "2.1.17", | ||||
| 		"cross-env": "7.0.3", | ||||
| 		"cypress": "8.3.0", | ||||
| 		"start-server-and-test": "1.13.1" | ||||
| 		"cypress": "8.4.1", | ||||
| 		"start-server-and-test": "1.14.0" | ||||
| 	} | ||||
| } | ||||
|   | ||||
| @@ -1,8 +1,9 @@ | ||||
| import { get, set } from '@client/scripts/idb-proxy'; | ||||
| import { del, get, set } from '@client/scripts/idb-proxy'; | ||||
| import { reactive } from 'vue'; | ||||
| import { apiUrl } from '@client/config'; | ||||
| import { waiting } from '@client/os'; | ||||
| import { unisonReload, reloadChannel } from '@client/scripts/unison-reload'; | ||||
| import { showSuspendedDialog } from './scripts/show-suspended-dialog'; | ||||
|  | ||||
| // TODO: 他のタブと永続化されたstateを同期 | ||||
|  | ||||
| @@ -26,21 +27,33 @@ export async function signout() { | ||||
| 	//#region Remove account | ||||
| 	const accounts = await getAccounts(); | ||||
| 	accounts.splice(accounts.findIndex(x => x.id === $i.id), 1); | ||||
| 	set('accounts', accounts); | ||||
|  | ||||
| 	if (accounts.length > 0) await set('accounts', accounts); | ||||
| 	else await del('accounts'); | ||||
| 	//#endregion | ||||
|  | ||||
| 	//#region Remove push notification registration | ||||
| 	//#region Remove service worker registration | ||||
| 	try { | ||||
| 		const registration = await navigator.serviceWorker.ready; | ||||
| 		const push = await registration.pushManager.getSubscription(); | ||||
| 		if (!push) return; | ||||
| 		await fetch(`${apiUrl}/sw/unregister`, { | ||||
| 			method: 'POST', | ||||
| 			body: JSON.stringify({ | ||||
| 				i: $i.token, | ||||
| 				endpoint: push.endpoint, | ||||
| 			}), | ||||
| 		}); | ||||
| 		if (navigator.serviceWorker.controller) { | ||||
| 			const registration = await navigator.serviceWorker.ready; | ||||
| 			const push = await registration.pushManager.getSubscription(); | ||||
| 			if (push) { | ||||
| 				await fetch(`${apiUrl}/sw/unregister`, { | ||||
| 					method: 'POST', | ||||
| 					body: JSON.stringify({ | ||||
| 						i: $i.token, | ||||
| 						endpoint: push.endpoint, | ||||
| 					}), | ||||
| 				}); | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (accounts.length === 0) { | ||||
| 			await navigator.serviceWorker.getRegistrations() | ||||
| 				.then(registrations => { | ||||
| 					return Promise.all(registrations.map(registration => registration.unregister())); | ||||
| 				}); | ||||
| 		} | ||||
| 	} catch (e) {} | ||||
| 	//#endregion | ||||
|  | ||||
| @@ -70,17 +83,20 @@ function fetchAccount(token): Promise<Account> { | ||||
| 				i: token | ||||
| 			}) | ||||
| 		}) | ||||
| 		.then(res => res.json()) | ||||
| 		.then(res => { | ||||
| 			// When failed to authenticate user | ||||
| 			if (res.status !== 200 && res.status < 500) { | ||||
| 				return signout(); | ||||
| 			if (res.error) { | ||||
| 				if (res.error.id === 'a8c724b3-6e9c-4b46-b1a8-bc3ed6258370') { | ||||
| 					showSuspendedDialog().then(() => { | ||||
| 						signout(); | ||||
| 					}); | ||||
| 				} else { | ||||
| 					signout(); | ||||
| 				} | ||||
| 			} else { | ||||
| 				res.token = token; | ||||
| 				done(res); | ||||
| 			} | ||||
|  | ||||
| 			// Parse response | ||||
| 			res.json().then(i => { | ||||
| 				i.token = token; | ||||
| 				done(i); | ||||
| 			}); | ||||
| 		}) | ||||
| 		.catch(fail); | ||||
| 	}); | ||||
|   | ||||
| @@ -73,6 +73,22 @@ export default defineComponent({ | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| @keyframes earwiggleleft { | ||||
| 	from { transform: rotate(37.6deg) skew(30deg); } | ||||
| 	25% { transform: rotate(10deg) skew(30deg); } | ||||
| 	50% { transform: rotate(20deg) skew(30deg); } | ||||
| 	75% { transform: rotate(0deg) skew(30deg); } | ||||
| 	to { transform: rotate(37.6deg) skew(30deg); } | ||||
| } | ||||
|  | ||||
| @keyframes earwiggleright { | ||||
| 	from { transform: rotate(-37.6deg) skew(-30deg); } | ||||
| 	30% { transform: rotate(-10deg) skew(-30deg); } | ||||
| 	55% { transform: rotate(-20deg) skew(-30deg); } | ||||
| 	75% { transform: rotate(0deg) skew(-30deg); } | ||||
| 	to { transform: rotate(-37.6deg) skew(-30deg); } | ||||
| } | ||||
|  | ||||
| .eiwwqkts { | ||||
| 	position: relative; | ||||
| 	display: inline-block; | ||||
| @@ -132,6 +148,16 @@ export default defineComponent({ | ||||
| 			border-radius: 75% 0 75% 75%; | ||||
| 			transform: rotate(-37.5deg) skew(-30deg); | ||||
| 		} | ||||
|  | ||||
| 		&:hover { | ||||
| 			&:before { | ||||
| 				animation: earwiggleleft 1s infinite; | ||||
| 			} | ||||
|  | ||||
| 			&:after { | ||||
| 				animation: earwiggleright 1s infinite; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import { concat } from '@client/../prelude/array'; | ||||
| import MkFormula from '@client/components/formula.vue'; | ||||
| import MkCode from '@client/components/code.vue'; | ||||
| import MkGoogle from '@client/components/google.vue'; | ||||
| import MkSparkle from '@client/components/sparkle.vue'; | ||||
| import MkA from '@client/components/global/a.vue'; | ||||
| import { host } from '@client/config'; | ||||
|  | ||||
| @@ -169,6 +170,19 @@ export default defineComponent({ | ||||
| 							style = this.$store.state.animatedMfm ? 'animation: mfm-rainbow 1s linear infinite;' : ''; | ||||
| 							break; | ||||
| 						} | ||||
| 						case 'sparkle': { | ||||
| 							if (!this.$store.state.animatedMfm) { | ||||
| 								return genEl(token.children); | ||||
| 							} | ||||
| 							let count = token.props.args.count ? parseInt(token.props.args.count) : 10; | ||||
| 							if (count > 100) { | ||||
| 								count = 100; | ||||
| 							} | ||||
| 							const speed = token.props.args.speed ? parseFloat(token.props.args.speed) : 1; | ||||
| 							return h(MkSparkle, { | ||||
| 								count, speed, | ||||
| 							}, genEl(token.children)); | ||||
| 						} | ||||
| 					} | ||||
| 					if (style == null) { | ||||
| 						return h('span', {}, ['[', token.props.name, ...genEl(token.children), ']']); | ||||
|   | ||||
| @@ -3,10 +3,10 @@ | ||||
| 	<MkA class="name" :to="userPage(note.user)" v-user-preview="note.user.id"> | ||||
| 		<MkUserName :user="note.user"/> | ||||
| 	</MkA> | ||||
| 	<span class="is-bot" v-if="note.user.isBot">bot</span> | ||||
| 	<span class="username"><MkAcct :user="note.user"/></span> | ||||
| 	<span class="admin" v-if="note.user.isAdmin"><i class="fas fa-bookmark"></i></span> | ||||
| 	<span class="moderator" v-if="!note.user.isAdmin && note.user.isModerator"><i class="far fa-bookmark"></i></span> | ||||
| 	<div class="is-bot" v-if="note.user.isBot">bot</div> | ||||
| 	<div class="username"><MkAcct :user="note.user"/></div> | ||||
| 	<div class="admin" v-if="note.user.isAdmin"><i class="fas fa-bookmark"></i></div> | ||||
| 	<div class="moderator" v-if="!note.user.isAdmin && note.user.isModerator"><i class="far fa-bookmark"></i></div> | ||||
| 	<div class="info"> | ||||
| 		<span class="mobile" v-if="note.viaMobile"><i class="fas fa-mobile-alt"></i></span> | ||||
| 		<MkA class="created-at" :to="notePage(note)"> | ||||
| @@ -55,6 +55,7 @@ export default defineComponent({ | ||||
| 	white-space: nowrap; | ||||
|  | ||||
| 	> .name { | ||||
| 		flex-shrink: 1; | ||||
| 		display: block; | ||||
| 		margin: 0 .5em 0 0; | ||||
| 		padding: 0; | ||||
| @@ -81,17 +82,20 @@ export default defineComponent({ | ||||
|  | ||||
| 	> .admin, | ||||
| 	> .moderator { | ||||
| 		flex-shrink: 0; | ||||
| 		margin-right: 0.5em; | ||||
| 		color: var(--badge); | ||||
| 	} | ||||
|  | ||||
| 	> .username { | ||||
| 		flex-shrink: 9999999; | ||||
| 		margin: 0 .5em 0 0; | ||||
| 		overflow: hidden; | ||||
| 		text-overflow: ellipsis; | ||||
| 	} | ||||
|  | ||||
| 	> .info { | ||||
| 		flex-shrink: 0; | ||||
| 		margin-left: auto; | ||||
| 		font-size: 0.9em; | ||||
|  | ||||
|   | ||||
| @@ -8,7 +8,7 @@ | ||||
| 	@closed="$emit('closed')" | ||||
| > | ||||
| 	<template #header> | ||||
| 		<XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()"/> | ||||
| 		<XHeader :info="pageInfo" :back-button="history.length > 0" @back="back()" :close-button="true" @close="close()" :title-only="true"/> | ||||
| 	</template> | ||||
| 	<div class="yrolvcoq _flat_"> | ||||
| 		<component :is="component" v-bind="props" :ref="changePage"/> | ||||
|   | ||||
| @@ -54,6 +54,7 @@ import { apiUrl, host } from '@client/config'; | ||||
| import { byteify, hexify } from '@client/scripts/2fa'; | ||||
| import * as os from '@client/os'; | ||||
| import { login } from '@client/account'; | ||||
| import { showSuspendedDialog } from '../scripts/show-suspended-dialog'; | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| @@ -111,7 +112,9 @@ export default defineComponent({ | ||||
|  | ||||
| 		onLogin(res) { | ||||
| 			if (this.autoSet) { | ||||
| 				login(res.i); | ||||
| 				return login(res.i); | ||||
| 			} else { | ||||
| 				return; | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| @@ -144,7 +147,7 @@ export default defineComponent({ | ||||
| 				}); | ||||
| 			}).then(res => { | ||||
| 				this.$emit('login', res); | ||||
| 				this.onLogin(res); | ||||
| 				return this.onLogin(res); | ||||
| 			}).catch(err => { | ||||
| 				if (err === null) return; | ||||
| 				os.dialog({ | ||||
| @@ -167,15 +170,7 @@ export default defineComponent({ | ||||
| 						this.signing = false; | ||||
| 						this.challengeData = res; | ||||
| 						return this.queryKey(); | ||||
| 					}).catch(() => { | ||||
| 						os.dialog({ | ||||
| 							type: 'error', | ||||
| 							text: this.$ts.signinFailed | ||||
| 						}); | ||||
| 						this.challengeData = null; | ||||
| 						this.totpLogin = false; | ||||
| 						this.signing = false; | ||||
| 					}); | ||||
| 					}).catch(this.loginFailed); | ||||
| 				} else { | ||||
| 					this.totpLogin = true; | ||||
| 					this.signing = false; | ||||
| @@ -188,14 +183,36 @@ export default defineComponent({ | ||||
| 				}).then(res => { | ||||
| 					this.$emit('login', res); | ||||
| 					this.onLogin(res); | ||||
| 				}).catch(() => { | ||||
| 				}).catch(this.loginFailed); | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		loginFailed(err) { | ||||
| 			switch (err.id) { | ||||
| 				case '6cc579cc-885d-43d8-95c2-b8c7fc963280': { | ||||
| 					os.dialog({ | ||||
| 						type: 'error', | ||||
| 						text: this.$ts.loginFailed | ||||
| 						title: this.$ts.loginFailed, | ||||
| 						text: this.$ts.noSuchUser | ||||
| 					}); | ||||
| 					this.signing = false; | ||||
| 				}); | ||||
| 					break; | ||||
| 				} | ||||
| 				case 'e03a5f46-d309-4865-9b69-56282d94e1eb': { | ||||
| 					showSuspendedDialog(); | ||||
| 					break; | ||||
| 				} | ||||
| 				default: { | ||||
| 					os.dialog({ | ||||
| 						type: 'error', | ||||
| 						title: this.$ts.loginFailed, | ||||
| 						text: JSON.stringify(err) | ||||
| 					}); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			this.challengeData = null; | ||||
| 			this.totpLogin = false; | ||||
| 			this.signing = false; | ||||
| 		}, | ||||
|  | ||||
| 		resetPassword() { | ||||
|   | ||||
| @@ -178,14 +178,14 @@ export default defineComponent({ | ||||
| 				'hcaptcha-response': this.hCaptchaResponse, | ||||
| 				'g-recaptcha-response': this.reCaptchaResponse, | ||||
| 			}).then(() => { | ||||
| 				os.api('signin', { | ||||
| 				return os.api('signin', { | ||||
| 					username: this.username, | ||||
| 					password: this.password | ||||
| 				}).then(res => { | ||||
| 					this.$emit('signup', res); | ||||
|  | ||||
| 					if (this.autoSet) { | ||||
| 						login(res.i); | ||||
| 						return login(res.i); | ||||
| 					} | ||||
| 				}); | ||||
| 			}).catch(() => { | ||||
|   | ||||
							
								
								
									
										180
									
								
								src/client/components/sparkle.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								src/client/components/sparkle.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | ||||
| <template> | ||||
| <span class="mk-sparkle"> | ||||
| 	<span ref="content"> | ||||
| 		<slot></slot> | ||||
| 	</span> | ||||
| 	<canvas ref="canvas"></canvas> | ||||
| </span> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from 'vue'; | ||||
| import * as os from '@client/os'; | ||||
|  | ||||
| const sprite = new Image(); | ||||
| sprite.src = "/static-assets/client/sparkle-spritesheet.png"; | ||||
|  | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	props: { | ||||
| 		count: { | ||||
| 			type: Number, | ||||
| 			required: true, | ||||
| 		}, | ||||
| 		speed: { | ||||
| 			type: Number, | ||||
| 			required: true, | ||||
| 		}, | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| 			sprites: [0,6,13,20], | ||||
| 			particles: [], | ||||
| 			anim: null, | ||||
| 			ctx: null, | ||||
| 		}; | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		createSparkles(w, h, count) { | ||||
| 			var holder = []; | ||||
|  | ||||
| 			for (var i = 0; i < count; i++) { | ||||
|  | ||||
| 				const color = '#' + ('000000' + Math.floor(Math.random() * 16777215).toString(16)).slice(-6); | ||||
|  | ||||
| 				holder[i] = { | ||||
| 					position: { | ||||
| 						x: Math.floor(Math.random() * w), | ||||
| 						y: Math.floor(Math.random() * h) | ||||
| 					}, | ||||
| 					style: this.sprites[ Math.floor(Math.random() * 4) ], | ||||
| 					delta: { | ||||
| 						x: Math.floor(Math.random() * 1000) - 500, | ||||
| 						y: Math.floor(Math.random() * 1000) - 500 | ||||
| 					}, | ||||
| 					color: color, | ||||
| 					opacity: Math.random(), | ||||
| 				}; | ||||
|  | ||||
| 			} | ||||
|  | ||||
| 			return holder; | ||||
| 		}, | ||||
| 		draw(time) { | ||||
| 			this.ctx.clearRect(0, 0, this.$refs.canvas.width, this.$refs.canvas.height); | ||||
| 			this.ctx.beginPath(); | ||||
|  | ||||
| 			const particleSize = Math.floor(this.fontSize / 2); | ||||
| 			this.particles.forEach((particle) => { | ||||
| 				var modulus = Math.floor(Math.random()*7); | ||||
|  | ||||
| 				if (Math.floor(time) % modulus === 0) { | ||||
| 					particle.style = this.sprites[ Math.floor(Math.random()*4) ]; | ||||
| 				} | ||||
|  | ||||
| 				this.ctx.save(); | ||||
| 				this.ctx.globalAlpha = particle.opacity; | ||||
| 				this.ctx.drawImage(sprite, particle.style, 0, 7, 7, particle.position.x, particle.position.y, particleSize, particleSize); | ||||
|  | ||||
| 				this.ctx.globalCompositeOperation = "source-atop"; | ||||
| 				this.ctx.globalAlpha = 0.5; | ||||
| 				this.ctx.fillStyle = particle.color; | ||||
| 				this.ctx.fillRect(particle.position.x, particle.position.y, particleSize, particleSize); | ||||
|  | ||||
| 				this.ctx.restore(); | ||||
| 			}); | ||||
| 			this.ctx.stroke(); | ||||
| 		}, | ||||
| 		tick() { | ||||
| 			this.anim = window.requestAnimationFrame((time) => { | ||||
| 				if (!this.$refs.canvas) { | ||||
| 					return; | ||||
| 				} | ||||
| 				this.particles.forEach((particle) => { | ||||
| 					if (!particle) { | ||||
| 						return; | ||||
| 					} | ||||
| 					var randX = Math.random() > Math.random() * 2; | ||||
| 					var randY = Math.random() > Math.random() * 3; | ||||
|  | ||||
| 					if (randX) { | ||||
| 						particle.position.x += (particle.delta.x * this.speed) / 1500; | ||||
| 					} | ||||
|  | ||||
| 					if (!randY) { | ||||
| 						particle.position.y -= (particle.delta.y * this.speed) / 800; | ||||
| 					} | ||||
|  | ||||
| 					if( particle.position.x > this.$refs.canvas.width ) { | ||||
| 						particle.position.x = -7; | ||||
| 					} else if (particle.position.x < -7) { | ||||
| 						particle.position.x = this.$refs.canvas.width; | ||||
| 					} | ||||
|  | ||||
| 					if (particle.position.y > this.$refs.canvas.height) { | ||||
| 						particle.position.y = -7; | ||||
| 						particle.position.x = Math.floor(Math.random() * this.$refs.canvas.width); | ||||
| 					} else if (particle.position.y < -7) { | ||||
| 						particle.position.y = this.$refs.canvas.height; | ||||
| 						particle.position.x = Math.floor(Math.random() * this.$refs.canvas.width); | ||||
| 					} | ||||
|  | ||||
| 					particle.opacity -= 0.005; | ||||
|  | ||||
| 					if (particle.opacity <= 0) { | ||||
| 						particle.opacity = 1; | ||||
| 					} | ||||
| 				}); | ||||
|  | ||||
| 				this.draw(time); | ||||
|  | ||||
| 				this.tick(); | ||||
| 			}); | ||||
| 		}, | ||||
| 		resize() { | ||||
| 			if (this.$refs.content) { | ||||
| 				const contentRect = this.$refs.content.getBoundingClientRect(); | ||||
| 				this.fontSize = parseFloat(getComputedStyle(this.$refs.content).fontSize); | ||||
| 				const padding = this.fontSize * 0.2; | ||||
|  | ||||
| 				this.$refs.canvas.width = parseInt(contentRect.width + padding); | ||||
| 				this.$refs.canvas.height = parseInt(contentRect.height + padding); | ||||
|  | ||||
| 				this.particles = this.createSparkles(this.$refs.canvas.width, this.$refs.canvas.height, this.count); | ||||
| 			} | ||||
| 		}, | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		this.ctx = this.$refs.canvas.getContext('2d'); | ||||
|  | ||||
| 		new ResizeObserver(this.resize).observe(this.$refs.content); | ||||
|  | ||||
| 		this.resize(); | ||||
| 		this.tick(); | ||||
| 	}, | ||||
| 	updated() { | ||||
| 		this.resize(); | ||||
| 	}, | ||||
| 	destroyed() { | ||||
| 		window.cancelAnimationFrame(this.anim); | ||||
| 	}, | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .mk-sparkle { | ||||
| 	position: relative; | ||||
| 	display: inline-block; | ||||
|  | ||||
| 	> span { | ||||
| 		display: inline-block; | ||||
| 	} | ||||
|  | ||||
| 	> canvas { | ||||
| 		position: absolute; | ||||
| 		top: -0.1em; | ||||
| 		left: -0.1em; | ||||
| 		pointer-events: none; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
| @@ -99,7 +99,8 @@ export default defineComponent({ | ||||
| 		z-index: 10; | ||||
| 		position: sticky; | ||||
| 		top: var(--stickyTop, 0px); | ||||
| 		background: var(--panel); | ||||
| 		padding: var(--x-padding); | ||||
| 		background: var(--x-header, var(--panel)); | ||||
| 		/* TODO panelの半透明バージョンをプログラマティックに作りたい | ||||
| 		background: var(--X17); | ||||
| 		-webkit-backdrop-filter: var(--blur, blur(8px)); | ||||
|   | ||||
| @@ -245,7 +245,7 @@ export default defineComponent({ | ||||
| 			font-size: 1em; | ||||
| 			color: var(--fg); | ||||
| 			background: var(--panel); | ||||
| 			border: solid 1px var(--inputBorder); | ||||
| 			border: solid 0.5px var(--inputBorder); | ||||
| 			border-radius: 6px; | ||||
| 			outline: none; | ||||
| 			box-shadow: none; | ||||
|   | ||||
| @@ -41,7 +41,7 @@ | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent, ref } from 'vue'; | ||||
| import { defineComponent, ref, unref } from 'vue'; | ||||
| import { focusPrev, focusNext } from '@client/scripts/focus'; | ||||
| import contains from '@client/scripts/contains'; | ||||
|  | ||||
| @@ -79,21 +79,26 @@ export default defineComponent({ | ||||
| 			}; | ||||
| 		}, | ||||
| 	}, | ||||
| 	created() { | ||||
| 		const items = ref(this.items.filter(item => item !== undefined)); | ||||
| 	watch: { | ||||
| 		items: { | ||||
| 			handler() { | ||||
| 				const items = ref(unref(this.items).filter(item => item !== undefined)); | ||||
|  | ||||
| 		for (let i = 0; i < items.value.length; i++) { | ||||
| 			const item = items.value[i]; | ||||
| 			 | ||||
| 			if (item && item.then) { // if item is Promise | ||||
| 				items.value[i] = { type: 'pending' }; | ||||
| 				item.then(actualItem => { | ||||
| 					items.value[i] = actualItem; | ||||
| 				}); | ||||
| 			} | ||||
| 				for (let i = 0; i < items.value.length; i++) { | ||||
| 					const item = items.value[i]; | ||||
| 					 | ||||
| 					if (item && item.then) { // if item is Promise | ||||
| 						items.value[i] = { type: 'pending' }; | ||||
| 						item.then(actualItem => { | ||||
| 							items.value[i] = actualItem; | ||||
| 						}); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				this._items = items; | ||||
| 			}, | ||||
| 			immediate: true | ||||
| 		} | ||||
|  | ||||
| 		this._items = items; | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		if (this.viaKeyboard) { | ||||
|   | ||||
| @@ -212,7 +212,7 @@ export default defineComponent({ | ||||
| 			font-size: 1em; | ||||
| 			color: var(--fg); | ||||
| 			background: var(--panel); | ||||
| 			border: solid 1px var(--inputBorder); | ||||
| 			border: solid 0.5px var(--inputBorder); | ||||
| 			border-radius: 6px; | ||||
| 			outline: none; | ||||
| 			box-shadow: none; | ||||
|   | ||||
| @@ -15,7 +15,7 @@ if (localStorage.getItem('accounts') != null) { | ||||
|  | ||||
| import * as Sentry from '@sentry/browser'; | ||||
| import { Integrations } from '@sentry/tracing'; | ||||
| import { computed, createApp, watch, markRaw } from 'vue'; | ||||
| import { computed, createApp, watch, markRaw, version as vueVersion } from 'vue'; | ||||
| import compareVersions from 'compare-versions'; | ||||
|  | ||||
| import widgets from '@client/widgets'; | ||||
| @@ -47,6 +47,8 @@ window.onunhandledrejection = null; | ||||
| if (_DEV_) { | ||||
| 	console.warn('Development mode!!!'); | ||||
|  | ||||
| 	console.info(`vue ${vueVersion}`); | ||||
|  | ||||
| 	(window as any).$i = $i; | ||||
| 	(window as any).$store = defaultStore; | ||||
|  | ||||
| @@ -215,7 +217,10 @@ if (lastVersion !== version) { | ||||
|  | ||||
| 	try { // 変なバージョン文字列来るとcompareVersionsでエラーになるため | ||||
| 		if (lastVersion != null && compareVersions(version, lastVersion) === 1) { | ||||
| 			popup(import('@client/components/updated.vue'), {}, {}, 'closed'); | ||||
| 			// ログインしてる場合だけ | ||||
| 			if ($i) { | ||||
| 				popup(import('@client/components/updated.vue'), {}, {}, 'closed'); | ||||
| 			} | ||||
| 		} | ||||
| 	} catch (e) { | ||||
| 	} | ||||
|   | ||||
| @@ -1,9 +1,10 @@ | ||||
| import { computed } from 'vue'; | ||||
| import { computed, ref } from 'vue'; | ||||
| import { search } from '@client/scripts/search'; | ||||
| import * as os from '@client/os'; | ||||
| import { i18n } from '@client/i18n'; | ||||
| import { $i } from './account'; | ||||
| import { unisonReload } from '@client/scripts/unison-reload'; | ||||
| import { router } from './router'; | ||||
|  | ||||
| export const menuDef = { | ||||
| 	notifications: { | ||||
| @@ -58,7 +59,26 @@ export const menuDef = { | ||||
| 		title: 'lists', | ||||
| 		icon: 'fas fa-list-ul', | ||||
| 		show: computed(() => $i != null), | ||||
| 		to: '/my/lists', | ||||
| 		active: computed(() => router.currentRoute.value.path.startsWith('/timeline/list/') || router.currentRoute.value.path === '/my/lists' || router.currentRoute.value.path.startsWith('/my/lists/')), | ||||
| 		action: (ev) => { | ||||
| 			const items = ref([{ | ||||
| 				type: 'pending' | ||||
| 			}]); | ||||
| 			os.api('users/lists/list').then(lists => { | ||||
| 				const _items = [...lists.map(list => ({ | ||||
| 					type: 'link', | ||||
| 					text: list.name, | ||||
| 					to: `/timeline/list/${list.id}` | ||||
| 				})), null, { | ||||
| 					type: 'link', | ||||
| 					to: '/my/lists', | ||||
| 					text: i18n.locale.manageLists, | ||||
| 					icon: 'fas fa-cog', | ||||
| 				}]; | ||||
| 				items.value = _items; | ||||
| 			}); | ||||
| 			os.popupMenu(items, ev.currentTarget || ev.target); | ||||
| 		}, | ||||
| 	}, | ||||
| 	groups: { | ||||
| 		title: 'groups', | ||||
| @@ -70,7 +90,26 @@ export const menuDef = { | ||||
| 		title: 'antennas', | ||||
| 		icon: 'fas fa-satellite', | ||||
| 		show: computed(() => $i != null), | ||||
| 		to: '/my/antennas', | ||||
| 		active: computed(() => router.currentRoute.value.path.startsWith('/timeline/antenna/') || router.currentRoute.value.path === '/my/antennas' || router.currentRoute.value.path.startsWith('/my/antennas/')), | ||||
| 		action: (ev) => { | ||||
| 			const items = ref([{ | ||||
| 				type: 'pending' | ||||
| 			}]); | ||||
| 			os.api('antennas/list').then(antennas => { | ||||
| 				const _items = [...antennas.map(antenna => ({ | ||||
| 					type: 'link', | ||||
| 					text: antenna.name, | ||||
| 					to: `/timeline/antenna/${antenna.id}` | ||||
| 				})), null, { | ||||
| 					type: 'link', | ||||
| 					to: '/my/antennas', | ||||
| 					text: i18n.locale.manageAntennas, | ||||
| 					icon: 'fas fa-cog', | ||||
| 				}]; | ||||
| 				items.value = _items; | ||||
| 			}); | ||||
| 			os.popupMenu(items, ev.currentTarget || ev.target); | ||||
| 		}, | ||||
| 	}, | ||||
| 	mentions: { | ||||
| 		title: 'mentions', | ||||
|   | ||||
| @@ -372,7 +372,7 @@ export async function openEmojiPicker(src?: HTMLElement, opts, initialTextarea: | ||||
| 	}); | ||||
| } | ||||
|  | ||||
| export function popupMenu(items: any[], src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) { | ||||
| export function popupMenu(items: any[] | Ref<any[]>, src?: HTMLElement, options?: { align?: string; viaKeyboard?: boolean }) { | ||||
| 	return new Promise((resolve, reject) => { | ||||
| 		let dispose; | ||||
| 		popup(import('@client/components/ui/popup-menu.vue'), { | ||||
|   | ||||
							
								
								
									
										147
									
								
								src/client/pages/antenna-timeline.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/client/pages/antenna-timeline.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| <template> | ||||
| <div class="tqmomfks" v-hotkey.global="keymap" v-size="{ min: [800] }"> | ||||
| 	<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> | ||||
| 	<div class="tl _block"> | ||||
| 		<XTimeline ref="tl" class="tl" | ||||
| 			:key="antennaId" | ||||
| 			src="antenna" | ||||
| 			:antenna="antennaId" | ||||
| 			:sound="true" | ||||
| 			@before="before()" | ||||
| 			@after="after()" | ||||
| 			@queue="queueUpdated" | ||||
| 		/> | ||||
| 	</div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent, defineAsyncComponent, computed } from 'vue'; | ||||
| import Progress from '@client/scripts/loading'; | ||||
| import XTimeline from '@client/components/timeline.vue'; | ||||
| import { scroll } from '@client/scripts/scroll'; | ||||
| import * as os from '@client/os'; | ||||
| import * as symbols from '@client/symbols'; | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		XTimeline, | ||||
| 	}, | ||||
|  | ||||
| 	props: { | ||||
| 		antennaId: { | ||||
| 			type: String, | ||||
| 			required: true | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			antenna: null, | ||||
| 			queue: 0, | ||||
| 			[symbols.PAGE_INFO]: computed(() => this.antenna ? { | ||||
| 				title: this.antenna.name, | ||||
| 				icon: 'fas fa-satellite', | ||||
| 				bg: 'var(--bg)', | ||||
| 				actions: [{ | ||||
| 					icon: 'fas fa-calendar-alt', | ||||
| 					text: this.$ts.jumpToSpecifiedDate, | ||||
| 					handler: this.timetravel | ||||
| 				}, { | ||||
| 					icon: 'fas fa-cog', | ||||
| 					text: this.$ts.settings, | ||||
| 					handler: this.settings | ||||
| 				}], | ||||
| 			} : null), | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	computed: { | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
| 				't': this.focus | ||||
| 			}; | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	watch: { | ||||
| 		antennaId: { | ||||
| 			async handler() { | ||||
| 				this.antenna = await os.api('antennas/show', { | ||||
| 					antennaId: this.antennaId | ||||
| 				}); | ||||
| 			}, | ||||
| 			immediate: true | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		before() { | ||||
| 			Progress.start(); | ||||
| 		}, | ||||
|  | ||||
| 		after() { | ||||
| 			Progress.done(); | ||||
| 		}, | ||||
|  | ||||
| 		queueUpdated(q) { | ||||
| 			this.queue = q; | ||||
| 		}, | ||||
|  | ||||
| 		top() { | ||||
| 			scroll(this.$el, 0); | ||||
| 		}, | ||||
|  | ||||
| 		async timetravel() { | ||||
| 			const { canceled, result: date } = await os.dialog({ | ||||
| 				title: this.$ts.date, | ||||
| 				input: { | ||||
| 					type: 'date' | ||||
| 				} | ||||
| 			}); | ||||
| 			if (canceled) return; | ||||
|  | ||||
| 			this.$refs.tl.timetravel(new Date(date)); | ||||
| 		}, | ||||
|  | ||||
| 		settings() { | ||||
| 			this.$router.push(`/my/antennas/${this.antennaId}`); | ||||
| 		}, | ||||
|  | ||||
| 		focus() { | ||||
| 			(this.$refs.tl as any).focus(); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .tqmomfks { | ||||
| 	padding: var(--margin); | ||||
|  | ||||
| 	> .new { | ||||
| 		position: sticky; | ||||
| 		top: calc(var(--stickyTop, 0px) + 16px); | ||||
| 		z-index: 1000; | ||||
| 		width: 100%; | ||||
|  | ||||
| 		> button { | ||||
| 			display: block; | ||||
| 			margin: var(--margin) auto 0 auto; | ||||
| 			padding: 8px 16px; | ||||
| 			border-radius: 32px; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	> .tl { | ||||
| 		background: var(--bg); | ||||
| 		border-radius: var(--radius); | ||||
| 		overflow: clip; | ||||
| 	} | ||||
|  | ||||
| 	&.min-width_800px { | ||||
| 		max-width: 800px; | ||||
| 		margin: 0 auto; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										134
									
								
								src/client/pages/emojis.category.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										134
									
								
								src/client/pages/emojis.category.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,134 @@ | ||||
| <template> | ||||
| <div class="driuhtrh"> | ||||
| 	<div class="query"> | ||||
| 		<MkInput v-model="q" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search"> | ||||
| 			<template #prefix><i class="fas fa-search"></i></template> | ||||
| 		</MkInput> | ||||
|  | ||||
| 		<div class="tags"> | ||||
| 			<span class="tag _button" v-for="tag in tags" :class="{ active: selectedTags.has(tag) }" @click="toggleTag(tag)">{{ tag }}</span> | ||||
| 		</div> | ||||
| 	</div> | ||||
|  | ||||
| 	<MkFolder class="emojis" v-if="searchEmojis"> | ||||
| 		<template #header>{{ $ts.searchResult }}</template> | ||||
| 		<div class="zuvgdzyt"> | ||||
| 			<XEmoji v-for="emoji in searchEmojis" :key="emoji.name" class="emoji" :emoji="emoji"/> | ||||
| 		</div> | ||||
| 	</MkFolder> | ||||
| 	 | ||||
| 	<MkFolder class="emojis" v-for="category in customEmojiCategories" :key="category"> | ||||
| 		<template #header>{{ category || $ts.other }}</template> | ||||
| 		<div class="zuvgdzyt"> | ||||
| 			<XEmoji v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" class="emoji" :emoji="emoji"/> | ||||
| 		</div> | ||||
| 	</MkFolder> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent, computed } from 'vue'; | ||||
| import MkButton from '@client/components/ui/button.vue'; | ||||
| import MkInput from '@client/components/ui/input.vue'; | ||||
| import MkSelect from '@client/components/ui/select.vue'; | ||||
| import MkFolder from '@client/components/ui/folder.vue'; | ||||
| import MkTab from '@client/components/tab.vue'; | ||||
| import * as os from '@client/os'; | ||||
| import * as symbols from '@client/symbols'; | ||||
| import { emojiCategories, emojiTags } from '@client/instance'; | ||||
| import XEmoji from './emojis.emoji.vue'; | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		MkButton, | ||||
| 		MkInput, | ||||
| 		MkSelect, | ||||
| 		MkFolder, | ||||
| 		MkTab, | ||||
| 		XEmoji, | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			q: '', | ||||
| 			customEmojiCategories: emojiCategories, | ||||
| 			customEmojis: this.$instance.emojis, | ||||
| 			tags: emojiTags, | ||||
| 			selectedTags: new Set(), | ||||
| 			searchEmojis: null, | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	watch: { | ||||
| 		q() { this.search(); }, | ||||
| 		selectedTags: { | ||||
| 			handler() { | ||||
| 				this.search(); | ||||
| 			}, | ||||
| 			deep: true | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		search() { | ||||
| 			if ((this.q === '' || this.q == null) && this.selectedTags.size === 0) { | ||||
| 				this.searchEmojis = null; | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			if (this.selectedTags.size === 0) { | ||||
| 				this.searchEmojis = this.customEmojis.filter(e => e.name.includes(this.q) || e.aliases.includes(this.q)); | ||||
| 			} else { | ||||
| 				this.searchEmojis = this.customEmojis.filter(e => (e.name.includes(this.q) || e.aliases.includes(this.q)) && [...this.selectedTags].every(t => e.aliases.includes(t))); | ||||
| 			} | ||||
| 		}, | ||||
|  | ||||
| 		toggleTag(tag) { | ||||
| 			if (this.selectedTags.has(tag)) { | ||||
| 				this.selectedTags.delete(tag); | ||||
| 			} else { | ||||
| 				this.selectedTags.add(tag); | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .driuhtrh { | ||||
| 	background: var(--bg); | ||||
|  | ||||
| 	> .query { | ||||
| 		background: var(--bg); | ||||
| 		padding: 16px; | ||||
|  | ||||
| 		> .tags { | ||||
| 			> .tag { | ||||
| 				display: inline-block; | ||||
| 				margin: 8px 8px 0 0; | ||||
| 				padding: 4px 8px; | ||||
| 				font-size: 0.9em; | ||||
| 				background: var(--accentedBg); | ||||
| 				border-radius: 5px; | ||||
|  | ||||
| 				&.active { | ||||
| 					background: var(--accent); | ||||
| 					color: var(--fgOnAccent); | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	> .emojis { | ||||
| 		--x-header: var(--bg); | ||||
| 		--x-padding: 0 16px; | ||||
|  | ||||
| 		.zuvgdzyt { | ||||
| 			display: grid; | ||||
| 			grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); | ||||
| 			grid-gap: 12px; | ||||
| 			margin: 0 var(--margin) var(--margin) var(--margin); | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										92
									
								
								src/client/pages/emojis.emoji.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/client/pages/emojis.emoji.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,92 @@ | ||||
| <template> | ||||
| <button class="zuvgdzyu _button" @click="menu"> | ||||
| 	<img :src="emoji.url" class="img" :alt="emoji.name"/> | ||||
| 	<div class="body"> | ||||
| 		<div class="name _monospace">{{ emoji.name }}</div> | ||||
| 		<div class="info">{{ emoji.aliases.join(' ') }}</div> | ||||
| 	</div> | ||||
| </button> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from 'vue'; | ||||
| import * as os from '@client/os'; | ||||
| import copyToClipboard from '@client/scripts/copy-to-clipboard'; | ||||
| import VanillaTilt from 'vanilla-tilt'; | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	props: { | ||||
| 		emoji: { | ||||
| 			type: Object, | ||||
| 			required: true, | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	mounted() { | ||||
| 		VanillaTilt.init(this.$el, { | ||||
| 			reverse: true, | ||||
| 			gyroscope: false, | ||||
| 			scale: 1.1, | ||||
| 			speed: 500, | ||||
| 		}); | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		menu(ev) { | ||||
| 			os.popupMenu([{ | ||||
| 				type: 'label', | ||||
| 				text: ':' + this.emoji.name + ':', | ||||
| 			}, { | ||||
| 				text: this.$ts.copy, | ||||
| 				icon: 'fas fa-copy', | ||||
| 				action: () => { | ||||
| 					copyToClipboard(`:${this.emoji.name}:`); | ||||
| 					os.success(); | ||||
| 				} | ||||
| 			}], ev.currentTarget || ev.target); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .zuvgdzyu { | ||||
| 	display: flex; | ||||
| 	align-items: center; | ||||
| 	padding: 12px; | ||||
| 	text-align: left; | ||||
| 	background: var(--panel); | ||||
| 	border-radius: 8px; | ||||
| 	transform-style: preserve-3d; | ||||
| 	transform: perspective(1000px); | ||||
|  | ||||
| 	&:hover { | ||||
| 		border-color: var(--accent); | ||||
| 	} | ||||
|  | ||||
| 	> .img { | ||||
| 		width: 42px; | ||||
| 		height: 42px; | ||||
| 		transform: translateZ(20px); | ||||
| 	} | ||||
|  | ||||
| 	> .body { | ||||
| 		padding: 0 0 0 8px; | ||||
| 		white-space: nowrap; | ||||
| 		overflow: hidden; | ||||
| 		transform: translateZ(10px); | ||||
|  | ||||
| 		> .name { | ||||
| 			text-overflow: ellipsis; | ||||
| 			overflow: hidden; | ||||
| 		} | ||||
|  | ||||
| 		> .info { | ||||
| 			opacity: 0.5; | ||||
| 			font-size: 0.9em; | ||||
| 			text-overflow: ellipsis; | ||||
| 			overflow: hidden; | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
| @@ -1,151 +1,36 @@ | ||||
| <template> | ||||
| <div class="driuhtrh"> | ||||
| 	<div class="query"> | ||||
| 		<MkInput v-model="q" class="_inputNoTopMargin _inputNoBottomMargin" :placeholder="$ts.search"> | ||||
| 			<template #prefix><i class="fas fa-search"></i></template> | ||||
| 		</MkInput> | ||||
| 	</div> | ||||
|  | ||||
| 	<div class="emojis"> | ||||
| 		<MkFolder v-if="searchEmojis"> | ||||
| 			<template #header>{{ $ts.searchResult }}</template> | ||||
| 			<div class="zuvgdzyt"> | ||||
| 				<button v-for="emoji in searchEmojis" :key="emoji.name" class="emoji _button" @click="menu(emoji, $event)"> | ||||
| 					<img :src="emoji.url" class="img" :alt="emoji.name"/> | ||||
| 					<div class="body"> | ||||
| 						<div class="name _monospace">{{ emoji.name }}</div> | ||||
| 						<div class="info">{{ emoji.aliases.join(' ') }}</div> | ||||
| 					</div> | ||||
| 				</button> | ||||
| 			</div> | ||||
| 		</MkFolder> | ||||
| 		<MkFolder v-for="category in customEmojiCategories" :key="category"> | ||||
| 			<template #header>{{ category || $ts.other }}</template> | ||||
| 			<div class="zuvgdzyt"> | ||||
| 				<button v-for="emoji in customEmojis.filter(e => e.category === category)" :key="emoji.name" class="emoji _button" @click="menu(emoji, $event)"> | ||||
| 					<img :src="emoji.url" class="img" :alt="emoji.name"/> | ||||
| 					<div class="body"> | ||||
| 						<div class="name _monospace">{{ emoji.name }}</div> | ||||
| 						<div class="info">{{ emoji.aliases.join(' ') }}</div> | ||||
| 					</div> | ||||
| 				</button> | ||||
| 			</div> | ||||
| 		</MkFolder> | ||||
| 	</div> | ||||
| <div :class="$style.root"> | ||||
| 	<XCategory v-if="tab === 'category'"/> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent } from 'vue'; | ||||
| import MkButton from '@client/components/ui/button.vue'; | ||||
| import MkInput from '@client/components/ui/input.vue'; | ||||
| import MkSelect from '@client/components/ui/select.vue'; | ||||
| import MkFolder from '@client/components/ui/folder.vue'; | ||||
| import { defineComponent, computed } from 'vue'; | ||||
| import * as os from '@client/os'; | ||||
| import * as symbols from '@client/symbols'; | ||||
| import { emojiCategories } from '@client/instance'; | ||||
| import copyToClipboard from '@client/scripts/copy-to-clipboard'; | ||||
| import XCategory from './emojis.category.vue'; | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		MkButton, | ||||
| 		MkInput, | ||||
| 		MkSelect, | ||||
| 		MkFolder, | ||||
| 		XCategory, | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			[symbols.PAGE_INFO]: { | ||||
| 			[symbols.PAGE_INFO]: computed(() => ({ | ||||
| 				title: this.$ts.customEmojis, | ||||
| 				icon: 'fas fa-laugh' | ||||
| 			}, | ||||
| 			q: '', | ||||
| 			customEmojiCategories: emojiCategories, | ||||
| 			customEmojis: this.$instance.emojis, | ||||
| 			searchEmojis: null, | ||||
| 				icon: 'fas fa-laugh', | ||||
| 				bg: 'var(--bg)', | ||||
| 			})), | ||||
| 			tab: 'category', | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	watch: { | ||||
| 		q() { | ||||
| 			if (this.q === '' || this.q == null) { | ||||
| 				this.searchEmojis = null; | ||||
| 				return; | ||||
| 			} | ||||
|  | ||||
| 			this.searchEmojis = this.customEmojis.filter(e => e.name.includes(this.q) || e.aliases.includes(this.q)); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		menu(emoji, ev) { | ||||
| 			os.popupMenu([{ | ||||
| 				type: 'label', | ||||
| 				text: ':' + emoji.name + ':', | ||||
| 			}, { | ||||
| 				text: this.$ts.copy, | ||||
| 				icon: 'fas fa-copy', | ||||
| 				action: () => { | ||||
| 					copyToClipboard(`:${emoji.name}:`); | ||||
| 					os.success(); | ||||
| 				} | ||||
| 			}], ev.currentTarget || ev.target); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .driuhtrh { | ||||
| 	> .query { | ||||
| 		background: var(--bg); | ||||
| 		padding: 16px; | ||||
| 	} | ||||
|  | ||||
| 	> .emojis { | ||||
| 		.zuvgdzyt { | ||||
| 			display: grid; | ||||
| 			grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); | ||||
| 			grid-gap: 12px; | ||||
| 			margin: 0 var(--margin) var(--margin) var(--margin); | ||||
|  | ||||
| 			> .emoji { | ||||
| 				display: flex; | ||||
| 				align-items: center; | ||||
| 				padding: 12px; | ||||
| 				text-align: left; | ||||
| 				border: solid 1px var(--divider); | ||||
| 				border-radius: 8px; | ||||
|  | ||||
| 				&:hover { | ||||
| 					border-color: var(--accent); | ||||
| 				} | ||||
|  | ||||
| 				> .img { | ||||
| 					width: 42px; | ||||
| 					height: 42px; | ||||
| 				} | ||||
|  | ||||
| 				> .body { | ||||
| 					padding: 0 0 0 8px; | ||||
| 					white-space: nowrap; | ||||
| 					overflow: hidden; | ||||
|  | ||||
| 					> .name { | ||||
| 						text-overflow: ellipsis; | ||||
| 						overflow: hidden; | ||||
| 					} | ||||
|  | ||||
| 					> .info { | ||||
| 						opacity: 0.5; | ||||
| 						font-size: 0.9em; | ||||
| 						text-overflow: ellipsis; | ||||
| 						overflow: hidden; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| <style lang="scss" module> | ||||
| .root { | ||||
| 	max-width: 1000px; | ||||
| 	margin: 0 auto; | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -22,7 +22,8 @@ export default defineComponent({ | ||||
| 		return { | ||||
| 			[symbols.PAGE_INFO]: { | ||||
| 				title: this.$ts.favorites, | ||||
| 				icon: 'fas fa-star' | ||||
| 				icon: 'fas fa-star', | ||||
| 				bg: 'var(--bg)', | ||||
| 			}, | ||||
| 			pagination: { | ||||
| 				endpoint: 'i/favorites', | ||||
|   | ||||
| @@ -28,14 +28,14 @@ | ||||
| 				<option value="-following">{{ $ts.following }} ({{ $ts.ascendingOrder }})</option> | ||||
| 				<option value="+followers">{{ $ts.followers }} ({{ $ts.descendingOrder }})</option> | ||||
| 				<option value="-followers">{{ $ts.followers }} ({{ $ts.ascendingOrder }})</option> | ||||
| 				<option value="+caughtAt">{{ $ts.caughtAt }} ({{ $ts.descendingOrder }})</option> | ||||
| 				<option value="-caughtAt">{{ $ts.caughtAt }} ({{ $ts.ascendingOrder }})</option> | ||||
| 				<option value="+lastCommunicatedAt">{{ $ts.lastCommunicatedAt }} ({{ $ts.descendingOrder }})</option> | ||||
| 				<option value="-lastCommunicatedAt">{{ $ts.lastCommunicatedAt }} ({{ $ts.ascendingOrder }})</option> | ||||
| 				<option value="+caughtAt">{{ $ts.registeredAt }} ({{ $ts.descendingOrder }})</option> | ||||
| 				<option value="-caughtAt">{{ $ts.registeredAt }} ({{ $ts.ascendingOrder }})</option> | ||||
| 				<option value="+lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.descendingOrder }})</option> | ||||
| 				<option value="-lastCommunicatedAt">{{ $ts.lastCommunication }} ({{ $ts.ascendingOrder }})</option> | ||||
| 				<option value="+driveUsage">{{ $ts.driveUsage }} ({{ $ts.descendingOrder }})</option> | ||||
| 				<option value="-driveUsage">{{ $ts.driveUsage }} ({{ $ts.ascendingOrder }})</option> | ||||
| 				<option value="+driveFiles">{{ $ts.driveFiles }} ({{ $ts.descendingOrder }})</option> | ||||
| 				<option value="-driveFiles">{{ $ts.driveFiles }} ({{ $ts.ascendingOrder }})</option> | ||||
| 				<option value="+driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.descendingOrder }})</option> | ||||
| 				<option value="-driveFiles">{{ $ts.driveFilesCount }} ({{ $ts.ascendingOrder }})</option> | ||||
| 			</MkSelect> | ||||
| 		</div> | ||||
| 	</div> | ||||
|   | ||||
| @@ -271,6 +271,16 @@ | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<div class="section _block"> | ||||
| 		<div class="title">{{ $ts._mfm.sparkle }}</div> | ||||
| 		<div class="content"> | ||||
| 			<p>{{ $ts._mfm.sparkleDescription }}</p> | ||||
| 			<div class="preview"> | ||||
| 				<Mfm :text="preview_sparkle"/> | ||||
| 				<MkTextarea v-model="preview_sparkle"><span>MFM</span></MkTextarea> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 	</div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| @@ -294,7 +304,7 @@ export default defineComponent({ | ||||
| 			preview_hashtag: '#test', | ||||
| 			preview_url: `https://example.com`, | ||||
| 			preview_link: `[${this.$ts._mfm.dummy}](https://example.com)`, | ||||
| 			preview_emoji: `:${this.$instance.emojis[0].name}:`, | ||||
| 			preview_emoji: this.$instance.emojis.length ? `:${this.$instance.emojis[0].name}:` : `:emojiname:`, | ||||
| 			preview_bold: `**${this.$ts._mfm.dummy}**`, | ||||
| 			preview_small: `<small>${this.$ts._mfm.dummy}</small>`, | ||||
| 			preview_center: `<center>${this.$ts._mfm.dummy}</center>`, | ||||
| @@ -317,6 +327,7 @@ export default defineComponent({ | ||||
| 			preview_x4: `$[x4 🍮]`, | ||||
| 			preview_blur: `$[blur ${this.$ts._mfm.dummy}]`, | ||||
| 			preview_rainbow: `$[rainbow 🍮]`, | ||||
| 			preview_sparkle: `$[sparkle 🍮]`, | ||||
| 		} | ||||
| 	}, | ||||
| }); | ||||
|   | ||||
| @@ -1,37 +1,39 @@ | ||||
| <template> | ||||
| <div class="fcuexfpr _root"> | ||||
| 	<transition name="fade" mode="out-in"> | ||||
| 		<div v-if="note" class="note"> | ||||
| 			<div class="_gap" v-if="showNext"> | ||||
| 				<XNotes class="_content" :pagination="next" :no-gap="true"/> | ||||
| 			</div> | ||||
|  | ||||
| 			<div class="main _gap"> | ||||
| 				<MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><i class="fas fa-chevron-up"></i></MkButton> | ||||
| 				<div class="note _gap"> | ||||
| 					<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_isolated"/> | ||||
| 					<XNoteDetailed v-model:note="note" :key="note.id" class="_isolated note"/> | ||||
| <div class="fcuexfpr"> | ||||
| 	<div class="_root"> | ||||
| 		<transition name="fade" mode="out-in"> | ||||
| 			<div v-if="note" class="note"> | ||||
| 				<div class="_gap" v-if="showNext"> | ||||
| 					<XNotes class="_content" :pagination="next" :no-gap="true"/> | ||||
| 				</div> | ||||
| 				<div class="_content clips _gap" v-if="clips && clips.length > 0"> | ||||
| 					<div class="title">{{ $ts.clip }}</div> | ||||
| 					<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap"> | ||||
| 						<b>{{ item.name }}</b> | ||||
| 						<div v-if="item.description" class="description">{{ item.description }}</div> | ||||
| 						<div class="user"> | ||||
| 							<MkAvatar :user="item.user" class="avatar" :show-indicator="true"/> <MkUserName :user="item.user" :nowrap="false"/> | ||||
| 						</div> | ||||
| 					</MkA> | ||||
| 				</div> | ||||
| 				<MkButton v-if="!showPrev && hasPrev" class="load prev" @click="showPrev = true"><i class="fas fa-chevron-down"></i></MkButton> | ||||
| 			</div> | ||||
|  | ||||
| 			<div class="_gap" v-if="showPrev"> | ||||
| 				<XNotes class="_content" :pagination="prev" :no-gap="true"/> | ||||
| 				<div class="main _gap"> | ||||
| 					<MkButton v-if="!showNext && hasNext" class="load next" @click="showNext = true"><i class="fas fa-chevron-up"></i></MkButton> | ||||
| 					<div class="note _gap"> | ||||
| 						<MkRemoteCaution v-if="note.user.host != null" :href="note.url || note.uri" class="_isolated"/> | ||||
| 						<XNoteDetailed v-model:note="note" :key="note.id" class="_isolated note"/> | ||||
| 					</div> | ||||
| 					<div class="_content clips _gap" v-if="clips && clips.length > 0"> | ||||
| 						<div class="title">{{ $ts.clip }}</div> | ||||
| 						<MkA v-for="item in clips" :key="item.id" :to="`/clips/${item.id}`" class="item _panel _gap"> | ||||
| 							<b>{{ item.name }}</b> | ||||
| 							<div v-if="item.description" class="description">{{ item.description }}</div> | ||||
| 							<div class="user"> | ||||
| 								<MkAvatar :user="item.user" class="avatar" :show-indicator="true"/> <MkUserName :user="item.user" :nowrap="false"/> | ||||
| 							</div> | ||||
| 						</MkA> | ||||
| 					</div> | ||||
| 					<MkButton v-if="!showPrev && hasPrev" class="load prev" @click="showPrev = true"><i class="fas fa-chevron-down"></i></MkButton> | ||||
| 				</div> | ||||
|  | ||||
| 				<div class="_gap" v-if="showPrev"> | ||||
| 					<XNotes class="_content" :pagination="prev" :no-gap="true"/> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<MkError v-else-if="error" @retry="fetch()"/> | ||||
| 		<MkLoading v-else/> | ||||
| 	</transition> | ||||
| 			<MkError v-else-if="error" @retry="fetch()"/> | ||||
| 			<MkLoading v-else/> | ||||
| 		</transition> | ||||
| 	</div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| @@ -63,12 +65,14 @@ export default defineComponent({ | ||||
| 		return { | ||||
| 			[symbols.PAGE_INFO]: computed(() => this.note ? { | ||||
| 				title: this.$ts.note, | ||||
| 				subtitle: new Date(this.note.createdAt).toLocaleString(), | ||||
| 				avatar: this.note.user, | ||||
| 				path: `/notes/${this.note.id}`, | ||||
| 				share: { | ||||
| 					title: this.$t('noteOf', { user: this.note.user.name }), | ||||
| 					text: this.note.text, | ||||
| 				}, | ||||
| 				bg: 'var(--bg)', | ||||
| 			} : null), | ||||
| 			note: null, | ||||
| 			clips: null, | ||||
| @@ -149,52 +153,54 @@ export default defineComponent({ | ||||
| .fcuexfpr { | ||||
| 	background: var(--bg); | ||||
|  | ||||
| 	> .note { | ||||
| 		> .main { | ||||
| 			> .load { | ||||
| 				min-width: 0; | ||||
| 				margin: 0 auto; | ||||
| 				border-radius: 999px; | ||||
| 	> ._root { | ||||
| 		> .note { | ||||
| 			> .main { | ||||
| 				> .load { | ||||
| 					min-width: 0; | ||||
| 					margin: 0 auto; | ||||
| 					border-radius: 999px; | ||||
|  | ||||
| 				&.next { | ||||
| 					margin-bottom: var(--margin); | ||||
| 				} | ||||
|  | ||||
| 				&.prev { | ||||
| 					margin-top: var(--margin); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			> .note { | ||||
| 				> .note { | ||||
| 					border-radius: var(--radius); | ||||
| 					background: var(--panel); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			> .clips { | ||||
| 				> .title { | ||||
| 					font-weight: bold; | ||||
| 					padding: 12px; | ||||
| 				} | ||||
|  | ||||
| 				> .item { | ||||
| 					display: block; | ||||
| 					padding: 16px; | ||||
|  | ||||
| 					> .description { | ||||
| 						padding: 8px 0; | ||||
| 					&.next { | ||||
| 						margin-bottom: var(--margin); | ||||
| 					} | ||||
|  | ||||
| 					> .user { | ||||
| 						$height: 32px; | ||||
| 						padding-top: 16px; | ||||
| 						border-top: solid 0.5px var(--divider); | ||||
| 						line-height: $height; | ||||
| 					&.prev { | ||||
| 						margin-top: var(--margin); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 						> .avatar { | ||||
| 							width: $height; | ||||
| 							height: $height; | ||||
| 				> .note { | ||||
| 					> .note { | ||||
| 						border-radius: var(--radius); | ||||
| 						background: var(--panel); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				> .clips { | ||||
| 					> .title { | ||||
| 						font-weight: bold; | ||||
| 						padding: 12px; | ||||
| 					} | ||||
|  | ||||
| 					> .item { | ||||
| 						display: block; | ||||
| 						padding: 16px; | ||||
|  | ||||
| 						> .description { | ||||
| 							padding: 8px 0; | ||||
| 						} | ||||
|  | ||||
| 						> .user { | ||||
| 							$height: 32px; | ||||
| 							padding-top: 16px; | ||||
| 							border-top: solid 0.5px var(--divider); | ||||
| 							line-height: $height; | ||||
|  | ||||
| 							> .avatar { | ||||
| 								width: $height; | ||||
| 								height: $height; | ||||
| 							} | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|   | ||||
| @@ -21,6 +21,7 @@ export default defineComponent({ | ||||
| 			[symbols.PAGE_INFO]: { | ||||
| 				title: this.$ts.notifications, | ||||
| 				icon: 'fas fa-bell', | ||||
| 				bg: 'var(--bg)', | ||||
| 				actions: [{ | ||||
| 					text: this.$ts.markAllAsRead, | ||||
| 					icon: 'fas fa-check', | ||||
|   | ||||
| @@ -45,6 +45,10 @@ | ||||
| 		</FormSwitch> | ||||
| 	</FormGroup> | ||||
|  | ||||
| 	<FormGroup> | ||||
| 		<FormSwitch v-model:value="aiChanMode">{{ $ts.aiChanMode }}</FormSwitch> | ||||
| 	</FormGroup> | ||||
|  | ||||
| 	<FormRadios v-model="fontSize"> | ||||
| 		<template #desc>{{ $ts.fontSize }}</template> | ||||
| 		<option value="small"><span style="font-size: 14px;">Aa</span></option> | ||||
| @@ -149,6 +153,7 @@ export default defineComponent({ | ||||
| 		enableInfiniteScroll: defaultStore.makeGetterSetter('enableInfiniteScroll'), | ||||
| 		useReactionPickerForContextMenu: defaultStore.makeGetterSetter('useReactionPickerForContextMenu'), | ||||
| 		squareAvatars: defaultStore.makeGetterSetter('squareAvatars'), | ||||
| 		aiChanMode: defaultStore.makeGetterSetter('aiChanMode'), | ||||
| 	}, | ||||
|  | ||||
| 	watch: { | ||||
| @@ -184,6 +189,10 @@ export default defineComponent({ | ||||
| 			this.reloadAsk(); | ||||
| 		}, | ||||
|  | ||||
| 		aiChanMode() { | ||||
| 			this.reloadAsk(); | ||||
| 		}, | ||||
|  | ||||
| 		showGapBetweenNotesInTimeline() { | ||||
| 			this.reloadAsk(); | ||||
| 		}, | ||||
|   | ||||
| @@ -86,7 +86,8 @@ export default defineComponent({ | ||||
| 	setup(props, context) { | ||||
| 		const indexInfo = { | ||||
| 			title: i18n.locale.settings, | ||||
| 			icon: 'fas fa-cog' | ||||
| 			icon: 'fas fa-cog', | ||||
| 			bg: 'var(--bg)', | ||||
| 		}; | ||||
| 		const INFO = ref(indexInfo); | ||||
| 		const page = ref(props.initialPage); | ||||
|   | ||||
| @@ -26,7 +26,7 @@ | ||||
| 	<FormLink to="/bios" behavior="browser"><template #icon><i class="fas fa-door-open"></i></template>BIOS</FormLink> | ||||
| 	<FormLink to="/cli" behavior="browser"><template #icon><i class="fas fa-door-open"></i></template>CLI</FormLink> | ||||
|  | ||||
| 	<FormLink to="./delete-account"><template #icon><i class="fas fa-exclamation-triangle"></i></template>{{ $ts.closeAccount }}</FormLink> | ||||
| 	<FormLink to="/settings/delete-account"><template #icon><i class="fas fa-exclamation-triangle"></i></template>{{ $ts.closeAccount }}</FormLink> | ||||
| </FormBase> | ||||
| </template> | ||||
|  | ||||
|   | ||||
| @@ -28,6 +28,7 @@ | ||||
| 		</FormSelect> | ||||
| 		<FormSwitch v-model:value="defaultNoteLocalOnly">{{ $ts._visibility.localOnly }}</FormSwitch> | ||||
| 	</FormGroup> | ||||
| 	<FormSwitch v-model:value="keepCw" @update:value="save()">{{ $ts.keepCw }}</FormSwitch> | ||||
| </FormBase> | ||||
| </template> | ||||
|  | ||||
| @@ -69,6 +70,7 @@ export default defineComponent({ | ||||
| 		defaultNoteVisibility: defaultStore.makeGetterSetter('defaultNoteVisibility'), | ||||
| 		defaultNoteLocalOnly: defaultStore.makeGetterSetter('defaultNoteLocalOnly'), | ||||
| 		rememberNoteVisibility: defaultStore.makeGetterSetter('rememberNoteVisibility'), | ||||
| 		keepCw: defaultStore.makeGetterSetter('keepCw'), | ||||
| 	}, | ||||
|  | ||||
| 	created() { | ||||
|   | ||||
| @@ -1,31 +1,13 @@ | ||||
| <template> | ||||
| <div class="cmuxhskf" v-hotkey.global="keymap" v-size="{ min: [800] }"> | ||||
| 	<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block _isolated"/> | ||||
| 	<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block _isolated" fixed/> | ||||
| 	<div class="tabs"> | ||||
| 		<div class="left"> | ||||
| 			<button class="_button tab" @click="() => { src = 'home'; saveSrc(); }" :class="{ active: src === 'home' }" v-tooltip="$ts._timelines.home"><i class="fas fa-home"></i></button> | ||||
| 			<button class="_button tab" @click="() => { src = 'local'; saveSrc(); }" :class="{ active: src === 'local' }" v-tooltip="$ts._timelines.local" v-if="isLocalTimelineAvailable"><i class="fas fa-comments"></i></button> | ||||
| 			<button class="_button tab" @click="() => { src = 'social'; saveSrc(); }" :class="{ active: src === 'social' }" v-tooltip="$ts._timelines.social" v-if="isLocalTimelineAvailable"><i class="fas fa-share-alt"></i></button> | ||||
| 			<button class="_button tab" @click="() => { src = 'global'; saveSrc(); }" :class="{ active: src === 'global' }" v-tooltip="$ts._timelines.global" v-if="isGlobalTimelineAvailable"><i class="fas fa-globe"></i></button> | ||||
| 			<span class="divider"></span> | ||||
| 			<button class="_button tab" @click="() => { src = 'mentions'; saveSrc(); }" :class="{ active: src === 'mentions' }" v-tooltip="$ts.mentions"><i class="fas fa-at"></i><i v-if="$i.hasUnreadMentions" class="fas fa-circle i"></i></button> | ||||
| 			<button class="_button tab" @click="() => { src = 'directs'; saveSrc(); }" :class="{ active: src === 'directs' }" v-tooltip="$ts.directNotes"><i class="fas fa-envelope"></i><i v-if="$i.hasUnreadSpecifiedNotes" class="fas fa-circle i"></i></button> | ||||
| 		</div> | ||||
| 		<div class="right"> | ||||
| 			<button class="_button tab" @click="chooseChannel" :class="{ active: src === 'channel' }" v-tooltip="$ts.channel"><i class="fas fa-satellite-dish"></i><i v-if="$i.hasUnreadChannel" class="fas fa-circle i"></i></button> | ||||
| 			<button class="_button tab" @click="chooseAntenna" :class="{ active: src === 'antenna' }" v-tooltip="$ts.antennas"><i class="fas fa-satellite"></i><i v-if="$i.hasUnreadAntenna" class="fas fa-circle i"></i></button> | ||||
| 			<button class="_button tab" @click="chooseList" :class="{ active: src === 'list' }" v-tooltip="$ts.lists"><i class="fas fa-list-ul"></i></button> | ||||
| 		</div> | ||||
| 	</div> | ||||
| 	<XTutorial v-if="$store.reactiveState.tutorial.value != -1" class="tutorial _block"/> | ||||
| 	<XPostForm v-if="$store.reactiveState.showFixedPostForm.value" class="post-form _block" fixed/> | ||||
|  | ||||
| 	<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> | ||||
| 	<div class="tl"> | ||||
| 	<div class="tl _block"> | ||||
| 		<XTimeline ref="tl" class="tl" | ||||
| 			:key="src === 'list' ? `list:${list.id}` : src === 'antenna' ? `antenna:${antenna.id}` : src === 'channel' ? `channel:${channel.id}` : src" | ||||
| 			:key="src" | ||||
| 			:src="src" | ||||
| 			:list="list ? list.id : null" | ||||
| 			:antenna="antenna ? antenna.id : null" | ||||
| 			:channel="channel ? channel.id : null" | ||||
| 			:sound="true" | ||||
| 			@before="before()" | ||||
| 			@after="after()" | ||||
| @@ -56,19 +38,52 @@ export default defineComponent({ | ||||
| 	data() { | ||||
| 		return { | ||||
| 			src: 'home', | ||||
| 			list: null, | ||||
| 			antenna: null, | ||||
| 			channel: null, | ||||
| 			menuOpened: false, | ||||
| 			queue: 0, | ||||
| 			[symbols.PAGE_INFO]: computed(() => ({ | ||||
| 				title: this.$ts.timeline, | ||||
| 				subtitle: this.src === 'local' ? this.$ts._timelines.local : this.src === 'social' ? this.$ts._timelines.social : this.src === 'global' ? this.$ts._timelines.global : this.$ts._timelines.home, | ||||
| 				icon: this.src === 'local' ? 'fas fa-comments' : this.src === 'social' ? 'fas fa-share-alt' : this.src === 'global' ? 'fas fa-globe' : 'fas fa-home', | ||||
| 				bg: 'var(--bg)', | ||||
| 				actions: [{ | ||||
| 					icon: 'fas fa-list-ul', | ||||
| 					text: this.$ts.lists, | ||||
| 					handler: this.chooseList | ||||
| 				}, { | ||||
| 					icon: 'fas fa-satellite', | ||||
| 					text: this.$ts.antennas, | ||||
| 					handler: this.chooseAntenna | ||||
| 				}, { | ||||
| 					icon: 'fas fa-satellite-dish', | ||||
| 					text: this.$ts.channel, | ||||
| 					handler: this.chooseChannel | ||||
| 				}, { | ||||
| 					icon: 'fas fa-calendar-alt', | ||||
| 					text: this.$ts.jumpToSpecifiedDate, | ||||
| 					handler: this.timetravel | ||||
| 				}], | ||||
| 				tabs: [{ | ||||
| 					active: this.src === 'home', | ||||
| 					title: this.$ts._timelines.home, | ||||
| 					icon: 'fas fa-home', | ||||
| 					iconOnly: true, | ||||
| 					onClick: () => { this.src = 'home'; this.saveSrc(); }, | ||||
| 				}, { | ||||
| 					active: this.src === 'local', | ||||
| 					title: this.$ts._timelines.local, | ||||
| 					icon: 'fas fa-comments', | ||||
| 					iconOnly: true, | ||||
| 					onClick: () => { this.src = 'local'; this.saveSrc(); }, | ||||
| 				}, { | ||||
| 					active: this.src === 'social', | ||||
| 					title: this.$ts._timelines.social, | ||||
| 					icon: 'fas fa-share-alt', | ||||
| 					iconOnly: true, | ||||
| 					onClick: () => { this.src = 'social'; this.saveSrc(); }, | ||||
| 				}, { | ||||
| 					active: this.src === 'global', | ||||
| 					title: this.$ts._timelines.global, | ||||
| 					icon: 'fas fa-globe', | ||||
| 					iconOnly: true, | ||||
| 					onClick: () => { this.src = 'global'; this.saveSrc(); }, | ||||
| 				}] | ||||
| 			})), | ||||
| 		}; | ||||
| @@ -94,32 +109,10 @@ export default defineComponent({ | ||||
| 		src() { | ||||
| 			this.showNav = false; | ||||
| 		}, | ||||
| 		list(x) { | ||||
| 			this.showNav = false; | ||||
| 			if (x != null) this.antenna = null; | ||||
| 			if (x != null) this.channel = null; | ||||
| 		}, | ||||
| 		antenna(x) { | ||||
| 			this.showNav = false; | ||||
| 			if (x != null) this.list = null; | ||||
| 			if (x != null) this.channel = null; | ||||
| 		}, | ||||
| 		channel(x) { | ||||
| 			this.showNav = false; | ||||
| 			if (x != null) this.antenna = null; | ||||
| 			if (x != null) this.list = null; | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	created() { | ||||
| 		this.src = this.$store.state.tl.src; | ||||
| 		if (this.src === 'list') { | ||||
| 			this.list = this.$store.state.tl.arg; | ||||
| 		} else if (this.src === 'antenna') { | ||||
| 			this.antenna = this.$store.state.tl.arg; | ||||
| 		} else if (this.src === 'channel') { | ||||
| 			this.channel = this.$store.state.tl.arg; | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| @@ -142,12 +135,9 @@ export default defineComponent({ | ||||
| 		async chooseList(ev) { | ||||
| 			const lists = await os.api('users/lists/list'); | ||||
| 			const items = lists.map(list => ({ | ||||
| 				type: 'link', | ||||
| 				text: list.name, | ||||
| 				action: () => { | ||||
| 					this.list = list; | ||||
| 					this.src = 'list'; | ||||
| 					this.saveSrc(); | ||||
| 				} | ||||
| 				to: `/timeline/list/${list.id}` | ||||
| 			})); | ||||
| 			os.popupMenu(items, ev.currentTarget || ev.target); | ||||
| 		}, | ||||
| @@ -155,13 +145,10 @@ export default defineComponent({ | ||||
| 		async chooseAntenna(ev) { | ||||
| 			const antennas = await os.api('antennas/list'); | ||||
| 			const items = antennas.map(antenna => ({ | ||||
| 				type: 'link', | ||||
| 				text: antenna.name, | ||||
| 				indicate: antenna.hasUnreadNote, | ||||
| 				action: () => { | ||||
| 					this.antenna = antenna; | ||||
| 					this.src = 'antenna'; | ||||
| 					this.saveSrc(); | ||||
| 				} | ||||
| 				to: `/timeline/antenna/${antenna.id}` | ||||
| 			})); | ||||
| 			os.popupMenu(items, ev.currentTarget || ev.target); | ||||
| 		}, | ||||
| @@ -169,15 +156,10 @@ export default defineComponent({ | ||||
| 		async chooseChannel(ev) { | ||||
| 			const channels = await os.api('channels/followed'); | ||||
| 			const items = channels.map(channel => ({ | ||||
| 				type: 'link', | ||||
| 				text: channel.name, | ||||
| 				indicate: channel.hasUnreadNote, | ||||
| 				action: () => { | ||||
| 					// NOTE: チャンネルタイムラインをこのコンポーネントで表示するようにすると投稿フォームはどうするかなどの問題が生じるのでとりあえずページ遷移で | ||||
| 					//this.channel = channel; | ||||
| 					//this.src = 'channel'; | ||||
| 					//this.saveSrc(); | ||||
| 					this.$router.push(`/channels/${channel.id}`); | ||||
| 				} | ||||
| 				to: `/channels/${channel.id}` | ||||
| 			})); | ||||
| 			os.popupMenu(items, ev.currentTarget || ev.target); | ||||
| 		}, | ||||
| @@ -185,10 +167,6 @@ export default defineComponent({ | ||||
| 		saveSrc() { | ||||
| 			this.$store.set('tl', { | ||||
| 				src: this.src, | ||||
| 				arg: | ||||
| 					this.src === 'list' ? this.list : | ||||
| 					this.src === 'antenna' ? this.antenna : | ||||
| 					this.channel | ||||
| 			}); | ||||
| 		}, | ||||
|  | ||||
| @@ -213,6 +191,8 @@ export default defineComponent({ | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .cmuxhskf { | ||||
| 	padding: var(--margin); | ||||
|  | ||||
| 	> .new { | ||||
| 		position: sticky; | ||||
| 		top: calc(var(--stickyTop, 0px) + 16px); | ||||
| @@ -227,79 +207,15 @@ export default defineComponent({ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	> .tabs { | ||||
| 		display: flex; | ||||
| 		box-sizing: border-box; | ||||
| 		padding: 0 8px; | ||||
| 		white-space: nowrap; | ||||
| 		overflow: auto; | ||||
| 		border-bottom: solid 0.5px var(--divider); | ||||
|  | ||||
| 		// 影の都合上 | ||||
| 		position: relative; | ||||
|  | ||||
| 		> .right { | ||||
| 			margin-left: auto; | ||||
| 		} | ||||
|  | ||||
| 		> .left, > .right { | ||||
| 			> .tab { | ||||
| 				position: relative; | ||||
| 				height: 50px; | ||||
| 				padding: 0 12px; | ||||
|  | ||||
| 				&:hover { | ||||
| 					color: var(--fgHighlighted); | ||||
| 				} | ||||
|  | ||||
| 				&.active { | ||||
| 					color: var(--fgHighlighted); | ||||
|  | ||||
| 					&:after { | ||||
| 						content: ""; | ||||
| 						display: block; | ||||
| 						position: absolute; | ||||
| 						bottom: 0; | ||||
| 						left: 0; | ||||
| 						right: 0; | ||||
| 						margin: 0 auto; | ||||
| 						width: 100%; | ||||
| 						height: 2px; | ||||
| 						background: var(--accent); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				> .i { | ||||
| 					position: absolute; | ||||
| 					top: 16px; | ||||
| 					right: 8px; | ||||
| 					color: var(--indicator); | ||||
| 					font-size: 8px; | ||||
| 					animation: blink 1s infinite; | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			> .divider { | ||||
| 				display: inline-block; | ||||
| 				width: 1px; | ||||
| 				height: 28px; | ||||
| 				vertical-align: middle; | ||||
| 				margin: 0 8px; | ||||
| 				background: var(--divider); | ||||
| 			} | ||||
| 		} | ||||
| 	> .tl { | ||||
| 		background: var(--bg); | ||||
| 		border-radius: var(--radius); | ||||
| 		overflow: clip; | ||||
| 	} | ||||
|  | ||||
| 	&.min-width_800px { | ||||
| 		> .tl { | ||||
| 			background: var(--bg); | ||||
| 			padding: 32px 0; | ||||
|  | ||||
| 			> .tl { | ||||
| 				max-width: 800px; | ||||
| 				margin: 0 auto; | ||||
| 			} | ||||
| 		} | ||||
| 		max-width: 800px; | ||||
| 		margin: 0 auto; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|   | ||||
							
								
								
									
										147
									
								
								src/client/pages/user-list-timeline.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										147
									
								
								src/client/pages/user-list-timeline.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,147 @@ | ||||
| <template> | ||||
| <div class="eqqrhokj" v-hotkey.global="keymap" v-size="{ min: [800] }"> | ||||
| 	<div class="new" v-if="queue > 0"><button class="_buttonPrimary" @click="top()">{{ $ts.newNoteRecived }}</button></div> | ||||
| 	<div class="tl _block"> | ||||
| 		<XTimeline ref="tl" class="tl" | ||||
| 			:key="listId" | ||||
| 			src="list" | ||||
| 			:list="listId" | ||||
| 			:sound="true" | ||||
| 			@before="before()" | ||||
| 			@after="after()" | ||||
| 			@queue="queueUpdated" | ||||
| 		/> | ||||
| 	</div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent, defineAsyncComponent, computed } from 'vue'; | ||||
| import Progress from '@client/scripts/loading'; | ||||
| import XTimeline from '@client/components/timeline.vue'; | ||||
| import { scroll } from '@client/scripts/scroll'; | ||||
| import * as os from '@client/os'; | ||||
| import * as symbols from '@client/symbols'; | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	components: { | ||||
| 		XTimeline, | ||||
| 	}, | ||||
|  | ||||
| 	props: { | ||||
| 		listId: { | ||||
| 			type: String, | ||||
| 			required: true | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			list: null, | ||||
| 			queue: 0, | ||||
| 			[symbols.PAGE_INFO]: computed(() => this.list ? { | ||||
| 				title: this.list.name, | ||||
| 				icon: 'fas fa-list-ul', | ||||
| 				bg: 'var(--bg)', | ||||
| 				actions: [{ | ||||
| 					icon: 'fas fa-calendar-alt', | ||||
| 					text: this.$ts.jumpToSpecifiedDate, | ||||
| 					handler: this.timetravel | ||||
| 				}, { | ||||
| 					icon: 'fas fa-cog', | ||||
| 					text: this.$ts.settings, | ||||
| 					handler: this.settings | ||||
| 				}], | ||||
| 			} : null), | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	computed: { | ||||
| 		keymap(): any { | ||||
| 			return { | ||||
| 				't': this.focus | ||||
| 			}; | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	watch: { | ||||
| 		listId: { | ||||
| 			async handler() { | ||||
| 				this.list = await os.api('users/lists/show', { | ||||
| 					listId: this.listId | ||||
| 				}); | ||||
| 			}, | ||||
| 			immediate: true | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| 		before() { | ||||
| 			Progress.start(); | ||||
| 		}, | ||||
|  | ||||
| 		after() { | ||||
| 			Progress.done(); | ||||
| 		}, | ||||
|  | ||||
| 		queueUpdated(q) { | ||||
| 			this.queue = q; | ||||
| 		}, | ||||
|  | ||||
| 		top() { | ||||
| 			scroll(this.$el, 0); | ||||
| 		}, | ||||
|  | ||||
| 		settings() { | ||||
| 			this.$router.push(`/my/lists/${this.listId}`); | ||||
| 		}, | ||||
|  | ||||
| 		async timetravel() { | ||||
| 			const { canceled, result: date } = await os.dialog({ | ||||
| 				title: this.$ts.date, | ||||
| 				input: { | ||||
| 					type: 'date' | ||||
| 				} | ||||
| 			}); | ||||
| 			if (canceled) return; | ||||
|  | ||||
| 			this.$refs.tl.timetravel(new Date(date)); | ||||
| 		}, | ||||
|  | ||||
| 		focus() { | ||||
| 			(this.$refs.tl as any).focus(); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .eqqrhokj { | ||||
| 	padding: var(--margin); | ||||
|  | ||||
| 	> .new { | ||||
| 		position: sticky; | ||||
| 		top: calc(var(--stickyTop, 0px) + 16px); | ||||
| 		z-index: 1000; | ||||
| 		width: 100%; | ||||
|  | ||||
| 		> button { | ||||
| 			display: block; | ||||
| 			margin: var(--margin) auto 0 auto; | ||||
| 			padding: 8px 16px; | ||||
| 			border-radius: 32px; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	> .tl { | ||||
| 		background: var(--bg); | ||||
| 		border-radius: var(--radius); | ||||
| 		overflow: clip; | ||||
| 	} | ||||
|  | ||||
| 	&.min-width_800px { | ||||
| 		max-width: 800px; | ||||
| 		margin: 0 auto; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
| @@ -60,23 +60,9 @@ | ||||
| 				<XPhotos :user="user" :key="user.id" class="_gap"/> | ||||
| 			</div> | ||||
| 			<div class="main"> | ||||
| 				<div class="nav _gap"> | ||||
| 					<MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link"> | ||||
| 						<i class="fas fa-comment-alt icon"></i> | ||||
| 						<span>{{ $ts.notes }}</span> | ||||
| 					</MkA> | ||||
| 					<MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link"> | ||||
| 						<i class="fas fa-paperclip icon"></i> | ||||
| 						<span>{{ $ts.clips }}</span> | ||||
| 					</MkA> | ||||
| 					<MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link"> | ||||
| 						<i class="fas fa-file-alt icon"></i> | ||||
| 						<span>{{ $ts.pages }}</span> | ||||
| 					</MkA> | ||||
| 					<div class="actions"> | ||||
| 						<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> | ||||
| 						<MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> | ||||
| 					</div> | ||||
| 				<div class="actions"> | ||||
| 					<button @click="menu" class="menu _button"><i class="fas fa-ellipsis-h"></i></button> | ||||
| 					<MkFollowButton v-if="!$i || $i.id != user.id" :user="user" :inline="true" :transparent="false" :full="true" large class="koudoku"/> | ||||
| 				</div> | ||||
| 				<template v-if="page === 'index'"> | ||||
| 					<div v-if="user.pinnedNotes.length > 0" class="_gap"> | ||||
| @@ -178,25 +164,6 @@ | ||||
| 		</div> | ||||
|  | ||||
| 		<div class="contents"> | ||||
| 			<div class="nav _gap"> | ||||
| 				<MkA :to="userPage(user)" :class="{ active: page === 'index' }" class="link" v-click-anime> | ||||
| 					<i class="fas fa-comment-alt icon"></i> | ||||
| 					<span>{{ $ts.notes }}</span> | ||||
| 				</MkA> | ||||
| 				<MkA :to="userPage(user, 'clips')" :class="{ active: page === 'clips' }" class="link" v-click-anime> | ||||
| 					<i class="fas fa-paperclip icon"></i> | ||||
| 					<span>{{ $ts.clips }}</span> | ||||
| 				</MkA> | ||||
| 				<MkA :to="userPage(user, 'pages')" :class="{ active: page === 'pages' }" class="link" v-click-anime> | ||||
| 					<i class="fas fa-file-alt icon"></i> | ||||
| 					<span>{{ $ts.pages }}</span> | ||||
| 				</MkA> | ||||
| 				<MkA :to="userPage(user, 'gallery')" :class="{ active: page === 'gallery' }" class="link" v-click-anime> | ||||
| 					<i class="fas fa-icons icon"></i> | ||||
| 					<span>{{ $ts.gallery }}</span> | ||||
| 				</MkA> | ||||
| 			</div> | ||||
|  | ||||
| 			<template v-if="page === 'index'"> | ||||
| 				<div> | ||||
| 					<div v-if="user.pinnedNotes.length > 0" class="_gap"> | ||||
| @@ -283,6 +250,27 @@ export default defineComponent({ | ||||
| 				share: { | ||||
| 					title: this.user.name, | ||||
| 				}, | ||||
| 				bg: 'var(--bg)', | ||||
| 				tabs: [{ | ||||
| 					active: this.page === 'index', | ||||
| 					title: this.$ts.overview, | ||||
| 					icon: 'fas fa-home', | ||||
| 				}, { | ||||
| 					active: this.page === 'clips', | ||||
| 					title: this.$ts.clips, | ||||
| 					icon: 'fas fa-paperclip', | ||||
| 					onClick: () => { this.page = 'clips'; }, | ||||
| 				}, { | ||||
| 					active: this.page === 'pages', | ||||
| 					title: this.$ts.pages, | ||||
| 					icon: 'fas fa-file-alt', | ||||
| 					onClick: () => { this.page = 'pages'; }, | ||||
| 				}, { | ||||
| 					active: this.page === 'gallery', | ||||
| 					title: this.$ts.gallery, | ||||
| 					icon: 'fas fa-icons', | ||||
| 					onClick: () => { this.page = 'gallery'; }, | ||||
| 				}] | ||||
| 			} : null), | ||||
| 			user: null, | ||||
| 			error: null, | ||||
| @@ -314,7 +302,7 @@ export default defineComponent({ | ||||
|  | ||||
| 	mounted() { | ||||
| 		window.requestAnimationFrame(this.parallaxLoop); | ||||
| 		this.narrow = this.$el.clientWidth < 1000; | ||||
| 		this.narrow = true//this.$el.clientWidth < 1000; | ||||
| 	}, | ||||
|  | ||||
| 	beforeUnmount() { | ||||
| @@ -772,37 +760,6 @@ export default defineComponent({ | ||||
| 	} | ||||
|  | ||||
| 	> .contents { | ||||
| 		> .nav { | ||||
| 			display: flex; | ||||
| 			align-items: center; | ||||
| 			font-size: 90%; | ||||
|  | ||||
| 			> .link { | ||||
| 				flex: 1; | ||||
| 				display: inline-block; | ||||
| 				padding: 16px; | ||||
| 				text-align: center; | ||||
| 				border-bottom: solid 3px transparent; | ||||
|  | ||||
| 				&:hover { | ||||
| 					text-decoration: none; | ||||
| 				} | ||||
|  | ||||
| 				&.active { | ||||
| 					color: var(--accent); | ||||
| 					border-bottom-color: var(--accent); | ||||
| 				} | ||||
|  | ||||
| 				&:not(.active):hover { | ||||
| 					color: var(--fgHighlighted); | ||||
| 				} | ||||
|  | ||||
| 				> .icon { | ||||
| 					margin-right: 6px; | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		> .content { | ||||
| 			margin-bottom: var(--margin); | ||||
| 		} | ||||
|   | ||||
| @@ -53,7 +53,7 @@ export default defineComponent({ | ||||
| 				username: this.username, | ||||
| 				password: this.password, | ||||
| 			}).then(res => { | ||||
| 				login(res.i); | ||||
| 				return login(res.token); | ||||
| 			}).catch(() => { | ||||
| 				this.submitting = false; | ||||
|  | ||||
|   | ||||
| @@ -48,6 +48,8 @@ const defaultRoutes = [ | ||||
| 	{ path: '/channels/:channelId/edit', component: page('channel-editor'), props: true }, | ||||
| 	{ path: '/channels/:channelId', component: page('channel'), props: route => ({ channelId: route.params.channelId }) }, | ||||
| 	{ path: '/clips/:clipId', component: page('clip'), props: route => ({ clipId: route.params.clipId }) }, | ||||
| 	{ path: '/timeline/list/:listId', component: page('user-list-timeline'), props: route => ({ listId: route.params.listId }) }, | ||||
| 	{ path: '/timeline/antenna/:antennaId', component: page('antenna-timeline'), props: route => ({ antennaId: route.params.antennaId }) }, | ||||
| 	{ path: '/my/notifications', component: page('notifications') }, | ||||
| 	{ path: '/my/favorites', component: page('favorites') }, | ||||
| 	{ path: '/my/messages', component: page('messages') }, | ||||
|   | ||||
							
								
								
									
										10
									
								
								src/client/scripts/show-suspended-dialog.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/client/scripts/show-suspended-dialog.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| import * as os from '@client/os'; | ||||
| import { i18n } from '@client/i18n'; | ||||
|  | ||||
| export function showSuspendedDialog() { | ||||
| 	return os.dialog({ | ||||
| 		type: 'error', | ||||
| 		title: i18n.locale.yourAccountSuspendedTitle, | ||||
| 		text: i18n.locale.yourAccountSuspendedDescription | ||||
| 	}); | ||||
| } | ||||
| @@ -210,6 +210,10 @@ export const defaultStore = markRaw(new Storage('base', { | ||||
| 		where: 'device', | ||||
| 		default: '' | ||||
| 	}, | ||||
| 	aiChanMode: { | ||||
| 		where: 'device', | ||||
| 		default: false | ||||
| 	}, | ||||
| })); | ||||
|  | ||||
| // TODO: 他のタブと永続化されたstateを同期 | ||||
|   | ||||
| @@ -159,7 +159,6 @@ hr { | ||||
| 	display: inline-block; | ||||
| 	padding: 0; | ||||
| 	margin: 0; // for Safari | ||||
| 	width: max-content; | ||||
| 	background: none; | ||||
| 	border: none; | ||||
| 	cursor: pointer; | ||||
| @@ -246,7 +245,6 @@ hr { | ||||
| ._panel { | ||||
| 	background: var(--panel); | ||||
| 	border-radius: var(--radius); | ||||
| 	border: var(--panelBorder); | ||||
| 	overflow: clip; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| 		accent: '#86b300', | ||||
| 		accentDarken: ':darken<10<@accent', | ||||
| 		accentLighten: ':lighten<10<@accent', | ||||
| 		accentedBg: ':alpha<0.15<@accent', | ||||
| 		focus: ':alpha<0.3<@accent', | ||||
| 		bg: '#000', | ||||
| 		acrylicBg: ':alpha<0.5<@bg', | ||||
| @@ -36,7 +37,7 @@ | ||||
| 		navFg: '@fg', | ||||
| 		navHoverFg: ':lighten<17<@fg', | ||||
| 		navActive: '@accent', | ||||
| 		navIndicator: '@accent', | ||||
| 		navIndicator: '@indicator', | ||||
| 		link: '#44a4c1', | ||||
| 		hashtag: '#ff9156', | ||||
| 		mention: '@accent', | ||||
|   | ||||
| @@ -12,6 +12,7 @@ | ||||
| 		accent: '#86b300', | ||||
| 		accentDarken: ':darken<10<@accent', | ||||
| 		accentLighten: ':lighten<10<@accent', | ||||
| 		accentedBg: ':alpha<0.15<@accent', | ||||
| 		focus: ':alpha<0.3<@accent', | ||||
| 		bg: '#fff', | ||||
| 		acrylicBg: ':alpha<0.5<@bg', | ||||
| @@ -36,7 +37,7 @@ | ||||
| 		navFg: '@fg', | ||||
| 		navHoverFg: ':darken<17<@fg', | ||||
| 		navActive: '@accent', | ||||
| 		navIndicator: '@accent', | ||||
| 		navIndicator: '@indicator', | ||||
| 		link: '#44a4c1', | ||||
| 		hashtag: '#ff9156', | ||||
| 		mention: '@accent', | ||||
|   | ||||
| @@ -21,7 +21,8 @@ | ||||
| 		"baseUrl": ".", | ||||
| 		"paths": { | ||||
| 			"@/*": ["../*"], | ||||
| 			"@client/*": ["./*"] | ||||
| 			"@client/*": ["./*"], | ||||
| 			"@lib/*": ["../../lib/*"], | ||||
| 		}, | ||||
| 		"typeRoots": [ | ||||
| 			"node_modules/@types", | ||||
|   | ||||
| @@ -1,31 +1,41 @@ | ||||
| <template> | ||||
| <div class="fdidabkb" :class="{ center }" :style="`--height:${height};`" :key="key"> | ||||
| <div class="fdidabkb" :class="{ slim: titleOnly || narrow }" :style="`--height:${height};`" :key="key"> | ||||
| 	<transition :name="$store.state.animation ? 'header' : ''" mode="out-in" appear> | ||||
| 		<div class="buttons left" v-if="backButton"> | ||||
| 			<button class="_button button back" @click.stop="$emit('back')" @touchstart="preventDrag" v-tooltip="$ts.goBack"><i class="fas fa-chevron-left"></i></button> | ||||
| 		</div> | ||||
| 	</transition> | ||||
| 	<template v-if="info"> | ||||
| 		<div class="titleContainer"> | ||||
| 		<div class="titleContainer" @click="showTabsPopup"> | ||||
| 			<i v-if="info.icon" class="icon" :class="info.icon"></i> | ||||
| 			<MkAvatar v-else-if="info.avatar" class="avatar" :user="info.avatar" :disable-preview="true" :show-indicator="true"/> | ||||
|  | ||||
| 			<div class="title"> | ||||
| 				<MkUserName v-if="info.userName" :user="info.userName" :nowrap="false" class="title"/> | ||||
| 				<div v-else-if="info.title" class="title">{{ info.title }}</div> | ||||
| 				<div class="subtitle" v-if="info.subtitle"> | ||||
| 				<div class="subtitle" v-if="!narrow && info.subtitle"> | ||||
| 					{{ info.subtitle }} | ||||
| 				</div> | ||||
| 				<div class="subtitle activeTab" v-if="narrow && hasTabs"> | ||||
| 					{{ info.tabs.find(tab => tab.active)?.title }} | ||||
| 					<i class="chevron fas fa-chevron-down"></i> | ||||
| 				</div> | ||||
| 			</div> | ||||
| 		</div> | ||||
| 		<div class="buttons right"> | ||||
| 			<template v-if="info.actions && showActions"> | ||||
| 				<button v-for="action in info.actions" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button> | ||||
| 			</template> | ||||
| 			<button v-if="shouldShowMenu" class="_button button" @click.stop="showMenu" @touchstart="preventDrag" v-tooltip="$ts.menu"><i class="fas fa-ellipsis-h"></i></button> | ||||
| 			<button v-if="closeButton" class="_button button" @click.stop="$emit('close')" @touchstart="preventDrag" v-tooltip="$ts.close"><i class="fas fa-times"></i></button> | ||||
| 		<div class="tabs" v-if="!narrow"> | ||||
| 			<button class="tab _button" v-for="tab in info.tabs" :class="{ active: tab.active }" @click="tab.onClick" v-tooltip="tab.title"> | ||||
| 				<i v-if="tab.icon" class="icon" :class="tab.icon"></i> | ||||
| 				<span v-if="!tab.iconOnly" class="title">{{ tab.title }}</span> | ||||
| 			</button> | ||||
| 		</div> | ||||
| 	</template> | ||||
| 	<div class="buttons right"> | ||||
| 		<template v-if="info && info.actions && !narrow"> | ||||
| 			<button v-for="action in info.actions" class="_button button" :class="{ highlighted: action.highlighted }" @click.stop="action.handler" @touchstart="preventDrag" v-tooltip="action.text"><i :class="action.icon"></i></button> | ||||
| 		</template> | ||||
| 		<button v-if="shouldShowMenu" class="_button button" @click.stop="showMenu" @touchstart="preventDrag" v-tooltip="$ts.menu"><i class="fas fa-ellipsis-h"></i></button> | ||||
| 		<button v-if="closeButton" class="_button button" @click.stop="$emit('close')" @touchstart="preventDrag" v-tooltip="$ts.close"><i class="fas fa-times"></i></button> | ||||
| 	</div> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| @@ -52,24 +62,29 @@ export default defineComponent({ | ||||
| 			required: false, | ||||
| 			default: false, | ||||
| 		}, | ||||
| 		center: { | ||||
| 		titleOnly: { | ||||
| 			type: Boolean, | ||||
| 			required: false, | ||||
| 			default: true, | ||||
| 			default: false, | ||||
| 		}, | ||||
| 	}, | ||||
|  | ||||
| 	data() { | ||||
| 		return { | ||||
| 			showActions: false, | ||||
| 			narrow: false, | ||||
| 			height: 0, | ||||
| 			key: 0, | ||||
| 		}; | ||||
| 	}, | ||||
|  | ||||
| 	computed: { | ||||
| 		hasTabs(): boolean { | ||||
| 			return this.info.tabs && this.info.tabs.length > 0; | ||||
| 		}, | ||||
|  | ||||
| 		shouldShowMenu() { | ||||
| 			if (this.info.actions != null && !this.showActions) return true; | ||||
| 			if (this.info == null) return false; | ||||
| 			if (this.info.actions != null && this.narrow) return true; | ||||
| 			if (this.info.menu != null) return true; | ||||
| 			if (this.info.share != null) return true; | ||||
| 			if (this.menu != null) return true; | ||||
| @@ -85,10 +100,10 @@ export default defineComponent({ | ||||
|  | ||||
| 	mounted() { | ||||
| 		this.height = this.$el.parentElement.offsetHeight + 'px'; | ||||
| 		this.showActions = this.$el.parentElement.offsetWidth >= 500; | ||||
| 		this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500; | ||||
| 		new ResizeObserver((entries, observer) => { | ||||
| 			this.height = this.$el.parentElement.offsetHeight + 'px'; | ||||
| 			this.showActions = this.$el.parentElement.offsetWidth >= 500; | ||||
| 			this.narrow = this.titleOnly || this.$el.parentElement.offsetWidth < 500; | ||||
| 		}).observe(this.$el); | ||||
| 	}, | ||||
|  | ||||
| @@ -102,7 +117,7 @@ export default defineComponent({ | ||||
|  | ||||
| 		showMenu(ev) { | ||||
| 			let menu = this.info.menu ? this.info.menu() : []; | ||||
| 			if (!this.showActions && this.info.actions) { | ||||
| 			if (this.narrow && this.info.actions) { | ||||
| 				menu = [...this.info.actions.map(x => ({ | ||||
| 					text: x.text, | ||||
| 					icon: x.icon, | ||||
| @@ -124,6 +139,18 @@ export default defineComponent({ | ||||
| 			popupMenu(menu, ev.currentTarget || ev.target); | ||||
| 		}, | ||||
|  | ||||
| 		showTabsPopup(ev) { | ||||
| 			if (!this.hasTabs) return; | ||||
| 			ev.preventDefault(); | ||||
| 			ev.stopPropagation(); | ||||
| 			const menu = this.info.tabs.map(tab => ({ | ||||
| 				text: tab.title, | ||||
| 				icon: tab.icon, | ||||
| 				action: tab.onClick, | ||||
| 			})); | ||||
| 			popupMenu(menu, ev.currentTarget || ev.target); | ||||
| 		}, | ||||
|  | ||||
| 		preventDrag(ev) { | ||||
| 			ev.stopPropagation(); | ||||
| 		} | ||||
| @@ -135,7 +162,7 @@ export default defineComponent({ | ||||
| .fdidabkb { | ||||
| 	display: flex; | ||||
|  | ||||
| 	&.center { | ||||
| 	&.slim { | ||||
| 		text-align: center; | ||||
|  | ||||
| 		> .titleContainer { | ||||
| @@ -190,6 +217,7 @@ export default defineComponent({ | ||||
| 		overflow: auto; | ||||
| 		white-space: nowrap; | ||||
| 		text-align: left; | ||||
| 		font-weight: bold; | ||||
|  | ||||
| 		> .avatar { | ||||
| 			$size: 32px; | ||||
| @@ -219,6 +247,54 @@ export default defineComponent({ | ||||
| 				white-space: nowrap; | ||||
| 				overflow: hidden; | ||||
| 				text-overflow: ellipsis; | ||||
|  | ||||
| 				&.activeTab { | ||||
| 					text-align: center; | ||||
|  | ||||
| 					> .chevron { | ||||
| 						display: inline-block; | ||||
| 						margin-left: 6px; | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	> .tabs { | ||||
| 		margin-left: 16px; | ||||
| 		font-size: 0.8em; | ||||
|  | ||||
| 		> .tab { | ||||
| 			display: inline-block; | ||||
| 			position: relative; | ||||
| 			padding: 0 10px; | ||||
| 			height: 100%; | ||||
| 			font-weight: normal; | ||||
| 			opacity: 0.7; | ||||
|  | ||||
| 			&:hover { | ||||
| 				opacity: 1; | ||||
| 			} | ||||
|  | ||||
| 			&.active { | ||||
| 				opacity: 1; | ||||
|  | ||||
| 				&:after { | ||||
| 					content: ""; | ||||
| 					display: block; | ||||
| 					position: absolute; | ||||
| 					bottom: 0; | ||||
| 					left: 0; | ||||
| 					right: 0; | ||||
| 					margin: 0 auto; | ||||
| 					width: 100%; | ||||
| 					height: 3px; | ||||
| 					background: var(--accent); | ||||
| 				} | ||||
| 			} | ||||
|  | ||||
| 			> .icon + .title { | ||||
| 				margin-left: 8px; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|   | ||||
| @@ -11,28 +11,28 @@ | ||||
| 	<transition name="nav"> | ||||
| 		<nav class="nav" :class="{ iconOnly, hidden }" v-show="showing"> | ||||
| 			<div> | ||||
| 				<button class="item _button account" @click="openAccountMenu"> | ||||
| 				<button class="item _button account" @click="openAccountMenu" v-click-anime> | ||||
| 					<MkAvatar :user="$i" class="avatar"/><MkAcct class="text" :user="$i"/> | ||||
| 				</button> | ||||
| 				<MkA class="item index" active-class="active" to="/" exact> | ||||
| 				<MkA class="item index" active-class="active" to="/" exact v-click-anime> | ||||
| 					<i class="fas fa-home fa-fw"></i><span class="text">{{ $ts.timeline }}</span> | ||||
| 				</MkA> | ||||
| 				<template v-for="item in menu"> | ||||
| 					<div v-if="item === '-'" class="divider"></div> | ||||
| 					<component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'MkA' : 'button'" class="item _button" :class="item" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to"> | ||||
| 					<component v-else-if="menuDef[item] && (menuDef[item].show !== false)" :is="menuDef[item].to ? 'MkA' : 'button'" class="item _button" :class="[item, { active: menuDef[item].active }]" active-class="active" v-on="menuDef[item].action ? { click: menuDef[item].action } : {}" :to="menuDef[item].to" v-click-anime> | ||||
| 						<i class="fa-fw" :class="menuDef[item].icon"></i><span class="text">{{ $ts[menuDef[item].title] }}</span> | ||||
| 						<span v-if="menuDef[item].indicated" class="indicator"><i class="fas fa-circle"></i></span> | ||||
| 					</component> | ||||
| 				</template> | ||||
| 				<div class="divider"></div> | ||||
| 				<MkA v-if="$i.isAdmin || $i.isModerator" class="item" active-class="active" to="/instance"> | ||||
| 				<MkA v-if="$i.isAdmin || $i.isModerator" class="item" active-class="active" to="/instance" v-click-anime> | ||||
| 					<i class="fas fa-server fa-fw"></i><span class="text">{{ $ts.instance }}</span> | ||||
| 				</MkA> | ||||
| 				<button class="item _button" @click="more"> | ||||
| 				<button class="item _button" @click="more" v-click-anime> | ||||
| 					<i class="fa fa-ellipsis-h fa-fw"></i><span class="text">{{ $ts.more }}</span> | ||||
| 					<span v-if="otherNavItemIndicated" class="indicator"><i class="fas fa-circle"></i></span> | ||||
| 				</button> | ||||
| 				<MkA class="item" active-class="active" to="/settings"> | ||||
| 				<MkA class="item" active-class="active" to="/settings" v-click-anime> | ||||
| 					<i class="fas fa-cog fa-fw"></i><span class="text">{{ $ts.settings }}</span> | ||||
| 				</MkA> | ||||
| 				<button class="item _button post" @click="post"> | ||||
| @@ -263,24 +263,32 @@ export default defineComponent({ | ||||
|  | ||||
| 					> .item { | ||||
| 						padding-left: 0; | ||||
| 						padding: 18px 0; | ||||
| 						width: 100%; | ||||
| 						text-align: center; | ||||
| 						font-size: $ui-font-size * 1.1; | ||||
| 						line-height: 3.7rem; | ||||
| 						line-height: initial; | ||||
|  | ||||
| 						> i, | ||||
| 						> .avatar { | ||||
| 							margin-right: 0; | ||||
| 							display: block; | ||||
| 							margin: 0 auto; | ||||
| 						} | ||||
|  | ||||
| 						> i { | ||||
| 							left: 10px; | ||||
| 							opacity: 0.7; | ||||
| 						} | ||||
|  | ||||
| 						> .text { | ||||
| 							display: none; | ||||
| 						} | ||||
|  | ||||
| 						&:hover, &.active { | ||||
| 							> i, > .text { | ||||
| 								opacity: 1; | ||||
| 							} | ||||
| 						} | ||||
|  | ||||
| 						&:first-child { | ||||
| 							margin-bottom: 8px; | ||||
| 						} | ||||
| @@ -314,10 +322,11 @@ export default defineComponent({ | ||||
| 			height: calc(var(--vh, 1vh) * 100); | ||||
| 			box-sizing: border-box; | ||||
| 			overflow: auto; | ||||
| 			overflow-x: clip; | ||||
| 			background: var(--navBg); | ||||
|  | ||||
| 			> .divider { | ||||
| 				margin: 16px 0; | ||||
| 				margin: 16px 16px; | ||||
| 				border-top: solid 0.5px var(--divider); | ||||
| 			} | ||||
|  | ||||
| @@ -326,7 +335,7 @@ export default defineComponent({ | ||||
| 				display: block; | ||||
| 				padding-left: 24px; | ||||
| 				font-size: $ui-font-size; | ||||
| 				line-height: 3rem; | ||||
| 				line-height: 2.85rem; | ||||
| 				text-overflow: ellipsis; | ||||
| 				overflow: hidden; | ||||
| 				white-space: nowrap; | ||||
| @@ -336,6 +345,7 @@ export default defineComponent({ | ||||
| 				color: var(--navFg); | ||||
|  | ||||
| 				> i { | ||||
| 					position: relative; | ||||
| 					width: 32px; | ||||
| 				} | ||||
|  | ||||
| @@ -359,6 +369,11 @@ export default defineComponent({ | ||||
| 					animation: blink 1s infinite; | ||||
| 				} | ||||
|  | ||||
| 				> .text { | ||||
| 					position: relative; | ||||
| 					font-size: 0.9em; | ||||
| 				} | ||||
|  | ||||
| 				&:hover { | ||||
| 					text-decoration: none; | ||||
| 					color: var(--navHoverFg); | ||||
| @@ -368,6 +383,23 @@ export default defineComponent({ | ||||
| 					color: var(--navActive); | ||||
| 				} | ||||
|  | ||||
| 				&:hover, &.active { | ||||
| 					&:before { | ||||
| 						content: ""; | ||||
| 						display: block; | ||||
| 						width: calc(100% - 24px); | ||||
| 						height: 100%; | ||||
| 						margin: auto; | ||||
| 						position: absolute; | ||||
| 						top: 0; | ||||
| 						left: 0; | ||||
| 						right: 0; | ||||
| 						bottom: 0; | ||||
| 						border-radius: 8px; | ||||
| 						background: var(--accentedBg); | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				&:first-child, &:last-child { | ||||
| 					position: sticky; | ||||
| 					z-index: 1; | ||||
| @@ -380,14 +412,38 @@ export default defineComponent({ | ||||
|  | ||||
| 				&:first-child { | ||||
| 					top: 0; | ||||
| 					margin-bottom: 16px; | ||||
| 					border-bottom: solid 0.5px var(--divider); | ||||
|  | ||||
| 					&:hover, &.active { | ||||
| 						&:before { | ||||
| 							content: none; | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
|  | ||||
| 				&:last-child { | ||||
| 					bottom: 0; | ||||
| 					margin-top: 16px; | ||||
| 					border-top: solid 0.5px var(--divider); | ||||
| 					color: var(--fgOnAccent); | ||||
|  | ||||
| 					&:before { | ||||
| 						content: ""; | ||||
| 						display: block; | ||||
| 						width: calc(100% - 20px); | ||||
| 						height: calc(100% - 20px); | ||||
| 						margin: auto; | ||||
| 						position: absolute; | ||||
| 						top: 0; | ||||
| 						left: 0; | ||||
| 						right: 0; | ||||
| 						bottom: 0; | ||||
| 						border-radius: 999px; | ||||
| 						background: var(--accent); | ||||
| 					} | ||||
| 					 | ||||
| 					&:hover, &.active { | ||||
| 						&:before { | ||||
| 							background: var(--accentLighten); | ||||
| 						} | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| 			</div> | ||||
| 		</template> | ||||
|  | ||||
| 		<main class="main" @contextmenu.stop="onContextmenu"> | ||||
| 		<main class="main" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }"> | ||||
| 			<header class="header" @click="onHeaderClick"> | ||||
| 				<XHeader :info="pageInfo" :back-button="true" @back="back()"/> | ||||
| 			</header> | ||||
| @@ -54,12 +54,14 @@ | ||||
| 		<XWidgets v-if="widgetsShowing" class="tray"/> | ||||
| 	</transition> | ||||
|  | ||||
| 	<iframe v-if="$store.state.aiChanMode" class="ivnzpscs" ref="live2d" src="https://misskey-dev.github.io/mascot-web/?scale=2&y=1.4"></iframe> | ||||
|  | ||||
| 	<XCommon/> | ||||
| </div> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent, defineAsyncComponent } from 'vue'; | ||||
| import { defineComponent, defineAsyncComponent, markRaw } from 'vue'; | ||||
| import { instanceName } from '@client/config'; | ||||
| import { StickySidebar } from '@client/scripts/sticky-sidebar'; | ||||
| import XSidebar from './default.sidebar.vue'; | ||||
| @@ -131,6 +133,28 @@ export default defineComponent({ | ||||
| 			this.isMobile = (window.innerWidth <= MOBILE_THRESHOLD); | ||||
| 			this.isDesktop = (window.innerWidth >= DESKTOP_THRESHOLD); | ||||
| 		}, { passive: true }); | ||||
|  | ||||
| 		if (this.$store.state.aiChanMode) { | ||||
| 			const iframeRect = this.$refs.live2d.getBoundingClientRect(); | ||||
| 			window.addEventListener('mousemove', ev => { | ||||
| 				this.$refs.live2d.contentWindow.postMessage({ | ||||
| 					type: 'moveCursor', | ||||
| 					body: { | ||||
| 						x: ev.clientX - iframeRect.left, | ||||
| 						y: ev.clientY - iframeRect.top, | ||||
| 					} | ||||
| 				}, '*'); | ||||
| 			}, { passive: true }); | ||||
| 			window.addEventListener('touchmove', ev => { | ||||
| 				this.$refs.live2d.contentWindow.postMessage({ | ||||
| 					type: 'moveCursor', | ||||
| 					body: { | ||||
| 						x: ev.touches[0].clientX - iframeRect.left, | ||||
| 						y: ev.touches[0].clientY - iframeRect.top, | ||||
| 					} | ||||
| 				}, '*'); | ||||
| 			}, { passive: true }); | ||||
| 		} | ||||
| 	}, | ||||
|  | ||||
| 	methods: { | ||||
| @@ -201,6 +225,10 @@ export default defineComponent({ | ||||
| 				} | ||||
| 			}], e); | ||||
| 		}, | ||||
|  | ||||
| 		onAiClick(ev) { | ||||
| 			//if (this.live2d) this.live2d.click(ev); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
| @@ -458,5 +486,15 @@ export default defineComponent({ | ||||
| 		overflow: auto; | ||||
| 		background: var(--bg); | ||||
| 	} | ||||
|  | ||||
| 	> .ivnzpscs { | ||||
| 		position: fixed; | ||||
| 		bottom: 0; | ||||
| 		right: 0; | ||||
| 		width: 300px; | ||||
| 		height: 600px; | ||||
| 		border: none; | ||||
| 		pointer-events: none; | ||||
| 	} | ||||
| } | ||||
| </style> | ||||
|   | ||||
| @@ -2,8 +2,8 @@ | ||||
| <div class="mk-app" :class="{ wallpaper }"> | ||||
| 	<XSidebar ref="nav" class="sidebar"/> | ||||
|  | ||||
| 	<div class="contents" ref="contents" @contextmenu.stop="onContextmenu"> | ||||
| 		<header class="header" ref="header" @click="onHeaderClick"> | ||||
| 	<div class="contents" ref="contents" @contextmenu.stop="onContextmenu" :style="{ background: pageInfo?.bg }"> | ||||
| 		<header class="header" ref="header" @click="onHeaderClick" :style="{ background: pageInfo?.bg }"> | ||||
| 			<XHeader :info="pageInfo" :back-button="true" @back="back()"/> | ||||
| 		</header> | ||||
| 		<main ref="main"> | ||||
| @@ -258,7 +258,6 @@ export default defineComponent({ | ||||
| 	} | ||||
|  | ||||
| 	> .sidebar { | ||||
| 		border-right: solid 0.5px var(--divider); | ||||
| 	} | ||||
|  | ||||
| 	> .contents { | ||||
| @@ -314,6 +313,7 @@ export default defineComponent({ | ||||
| 	> .widgets { | ||||
| 		padding: 0 var(--margin); | ||||
| 		border-left: solid 0.5px var(--divider); | ||||
| 		background: var(--bg); | ||||
|  | ||||
| 		@media (max-width: $widgets-hide-threshold) { | ||||
| 			display: none; | ||||
|   | ||||
							
								
								
									
										59
									
								
								src/client/widgets/aichan.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								src/client/widgets/aichan.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,59 @@ | ||||
| <template> | ||||
| <MkContainer :naked="props.transparent" :show-header="false"> | ||||
| 	<iframe class="dedjhjmo" ref="live2d" @click="touched" src="https://misskey-dev.github.io/mascot-web/?scale=1.5&y=1.1&eyeY=100"></iframe> | ||||
| </MkContainer> | ||||
| </template> | ||||
|  | ||||
| <script lang="ts"> | ||||
| import { defineComponent, markRaw } from 'vue'; | ||||
| import define from './define'; | ||||
| import MkContainer from '@client/components/ui/container.vue'; | ||||
| import * as os from '@client/os'; | ||||
|  | ||||
| const widget = define({ | ||||
| 	name: 'ai', | ||||
| 	props: () => ({ | ||||
| 		transparent: { | ||||
| 			type: 'boolean', | ||||
| 			default: false, | ||||
| 		}, | ||||
| 	}) | ||||
| }); | ||||
|  | ||||
| export default defineComponent({ | ||||
| 	extends: widget, | ||||
| 	components: { | ||||
| 		MkContainer, | ||||
| 	}, | ||||
| 	data() { | ||||
| 		return { | ||||
| 		}; | ||||
| 	}, | ||||
| 	mounted() { | ||||
| 		window.addEventListener('mousemove', ev => { | ||||
| 			const iframeRect = this.$refs.live2d.getBoundingClientRect(); | ||||
| 			this.$refs.live2d.contentWindow.postMessage({ | ||||
| 				type: 'moveCursor', | ||||
| 				body: { | ||||
| 					x: ev.clientX - iframeRect.left, | ||||
| 					y: ev.clientY - iframeRect.top, | ||||
| 				} | ||||
| 			}, '*'); | ||||
| 		}, { passive: true }); | ||||
| 	}, | ||||
| 	methods: { | ||||
| 		touched() { | ||||
| 			//if (this.live2d) this.live2d.changeExpression('gurugurume'); | ||||
| 		} | ||||
| 	} | ||||
| }); | ||||
| </script> | ||||
|  | ||||
| <style lang="scss" scoped> | ||||
| .dedjhjmo { | ||||
| 	width: 100%; | ||||
| 	height: 350px; | ||||
| 	border: none; | ||||
| 	pointer-events: none; | ||||
| } | ||||
| </style> | ||||
| @@ -19,6 +19,7 @@ export default function(app: App) { | ||||
| 	app.component('MkwJobQueue', defineAsyncComponent(() => import('./job-queue.vue'))); | ||||
| 	app.component('MkwButton', defineAsyncComponent(() => import('./button.vue'))); | ||||
| 	app.component('MkwAiscript', defineAsyncComponent(() => import('./aiscript.vue'))); | ||||
| 	app.component('MkwAichan', defineAsyncComponent(() => import('./aichan.vue'))); | ||||
| } | ||||
|  | ||||
| export const widgets = [ | ||||
| @@ -40,4 +41,5 @@ export const widgets = [ | ||||
| 	'jobQueue', | ||||
| 	'button', | ||||
| 	'aiscript', | ||||
| 	'aichan', | ||||
| ]; | ||||
|   | ||||
| @@ -37,6 +37,10 @@ export type Source = { | ||||
| 	proxySmtp?: string; | ||||
| 	proxyBypassHosts?: string[]; | ||||
|  | ||||
| 	allowedPrivateNetworks?: string[]; | ||||
|  | ||||
| 	maxFileSize?: number; | ||||
|  | ||||
| 	accesslog?: string; | ||||
|  | ||||
| 	clusterLimit?: number; | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| # サードパーティアプリのリスト | ||||
| ## クライアント | ||||
| ## العملاء | ||||
| todo | ||||
|  | ||||
| ## 連携サービス | ||||
| ## الخدمات المترابطة | ||||
| todo | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # AiScript | ||||
| AiScriptは、Misskeyで使用できるスクリプト言語です。 | ||||
| AiScript is a scripting language for Misskey. | ||||
|  | ||||
| <div class="info">ℹ️ AiScript実装はMisskeyとは別リポジトリで、<a href="https://github.com/syuilo/aiscript" target="_blank">オープンソースで公開されています。</a></div> | ||||
| <div class="info">ℹ️ AiScript is open source and hosted in a separate repository from Misskey. </a></div> | ||||
|  | ||||
| ## 使い方 | ||||
| AiScriptの構文や組み込み関数などのドキュメントは、[こちら](https://github.com/syuilo/aiscript/tree/master/docs)で公開されています。 | ||||
| AiScript documentation such as syntax and built-in functions can be found [here](https://github.com/syuilo/aiscript/tree/master/docs). | ||||
|   | ||||
| @@ -47,7 +47,7 @@ UUIDを生成する。以後これをセッションIDと呼びます。 | ||||
|  | ||||
| レスポンスに含まれるプロパティ: | ||||
| * `token` ... ユーザーのアクセストークン | ||||
| * `user` ... ユーザーの情報 | ||||
| * `user` ... Informoj de uzanto | ||||
|  | ||||
| [「APIの使い方」へ進む](#APIの使い方) | ||||
|  | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # プラグインの作成 | ||||
| Misskey Webクライアントのプラグイン機能を使うと、クライアントを拡張し、様々な機能を追加できます。 ここではプラグインの作成にあたってのメタデータ定義や、AiScript APIリファレンスを掲載します。 | ||||
|  | ||||
| ## メタデータ | ||||
| ## Metadatumoj | ||||
| プラグインは、AiScriptのメタデータ埋め込み機能を使って、デフォルトとしてプラグインのメタデータを定義する必要があります。 メタデータは次のプロパティを含むオブジェクトです。 | ||||
|  | ||||
| ### name | ||||
| @@ -11,7 +11,7 @@ Nomo de kromaĵo | ||||
| プラグイン作者 | ||||
|  | ||||
| ### version | ||||
| プラグインバージョン。数値を指定してください。 | ||||
| Versio de kromaĵo.数値を指定してください。 | ||||
|  | ||||
| ### description | ||||
| プラグインの説明 | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # Botの作成 | ||||
| # Evoluigi robotan uzanton | ||||
| [Misskey API](./api)を利用してBotの開発が可能です。 また、いくつかのBot実装が公開されているため、ぜひ参考にしてください。 | ||||
|  | ||||
| - [syuilo/ai](https://github.com/syuilo/ai) ... Node.js上で動く、TypeScript製Bot実装 | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # Preferi | ||||
| # Preferataĵoj | ||||
| [ノート](./node)をお気に入りとして登録できる機能です。 お気に入り登録したノートは、[お気に入りページ](./my/favorites)で一覧することができます。 お気に入りに登録したことは相手に通知されず、お気に入りは自分しか見ることができません。 | ||||
|  | ||||
| ノートをお気に入り登録するには、ノートメニューの「お気に入り」を押します。お気に入り解除するには、ノートメニューの「お気に入り解除」を押します。 | ||||
|   | ||||
| @@ -1,15 +1,15 @@ | ||||
| # キーボードショートカット | ||||
| # Fulmoklavoj | ||||
|  | ||||
| ## Malloka | ||||
| これらのショートカットは基本的にどこでも使えます。 | ||||
| <table> | ||||
|     <thead> | ||||
|         <tr><th>ショートカット</th><th>効果</th><th>由来</th></tr> | ||||
|         <tr><th>Fulmoklavoj</th><th>効果</th><th>由来</th></tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|         <tr><td><kbd class="key">P</kbd>, <kbd class="key">N</kbd></td><td>新規投稿</td><td><b>P</b>ost, <b>N</b>ew, <b>N</b>ote</td></tr> | ||||
|         <tr><td><kbd class="key">P</kbd>, <kbd class="key">N</kbd></td><td>Skribi novan noton</td><td><b>P</b>ost, <b>N</b>ew, <b>N</b>ote</td></tr> | ||||
|         <tr><td><kbd class="key">T</kbd></td><td>タイムラインの最も新しい投稿にフォーカス</td><td><b>T</b>imeline, <b>T</b>op</td></tr> | ||||
|         <tr><td><kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">N</kbd></kbd></td><td>通知を表示/隠す</td><td><b>N</b>otifications</td></tr> | ||||
|         <tr><td><kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">N</kbd></kbd></td><td>Malfermi sekcio de sciigoj</td><td><b>N</b>otifications</td></tr> | ||||
|         <tr><td><kbd class="key">S</kbd></td><td>Serĉi</td><td><b>S</b>earch</td></tr> | ||||
|         <tr><td><kbd class="key">H</kbd>, <kbd class="key">?</kbd></td><td>ヘルプを表示</td><td><b>H</b>elp</td></tr> | ||||
|     </tbody> | ||||
| @@ -18,19 +18,19 @@ | ||||
| ## 投稿にフォーカスされた状態 | ||||
| <table> | ||||
|     <thead> | ||||
|         <tr><th>ショートカット</th><th>効果</th><th>由来</th></tr> | ||||
|         <tr><th>Fulmoklavoj</th><th>効果</th><th>Deveno (angla)</th></tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|         <tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd>, <kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">Tab</kbd></kbd></td><td>上の投稿にフォーカスを移動</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">↓</kbd>, <kbd class="key">J</kbd>, <kbd class="key">Tab</kbd></td><td>下の投稿にフォーカスを移動</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">R</kbd></td><td>返信フォームを開く</td><td><b>R</b>eply</td></tr> | ||||
|         <tr><td><kbd class="key">Q</kbd></td><td>Renoteフォームを開く</td><td><b>Q</b>uote</td></tr> | ||||
|         <tr><td><kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">Q</kbd></kbd></td><td>即刻Renoteする(フォームを開かずに)</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">Q</kbd></kbd></td><td>Tuj plusendos (sen la fasado)</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">E</kbd>, <kbd class="key">A</kbd>, <kbd class="key">+</kbd></td><td>リアクションフォームを開く</td><td><b>E</b>mote, re<b>A</b>ction</td></tr> | ||||
|         <tr><td><kbd class="key">0</kbd>~<kbd class="key">9</kbd></td><td>数字に対応したリアクションをする(対応については後述)</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">F</kbd>, <kbd class="key">B</kbd></td><td>お気に入りに登録</td><td><b>F</b>avorite, <b>B</b>ookmark</td></tr> | ||||
|         <tr><td><kbd class="key">Del</kbd>, <kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">D</kbd></kbd></td><td>投稿を削除</td><td><b>D</b>elete</tr> | ||||
|         <tr><td><kbd class="key">M</kbd>, <kbd class="key">O</kbd></td><td>投稿に対するメニューを開く</td><td><b>M</b>ore, <b>O</b>ther</td></tr> | ||||
|         <tr><td><kbd class="key">0</kbd>-<kbd class="key">9</kbd></td><td>数字に対応したリアクションをする(対応については後述)</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">F</kbd>, <kbd class="key">B</kbd></td><td>Aldoni vian liston de preferaĵoj</td><td><b>F</b>avorite, <b>B</b>ookmark</td></tr> | ||||
|         <tr><td><kbd class="key">Del</kbd>, <kbd class="group"><kbd class="key">Ctrl</kbd> + <kbd class="key">D</kbd></kbd></td><td>Forviŝi la noton</td><td><b>D</b>elete</tr> | ||||
|         <tr><td><kbd class="key">M</kbd>, <kbd class="key">O</kbd></td><td>Malfelmi poŝtaĵan menuon</td><td><b>M</b>ore, <b>O</b>ther</td></tr> | ||||
|         <tr><td><kbd class="key">S</kbd></td><td>CWで隠された部分を表示 or 隠す</td><td><b>S</b>how, <b>S</b>ee</td></tr> | ||||
|         <tr><td><kbd class="key">Esc</kbd></td><td>フォーカスを外す</td><td>-</td></tr> | ||||
|     </tbody> | ||||
| @@ -39,11 +39,11 @@ | ||||
| ## Renoteフォーム | ||||
| <table> | ||||
|     <thead> | ||||
|         <tr><th>ショートカット</th><th>効果</th><th>由来</th></tr> | ||||
|         <tr><th>Fulmoklavoj</th><th>Efektoj</th><th>Deveno (angla)</th></tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|         <tr><td><kbd class="key">Enter</kbd></td><td>Renoteする</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">Q</kbd></td><td>フォームを展開する</td><td><b>Q</b>uote</td></tr> | ||||
|         <tr><td><kbd class="key">Enter</kbd></td><td>Plusendi</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">Q</kbd></td><td>Malfermi sekcio</td><td><b>Q</b>uote</td></tr> | ||||
|         <tr><td><kbd class="key">Esc</kbd></td><td>フォームを閉じる</td><td>-</td></tr> | ||||
|     </tbody> | ||||
| </table> | ||||
| @@ -52,7 +52,7 @@ | ||||
| デフォルトで「👍」にフォーカスが当たっている状態です。 | ||||
| <table> | ||||
|     <thead> | ||||
|         <tr><th>ショートカット</th><th>効果</th><th>由来</th></tr> | ||||
|         <tr><th>Fulmoklavoj</th><th>効果</th><th>Deveno (angla)</th></tr> | ||||
|     </thead> | ||||
|     <tbody> | ||||
|         <tr><td><kbd class="key">↑</kbd>, <kbd class="key">K</kbd></td><td>上のリアクションにフォーカスを移動</td><td>-</td></tr> | ||||
| @@ -60,7 +60,7 @@ | ||||
|         <tr><td><kbd class="key">←</kbd>, <kbd class="key">H</kbd>, <kbd class="group"><kbd class="key">Shift</kbd> + <kbd class="key">Tab</kbd></kbd></td><td>左のリアクションにフォーカスを移動</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">→</kbd>, <kbd class="key">L</kbd>, <kbd class="key">Tab</kbd></td><td>右のリアクションにフォーカスを移動</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">Enter</kbd>, <kbd class="key">Space</kbd>, <kbd class="key">+</kbd></td><td>リアクション確定</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">0</kbd>~<kbd class="key">9</kbd></td><td>数字に対応したリアクションで確定</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">0</kbd>-<kbd class="key">9</kbd></td><td>数字に対応したリアクションで確定</td><td>-</td></tr> | ||||
|         <tr><td><kbd class="key">Esc</kbd></td><td>リアクションするのをやめる</td><td>-</td></tr> | ||||
|     </tbody> | ||||
| </table> | ||||
|   | ||||
| @@ -4,9 +4,9 @@ MFMは、Misskey Flavored Markdownの略で、Misskeyの様々な場所で使用 | ||||
| ## MFMが使用可能な場所の例 | ||||
| - ノート本文 | ||||
| - CW注釈 | ||||
| - ユーザーの名前 | ||||
| - ユーザーの自己紹介 | ||||
| - Nomo de uzanto | ||||
| - Profilo de uzanto | ||||
|  | ||||
| ## 開発者向け情報 | ||||
| ## Informoj por programistoj | ||||
| MFMのパーサー実装はライブラリとして公開されており、簡単にクライアントにMFMを組み込むことが可能です。 | ||||
| - [misskey-dev/mfm.js](https://github.com/misskey-dev/mfm.js) - JavaScriptパーサー実装 | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # Silentigatoj kaj blokatoj | ||||
| # Silentigitoj kaj blokitoj | ||||
| 好みではないユーザーがいる場合は、ミュートを行うことでそのユーザーが自分から見えないようにすることができます。 また、より強力な措置として、ブロックを行うことでそのユーザーから自分のコンテンツが見えないようになるほか、自分に対して関わることができないようにすることができます。 ミュートされていることは相手は分かりませんが、ブロックされていることは相手に分かります。どちらを選ぶかはご自身の判断で行ってください。 | ||||
|  | ||||
| <div class="info">ℹ️ ミュートとブロックは併用できます。</div> | ||||
|   | ||||
| @@ -12,7 +12,7 @@ | ||||
| <div class="info">ℹ️ コンピューターのクリップボードに画像データがある状態で、フォーム内のテキストボックスにペーストするとその画像を添付することができます。</div> | ||||
| <div class="info">ℹ️ テキストボックス内で<kbd class="key">Ctrl + Enter</kbd>を押すことでも投稿できます。</div> | ||||
|  | ||||
| ## Fari renoton | ||||
| ## Plusendi la noton | ||||
| 既にあるノートを引用、もしくはそのノートを新しいノートとして共有する行為、またそれによって作成されたノートをRenoteと呼びます。 自分がフォローしているユーザーの、気に入ったノートを自分のフォロワーに共有したい場合や、過去の自分のノートを再度共有したい場合に使います。 同じノートに対して無制限にRenoteを行うことができますが、あまり連続して使用すると迷惑になる場合もあるので、注意しましょう。 | ||||
| <div class="warn">⚠️ 公開範囲がフォロワーやダイレクトのノートはRenoteできません</div> | ||||
|  | ||||
| @@ -21,20 +21,20 @@ Renoteを削除するには、Renoteの時刻表示の隣にある「...」を | ||||
| ## CW | ||||
| Contents Warningの略で、ノートの内容を、閲覧者の操作なしには表示しないようにできる機能です。主に長大な内容を隠すためや、ネタバレ防止などに使うことができます。 設定するには、フォームの「内容を隠す」ボタン(目のアイコン)を押します。すると新しい入力エリアが表れるので、そこに内容の要約を記入します。 | ||||
|  | ||||
| ## 公開範囲 | ||||
| ## Videbleco | ||||
| ノートごとに、そのノートが公開される範囲を設定することができます。フォームの「ノート」ボタンの左にあるアイコンを押すと公開範囲を以下から選択できます。 | ||||
|  | ||||
| ### パブリック | ||||
| ### Publika | ||||
| 全ての人に対してノートが公開されるほか、サーバーの全てのタイムライン(ホームタイムライン、ローカルタイムライン、ソーシャルタイムライン、グローバルタイムライン)にノートが流れます。 | ||||
| <div class="warn">⚠️ アカウントが<a href="./silence">サイレンス</a>状態の時は、この公開範囲は使用できません。</div> | ||||
|  | ||||
| ### Hejmo | ||||
| ### Hejma | ||||
| 全ての人に対してノートが公開されますが、フォロワー以外のローカルタイムライン、ソーシャルタイムライン、グローバルタイムラインにはノートは流れません。 | ||||
|  | ||||
| ### Sekvantoj | ||||
| 自分のフォロワーに対してのみノートを公開します。フォロワーの全てのタイムラインに流れます。 | ||||
|  | ||||
| ### ダイレクト | ||||
| ### Rekta | ||||
| 指定したユーザーに対してのみノートを公開します。指定したユーザーの全てのタイムラインに流れます。 | ||||
|  | ||||
| ### 「ローカルのみ」オプション | ||||
| @@ -42,12 +42,12 @@ Contents Warningの略で、ノートの内容を、閲覧者の操作なしに | ||||
|  | ||||
| ### 公開範囲の比較 | ||||
| <table> | ||||
|     <tr><th></th><th>パブリック</th><th>Hejmo</th><th>Sekvantoj</th><th>ダイレクト</th></tr> | ||||
|     <tr><th></th><th>Publika</th><th>Hejma</th><th>Sekvantoj</th><th>Rekta</th></tr> | ||||
|     <tr><th>フォロワーのLTL/STL/GTL</th><td>✔</td><td>✔</td><td>✔</td><td></td></tr> | ||||
|     <tr><th>非フォロワーのLTL/STL/GTL</th><td>✔</td><td></td><td></td><td></td></tr> | ||||
| </table> | ||||
|  | ||||
| ## Alpingli sur la profilo | ||||
| ## Alpingli al la profilo | ||||
| ノートをピン留めすると、ユーザーページに常にそのノートを表示しておくことができます。 ノートのメニューを開き、「ピン留め」を選択してピン留めできます。 複数のノートをピン留めできます。 | ||||
|  | ||||
| ## Observi | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # Templinio | ||||
| タイムラインは、[ノート](./note)が時系列で表示される機能です。 タイムラインには以下で示す種類があり、種類によって表示されるノートも異なります。 なお、タイムラインの種類によってはサーバーにより無効になっている場合があります。 | ||||
|  | ||||
| ## Hejmo | ||||
| ## Hejma | ||||
| 自分のフォローしているユーザーの投稿が流れます。HTLと略されます。 | ||||
|  | ||||
| ## Loka | ||||
| @@ -14,18 +14,18 @@ | ||||
| 全てのローカルユーザーの「ホーム」指定されていない投稿と、サーバーに届いた全てのリモートユーザーの「ホーム」指定されていない投稿が流れます。GTLと略されます。 | ||||
|  | ||||
| ## 比較 | ||||
| | ソース          |           |       | Templinio |         |         | | ||||
| | ------------ | --------- | ----- | --------- | ------- | ------- | | ||||
| | Uzanto       | 公開範囲      | Hejmo | Loka      | Sociala | Malloka | | ||||
| | ローカル (フォロー)  | Publikigi | ✔     | ✔         | ✔       | ✔       | | ||||
| |              | Hejmo     | ✔     |           | ✔       |         | | ||||
| |              | Sekvantoj | ✔     | ✔         | ✔       | ✔       | | ||||
| | リモート (フォロー)  | Publikigi | ✔     |           | ✔       | ✔       | | ||||
| |              | Hejmo     | ✔     |           | ✔       |         | | ||||
| |              | Sekvantoj | ✔     |           | ✔       | ✔       | | ||||
| | ローカル (未フォロー) | Publikigi |       | ✔         | ✔       | ✔       | | ||||
| |              | Hejmo     |       |           |         |         | | ||||
| |              | Sekvantoj |       |           |         |         | | ||||
| | リモート (未フォロー) | Publikigi |       |           |         | ✔       | | ||||
| |              | Hejmo     |       |           |         |         | | ||||
| |              | Sekvantoj |       |           |         |         | | ||||
| | ソース                   |           |       | Templinio |         |         | | ||||
| | --------------------- | --------- | ----- | --------- | ------- | ------- | | ||||
| | Uzantoj               | Videbleco | Hejma | Loka      | Sociala | Malloka | | ||||
| | Lokaj (sekvataj)      | Publika   | ✔     | ✔         | ✔       | ✔       | | ||||
| |                       | Nur hejma | ✔     |           | ✔       |         | | ||||
| |                       | Sekvantoj | ✔     | ✔         | ✔       | ✔       | | ||||
| | Transaj (sekvataj)    | Publika   | ✔     |           | ✔       | ✔       | | ||||
| |                       | Nur hejma | ✔     |           | ✔       |         | | ||||
| |                       | Sekvantoj | ✔     |           | ✔       | ✔       | | ||||
| | Lokaj (ne sekvataj)   | Publika   |       | ✔         | ✔       | ✔       | | ||||
| |                       | Nur hejma |       |           |         |         | | ||||
| |                       | Sekvantoj |       |           |         |         | | ||||
| | Transaj (ne sekvataj) | Publika   |       |           |         | ✔       | | ||||
| |                       | Nur hejma |       |           |         |         | | ||||
| |                       | Sekvantoj |       |           |         |         | | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # Silentigo de vortoj | ||||
| # Silentigi specifajn vortojn | ||||
| ワードミュートの設定をすると、条件に合致したノートが表示されなくなります。 | ||||
|  | ||||
| ワードミュートには、ソフトワードミュートとハードワードミュートの2種類があります。それぞれについて設定の方法と挙動を説明します。 | ||||
|   | ||||
| @@ -10,23 +10,23 @@ Misskeyに関する用語集です。 | ||||
| ## API | ||||
| (読み: えーぴーあい) Misskeyのサーバーが公開している、プログラムからMisskeyを扱うためのインターフェース。詳細は[こちら。](../advanced/api) | ||||
|  | ||||
| ## Bot | ||||
| ## Roboto | ||||
| (読み: ぼっと) プログラムによって動作しているアカウント。 | ||||
|  | ||||
| ## CW | ||||
| (読み: こんてんつわーにんぐ) Contents Warningの略。ノートの内容を、操作なしには表示しないようにできる機能。主に長大な内容を隠すためや、ネタバレ防止などに使われます。 | ||||
|  | ||||
| ## Fediverse | ||||
| ## Fediverso | ||||
| (読み: ふぇでぃばーす) Misskeyを含む様々な分散型ソフトウェアのサーバーで構成されたネットワーク。 | ||||
|  | ||||
| ## MTL | ||||
| An abbreviation for "Malloka TempLinio".タイムラインの詳細は[こちら。](../features/timeline) | ||||
| ## MTL (GTL) | ||||
| Kaplitero de "Malloka TempLinio".タイムラインの詳細は[こちら。](../features/timeline) | ||||
|  | ||||
| ## HTL | ||||
| ホームタイムライン(Home TimeLine)の略。タイムラインの詳細は[こちら。](../features/timeline) | ||||
| Kaplitero de "Hejma TempLinio".タイムラインの詳細は[こちら。](../features/timeline) | ||||
|  | ||||
| ## LTL | ||||
| An abbreviation for "Loka TempLinio".タイムラインの詳細は[こちら。](../features/timeline) | ||||
| Kaplitero de "Loka TempLinio".タイムラインの詳細は[こちら。](../features/timeline) | ||||
|  | ||||
| ## MFM | ||||
| (読み: えむえふえむ) Misskey Flavored Markdownの略で、Misskey上で使用できるマークアップ言語です。詳細は[こちら。](../features/mfm) | ||||
| @@ -34,26 +34,26 @@ An abbreviation for "Loka TempLinio".タイムラインの詳細は[こちら。 | ||||
| ## NSFW | ||||
| (読み: のっとせーふふぉーわーく) Not Safe For Workの略。画像を「閲覧注意」扱いにし、操作なしには表示しないようにすることができる機能。 | ||||
|  | ||||
| ## Renoto | ||||
| (読み: りのーと) 既にあるノートを引用、もしくはそのノートを新しいノートとして共有する行為、またそれによって作成されたノート。詳細は[こちら。](../features/note) | ||||
| ## Notoj plusenditaj | ||||
| (読み: りのーと) 既にあるノートを引用、もしくはそのノートを新しいノートとして共有する行為、またそれによって作成されたノート。Rigardu por sciu pli tie[.](../features/note) | ||||
|  | ||||
| ## STL | ||||
| An abbreviation for "Sociala TempLinio".タイムラインの詳細は[こちら。](../features/timeline) | ||||
| Kaplitero de "Sociala TempLinio".Por sciu pri la templinio, rigardu tie[.](../features/timeline) | ||||
|  | ||||
| ## Ai | ||||
| Ai estas oficiala maskoto de Misskey. | ||||
|  | ||||
| ## Aktivaj Uzantoj: | ||||
| ## Aktiva uzanto | ||||
| インスタンスにアカウントを作っているユーザーのうち、現在も実際にサービスを利用しているユーザーのこと。 | ||||
|  | ||||
| ## Ekzemplo | ||||
| ## Nodo | ||||
| todo | ||||
|  | ||||
| ## Ŝaltpodio | ||||
| ## Personecigitaj emoĵioj | ||||
| サーバーで用意された絵文字。カスタム絵文字ではない通常の絵文字は「Unicode絵文字」と区別して呼ばれる。 | ||||
|  | ||||
| ## コントロールパネル | ||||
| todo | ||||
| ## Ŝaltpodio | ||||
| インスタンスの設定画面のこと。 | ||||
|  | ||||
| ## Servilo | ||||
| todo | ||||
| @@ -61,28 +61,28 @@ todo | ||||
| ## Mutigi | ||||
| ノートをパブリックな公開範囲で投稿できなくされている状態。モデレーターの判断でユーザーごとに設定されます。詳細は[こちら。](../features/silence) | ||||
|  | ||||
| ## Disko | ||||
| ## ジョブキュー | ||||
| アクティビティ配送などを順番に行うためのシステム。 | ||||
|  | ||||
| ## Flostigi | ||||
| ## Flostigita | ||||
| アカウントが使用不可に設定されている状態。 | ||||
|  | ||||
| ## Miskiisto | ||||
| ## Disko | ||||
| Misskeyにアップロードしたファイルを管理する機能。詳細は[こちら。](../features/drive) | ||||
|  | ||||
| ## Notoj | ||||
| Misskeyに投稿される、文章、ファイル、アンケートなどを含めることができるコンテンツ。詳細は[こちら。](../features/note) | ||||
| Misskeyに投稿される、文章、ファイル、アンケートなどを含めることができるコンテンツ。Rigardu por sciu pli tie[.](../features/note) | ||||
|  | ||||
| ## Transa aŭ fora | ||||
| Misskeyを使う人のこと。 | ||||
| ## Miskiisto | ||||
| Uzuloj de Misskey. | ||||
|  | ||||
| ## Moderigisto | ||||
| ## Kontrolisto | ||||
| スパムの凍結およびサイレンスや不適切な投稿の削除など、コミュニティ運営に関する権限を持つユーザー。 | ||||
|  | ||||
| ## Transa | ||||
| ## Transa, Surloka | ||||
| 他サーバーのことを指します。リモートユーザーといったように接頭辞としても使われます。ローカルの逆です。 | ||||
|  | ||||
| ## Kunfederaĵo | ||||
| ## Kunfederado | ||||
| サーバー上で作成された情報が他のサーバーに伝わること。 | ||||
|  | ||||
| ## Loka | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| # リンク集 | ||||
|  | ||||
| ## Webサイト | ||||
| - [Official Discord](https://discord.gg/Wp8gVStHW3) - Misskey公式Discordサーバー | ||||
| - [Oficiala Discord](https://discord.gg/Wp8gVStHW3) - la Servilo Discord'a oficiala de Misskey | ||||
| - [Misskey Forum](https://forum.misskey.io/) - Misskeyに関する話題を扱うフォーラム | ||||
|  | ||||
| ## Kontoj | ||||
|   | ||||
| @@ -1,6 +1,6 @@ | ||||
| # Pri Misskey | ||||
|  | ||||
| Misskey estas malfermitkoda distribuita mikroblogo. Ĝia trajtoj estas diversaj funkcioj je disko aŭ reagoj ktp, kaj alte agordebla fasado. Evoluigo ekfaris de syuilo de 2014. | ||||
| Misskey estas malfermitkoda distribuita mikroblogo. Ĝi enhavas diversaj funkcioj ekzemple disko, reagoj, ktp kaj alte agordebla fasado. Evoluigo ekfaris de syuilo de 2014. | ||||
|  | ||||
| ## Historio | ||||
| 開発当初は掲示板がメインのサービスでしたが、ユーザーが短文を投稿し、それを時系列で流れるタイムライン機能を追加したところ人気が高まり、徐々にそれがメインとして開発が進むようになりました。 当初は分散型ではありませんでしたが、2018年にActivityPubを実装し分散型になったことで、より多くの方に認知され利用されるサービスになり、現在に至ります。 | ||||
| @@ -12,7 +12,7 @@ Misskey estas malfermitkoda distribuita mikroblogo. Ĝia trajtoj estas diversaj | ||||
|  | ||||
| <b>分散(distributed)型</b>とは、<b>非中央集権(decentralized)</b>とも呼ばれ、コミュニティが多数のサーバーに分散して存在し、それらが相互に<b>通信(連合、federation)</b>することでコンテンツ共有<b>ネットワーク(Fediverse)</b>を形成していることが特徴のサービスです。 単一のサーバーしか存在しない、もしくは複数存在しても互いに独立している場合は中央集権なサービスと言われ、例えばTwitterやFacebookなどほとんどのサービスがそれに該当します。 分散型のメリットは、自分に合った運営者やテーマのサーバーを選択できることです。自分でサーバーを作成することもできます。連合するおかげで、どのサーバーを選んでも、同じコミュニティにアクセスできます。 | ||||
|  | ||||
| ## Ĉiam malfermitkode | ||||
| ## Ĉiam malfermitkoda | ||||
| Misskeyはこれまでもこれからも、オープンソースであり続けます。オープンソースとは、簡単に言うと<b>ソフトウェアのソースコード(プログラム)が公開されている</b>ことです。ソースコードの修正や再配布が可能であることを定義に含めることもあります。 Misskeyのすべてのソースコードは[AGPL](https://github.com/misskey-dev/misskey/blob/develop/LICENSE)というオープンソースライセンスの下に[公開](https://github.com/misskey-dev)されていて、誰でも自由に閲覧、使用、修正、改変、再配布をすることができます。 オープンソースは、自分で好きなように変えたり、有害な処理が含まれていないことを確認することができたり、誰でも開発に参加できるなどの、様々なメリットがあります。 上述の分散型を実現するためにも、オープンソースであるということは必要不可欠な要素です。 再び引き合いに出しますが、TwitterやFacebookなどの利益を得ているほとんどのサービスはオープンソースではありません。 | ||||
|  | ||||
| <div class="info">ℹ️ 技術的に言うと、MisskeyのソースコードはGitで管理されていて、リポジトリは<a href="https://github.com/misskey-dev" target="_blank">GitHub上でホスティングされています。</a></div> | ||||
| @@ -26,7 +26,7 @@ Misskeyを気に入っていただけたら、ぜひプロジェクトを支援 | ||||
| ### 議論に参加する | ||||
| 新しい機能、または既存の機能について意見を述べたり、不具合を報告したりすることでも貢献できます。 そのようなディスカッションは[GitHub](https://github.com/misskey-dev)上か、[フォーラム](https://forum.misskey.io/)等で行われます。 | ||||
|  | ||||
| ### テキストを翻訳する | ||||
| ### Traduki tekston | ||||
| Misskeyは様々な言語に対応しています(i18n -internationalizationの略- と呼ばれます)。元の言語は基本的に日本語ですが、有志によって他の言語へと翻訳されています。 その翻訳作業に加わっていただくことでもMisskeyに貢献できます。 Misskeyは[Crowdinというサービスを使用して翻訳の管理を行っています。](https://crowdin.com/project/misskey) | ||||
|  | ||||
| ### 感想を投稿する | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # トラブルシューティング | ||||
| # Problemsolvi | ||||
| <div class="info">ℹ️ <a href="./faq">よくある質問</a>も合わせてお役立てください。</div> | ||||
|  | ||||
| 問題が発生したときは、まずこちらをご確認ください。 該当する項目が無い、もしくは手順を試しても効果がない場合は、サーバーの管理者に連絡するか[不具合を報告](./report-issue)してください。 | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| # Misskey API | ||||
| # API de Misskey | ||||
|  | ||||
| MisskeyAPIを使ってMisskeyクライアント、Misskey連携Webサービス、Bot等(以下「アプリケーション」と呼びます)を開発できます。 ストリーミングAPIもあるので、リアルタイム性のあるアプリケーションを作ることも可能です。 | ||||
|  | ||||
| @@ -18,13 +18,13 @@ APIを使い始めるには、まずアクセストークンを取得する必 | ||||
| ### アプリケーション利用者にアクセストークンの発行をリクエストする | ||||
| アプリケーション利用者のアクセストークンを取得するには、以下の手順で発行をリクエストします。 | ||||
|  | ||||
| #### Step 1 | ||||
| #### Étape 1 | ||||
|  | ||||
| UUIDを生成する。以後これをセッションIDと呼びます。 | ||||
|  | ||||
| > このセッションIDは毎回生成し、使いまわさないようにしてください。 | ||||
|  | ||||
| #### Step 2 | ||||
| #### Étape 2 | ||||
|  | ||||
| `{_URL_}/miauth/{session}`をユーザーのブラウザで表示させる。`{session}`の部分は、セッションIDに置き換えてください。 | ||||
| > 例: `{_URL_}/miauth/c1f6d42b-468b-4fd2-8274-e58abdedef6f` | ||||
| @@ -42,7 +42,7 @@ UUIDを生成する。以後これをセッションIDと呼びます。 | ||||
|     * 要求する権限を`,`で区切って列挙します | ||||
|     * どのような権限があるかは[APIリファレンス](/api-doc)で確認できます | ||||
|  | ||||
| #### Step 3 | ||||
| #### Étape 3 | ||||
| ユーザーが発行を許可した後、`{_URL_}/api/miauth/{session}/check`にPOSTリクエストすると、レスポンスとしてアクセストークンを含むJSONが返ります。 | ||||
|  | ||||
| レスポンスに含まれるプロパティ: | ||||
|   | ||||
| @@ -16,11 +16,11 @@ Bien que cela arrive rarement, il se peut que le cache soit à l'origine du prob | ||||
| Si le problème persiste malgré tout, il est très probable qu'il s'agisse d'une panne côté serveur ; nous vous invitons donc à contacter l'administrateur·rice de votre instance. | ||||
|  | ||||
| ## Le client est lent | ||||
| 以下を試してみてください: | ||||
| Essayez les solutions proposées ci-dessous : | ||||
|  | ||||
| - クライアント設定で「UIのアニメーションを減らす」を有効にする | ||||
| - クライアント設定で「モーダルにぼかし効果を使用」を無効にする | ||||
| - お使いのブラウザの設定でハードウェアアクセラレーションを有効にする | ||||
| - activer l'option « Réduire les animations dans l'interface » dans les paramètres du client | ||||
| - désactiver l'option « Utiliser un effet de flou pour les modals » dans les paramètres du client | ||||
| - activer l'accélération matérielle dans les paramètres de votre navigateur | ||||
| - お使いのデバイスのスペックを上げる | ||||
|  | ||||
| ## UIの一部の表示がおかしい(背景が透明になっている等) | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user