Compare commits
	
		
			142 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					9dd21a19ff | ||
| 
						 | 
					a8d05cba5a | ||
| 
						 | 
					f5ddfb29f2 | ||
| 
						 | 
					ba228a6b10 | ||
| 
						 | 
					cb6f390fb6 | ||
| 
						 | 
					5675ecead9 | ||
| 
						 | 
					001bb7bbcd | ||
| 
						 | 
					1585bb12cf | ||
| 
						 | 
					26b47c18fd | ||
| 
						 | 
					665fa7f2aa | ||
| 
						 | 
					0068dc30d3 | ||
| 
						 | 
					8f39655fef | ||
| 
						 | 
					b1a4fc03bc | ||
| 
						 | 
					05d20f1044 | ||
| 
						 | 
					66a90b3fb1 | ||
| 
						 | 
					826d9d9fdf | ||
| 
						 | 
					4a9a61f108 | ||
| 
						 | 
					b72d15b56c | ||
| 
						 | 
					8c68992594 | ||
| 
						 | 
					c052028fc3 | ||
| 
						 | 
					c46fbcf345 | ||
| 
						 | 
					06b66f0209 | ||
| 
						 | 
					2de48110bb | ||
| 
						 | 
					87d4452d19 | ||
| 
						 | 
					328fc64ca9 | ||
| 
						 | 
					a6f8327aa2 | ||
| 
						 | 
					d5ab6b41c9 | ||
| 
						 | 
					ffdd0b7de7 | ||
| 
						 | 
					1808eb6eee | ||
| 
						 | 
					438563b505 | ||
| 
						 | 
					92dfcdad57 | ||
| 
						 | 
					c178cfabfa | ||
| 
						 | 
					260e4c955d | ||
| 
						 | 
					0c46f5ce70 | ||
| 
						 | 
					6d67cd07a0 | ||
| 
						 | 
					fb8af53751 | ||
| 
						 | 
					37999f4af7 | ||
| 
						 | 
					3b6ab327c1 | ||
| 
						 | 
					d3ff3a7d54 | ||
| 
						 | 
					cf36106520 | ||
| 
						 | 
					1642fbec31 | ||
| 
						 | 
					b195fd8145 | ||
| 
						 | 
					5f59b980a7 | ||
| 
						 | 
					2a5c19cd01 | ||
| 
						 | 
					42e007ddb7 | ||
| 
						 | 
					756dc397d9 | ||
| 
						 | 
					8f714b5b12 | ||
| 
						 | 
					06bb2a1c7c | ||
| 
						 | 
					ac50bb9225 | ||
| 
						 | 
					8fd95de25b | ||
| 
						 | 
					0e14b2eba4 | ||
| 
						 | 
					08413a7550 | ||
| 
						 | 
					5e0f2a5b06 | ||
| 
						 | 
					3b505709c6 | ||
| 
						 | 
					af32d1f81e | ||
| 
						 | 
					67d8773e38 | ||
| 
						 | 
					e445d39c2f | ||
| 
						 | 
					961ed969db | ||
| 
						 | 
					e9a3495225 | ||
| 
						 | 
					6c5a78aeb2 | ||
| 
						 | 
					34e249317a | ||
| 
						 | 
					6d8ea89f09 | ||
| 
						 | 
					64f89ba13e | ||
| 
						 | 
					f6b2f76bbf | ||
| 
						 | 
					1235bef038 | ||
| 
						 | 
					2e11f3a843 | ||
| 
						 | 
					84b7e0bb7d | ||
| 
						 | 
					9f5dc2c0df | ||
| 
						 | 
					e640dbc501 | ||
| 
						 | 
					85db090d9f | ||
| 
						 | 
					9f2d8e1d51 | ||
| 
						 | 
					0c98a90b75 | ||
| 
						 | 
					0047920c1a | ||
| 
						 | 
					e4bb534f20 | ||
| 
						 | 
					3fc04fcdc5 | ||
| 
						 | 
					e542dcac30 | ||
| 
						 | 
					a0b13505a0 | ||
| 
						 | 
					389f9bfea2 | ||
| 
						 | 
					630a534cee | ||
| 
						 | 
					5744c391e6 | ||
| 
						 | 
					b9b05a7401 | ||
| 
						 | 
					359470a263 | ||
| 
						 | 
					3fe934ee62 | ||
| 
						 | 
					3abe632f06 | ||
| 
						 | 
					65961bc15b | ||
| 
						 | 
					12f932d48a | ||
| 
						 | 
					54e9147782 | ||
| 
						 | 
					31b7626d01 | ||
| 
						 | 
					200ebefe92 | ||
| 
						 | 
					9d29a2e85a | ||
| 
						 | 
					c62a225542 | ||
| 
						 | 
					d5d995a3e6 | ||
| 
						 | 
					b7f10fdc10 | ||
| 
						 | 
					cbba03b376 | ||
| 
						 | 
					f84e9c7dc8 | ||
| 
						 | 
					a22ddb1fb9 | ||
| 
						 | 
					0d23ce3d45 | ||
| 
						 | 
					9719387bee | ||
| 
						 | 
					dca110ebaa | ||
| 
						 | 
					136f23c7ad | ||
| 
						 | 
					0963e6d6e1 | ||
| 
						 | 
					712802e682 | ||
| 
						 | 
					abe99c3c73 | ||
| 
						 | 
					d7a3b71028 | ||
| 
						 | 
					10c434f24a | ||
| 
						 | 
					fe46c53ea6 | ||
| 
						 | 
					cdd123dfd3 | ||
| 
						 | 
					a1a3ee44b5 | ||
| 
						 | 
					4e7fbd8967 | ||
| 
						 | 
					a86c419f95 | ||
| 
						 | 
					e3ec0ad97e | ||
| 
						 | 
					75791981ce | ||
| 
						 | 
					e813fe16b9 | ||
| 
						 | 
					42ac7b954d | ||
| 
						 | 
					c1bbf5dab6 | ||
| 
						 | 
					e16dc2a910 | ||
| 
						 | 
					e236c05d79 | ||
| 
						 | 
					454c1e3faf | ||
| 
						 | 
					43daf814df | ||
| 
						 | 
					c40b630530 | ||
| 
						 | 
					7fc0698ecf | ||
| 
						 | 
					4f3c8b940e | ||
| 
						 | 
					1855ab60f1 | ||
| 
						 | 
					af4f1a7bd6 | ||
| 
						 | 
					8646a9c49c | ||
| 
						 | 
					8d7c033cf5 | ||
| 
						 | 
					b8900e32de | ||
| 
						 | 
					d48c25d2c9 | ||
| 
						 | 
					a87c5899c5 | ||
| 
						 | 
					147ad69864 | ||
| 
						 | 
					c146006476 | ||
| 
						 | 
					a0f10d7ca1 | ||
| 
						 | 
					299b91edc4 | ||
| 
						 | 
					95c89ca6db | ||
| 
						 | 
					7fe0d71e7f | ||
| 
						 | 
					fbbb506e86 | ||
| 
						 | 
					ec80b06a45 | ||
| 
						 | 
					41e1619f1f | ||
| 
						 | 
					ba6a9c6a93 | ||
| 
						 | 
					18571c52fb | ||
| 
						 | 
					5d5dfeaa83 | ||
| 
						 | 
					3669d8c0f3 | 
@@ -23,6 +23,10 @@ jobs:
 | 
			
		||||
    executor: default
 | 
			
		||||
    steps:
 | 
			
		||||
      - checkout
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Ensure package-lock.json
 | 
			
		||||
          command: |
 | 
			
		||||
            [ ! -e package-lock.json ] && echo '{}' > package-lock.json
 | 
			
		||||
      - restore_cache:
 | 
			
		||||
          name: Restore npm package caches
 | 
			
		||||
          keys:
 | 
			
		||||
@@ -35,11 +39,12 @@ jobs:
 | 
			
		||||
          name: Install Dependencies
 | 
			
		||||
          command: |
 | 
			
		||||
            npm install
 | 
			
		||||
            npm prune
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Configure
 | 
			
		||||
          command: |
 | 
			
		||||
            cp .ci/default.yml .config
 | 
			
		||||
            cp .ci/test.yml .config
 | 
			
		||||
            cp .circleci/misskey/default.yml .config
 | 
			
		||||
            cp .circleci/misskey/test.yml .config
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Build
 | 
			
		||||
          command: |
 | 
			
		||||
@@ -50,8 +55,8 @@ jobs:
 | 
			
		||||
          key: npm-v1-arch-{{ arch }}-env-{{ .Environment.variableName }}-package-{{ checksum "package.json" }}-lock-{{ checksum "package-lock.json" }}-ls-{{ checksum "ls" }}
 | 
			
		||||
          paths:
 | 
			
		||||
            - node_modules
 | 
			
		||||
      - store_artifacts:
 | 
			
		||||
          path: built
 | 
			
		||||
#      - store_artifacts:
 | 
			
		||||
#          path: built
 | 
			
		||||
      - persist_to_workspace:
 | 
			
		||||
          root: .
 | 
			
		||||
          paths:
 | 
			
		||||
@@ -97,8 +102,7 @@ jobs:
 | 
			
		||||
      - run:
 | 
			
		||||
          name: Build
 | 
			
		||||
          command: |
 | 
			
		||||
            docker build . | tee docker.log
 | 
			
		||||
            tail -n 1 docker.log | read __Successfully __built tag
 | 
			
		||||
            docker build -t misskey/misskey .
 | 
			
		||||
      - when:
 | 
			
		||||
          condition: <<parameters.with_deploy>>
 | 
			
		||||
          steps:
 | 
			
		||||
@@ -107,7 +111,6 @@ jobs:
 | 
			
		||||
                command: |
 | 
			
		||||
                  if [ "$DOCKERHUB_USERNAME$DOCKERHUB_PASSWORD" ]
 | 
			
		||||
                   then
 | 
			
		||||
                    docker tag $tag misskey/misskey
 | 
			
		||||
                    docker login -u $DOCKERHUB_USERNAME -p $DOCKERHUB_PASSWORD
 | 
			
		||||
                    docker push misskey/misskey
 | 
			
		||||
                   else
 | 
			
		||||
@@ -126,10 +129,13 @@ workflows:
 | 
			
		||||
          without_redis: "true"
 | 
			
		||||
          requires:
 | 
			
		||||
            - build
 | 
			
		||||
      - docker:
 | 
			
		||||
          filters:
 | 
			
		||||
            branches:
 | 
			
		||||
              ignore: master
 | 
			
		||||
              only: master
 | 
			
		||||
#      - docker:
 | 
			
		||||
#          filters:
 | 
			
		||||
#            branches:
 | 
			
		||||
#              ignore: master
 | 
			
		||||
      - docker:
 | 
			
		||||
          with_deploy: "true"
 | 
			
		||||
          filters:
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,3 @@
 | 
			
		||||
maintainer:
 | 
			
		||||
  name: syuilo
 | 
			
		||||
  url: 'https://syuilo.com'
 | 
			
		||||
url: 'http://misskey.local'
 | 
			
		||||
port: 80
 | 
			
		||||
mongodb:
 | 
			
		||||
@@ -1,6 +1,3 @@
 | 
			
		||||
maintainer:
 | 
			
		||||
  name: syuilo
 | 
			
		||||
  url: 'https://syuilo.com'
 | 
			
		||||
url: 'http://misskey.local'
 | 
			
		||||
port: 80
 | 
			
		||||
mongodb:
 | 
			
		||||
@@ -1,13 +1,3 @@
 | 
			
		||||
name: example-instance-name # Name of your instance
 | 
			
		||||
description: example-description # Description of your instance
 | 
			
		||||
 | 
			
		||||
maintainer:
 | 
			
		||||
  name: example-maitainer-name # Your name
 | 
			
		||||
  url: http://example.com/ # Your contact (http or mailto)
 | 
			
		||||
  repository_url: https://github.com/syuilo/misskey # Repository URL
 | 
			
		||||
  feedback_url: https://github.com/syuilo/misskey/issues # Feedback URL (e.g. github issue)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# Final accessible URL seen by a user.
 | 
			
		||||
url: https://example.tld/
 | 
			
		||||
 | 
			
		||||
@@ -25,7 +15,7 @@ url: https://example.tld/
 | 
			
		||||
#   +------+      |+-------------+      +----------------+|
 | 
			
		||||
#                 +---------------------------------------+
 | 
			
		||||
#
 | 
			
		||||
#   You need to setup reverse proxy. (eg. Nginx)
 | 
			
		||||
#   You need to setup reverse proxy. (eg. nginx)
 | 
			
		||||
#   You do not define 'https' section.
 | 
			
		||||
 | 
			
		||||
# Option 2: Standalone
 | 
			
		||||
@@ -60,21 +50,6 @@ mongodb:
 | 
			
		||||
  user: example-misskey-user
 | 
			
		||||
  pass: example-misskey-pass
 | 
			
		||||
 | 
			
		||||
# Drive capacity of a local user (MB)
 | 
			
		||||
localDriveCapacityMb: 256
 | 
			
		||||
 | 
			
		||||
# Drive capacity of a remote user (MB)
 | 
			
		||||
remoteDriveCapacityMb: 8
 | 
			
		||||
 | 
			
		||||
# If enabled:
 | 
			
		||||
#  Server will not cache remote files (Using direct link instead).
 | 
			
		||||
#  You can save your storage.
 | 
			
		||||
#
 | 
			
		||||
#  NOTE:
 | 
			
		||||
#  * Users cannot see remote images when they turn off "Show media from a remote server" setting.
 | 
			
		||||
#  * Since thumbnails are not provided, traffic increases.
 | 
			
		||||
preventCacheRemoteFiles: false
 | 
			
		||||
 | 
			
		||||
drive:
 | 
			
		||||
  storage: 'db'
 | 
			
		||||
 | 
			
		||||
@@ -113,6 +88,10 @@ drive:
 | 
			
		||||
  #   accessKey: XXX
 | 
			
		||||
  #   secretKey: YYY
 | 
			
		||||
 | 
			
		||||
# If enabled:
 | 
			
		||||
#  The first account created is automatically marked as Admin.
 | 
			
		||||
autoAdmin: true
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Below settings are optional
 | 
			
		||||
#
 | 
			
		||||
@@ -129,11 +108,6 @@ drive:
 | 
			
		||||
#  port: 9200
 | 
			
		||||
#  pass: null
 | 
			
		||||
 | 
			
		||||
# reCAPTCHA
 | 
			
		||||
#recaptcha:
 | 
			
		||||
#  site_key: example-site-key
 | 
			
		||||
#  secret_key: example-secret-key
 | 
			
		||||
 | 
			
		||||
# ServiceWorker
 | 
			
		||||
#sw:
 | 
			
		||||
#  # Public key of VAPID
 | 
			
		||||
@@ -142,17 +116,6 @@ drive:
 | 
			
		||||
#  # Private key of VAPID
 | 
			
		||||
#  private_key: example-sw-private-key
 | 
			
		||||
 | 
			
		||||
# Twitter integration
 | 
			
		||||
# You need to set the oauth callback url as : https://<your-misskey-instance>/api/tw/cb
 | 
			
		||||
#twitter:
 | 
			
		||||
#  consumer_key: example-twitter-consumer-key
 | 
			
		||||
#  consumer_secret: example-twitter-consumer-secret-key
 | 
			
		||||
 | 
			
		||||
# Ghost
 | 
			
		||||
# Ghost account is an account used for the purpose of delegating
 | 
			
		||||
# followers when putting users in the list.
 | 
			
		||||
#ghost: user-id-of-your-ghost-account
 | 
			
		||||
 | 
			
		||||
# Clustering
 | 
			
		||||
#clusterLimit: 1
 | 
			
		||||
 | 
			
		||||
@@ -164,6 +127,3 @@ drive:
 | 
			
		||||
#  external: true
 | 
			
		||||
#  engine: http://vinayaka.distsn.org/cgi-bin/vinayaka-user-match-misskey-api.cgi?{{host}}+{{user}}+{{limit}}+{{offset}}
 | 
			
		||||
#  timeout: 300000
 | 
			
		||||
 | 
			
		||||
# Max allowed note text length in charactors
 | 
			
		||||
maxNoteTextLength: 1000
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								.travis.yml
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								.travis.yml
									
									
									
									
									
								
							@@ -1,41 +0,0 @@
 | 
			
		||||
# travis file
 | 
			
		||||
# https://docs.travis-ci.com/user/customizing-the-build
 | 
			
		||||
 | 
			
		||||
notifications:
 | 
			
		||||
  email: false
 | 
			
		||||
 | 
			
		||||
branches:
 | 
			
		||||
  except:
 | 
			
		||||
    - l10n_master
 | 
			
		||||
 | 
			
		||||
language: node_js
 | 
			
		||||
 | 
			
		||||
node_js:
 | 
			
		||||
  - 11.0.0
 | 
			
		||||
 | 
			
		||||
env:
 | 
			
		||||
  - CXX=g++-4.8 NODE_ENV=production
 | 
			
		||||
 | 
			
		||||
addons:
 | 
			
		||||
  apt:
 | 
			
		||||
    sources:
 | 
			
		||||
      - ubuntu-toolchain-r-test
 | 
			
		||||
    packages:
 | 
			
		||||
      - g++-4.8
 | 
			
		||||
 | 
			
		||||
cache:
 | 
			
		||||
  directories:
 | 
			
		||||
    - node_modules
 | 
			
		||||
 | 
			
		||||
services:
 | 
			
		||||
  - mongodb
 | 
			
		||||
  - redis-server
 | 
			
		||||
 | 
			
		||||
before_script:
 | 
			
		||||
  - npm install
 | 
			
		||||
 | 
			
		||||
  # 設定ファイルを配置
 | 
			
		||||
  - cp ./.ci/default.yml ./.config
 | 
			
		||||
  - cp ./.ci/test.yml ./.config
 | 
			
		||||
 | 
			
		||||
  - travis_wait npm run build
 | 
			
		||||
@@ -23,5 +23,5 @@ Please use [Crowdin](https://crowdin.com/project/misskey) for localization.
 | 
			
		||||
* Test codes are located in `/test`.
 | 
			
		||||
 | 
			
		||||
## Continuous integration
 | 
			
		||||
Misskey uses Travis for automated test.
 | 
			
		||||
Configuration files are located in `/.travis`.
 | 
			
		||||
Misskey uses CircleCI for automated test.
 | 
			
		||||
Configuration files are located in `/.circleci`.
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,6 @@
 | 
			
		||||
================================================================
 | 
			
		||||
 | 
			
		||||
[](https://circleci.com/gh/syuilo/misskey)
 | 
			
		||||
[![][travis-badge]][travis-link]
 | 
			
		||||
[![][dependencies-badge]][dependencies-link]
 | 
			
		||||
[](http://makeapullrequest.com)
 | 
			
		||||
 | 
			
		||||
@@ -44,7 +43,7 @@ Easiest way to tell your emotions. Misskey allows you to add various type of rea
 | 
			
		||||
 | 
			
		||||
<h3 align="left">Interface</h3>
 | 
			
		||||
<p align="left">
 | 
			
		||||
No UI fits for everyone. Therefore, Misskey has a highly customizable UI for your taste. You can edit layouts of your timeline, place selectable widgets you can easily move and create your unique home as this place will be your home.
 | 
			
		||||
Highly customizable UI for your taste. We understand no UI fits for everyone. You can edit layouts of your timeline, place selectable widgets you can easily move and create your unique home as this place will be your home.
 | 
			
		||||
</p>
 | 
			
		||||
 | 
			
		||||
---
 | 
			
		||||
@@ -124,8 +123,6 @@ Misskey is an open-source software licensed under the [GNU AGPLv3](LICENSE).
 | 
			
		||||
 | 
			
		||||
[agpl-3.0]:           https://www.gnu.org/licenses/agpl-3.0.en.html
 | 
			
		||||
[agpl-3.0-badge]:     https://img.shields.io/badge/license-AGPL--3.0-444444.svg?style=flat-square
 | 
			
		||||
[travis-link]:        https://travis-ci.org/syuilo/misskey
 | 
			
		||||
[travis-badge]:       http://img.shields.io/travis/syuilo/misskey/master.svg?style=flat-square
 | 
			
		||||
[dependencies-link]:  https://david-dm.org/syuilo/misskey
 | 
			
		||||
[dependencies-badge]: https://img.shields.io/david/syuilo/misskey.svg?style=flat-square
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ adduser --disabled-password --disabled-login misskey
 | 
			
		||||
Please install and setup these softwares:
 | 
			
		||||
 | 
			
		||||
#### Dependencies :package:
 | 
			
		||||
* **[Node.js](https://nodejs.org/en/)**
 | 
			
		||||
* **[Node.js](https://nodejs.org/en/)** >= 10.0.0
 | 
			
		||||
* **[MongoDB](https://www.mongodb.com/)** >= 3.6
 | 
			
		||||
 | 
			
		||||
##### Optional
 | 
			
		||||
@@ -47,11 +47,6 @@ In root :
 | 
			
		||||
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest)
 | 
			
		||||
5. `npm install` Install misskey dependencies.
 | 
			
		||||
 | 
			
		||||
*(optional)* reCAPTCHA tokens
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
If you want to enable reCAPTCHA, you need to generate reCAPTCHA tokens:
 | 
			
		||||
Please visit https://www.google.com/recaptcha/intro/ and generate keys.
 | 
			
		||||
 | 
			
		||||
*(optional)* Generating VAPID keys
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
If you want to enable ServiceWorker, you need to generate VAPID keys:
 | 
			
		||||
@@ -62,13 +57,6 @@ npm install web-push -g
 | 
			
		||||
web-push generate-vapid-keys
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
*(optional)* Create a twitter application
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
If you want to enable the twitter integration, you need to create a twitter app at [https://developer.twitter.com/en/apply/user](https://developer.twitter.com/en/apply/user).
 | 
			
		||||
 | 
			
		||||
In the app you need to set the oauth callback url as : https://misskey-instance/api/tw/cb
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
*5.* Make configuration file
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
1. `cp .config/example.yml .config/default.yml` Copy the `.config/example.yml` and rename it to `default.yml`.
 | 
			
		||||
 
 | 
			
		||||
@@ -22,7 +22,7 @@ adduser --disabled-password --disabled-login misskey
 | 
			
		||||
これらのソフトウェアをインストール・設定してください:
 | 
			
		||||
 | 
			
		||||
#### 依存関係 :package:
 | 
			
		||||
* **[Node.js](https://nodejs.org/en/)**
 | 
			
		||||
* **[Node.js](https://nodejs.org/en/)** (10.0.0以上)
 | 
			
		||||
* **[MongoDB](https://www.mongodb.com/)** (3.6以上)
 | 
			
		||||
 | 
			
		||||
##### オプション
 | 
			
		||||
@@ -53,11 +53,6 @@ adduser --disabled-password --disabled-login misskey
 | 
			
		||||
4. `git checkout $(git tag -l | grep -v 'rc[0-9]*$' | sort -V | tail -n 1)` [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認
 | 
			
		||||
5. `npm install` Misskeyの依存パッケージをインストール
 | 
			
		||||
 | 
			
		||||
*(オプション)* reCAPTCHAトークン
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
reCAPTCHAを有効にする場合、reCAPTCHAトークンを取得する必要があります。
 | 
			
		||||
https://www.google.com/recaptcha/intro/ にアクセスしてトークンを取得してください。
 | 
			
		||||
 | 
			
		||||
*(オプション)* VAPIDキーペアの生成
 | 
			
		||||
----------------------------------------------------------------
 | 
			
		||||
ServiceWorkerを有効にする場合、VAPIDキーペアを生成する必要があります:
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,6 @@ import * as htmlmin from 'gulp-htmlmin';
 | 
			
		||||
const uglifyes = require('uglify-es');
 | 
			
		||||
 | 
			
		||||
const locales = require('./locales');
 | 
			
		||||
import { fa } from './src/misc/fa';
 | 
			
		||||
 | 
			
		||||
const uglify = uglifyComposer(uglifyes, console);
 | 
			
		||||
 | 
			
		||||
@@ -164,8 +163,7 @@ gulp.task('build:client:pug', [
 | 
			
		||||
		gulp.src('./src/client/app/base.pug')
 | 
			
		||||
			.pipe(pug({
 | 
			
		||||
				locals: {
 | 
			
		||||
					themeColor: constants.themeColor,
 | 
			
		||||
					facss: fa.dom.css()
 | 
			
		||||
					themeColor: constants.themeColor
 | 
			
		||||
				}
 | 
			
		||||
			}))
 | 
			
		||||
			.pipe(htmlmin({
 | 
			
		||||
 
 | 
			
		||||
@@ -131,6 +131,7 @@ common:
 | 
			
		||||
  show-full-acct: "ユーザー名のホストを省略しない"
 | 
			
		||||
  reduce-motion: "UIの動きを減らす"
 | 
			
		||||
  this-setting-is-this-device-only: "このデバイスのみ"
 | 
			
		||||
  use-os-default-emojis: "OS標準の絵文字を使用"
 | 
			
		||||
 | 
			
		||||
  do-not-use-in-production: 'これは開発ビルドです。本番環境で使用しないでください。'
 | 
			
		||||
 | 
			
		||||
@@ -417,6 +418,7 @@ common/views/components/signin.vue:
 | 
			
		||||
  signin: "サインイン"
 | 
			
		||||
  or: "または"
 | 
			
		||||
  signin-with-twitter: "Twitterでログイン"
 | 
			
		||||
  signin-with-github: "GitHubでログイン"
 | 
			
		||||
  login-failed: "ログインできませんでした。ユーザー名とパスワードを確認してください。"
 | 
			
		||||
 | 
			
		||||
common/views/components/signup.vue:
 | 
			
		||||
@@ -460,6 +462,14 @@ common/views/components/twitter-setting.vue:
 | 
			
		||||
  connect: "Twitterと接続する"
 | 
			
		||||
  disconnect: "切断する"
 | 
			
		||||
 | 
			
		||||
common/views/components/github-setting.vue:
 | 
			
		||||
  description: "お使いのGitHubアカウントをお使いのMisskeyアカウントに接続しておくと、プロフィールでGitHubアカウント情報が表示されるようになったり、GitHubを用いた便利なサインインを利用できるようになります。"
 | 
			
		||||
  connected-to: "次のGitHubアカウントに接続されています"
 | 
			
		||||
  detail: "詳細..."
 | 
			
		||||
  reconnect: "再接続する"
 | 
			
		||||
  connect: "GitHubと接続する"
 | 
			
		||||
  disconnect: "切断する"
 | 
			
		||||
 | 
			
		||||
common/views/components/uploader.vue:
 | 
			
		||||
  waiting: "待機中"
 | 
			
		||||
 | 
			
		||||
@@ -599,32 +609,6 @@ desktop/views/components/calendar.vue:
 | 
			
		||||
  next: "次の月"
 | 
			
		||||
  go: "クリックして時間遡行"
 | 
			
		||||
 | 
			
		||||
desktop/views/components/charts.vue:
 | 
			
		||||
  title: "チャート"
 | 
			
		||||
  per-day: "1日ごと"
 | 
			
		||||
  per-hour: "1時間ごと"
 | 
			
		||||
  federation: "フェデレーション"
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  network: "ネットワーク"
 | 
			
		||||
  charts:
 | 
			
		||||
    federation-instances: "インスタンスの増減"
 | 
			
		||||
    federation-instances-total: "インスタンスの積算"
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
    remote-notes: "投稿の増減 (リモート)"
 | 
			
		||||
    notes-total: "投稿の積算"
 | 
			
		||||
    users: "ユーザーの増減"
 | 
			
		||||
    users-total: "ユーザーの積算"
 | 
			
		||||
    drive: "ドライブ使用量の増減"
 | 
			
		||||
    drive-total: "ドライブ使用量の積算"
 | 
			
		||||
    drive-files: "ドライブのファイル数の増減"
 | 
			
		||||
    drive-files-total: "ドライブのファイル数の積算"
 | 
			
		||||
    network-requests: "リクエスト"
 | 
			
		||||
    network-time: "応答時間"
 | 
			
		||||
    network-usage: "通信量"
 | 
			
		||||
 | 
			
		||||
desktop/views/components/choose-file-from-drive-window.vue:
 | 
			
		||||
  choose-file: "ファイル選択中"
 | 
			
		||||
  upload: "PCからドライブにファイルをアップロード"
 | 
			
		||||
@@ -963,6 +947,7 @@ common/views/components/api-settings.vue:
 | 
			
		||||
    title: 'APIコンソール'
 | 
			
		||||
    endpoint: 'エンドポイント'
 | 
			
		||||
    parameter: 'パラメータ'
 | 
			
		||||
    credential-info: "「i」パラメータは自動で付与されます。"
 | 
			
		||||
    send: '送信'
 | 
			
		||||
    sending: '応答待ち'
 | 
			
		||||
    response: '結果'
 | 
			
		||||
@@ -1088,10 +1073,49 @@ admin/views/dashboard.vue:
 | 
			
		||||
  instances: "インスタンス"
 | 
			
		||||
  this-instance: "このインスタンス"
 | 
			
		||||
  federated: "連合"
 | 
			
		||||
 | 
			
		||||
admin/views/instance.vue:
 | 
			
		||||
  instance: "インスタンス"
 | 
			
		||||
  instance-name: "インスタンス名"
 | 
			
		||||
  instance-description: "インスタンスの紹介"
 | 
			
		||||
  banner-url: "バナー画像URL"
 | 
			
		||||
  languages: "インスタンスの対象言語"
 | 
			
		||||
  languages-desc: "スペースで区切って複数設定できます。"
 | 
			
		||||
  maintainer-config: "管理者情報"
 | 
			
		||||
  maintainer-name: "管理者名"
 | 
			
		||||
  maintainer-email: "管理者の連絡先"
 | 
			
		||||
  drive-config: "ドライブの設定"
 | 
			
		||||
  cache-remote-files: "リモートのファイルをキャッシュする"
 | 
			
		||||
  cache-remote-files-desc: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクするようになります。そのためサーバーのストレージを節約できますが、プライバシー設定で直リンクを無効にしているユーザーにはファイルが見えなくなったり、サムネイルが生成されないので通信量が増加します。通常はこの設定をオンにしておくことをおすすめします。"
 | 
			
		||||
  local-drive-capacity-mb: "ローカルユーザーひとりあたりのドライブ容量"
 | 
			
		||||
  remote-drive-capacity-mb: "リモートユーザーひとりあたりのドライブ容量"
 | 
			
		||||
  mb: "メガバイト単位"
 | 
			
		||||
  recaptcha-config: "reCAPTCHAの設定"
 | 
			
		||||
  recaptcha-info: "reCAPTCHAを有効にする場合、reCAPTCHAトークンを取得する必要があります。https://www.google.com/recaptcha/intro/ にアクセスしてトークンを取得してください。"
 | 
			
		||||
  enable-recaptcha: "reCAPTCHAを有効にする"
 | 
			
		||||
  recaptcha-site-key: "reCAPTCHA site key"
 | 
			
		||||
  recaptcha-secret-key: "reCAPTCHA secret key"
 | 
			
		||||
  twitter-integration-config: "Twitter連携の設定"
 | 
			
		||||
  twitter-integration-info: "コールバックURLは /api/tw/cb に設定します。"
 | 
			
		||||
  enable-twitter-integration: "Twitter連携を有効にする"
 | 
			
		||||
  twitter-integration-consumer-key: "Consumer key"
 | 
			
		||||
  twitter-integration-consumer-secret: "Consumer secret"
 | 
			
		||||
  github-integration-config: "GitHub連携の設定"
 | 
			
		||||
  github-integration-info: "コールバックURLは /api/gh/cb に設定します。"
 | 
			
		||||
  enable-github-integration: "GitHub連携を有効にする"
 | 
			
		||||
  github-integration-client-id: "Client ID"
 | 
			
		||||
  github-integration-client-secret: "Client secret"
 | 
			
		||||
  proxy-account-config: "プロキシアカウントの設定"
 | 
			
		||||
  proxy-account-info: "プロキシアカウントは、特定の条件下でユーザーのリモートフォローを代行するアカウントです。例えば、ユーザーがリモートユーザーをリストに入れたとき、リストに入れられたユーザーを誰もフォローしていないとアクティビティがサーバーに配達されないため、代わりにプロキシアカウントがフォローするようにします。"
 | 
			
		||||
  proxy-account-username: "プロキシアカウントのユーザー名"
 | 
			
		||||
  proxy-account-username-desc: "プロキシとして使用するアカウントのユーザー名を指定してください。"
 | 
			
		||||
  proxy-account-warn: "アカウントは自動で作られないため、そのユーザー名のアカウントを予め作成しておく必要があります。"
 | 
			
		||||
  max-note-text-length: "投稿の最大文字数"
 | 
			
		||||
  disable-registration: "ユーザー登録の受付を停止する"
 | 
			
		||||
  disable-local-timeline: "ローカルタイムラインを無効にする"
 | 
			
		||||
  invite: "招待"
 | 
			
		||||
  banner-url: "Banner URL"
 | 
			
		||||
  disableRegistration: "Disable new user registration"
 | 
			
		||||
  disableLocalTimeline: "Disable the local timeline"
 | 
			
		||||
  save: "保存"
 | 
			
		||||
  saved: "保存しました"
 | 
			
		||||
 | 
			
		||||
admin/views/charts.vue:
 | 
			
		||||
  title: "チャート"
 | 
			
		||||
@@ -1142,10 +1166,16 @@ admin/views/emoji.vue:
 | 
			
		||||
    aliases-desc: "スペースで区切って複数設定できます。"
 | 
			
		||||
    url: "絵文字画像URL"
 | 
			
		||||
    add: "追加"
 | 
			
		||||
    info: "50KB以下のPNG画像をおすすめします。"
 | 
			
		||||
    added: "絵文字を登録しました"
 | 
			
		||||
  emojis:
 | 
			
		||||
    title: "絵文字一覧"
 | 
			
		||||
    update: "更新"
 | 
			
		||||
    remove: "削除"
 | 
			
		||||
  updated: "更新しました"
 | 
			
		||||
  remove-emoji:
 | 
			
		||||
    are-you-sure: "「$1」を削除しますか?"
 | 
			
		||||
    removed: "削除しました"
 | 
			
		||||
 | 
			
		||||
admin/views/announcements.vue:
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
@@ -1154,6 +1184,10 @@ admin/views/announcements.vue:
 | 
			
		||||
  add: "追加"
 | 
			
		||||
  title: "タイトル"
 | 
			
		||||
  text: "内容"
 | 
			
		||||
  saved: "保存しました"
 | 
			
		||||
  _remove:
 | 
			
		||||
    are-you-sure: "「$1」を削除しますか?"
 | 
			
		||||
    removed: "削除しました"
 | 
			
		||||
 | 
			
		||||
admin/views/hashtags.vue:
 | 
			
		||||
  hided-tags: "Hidden Tags"
 | 
			
		||||
@@ -1173,12 +1207,6 @@ desktop/views/pages/deck/deck.user-column.vue:
 | 
			
		||||
  pinned-notes: "ピン留めされた投稿"
 | 
			
		||||
  push-to-a-list: "リストに追加"
 | 
			
		||||
 | 
			
		||||
desktop/views/pages/stats/stats.vue:
 | 
			
		||||
  all-users: "全てのユーザー"
 | 
			
		||||
  original-users: "このインスタンスのユーザー"
 | 
			
		||||
  all-notes: "全ての投稿"
 | 
			
		||||
  original-notes: "このインスタンスの投稿"
 | 
			
		||||
 | 
			
		||||
desktop/views/pages/welcome.vue:
 | 
			
		||||
  about: "詳しく..."
 | 
			
		||||
  gotit: "わかった"
 | 
			
		||||
@@ -1560,6 +1588,10 @@ mobile/views/pages/settings.vue:
 | 
			
		||||
  twitter-connect: "Twitterアカウントに接続する"
 | 
			
		||||
  twitter-reconnect: "再接続する"
 | 
			
		||||
  twitter-disconnect: "切断する"
 | 
			
		||||
  github: "GitHub連携"
 | 
			
		||||
  github-connect: "GitHubアカウントに接続する"
 | 
			
		||||
  github-reconnect: "再接続する"
 | 
			
		||||
  github-disconnect: "切断する"
 | 
			
		||||
  update: "Misskey Update"
 | 
			
		||||
  version: "バージョン:"
 | 
			
		||||
  latest-version: "最新のバージョン:"
 | 
			
		||||
 
 | 
			
		||||
@@ -186,7 +186,7 @@ common:
 | 
			
		||||
    stack-left: "左に重ねんで!"
 | 
			
		||||
    pop-right: "右に出すで!"
 | 
			
		||||
  dev: "アプリの作成あかんかったわ。もっぺんやってみて。"
 | 
			
		||||
  ai-chan-kawaii: "藍ちゃかわいい"
 | 
			
		||||
  ai-chan-kawaii: "藍ちゃめっさべっぴんさんや"
 | 
			
		||||
auth/views/form.vue:
 | 
			
		||||
  share-access: "<i>{{ app.name }}</i>があんさんのアカウントにアクセスすんのを<b>許可</b>してもええか?"
 | 
			
		||||
  permission-ask: "このアプリは次の権限を要求してんで:"
 | 
			
		||||
@@ -744,7 +744,7 @@ desktop/views/components/settings.vue:
 | 
			
		||||
  apps: "アプリ"
 | 
			
		||||
  mute-and-block: "ミュート/ブロック"
 | 
			
		||||
  blocking: "ブロック"
 | 
			
		||||
  security: "守護神セキュリティ"
 | 
			
		||||
  security: "セキュリティ"
 | 
			
		||||
  signin: "こんな感じでサインインしたらしいで"
 | 
			
		||||
  password: "パスワード"
 | 
			
		||||
  2fa: "二段階認証"
 | 
			
		||||
@@ -873,15 +873,15 @@ common/views/components/mute-and-block.vue:
 | 
			
		||||
  mute-and-block: "ミュートとブロック"
 | 
			
		||||
  mute: "ミュート"
 | 
			
		||||
  block: "ブロック"
 | 
			
		||||
  no-muted-users: "ミュートしているユーザーはいません"
 | 
			
		||||
  no-blocked-users: "ブロックしているユーザーはいません"
 | 
			
		||||
  no-muted-users: "ミュートしとるユーザーはおらんで"
 | 
			
		||||
  no-blocked-users: "ブロックしとるユーザーはおらんで"
 | 
			
		||||
common/views/components/password-settings.vue:
 | 
			
		||||
  reset: "パスワードを変更する"
 | 
			
		||||
  enter-current-password: "現在のパスワードを入力してください"
 | 
			
		||||
  enter-new-password: "新しいパスワードを入力してください"
 | 
			
		||||
  enter-new-password-again: "もう一度新しいパスワードを入力してください"
 | 
			
		||||
  not-match: "新しいパスワードが一致しません"
 | 
			
		||||
  changed: "パスワードを変更しました"
 | 
			
		||||
  reset: "パスワード変える"
 | 
			
		||||
  enter-current-password: "今のパスワードを入れてや"
 | 
			
		||||
  enter-new-password: "こんどのパスワード入れてや"
 | 
			
		||||
  enter-new-password-again: "もっぺん入れてや"
 | 
			
		||||
  not-match: "パスワードがおうとらん"
 | 
			
		||||
  changed: "パスワード変えたわ"
 | 
			
		||||
desktop/views/components/sub-note-content.vue:
 | 
			
		||||
  private: "この投稿は見せられへんわ"
 | 
			
		||||
  deleted: "この投稿なんか無くなってもうたわ"
 | 
			
		||||
@@ -953,7 +953,7 @@ admin/views/index.vue:
 | 
			
		||||
  emoji: "カスタム絵文字"
 | 
			
		||||
  users: "ユーザー"
 | 
			
		||||
  update: "更新"
 | 
			
		||||
  announcements: "お知らせ"
 | 
			
		||||
  announcements: "知っといてや"
 | 
			
		||||
  hashtags: "ハッシュタグ"
 | 
			
		||||
  back-to-misskey: "Misskeyに戻る"
 | 
			
		||||
admin/views/dashboard.vue:
 | 
			
		||||
@@ -962,9 +962,9 @@ admin/views/dashboard.vue:
 | 
			
		||||
  notes: "投稿"
 | 
			
		||||
  drive: "ドライブ"
 | 
			
		||||
  instances: "インスタンス"
 | 
			
		||||
  this-instance: "このインスタンス"
 | 
			
		||||
  this-instance: "ワイのインスタンス"
 | 
			
		||||
  federated: "連合"
 | 
			
		||||
  invite: "招待"
 | 
			
		||||
  invite: "来てや"
 | 
			
		||||
  banner-url: "Banner URL"
 | 
			
		||||
  disableRegistration: "Disable new user registration"
 | 
			
		||||
  disableLocalTimeline: "Disable the local timeline"
 | 
			
		||||
@@ -980,7 +980,7 @@ admin/views/charts.vue:
 | 
			
		||||
  charts:
 | 
			
		||||
    federation-instances: "インスタンスの増減"
 | 
			
		||||
    federation-instances-total: "インスタンスの積算"
 | 
			
		||||
    notes: "投稿の増減 (統合)"
 | 
			
		||||
    notes: "投稿の増減(統合)"
 | 
			
		||||
    local-notes: "投稿の増減 (ローカル)"
 | 
			
		||||
    remote-notes: "投稿の増減 (リモート)"
 | 
			
		||||
    notes-total: "投稿の積算"
 | 
			
		||||
@@ -1387,7 +1387,7 @@ mobile/views/pages/user.vue:
 | 
			
		||||
  mute: "ミュート"
 | 
			
		||||
  unmute: "ミュート解除"
 | 
			
		||||
  block: "ブロック"
 | 
			
		||||
  unblock: "ブロック解除"
 | 
			
		||||
  unblock: "ブロックやめたる"
 | 
			
		||||
mobile/views/pages/user/home.vue:
 | 
			
		||||
  recent-notes: "最近儲かりまっか?"
 | 
			
		||||
  images: "画像"
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										29
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										29
									
								
								package.json
									
									
									
									
									
								
							@@ -1,8 +1,8 @@
 | 
			
		||||
{
 | 
			
		||||
	"name": "misskey",
 | 
			
		||||
	"author": "syuilo <i@syuilo.com>",
 | 
			
		||||
	"version": "10.38.3",
 | 
			
		||||
	"clientVersion": "1.0.11490",
 | 
			
		||||
	"version": "10.44.1",
 | 
			
		||||
	"clientVersion": "1.0.11630",
 | 
			
		||||
	"codename": "nighthike",
 | 
			
		||||
	"main": "./built/index.js",
 | 
			
		||||
	"private": true,
 | 
			
		||||
@@ -20,10 +20,11 @@
 | 
			
		||||
		"format": "gulp format"
 | 
			
		||||
	},
 | 
			
		||||
	"dependencies": {
 | 
			
		||||
		"@fortawesome/fontawesome-svg-core": "1.2.6",
 | 
			
		||||
		"@fortawesome/free-brands-svg-icons": "5.4.1",
 | 
			
		||||
		"@fortawesome/free-regular-svg-icons": "5.4.1",
 | 
			
		||||
		"@fortawesome/free-solid-svg-icons": "5.4.1",
 | 
			
		||||
		"@fortawesome/fontawesome-svg-core": "1.2.8",
 | 
			
		||||
		"@fortawesome/free-brands-svg-icons": "5.5.0",
 | 
			
		||||
		"@fortawesome/free-regular-svg-icons": "5.5.0",
 | 
			
		||||
		"@fortawesome/free-solid-svg-icons": "5.5.0",
 | 
			
		||||
		"@fortawesome/vue-fontawesome": "0.1.2",
 | 
			
		||||
		"@koa/cors": "2.2.2",
 | 
			
		||||
		"@prezzemolo/rap": "0.1.2",
 | 
			
		||||
		"@prezzemolo/zip": "0.0.3",
 | 
			
		||||
@@ -52,7 +53,7 @@
 | 
			
		||||
		"@types/koa-logger": "3.1.1",
 | 
			
		||||
		"@types/koa-mount": "3.0.1",
 | 
			
		||||
		"@types/koa-multer": "1.0.0",
 | 
			
		||||
		"@types/koa-router": "7.0.32",
 | 
			
		||||
		"@types/koa-router": "7.0.33",
 | 
			
		||||
		"@types/koa-send": "4.1.1",
 | 
			
		||||
		"@types/koa-views": "2.0.3",
 | 
			
		||||
		"@types/koa__cors": "2.2.3",
 | 
			
		||||
@@ -62,12 +63,13 @@
 | 
			
		||||
		"@types/mongodb": "3.1.12",
 | 
			
		||||
		"@types/ms": "0.7.30",
 | 
			
		||||
		"@types/node": "10.12.2",
 | 
			
		||||
		"@types/oauth": "0.9.1",
 | 
			
		||||
		"@types/portscanner": "2.1.0",
 | 
			
		||||
		"@types/pug": "2.0.4",
 | 
			
		||||
		"@types/qrcode": "1.3.0",
 | 
			
		||||
		"@types/ratelimiter": "2.1.28",
 | 
			
		||||
		"@types/redis": "2.8.7",
 | 
			
		||||
		"@types/request": "2.48.0",
 | 
			
		||||
		"@types/request": "2.48.1",
 | 
			
		||||
		"@types/request-promise-native": "1.0.15",
 | 
			
		||||
		"@types/rimraf": "2.0.2",
 | 
			
		||||
		"@types/seedrandom": "2.4.27",
 | 
			
		||||
@@ -95,7 +97,6 @@
 | 
			
		||||
		"chai": "4.2.0",
 | 
			
		||||
		"chai-http": "4.2.0",
 | 
			
		||||
		"chalk": "2.4.1",
 | 
			
		||||
		"chart.js": "2.7.3",
 | 
			
		||||
		"commander": "2.19.0",
 | 
			
		||||
		"crc-32": "1.2.0",
 | 
			
		||||
		"css-loader": "1.0.1",
 | 
			
		||||
@@ -112,7 +113,7 @@
 | 
			
		||||
		"eslint-plugin-vue": "4.7.1",
 | 
			
		||||
		"eventemitter3": "3.1.0",
 | 
			
		||||
		"file-loader": "2.0.0",
 | 
			
		||||
		"file-type": "10.3.0",
 | 
			
		||||
		"file-type": "10.4.0",
 | 
			
		||||
		"fuckadblock": "3.2.1",
 | 
			
		||||
		"gulp": "3.9.1",
 | 
			
		||||
		"gulp-cssnano": "2.1.3",
 | 
			
		||||
@@ -173,7 +174,7 @@
 | 
			
		||||
		"promise-sequential": "1.1.1",
 | 
			
		||||
		"pug": "2.0.3",
 | 
			
		||||
		"punycode": "2.1.1",
 | 
			
		||||
		"qrcode": "1.3.0",
 | 
			
		||||
		"qrcode": "1.3.2",
 | 
			
		||||
		"ratelimiter": "3.2.0",
 | 
			
		||||
		"recaptcha-promise": "0.1.3",
 | 
			
		||||
		"reconnecting-websocket": "4.1.10",
 | 
			
		||||
@@ -204,19 +205,17 @@
 | 
			
		||||
		"ts-loader": "5.3.0",
 | 
			
		||||
		"ts-node": "7.0.1",
 | 
			
		||||
		"tslint": "5.10.0",
 | 
			
		||||
		"typescript": "3.1.5",
 | 
			
		||||
		"typescript": "3.1.6",
 | 
			
		||||
		"typescript-eslint-parser": "20.1.1",
 | 
			
		||||
		"uglify-es": "3.3.9",
 | 
			
		||||
		"url-loader": "1.1.2",
 | 
			
		||||
		"uuid": "3.3.2",
 | 
			
		||||
		"v-animate-css": "0.0.2",
 | 
			
		||||
		"vue": "2.5.17",
 | 
			
		||||
		"vue-chartjs": "3.4.0",
 | 
			
		||||
		"vue-color": "2.7.0",
 | 
			
		||||
		"vue-content-loading": "1.5.3",
 | 
			
		||||
		"vue-cropperjs": "2.2.2",
 | 
			
		||||
		"vue-js-modal": "1.3.26",
 | 
			
		||||
		"vue-json-tree-view": "2.1.4",
 | 
			
		||||
		"vue-loader": "15.4.2",
 | 
			
		||||
		"vue-router": "3.0.1",
 | 
			
		||||
		"vue-style-loader": "4.1.2",
 | 
			
		||||
@@ -229,7 +228,7 @@
 | 
			
		||||
		"vuex-persistedstate": "2.5.4",
 | 
			
		||||
		"web-push": "3.3.3",
 | 
			
		||||
		"webfinger.js": "2.6.6",
 | 
			
		||||
		"webpack": "4.23.1",
 | 
			
		||||
		"webpack": "4.25.1",
 | 
			
		||||
		"webpack-cli": "3.1.2",
 | 
			
		||||
		"websocket": "1.0.28",
 | 
			
		||||
		"ws": "6.1.0",
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="cdeuzmsthagexbkpofbmatmugjuvogfb">
 | 
			
		||||
	<ui-card>
 | 
			
		||||
		<div slot="title">%fa:broadcast-tower% %i18n:@announcements%</div>
 | 
			
		||||
		<div slot="title"><fa icon="broadcast-tower"/> %i18n:@announcements%</div>
 | 
			
		||||
		<section v-for="(announcement, i) in announcements" class="fit-top">
 | 
			
		||||
			<ui-input v-model="announcement.title" @change="save">
 | 
			
		||||
				<span>%i18n:@title%</span>
 | 
			
		||||
@@ -10,12 +10,12 @@
 | 
			
		||||
				<span>%i18n:@text%</span>
 | 
			
		||||
			</ui-textarea>
 | 
			
		||||
			<ui-horizon-group>
 | 
			
		||||
				<ui-button @click="save">%fa:save R% %i18n:@save%</ui-button>
 | 
			
		||||
				<ui-button @click="remove(i)">%fa:trash-alt R% %i18n:@remove%</ui-button>
 | 
			
		||||
				<ui-button @click="save()"><fa :icon="['far', 'save']"/> %i18n:@save%</ui-button>
 | 
			
		||||
				<ui-button @click="remove(i)"><fa :icon="['far', 'trash-alt']"/> %i18n:@remove%</ui-button>
 | 
			
		||||
			</ui-horizon-group>
 | 
			
		||||
		</section>
 | 
			
		||||
		<section>
 | 
			
		||||
			<ui-button @click="add">%fa:plus% %i18n:@add%</ui-button>
 | 
			
		||||
			<ui-button @click="add"><fa icon="plus"/> %i18n:@add%</ui-button>
 | 
			
		||||
		</section>
 | 
			
		||||
	</ui-card>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -46,17 +46,36 @@ export default Vue.extend({
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		remove(i) {
 | 
			
		||||
			this.announcements = this.announcements.filter((_, j) => j !== i);
 | 
			
		||||
			this.save();
 | 
			
		||||
			this.$swal({
 | 
			
		||||
				type: 'warning',
 | 
			
		||||
				text: '%i18n:@_remove.are-you-sure%'.replace('$1', this.announcements.find((_, j) => j == i).title),
 | 
			
		||||
				showCancelButton: true
 | 
			
		||||
			}).then(res => {
 | 
			
		||||
				if (!res.value) return;
 | 
			
		||||
				this.announcements = this.announcements.filter((_, j) => j !== i);
 | 
			
		||||
				this.save(true);
 | 
			
		||||
				this.$swal({
 | 
			
		||||
					type: 'success',
 | 
			
		||||
					text: '%i18n:@_remove.removed%'
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		save() {
 | 
			
		||||
		save(silent) {
 | 
			
		||||
			(this as any).api('admin/update-meta', {
 | 
			
		||||
				broadcasts: this.announcements
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Saved` });
 | 
			
		||||
				if (!silent) {
 | 
			
		||||
					this.$swal({
 | 
			
		||||
						type: 'success',
 | 
			
		||||
						text: '%i18n:@saved%'
 | 
			
		||||
					});
 | 
			
		||||
				}
 | 
			
		||||
			}).catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
			
		||||
				this.$swal({
 | 
			
		||||
					type: 'error',
 | 
			
		||||
					text: e
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,10 +3,10 @@
 | 
			
		||||
	<table>
 | 
			
		||||
		<thead>
 | 
			
		||||
			<tr>
 | 
			
		||||
				<th>%fa:exchange-alt% In/Out</th>
 | 
			
		||||
				<th>%fa:server% Host</th>
 | 
			
		||||
				<th>%fa:bolt% Activity</th>
 | 
			
		||||
				<th>%fa:user% Actor</th>
 | 
			
		||||
				<th><fa icon="exchange-alt"/> In/Out</th>
 | 
			
		||||
				<th><fa icon="server"/> Host</th>
 | 
			
		||||
				<th><fa icon="bolt"/> Activity</th>
 | 
			
		||||
				<th><fa icon="user"/> Actor</th>
 | 
			
		||||
			</tr>
 | 
			
		||||
		</thead>
 | 
			
		||||
		<tbody>
 | 
			
		||||
@@ -63,9 +63,9 @@ export default Vue.extend({
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.hyhctythnmwihguaaapnbrbszsjqxpio
 | 
			
		||||
	display block
 | 
			
		||||
	padding 16px
 | 
			
		||||
	padding 12px 16px 16px 16px
 | 
			
		||||
	height 250px
 | 
			
		||||
	overflow auto
 | 
			
		||||
	overflow hidden
 | 
			
		||||
	box-shadow 0 2px 4px rgba(0, 0, 0, 0.1)
 | 
			
		||||
	background var(--adminDashboardCardBg)
 | 
			
		||||
	border-radius 8px
 | 
			
		||||
@@ -77,10 +77,10 @@ export default Vue.extend({
 | 
			
		||||
		border-spacing 0
 | 
			
		||||
		border-collapse collapse
 | 
			
		||||
		color var(--adminDashboardCardFg)
 | 
			
		||||
		font-size 15px
 | 
			
		||||
		font-size 14px
 | 
			
		||||
 | 
			
		||||
		thead
 | 
			
		||||
			border-bottom solid 2px var(--adminDashboardCardDivider)
 | 
			
		||||
			border-bottom solid 1px var(--adminDashboardCardDivider)
 | 
			
		||||
 | 
			
		||||
			tr
 | 
			
		||||
				th
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="qvgidhudpqhjttdhxubzuyrhyzgslujw">
 | 
			
		||||
	<header>
 | 
			
		||||
		<b>%fa:chart-bar R% %i18n:@title%:</b>
 | 
			
		||||
		<b><fa :icon="['far', 'chart-bar']"/> %i18n:@title%:</b>
 | 
			
		||||
		<select v-model="src">
 | 
			
		||||
			<optgroup label="%i18n:@federation%">
 | 
			
		||||
				<option value="federation-instances">%i18n:@charts.federation-instances%</option>
 | 
			
		||||
@@ -274,12 +274,15 @@ export default Vue.extend({
 | 
			
		||||
			return {
 | 
			
		||||
				series: [{
 | 
			
		||||
					name: 'Combined',
 | 
			
		||||
					type: 'line',
 | 
			
		||||
					data: this.format(sum(this.stats.notes.local.total, this.stats.notes.remote.total))
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Local',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.notes.local.total)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Remote',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.notes.remote.total)
 | 
			
		||||
				}]
 | 
			
		||||
			};
 | 
			
		||||
@@ -289,18 +292,21 @@ export default Vue.extend({
 | 
			
		||||
			return {
 | 
			
		||||
				series: [{
 | 
			
		||||
					name: 'Combined',
 | 
			
		||||
					type: 'line',
 | 
			
		||||
					data: this.format(total
 | 
			
		||||
						? sum(this.stats.users.local.total, this.stats.users.remote.total)
 | 
			
		||||
						: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec), this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
 | 
			
		||||
					)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Local',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(total
 | 
			
		||||
						? this.stats.users.local.total
 | 
			
		||||
						: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec))
 | 
			
		||||
					)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Remote',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(total
 | 
			
		||||
						? this.stats.users.remote.total
 | 
			
		||||
						: sum(this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
 | 
			
		||||
@@ -314,6 +320,7 @@ export default Vue.extend({
 | 
			
		||||
				bytes: true,
 | 
			
		||||
				series: [{
 | 
			
		||||
					name: 'All',
 | 
			
		||||
					type: 'line',
 | 
			
		||||
					data: this.format(
 | 
			
		||||
						sum(
 | 
			
		||||
							this.stats.drive.local.incSize,
 | 
			
		||||
@@ -324,15 +331,19 @@ export default Vue.extend({
 | 
			
		||||
					)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Local +',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.drive.local.incSize)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Local -',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(negate(this.stats.drive.local.decSize))
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Remote +',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.drive.remote.incSize)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Remote -',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(negate(this.stats.drive.remote.decSize))
 | 
			
		||||
				}]
 | 
			
		||||
			};
 | 
			
		||||
@@ -343,12 +354,15 @@ export default Vue.extend({
 | 
			
		||||
				bytes: true,
 | 
			
		||||
				series: [{
 | 
			
		||||
					name: 'Combined',
 | 
			
		||||
					type: 'line',
 | 
			
		||||
					data: this.format(sum(this.stats.drive.local.totalSize, this.stats.drive.remote.totalSize))
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Local',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.drive.local.totalSize)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Remote',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.drive.remote.totalSize)
 | 
			
		||||
				}]
 | 
			
		||||
			};
 | 
			
		||||
@@ -358,6 +372,7 @@ export default Vue.extend({
 | 
			
		||||
			return {
 | 
			
		||||
				series: [{
 | 
			
		||||
					name: 'All',
 | 
			
		||||
					type: 'line',
 | 
			
		||||
					data: this.format(
 | 
			
		||||
						sum(
 | 
			
		||||
							this.stats.drive.local.incCount,
 | 
			
		||||
@@ -368,15 +383,19 @@ export default Vue.extend({
 | 
			
		||||
					)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Local +',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.drive.local.incCount)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Local -',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(negate(this.stats.drive.local.decCount))
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Remote +',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.drive.remote.incCount)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Remote -',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(negate(this.stats.drive.remote.decCount))
 | 
			
		||||
				}]
 | 
			
		||||
			};
 | 
			
		||||
@@ -386,12 +405,15 @@ export default Vue.extend({
 | 
			
		||||
			return {
 | 
			
		||||
				series: [{
 | 
			
		||||
					name: 'Combined',
 | 
			
		||||
					type: 'line',
 | 
			
		||||
					data: this.format(sum(this.stats.drive.local.totalCount, this.stats.drive.remote.totalCount))
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Local',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.drive.local.totalCount)
 | 
			
		||||
				}, {
 | 
			
		||||
					name: 'Remote',
 | 
			
		||||
					type: 'area',
 | 
			
		||||
					data: this.format(this.stats.drive.remote.totalCount)
 | 
			
		||||
				}]
 | 
			
		||||
			};
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,14 @@
 | 
			
		||||
<div class="zyknedwtlthezamcjlolyusmipqmjgxz">
 | 
			
		||||
	<div>
 | 
			
		||||
		<header>
 | 
			
		||||
			<span>%fa:microchip% CPU <span>{{ cpuP }}%</span></span>
 | 
			
		||||
			<span><fa icon="microchip"/> CPU <span>{{ cpuP }}%</span></span>
 | 
			
		||||
			<span v-if="meta">{{ meta.cpu.model }}</span>
 | 
			
		||||
		</header>
 | 
			
		||||
		<div ref="cpu"></div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div>
 | 
			
		||||
		<header>
 | 
			
		||||
			<span>%fa:memory% MEM <span>{{ memP }}%</span></span>
 | 
			
		||||
			<span><fa icon="memory"/> MEM <span>{{ memP }}%</span></span>
 | 
			
		||||
			<span v-if="meta"></span>
 | 
			
		||||
		</header>
 | 
			
		||||
		<div ref="mem"></div>
 | 
			
		||||
 
 | 
			
		||||
@@ -11,54 +11,54 @@
 | 
			
		||||
	<div v-if="stats" class="stats">
 | 
			
		||||
		<div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<div>%fa:user%</div>
 | 
			
		||||
				<div><fa icon="user"/></div>
 | 
			
		||||
				<div>
 | 
			
		||||
					<span>%i18n:@accounts%</span>
 | 
			
		||||
					<b class="primary">{{ stats.originalUsersCount | number }}</b>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span>%fa:home% %i18n:@this-instance%</span>
 | 
			
		||||
				<span @click="setChartSrc('users')">%fa:chart-bar R%</span>
 | 
			
		||||
				<span><fa icon="home"/> %i18n:@this-instance%</span>
 | 
			
		||||
				<span @click="setChartSrc('users')"><fa :icon="['far', 'chart-bar']"/></span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<div>%fa:pencil-alt%</div>
 | 
			
		||||
				<div><fa icon="pencil-alt"/></div>
 | 
			
		||||
				<div>
 | 
			
		||||
					<span>%i18n:@notes%</span>
 | 
			
		||||
					<b class="primary">{{ stats.originalNotesCount | number }}</b>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span>%fa:home% %i18n:@this-instance%</span>
 | 
			
		||||
				<span @click="setChartSrc('notes')">%fa:chart-bar R%</span>
 | 
			
		||||
				<span><fa icon="home"/> %i18n:@this-instance%</span>
 | 
			
		||||
				<span @click="setChartSrc('notes')"><fa :icon="['far', 'chart-bar']"/></span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<div>%fa:database%</div>
 | 
			
		||||
				<div><fa icon="database"/></div>
 | 
			
		||||
				<div>
 | 
			
		||||
					<span>%i18n:@drive%</span>
 | 
			
		||||
					<b>{{ stats.driveUsageLocal | bytes }}</b>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span>%fa:home% %i18n:@this-instance%</span>
 | 
			
		||||
				<span @click="setChartSrc('drive')">%fa:chart-bar R%</span>
 | 
			
		||||
				<span><fa icon="home"/> %i18n:@this-instance%</span>
 | 
			
		||||
				<span @click="setChartSrc('drive')"><fa :icon="['far', 'chart-bar']"/></span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<div>%fa:hdd R%</div>
 | 
			
		||||
				<div><fa :icon="['far', 'hdd']"/></div>
 | 
			
		||||
				<div>
 | 
			
		||||
					<span>%i18n:@instances%</span>
 | 
			
		||||
					<b>{{ stats.instances | number }}</b>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span>%fa:globe% %i18n:@federated%</span>
 | 
			
		||||
				<span @click="setChartSrc('federation-instances-total')">%fa:chart-bar R%</span>
 | 
			
		||||
				<span><fa icon="globe"/> %i18n:@federated%</span>
 | 
			
		||||
				<span @click="setChartSrc('federation-instances-total')"><fa :icon="['far', 'chart-bar']"/></span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
@@ -136,13 +136,16 @@ export default Vue.extend({
 | 
			
		||||
		border-bottom solid 1px var(--adminDashboardHeaderBorder)
 | 
			
		||||
		color var(--adminDashboardHeaderFg)
 | 
			
		||||
		font-size 14px
 | 
			
		||||
		white-space nowrap
 | 
			
		||||
 | 
			
		||||
		@media (max-width 1000px)
 | 
			
		||||
			display none
 | 
			
		||||
 | 
			
		||||
		> p
 | 
			
		||||
			display inline
 | 
			
		||||
			display block
 | 
			
		||||
			margin 0 32px 0 0
 | 
			
		||||
			overflow hidden
 | 
			
		||||
			text-overflow ellipsis
 | 
			
		||||
 | 
			
		||||
			> b
 | 
			
		||||
				&:after
 | 
			
		||||
 
 | 
			
		||||
@@ -1,45 +1,46 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="tumhkfkmgtvzljezfvmgkeurkfncshbe">
 | 
			
		||||
	<ui-card>
 | 
			
		||||
		<div slot="title">%fa:plus% %i18n:@add-emoji.title%</div>
 | 
			
		||||
		<div slot="title"><fa icon="plus"/> %i18n:@add-emoji.title%</div>
 | 
			
		||||
		<section class="fit-top">
 | 
			
		||||
			<ui-horizon-group inputs>
 | 
			
		||||
				<ui-input v-model="name">
 | 
			
		||||
					<span>%i18n:@add-emoji.name%</span>
 | 
			
		||||
					<span slot="text">%i18n:@add-emoji.name-desc%</span>
 | 
			
		||||
					<span slot="desc">%i18n:@add-emoji.name-desc%</span>
 | 
			
		||||
				</ui-input>
 | 
			
		||||
				<ui-input v-model="aliases">
 | 
			
		||||
					<span>%i18n:@add-emoji.aliases%</span>
 | 
			
		||||
					<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
 | 
			
		||||
					<span slot="desc">%i18n:@add-emoji.aliases-desc%</span>
 | 
			
		||||
				</ui-input>
 | 
			
		||||
			</ui-horizon-group>
 | 
			
		||||
			<ui-input v-model="url">
 | 
			
		||||
				<i slot="icon"><fa icon="link"/></i>
 | 
			
		||||
				<span>%i18n:@add-emoji.url%</span>
 | 
			
		||||
			</ui-input>
 | 
			
		||||
			<ui-info>%i18n:@add-emoji.info%</ui-info>
 | 
			
		||||
			<ui-button @click="add">%i18n:@add-emoji.add%</ui-button>
 | 
			
		||||
		</section>
 | 
			
		||||
	</ui-card>
 | 
			
		||||
 | 
			
		||||
	<ui-card>
 | 
			
		||||
		<div slot="title">%fa:grin R% %i18n:@emojis.title%</div>
 | 
			
		||||
		<div slot="title"><fa :icon="['far', 'grin']"/> %i18n:@emojis.title%</div>
 | 
			
		||||
		<section v-for="emoji in emojis">
 | 
			
		||||
			<img :src="emoji.url" :alt="emoji.name" style="width: 64px;"/>
 | 
			
		||||
			<ui-horizon-group inputs>
 | 
			
		||||
				<ui-input v-model="emoji.name">
 | 
			
		||||
					<span>%i18n:@add-emoji.name%</span>
 | 
			
		||||
					<span slot="text">%i18n:@add-emoji.name-desc%</span>
 | 
			
		||||
				</ui-input>
 | 
			
		||||
				<ui-input v-model="emoji.aliases">
 | 
			
		||||
					<span>%i18n:@add-emoji.aliases%</span>
 | 
			
		||||
					<span slot="text">%i18n:@add-emoji.aliases-desc%</span>
 | 
			
		||||
				</ui-input>
 | 
			
		||||
			</ui-horizon-group>
 | 
			
		||||
			<ui-input v-model="emoji.url">
 | 
			
		||||
				<i slot="icon"><fa icon="link"/></i>
 | 
			
		||||
				<span>%i18n:@add-emoji.url%</span>
 | 
			
		||||
			</ui-input>
 | 
			
		||||
			<ui-horizon-group>
 | 
			
		||||
				<ui-button @click="updateEmoji(emoji)">%fa:save R% %i18n:@emojis.update%</ui-button>
 | 
			
		||||
				<ui-button @click="removeEmoji(emoji)">%fa:trash-alt R% %i18n:@emojis.remove%</ui-button>
 | 
			
		||||
				<ui-button @click="updateEmoji(emoji)"><fa :icon="['far', 'save']"/> %i18n:@emojis.update%</ui-button>
 | 
			
		||||
				<ui-button @click="removeEmoji(emoji)"><fa :icon="['far', 'trash-alt']"/> %i18n:@emojis.remove%</ui-button>
 | 
			
		||||
			</ui-horizon-group>
 | 
			
		||||
		</section>
 | 
			
		||||
	</ui-card>
 | 
			
		||||
@@ -68,17 +69,24 @@ export default Vue.extend({
 | 
			
		||||
			(this as any).api('admin/emoji/add', {
 | 
			
		||||
				name: this.name,
 | 
			
		||||
				url: this.url,
 | 
			
		||||
				aliases: this.aliases.split(' ')
 | 
			
		||||
				aliases: this.aliases.split(' ').filter(x => x.length > 0)
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Added` });
 | 
			
		||||
				this.$swal({
 | 
			
		||||
					type: 'success',
 | 
			
		||||
					text: '%i18n:@add-emoji.added%'
 | 
			
		||||
				});
 | 
			
		||||
				this.fetchEmojis();
 | 
			
		||||
			}).catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
			
		||||
				this.$swal({
 | 
			
		||||
					type: 'error',
 | 
			
		||||
					text: e
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		fetchEmojis() {
 | 
			
		||||
			(this as any).api('admin/emoji/list').then(emojis => {
 | 
			
		||||
				emojis.reverse();
 | 
			
		||||
				emojis.forEach(e => e.aliases = (e.aliases || []).join(' '));
 | 
			
		||||
				this.emojis = emojis;
 | 
			
		||||
			});
 | 
			
		||||
@@ -89,22 +97,42 @@ export default Vue.extend({
 | 
			
		||||
				id: emoji.id,
 | 
			
		||||
				name: emoji.name,
 | 
			
		||||
				url: emoji.url,
 | 
			
		||||
				aliases: emoji.aliases.split(' ')
 | 
			
		||||
				aliases: emoji.aliases.split(' ').filter(x => x.length > 0)
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Updated` });
 | 
			
		||||
				this.$swal({
 | 
			
		||||
					type: 'success',
 | 
			
		||||
					text: '%i18n:@updated%'
 | 
			
		||||
				});
 | 
			
		||||
			}).catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
			
		||||
				this.$swal({
 | 
			
		||||
					type: 'error',
 | 
			
		||||
					text: e
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		removeEmoji(emoji) {
 | 
			
		||||
			(this as any).api('admin/emoji/remove', {
 | 
			
		||||
				id: emoji.id
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Removed` });
 | 
			
		||||
				this.fetchEmojis();
 | 
			
		||||
			}).catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
			
		||||
			this.$swal({
 | 
			
		||||
				type: 'warning',
 | 
			
		||||
				text: '%i18n:@remove-emoji.are-you-sure%'.replace('$1', emoji.name),
 | 
			
		||||
				showCancelButton: true
 | 
			
		||||
			}).then(res => {
 | 
			
		||||
				if (!res.value) return;
 | 
			
		||||
 | 
			
		||||
				(this as any).api('admin/emoji/remove', {
 | 
			
		||||
					id: emoji.id
 | 
			
		||||
				}).then(() => {
 | 
			
		||||
					this.$swal({
 | 
			
		||||
						type: 'success',
 | 
			
		||||
						text: '%i18n:@remove-emoji.removed%'
 | 
			
		||||
					});
 | 
			
		||||
					this.fetchEmojis();
 | 
			
		||||
				}).catch(e => {
 | 
			
		||||
					this.$swal({
 | 
			
		||||
						type: 'error',
 | 
			
		||||
						text: e
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,9 +29,9 @@ export default Vue.extend({
 | 
			
		||||
			(this as any).api('admin/update-meta', {
 | 
			
		||||
				hidedTags: this.hidedTags.split('\n')
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Saved` });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: `Saved` });
 | 
			
		||||
			}).catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-admin" :class="{ isMobile }">
 | 
			
		||||
	<header v-show="isMobile">
 | 
			
		||||
		<button class="nav" @click="navOpend = true">%fa:bars%</button>
 | 
			
		||||
		<button class="nav" @click="navOpend = true"><fa icon="bars"/></button>
 | 
			
		||||
		<span>MisskeyMyAdmin</span>
 | 
			
		||||
	</header>
 | 
			
		||||
	<div class="nav-backdrop"
 | 
			
		||||
@@ -18,30 +18,30 @@
 | 
			
		||||
			<p class="name">{{ $store.state.i | userName }}</p>
 | 
			
		||||
		</div>
 | 
			
		||||
		<ul>
 | 
			
		||||
			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }">%fa:home .fw%%i18n:@dashboard%</li>
 | 
			
		||||
			<li @click="nav('instance')" :class="{ active: page == 'instance' }">%fa:cog .fw%%i18n:@instance%</li>
 | 
			
		||||
			<li @click="nav('users')" :class="{ active: page == 'users' }">%fa:users .fw%%i18n:@users%</li>
 | 
			
		||||
			<li @click="nav('emoji')" :class="{ active: page == 'emoji' }">%fa:grin R .fw%%i18n:@emoji%</li>
 | 
			
		||||
			<li @click="nav('announcements')" :class="{ active: page == 'announcements' }">%fa:broadcast-tower .fw%%i18n:@announcements%</li>
 | 
			
		||||
			<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }">%fa:hashtag .fw%%i18n:@hashtags%</li>
 | 
			
		||||
			<li @click="nav('dashboard')" :class="{ active: page == 'dashboard' }"><fa icon="home" fixed-width/>%i18n:@dashboard%</li>
 | 
			
		||||
			<li @click="nav('instance')" :class="{ active: page == 'instance' }"><fa icon="cog" fixed-width/>%i18n:@instance%</li>
 | 
			
		||||
			<li @click="nav('users')" :class="{ active: page == 'users' }"><fa icon="users" fixed-width/>%i18n:@users%</li>
 | 
			
		||||
			<li @click="nav('emoji')" :class="{ active: page == 'emoji' }"><fa :icon="['far', 'grin']" fixed-width/>%i18n:@emoji%</li>
 | 
			
		||||
			<li @click="nav('announcements')" :class="{ active: page == 'announcements' }"><fa icon="broadcast-tower" fixed-width/>%i18n:@announcements%</li>
 | 
			
		||||
			<li @click="nav('hashtags')" :class="{ active: page == 'hashtags' }"><fa icon="hashtag" fixed-width/>%i18n:@hashtags%</li>
 | 
			
		||||
 | 
			
		||||
			<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }">%fa:cloud .fw%%i18n:common.drive%</li> -->
 | 
			
		||||
			<!-- <li @click="nav('drive')" :class="{ active: page == 'drive' }"><fa icon="cloud" fixed-width/>%i18n:common.drive%</li> -->
 | 
			
		||||
			<!-- <li @click="nav('update')" :class="{ active: page == 'update' }">%i18n:@update%</li> -->
 | 
			
		||||
		</ul>
 | 
			
		||||
		<div class="back-to-misskey">
 | 
			
		||||
			<a href="/">%fa:arrow-left% %i18n:@back-to-misskey%</a>
 | 
			
		||||
			<a href="/"><fa icon="arrow-left"/> %i18n:@back-to-misskey%</a>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="version">
 | 
			
		||||
			<small>Misskey {{ version }}</small>
 | 
			
		||||
		</div>
 | 
			
		||||
	</nav>
 | 
			
		||||
	<main>
 | 
			
		||||
		<div v-show="page == 'dashboard'"><x-dashboard/></div>
 | 
			
		||||
		<div v-show="page == 'instance'"><x-instance/></div>
 | 
			
		||||
		<div v-if="page == 'dashboard'"><x-dashboard/></div>
 | 
			
		||||
		<div v-if="page == 'instance'"><x-instance/></div>
 | 
			
		||||
		<div v-if="page == 'users'"><x-users/></div>
 | 
			
		||||
		<div v-show="page == 'emoji'"><x-emoji/></div>
 | 
			
		||||
		<div v-show="page == 'announcements'"><x-announcements/></div>
 | 
			
		||||
		<div v-show="page == 'hashtags'"><x-hashtags/></div>
 | 
			
		||||
		<div v-if="page == 'emoji'"><x-emoji/></div>
 | 
			
		||||
		<div v-if="page == 'announcements'"><x-announcements/></div>
 | 
			
		||||
		<div v-if="page == 'hashtags'"><x-hashtags/></div>
 | 
			
		||||
		<div v-if="page == 'drive'"></div>
 | 
			
		||||
		<div v-if="page == 'update'"></div>
 | 
			
		||||
	</main>
 | 
			
		||||
@@ -126,7 +126,7 @@ export default Vue.extend({
 | 
			
		||||
			line-height $headerHeight
 | 
			
		||||
			border-right solid 1px rgba(#000, 0.1)
 | 
			
		||||
 | 
			
		||||
			> [data-fa]
 | 
			
		||||
			> [data-icon]
 | 
			
		||||
				transition all 0.2s ease
 | 
			
		||||
 | 
			
		||||
	> nav
 | 
			
		||||
@@ -188,7 +188,7 @@ export default Vue.extend({
 | 
			
		||||
				&:hover
 | 
			
		||||
					color #fff
 | 
			
		||||
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 6px
 | 
			
		||||
 | 
			
		||||
		> .version
 | 
			
		||||
@@ -218,7 +218,7 @@ export default Vue.extend({
 | 
			
		||||
				&:hover
 | 
			
		||||
					color #fff
 | 
			
		||||
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 6px
 | 
			
		||||
 | 
			
		||||
				&.active
 | 
			
		||||
 
 | 
			
		||||
@@ -1,26 +1,78 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="axbwjelsbymowqjyywpirzhdlszoncqs">
 | 
			
		||||
	<ui-card>
 | 
			
		||||
		<div slot="title">%i18n:@banner-url%</div>
 | 
			
		||||
		<section class="fit-top">
 | 
			
		||||
			<ui-input v-model="bannerUrl"/>
 | 
			
		||||
		<div slot="title"><fa icon="cog"/> %i18n:@instance%</div>
 | 
			
		||||
		<section class="fit-top fit-bottom">
 | 
			
		||||
			<ui-input v-model="name">%i18n:@instance-name%</ui-input>
 | 
			
		||||
			<ui-textarea v-model="description">%i18n:@instance-description%</ui-textarea>
 | 
			
		||||
			<ui-input v-model="bannerUrl"><i slot="icon"><fa icon="link"/></i>%i18n:@banner-url%</ui-input>
 | 
			
		||||
			<ui-input v-model="languages"><i slot="icon"><fa icon="language"/></i>%i18n:@languages%<span slot="desc">%i18n:@languages-desc%</span></ui-input>
 | 
			
		||||
		</section>
 | 
			
		||||
		<section class="fit-bottom">
 | 
			
		||||
			<header><fa icon="headset"/> %i18n:@maintainer-config%</header>
 | 
			
		||||
			<ui-input v-model="maintainerName">%i18n:@maintainer-name%</ui-input>
 | 
			
		||||
			<ui-input v-model="maintainerEmail" type="email"><i slot="icon"><fa :icon="['far', 'envelope']"/></i>%i18n:@maintainer-email%</ui-input>
 | 
			
		||||
		</section>
 | 
			
		||||
		<section class="fit-top fit-bottom">
 | 
			
		||||
			<ui-input v-model="maxNoteTextLength">%i18n:@max-note-text-length%</ui-input>
 | 
			
		||||
		</section>
 | 
			
		||||
		<section class="fit-bottom">
 | 
			
		||||
			<header><fa icon="cloud"/> %i18n:@drive-config%</header>
 | 
			
		||||
			<ui-switch v-model="cacheRemoteFiles">%i18n:@cache-remote-files%<span slot="desc">%i18n:@cache-remote-files-desc%</span></ui-switch>
 | 
			
		||||
			<ui-input v-model="localDriveCapacityMb">%i18n:@local-drive-capacity-mb%<span slot="suffix">MB</span><span slot="desc">%i18n:@mb%</span></ui-input>
 | 
			
		||||
			<ui-input v-model="remoteDriveCapacityMb" :disabled="!cacheRemoteFiles">%i18n:@remote-drive-capacity-mb%<span slot="suffix">MB</span><span slot="desc">%i18n:@mb%</span></ui-input>
 | 
			
		||||
		</section>
 | 
			
		||||
		<section class="fit-bottom">
 | 
			
		||||
			<header><fa icon="shield-alt"/> %i18n:@recaptcha-config%</header>
 | 
			
		||||
			<ui-switch v-model="enableRecaptcha">%i18n:@enable-recaptcha%</ui-switch>
 | 
			
		||||
			<ui-info>%i18n:@recaptcha-info%</ui-info>
 | 
			
		||||
			<ui-input v-model="recaptchaSiteKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>%i18n:@recaptcha-site-key%</ui-input>
 | 
			
		||||
			<ui-input v-model="recaptchaSecretKey" :disabled="!enableRecaptcha"><i slot="icon"><fa icon="key"/></i>%i18n:@recaptcha-secret-key%</ui-input>
 | 
			
		||||
		</section>
 | 
			
		||||
		<section>
 | 
			
		||||
			<header><fa icon="ghost"/> %i18n:@proxy-account-config%</header>
 | 
			
		||||
			<ui-info>%i18n:@proxy-account-info%</ui-info>
 | 
			
		||||
			<ui-input v-model="proxyAccount"><span slot="prefix">@</span>%i18n:@proxy-account-username%<span slot="desc">%i18n:@proxy-account-username-desc%</span></ui-input>
 | 
			
		||||
			<ui-info warn>%i18n:@proxy-account-warn%</ui-info>
 | 
			
		||||
		</section>
 | 
			
		||||
		<section>
 | 
			
		||||
			<ui-switch v-model="disableRegistration">%i18n:@disable-registration%</ui-switch>
 | 
			
		||||
		</section>
 | 
			
		||||
		<section>
 | 
			
		||||
			<ui-switch v-model="disableLocalTimeline">%i18n:@disable-local-timeline%</ui-switch>
 | 
			
		||||
		</section>
 | 
			
		||||
		<section>
 | 
			
		||||
			<ui-button @click="updateMeta">%i18n:@save%</ui-button>
 | 
			
		||||
		</section>
 | 
			
		||||
	</ui-card>
 | 
			
		||||
 | 
			
		||||
	<ui-card>
 | 
			
		||||
		<div slot="title">%i18n:@disable-registration%</div>
 | 
			
		||||
		<div slot="title">%i18n:@invite%</div>
 | 
			
		||||
		<section>
 | 
			
		||||
			<input type="checkbox" v-model="disableRegistration" @change="updateMeta">
 | 
			
		||||
			<button class="ui" @click="invite">%i18n:@invite%</button>
 | 
			
		||||
			<ui-button @click="invite">%i18n:@invite%</ui-button>
 | 
			
		||||
			<p v-if="inviteCode">Code: <code>{{ inviteCode }}</code></p>
 | 
			
		||||
		</section>
 | 
			
		||||
	</ui-card>
 | 
			
		||||
 | 
			
		||||
	<ui-card>
 | 
			
		||||
		<div slot="title">%i18n:@disable-local-timeline%</div>
 | 
			
		||||
		<div slot="title"><fa :icon="['fab', 'twitter']"/> %i18n:@twitter-integration-config%</div>
 | 
			
		||||
		<section>
 | 
			
		||||
			<input type="checkbox" v-model="disableLocalTimeline" @change="updateMeta">
 | 
			
		||||
			<ui-switch v-model="enableTwitterIntegration">%i18n:@enable-twitter-integration%</ui-switch>
 | 
			
		||||
			<ui-info>%i18n:@twitter-integration-info%</ui-info>
 | 
			
		||||
			<ui-input v-model="twitterConsumerKey" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@twitter-integration-consumer-key%</ui-input>
 | 
			
		||||
			<ui-input v-model="twitterConsumerSecret" :disabled="!enableTwitterIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@twitter-integration-consumer-secret%</ui-input>
 | 
			
		||||
			<ui-button @click="updateMeta">%i18n:@save%</ui-button>
 | 
			
		||||
		</section>
 | 
			
		||||
	</ui-card>
 | 
			
		||||
 | 
			
		||||
	<ui-card>
 | 
			
		||||
		<div slot="title"><fa :icon="['fab', 'github']"/> %i18n:@github-integration-config%</div>
 | 
			
		||||
		<section>
 | 
			
		||||
			<ui-switch v-model="enableGithubIntegration">%i18n:@enable-github-integration%</ui-switch>
 | 
			
		||||
			<ui-info>%i18n:@github-integration-info%</ui-info>
 | 
			
		||||
			<ui-input v-model="githubClientId" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@github-integration-client-id%</ui-input>
 | 
			
		||||
			<ui-input v-model="githubClientSecret" :disabled="!enableGithubIntegration"><i slot="icon"><fa icon="key"/></i>%i18n:@github-integration-client-secret%</ui-input>
 | 
			
		||||
			<ui-button @click="updateMeta">%i18n:@save%</ui-button>
 | 
			
		||||
		</section>
 | 
			
		||||
	</ui-card>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -32,29 +84,103 @@ import Vue from "vue";
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			maintainerName: null,
 | 
			
		||||
			maintainerEmail: null,
 | 
			
		||||
			disableRegistration: false,
 | 
			
		||||
			disableLocalTimeline: false,
 | 
			
		||||
			bannerUrl: null,
 | 
			
		||||
			name: null,
 | 
			
		||||
			description: null,
 | 
			
		||||
			languages: null,
 | 
			
		||||
			cacheRemoteFiles: false,
 | 
			
		||||
			localDriveCapacityMb: null,
 | 
			
		||||
			remoteDriveCapacityMb: null,
 | 
			
		||||
			maxNoteTextLength: null,
 | 
			
		||||
			enableRecaptcha: false,
 | 
			
		||||
			recaptchaSiteKey: null,
 | 
			
		||||
			recaptchaSecretKey: null,
 | 
			
		||||
			enableTwitterIntegration: false,
 | 
			
		||||
			twitterConsumerKey: null,
 | 
			
		||||
			twitterConsumerSecret: null,
 | 
			
		||||
			enableGithubIntegration: false,
 | 
			
		||||
			githubClientId: null,
 | 
			
		||||
			githubClientSecret: null,
 | 
			
		||||
			proxyAccount: null,
 | 
			
		||||
			inviteCode: null,
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	created() {
 | 
			
		||||
		(this as any).os.getMeta().then(meta => {
 | 
			
		||||
			this.maintainerName = meta.maintainer.name;
 | 
			
		||||
			this.maintainerEmail = meta.maintainer.email;
 | 
			
		||||
			this.bannerUrl = meta.bannerUrl;
 | 
			
		||||
			this.name = meta.name;
 | 
			
		||||
			this.description = meta.description;
 | 
			
		||||
			this.languages = meta.langs.join(' ');
 | 
			
		||||
			this.cacheRemoteFiles = meta.cacheRemoteFiles;
 | 
			
		||||
			this.localDriveCapacityMb = meta.driveCapacityPerLocalUserMb;
 | 
			
		||||
			this.remoteDriveCapacityMb = meta.driveCapacityPerRemoteUserMb;
 | 
			
		||||
			this.maxNoteTextLength = meta.maxNoteTextLength;
 | 
			
		||||
			this.enableRecaptcha = meta.enableRecaptcha;
 | 
			
		||||
			this.recaptchaSiteKey = meta.recaptchaSiteKey;
 | 
			
		||||
			this.recaptchaSecretKey = meta.recaptchaSecretKey;
 | 
			
		||||
			this.proxyAccount = meta.proxyAccount;
 | 
			
		||||
			this.enableTwitterIntegration = meta.enableTwitterIntegration;
 | 
			
		||||
			this.twitterConsumerKey = meta.twitterConsumerKey;
 | 
			
		||||
			this.twitterConsumerSecret = meta.twitterConsumerSecret;
 | 
			
		||||
			this.enableGithubIntegration = meta.enableGithubIntegration;
 | 
			
		||||
			this.githubClientId = meta.githubClientId;
 | 
			
		||||
			this.githubClientSecret = meta.githubClientSecret;
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		invite() {
 | 
			
		||||
			(this as any).api('admin/invite').then(x => {
 | 
			
		||||
				this.inviteCode = x.code;
 | 
			
		||||
			}).catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
			
		||||
				this.$swal({
 | 
			
		||||
					type: 'error',
 | 
			
		||||
					text: e
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		updateMeta() {
 | 
			
		||||
			(this as any).api('admin/update-meta', {
 | 
			
		||||
				maintainerName: this.maintainerName,
 | 
			
		||||
				maintainerEmail: this.maintainerEmail,
 | 
			
		||||
				disableRegistration: this.disableRegistration,
 | 
			
		||||
				disableLocalTimeline: this.disableLocalTimeline,
 | 
			
		||||
				bannerUrl: this.bannerUrl
 | 
			
		||||
				bannerUrl: this.bannerUrl,
 | 
			
		||||
				name: this.name,
 | 
			
		||||
				description: this.description,
 | 
			
		||||
				langs: this.languages.split(' '),
 | 
			
		||||
				cacheRemoteFiles: this.cacheRemoteFiles,
 | 
			
		||||
				localDriveCapacityMb: parseInt(this.localDriveCapacityMb, 10),
 | 
			
		||||
				remoteDriveCapacityMb: parseInt(this.remoteDriveCapacityMb, 10),
 | 
			
		||||
				maxNoteTextLength: parseInt(this.maxNoteTextLength, 10),
 | 
			
		||||
				enableRecaptcha: this.enableRecaptcha,
 | 
			
		||||
				recaptchaSiteKey: this.recaptchaSiteKey,
 | 
			
		||||
				recaptchaSecretKey: this.recaptchaSecretKey,
 | 
			
		||||
				proxyAccount: this.proxyAccount,
 | 
			
		||||
				enableTwitterIntegration: this.enableTwitterIntegration,
 | 
			
		||||
				twitterConsumerKey: this.twitterConsumerKey,
 | 
			
		||||
				twitterConsumerSecret: this.twitterConsumerSecret,
 | 
			
		||||
				enableGithubIntegration: this.enableGithubIntegration,
 | 
			
		||||
				githubClientId: this.githubClientId,
 | 
			
		||||
				githubClientSecret: this.githubClientSecret,
 | 
			
		||||
			}).then(() => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Saved` });
 | 
			
		||||
				this.$swal({
 | 
			
		||||
					type: 'success',
 | 
			
		||||
					text: '%i18n:@saved%'
 | 
			
		||||
				});
 | 
			
		||||
			}).catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed ${e}` });
 | 
			
		||||
				this.$swal({
 | 
			
		||||
					type: 'error',
 | 
			
		||||
					text: e
 | 
			
		||||
				});
 | 
			
		||||
			});
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -67,11 +67,11 @@ export default Vue.extend({
 | 
			
		||||
			const process = async () => {
 | 
			
		||||
				const user = await (this as any).os.api('users/show', parseAcct(this.verifyUsername));
 | 
			
		||||
				await (this as any).os.api('admin/verify-user', { userId: user.id });
 | 
			
		||||
				(this as any).os.apis.dialog({ text: '%i18n:@verified%' });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: '%i18n:@verified%' });
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			await process().catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.verifying = false;
 | 
			
		||||
@@ -83,11 +83,11 @@ export default Vue.extend({
 | 
			
		||||
			const process = async () => {
 | 
			
		||||
				const user = await (this as any).os.api('users/show', parseAcct(this.unverifyUsername));
 | 
			
		||||
				await (this as any).os.api('admin/unverify-user', { userId: user.id });
 | 
			
		||||
				(this as any).os.apis.dialog({ text: '%i18n:@unverified%' });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: '%i18n:@unverified%' });
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			await process().catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.unverifying = false;
 | 
			
		||||
@@ -99,11 +99,11 @@ export default Vue.extend({
 | 
			
		||||
			const process = async () => {
 | 
			
		||||
				const user = await (this as any).os.api('users/show', parseAcct(this.suspendUsername));
 | 
			
		||||
				await (this as any).os.api('admin/suspend-user', { userId: user.id });
 | 
			
		||||
				(this as any).os.apis.dialog({ text: '%i18n:@suspended%' });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: '%i18n:@suspended%' });
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			await process().catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.suspending = false;
 | 
			
		||||
@@ -115,11 +115,11 @@ export default Vue.extend({
 | 
			
		||||
			const process = async () => {
 | 
			
		||||
				const user = await (this as any).os.api('users/show', parseAcct(this.unsuspendUsername));
 | 
			
		||||
				await (this as any).os.api('admin/unsuspend-user', { userId: user.id });
 | 
			
		||||
				(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: '%i18n:@unsuspended%' });
 | 
			
		||||
			};
 | 
			
		||||
 | 
			
		||||
			await process().catch(e => {
 | 
			
		||||
				(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
			
		||||
				//(this as any).os.apis.dialog({ text: `Failed: ${e}` });
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			this.unsuspending = false;
 | 
			
		||||
 
 | 
			
		||||
@@ -128,7 +128,7 @@ pre
 | 
			
		||||
		overflow auto
 | 
			
		||||
		tab-size 2
 | 
			
		||||
 | 
			
		||||
[data-fa]
 | 
			
		||||
[data-icon]
 | 
			
		||||
	display inline-block
 | 
			
		||||
 | 
			
		||||
.swal2-container
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<ui-card>
 | 
			
		||||
	<div slot="title">%fa:key% API</div>
 | 
			
		||||
	<div slot="title"><fa icon="key"/> API</div>
 | 
			
		||||
 | 
			
		||||
	<section class="fit-top">
 | 
			
		||||
		<ui-input :value="$store.state.i.token" readonly>
 | 
			
		||||
@@ -9,20 +9,21 @@
 | 
			
		||||
		<p>%i18n:@intro%</p>
 | 
			
		||||
		<ui-info warn>%i18n:@caution%</ui-info>
 | 
			
		||||
		<p>%i18n:@regeneration-of-token%</p>
 | 
			
		||||
		<ui-button @click="regenerateToken">%fa:sync-alt% %i18n:@regenerate-token%</ui-button>
 | 
			
		||||
		<ui-button @click="regenerateToken"><fa icon="sync-alt"/> %i18n:@regenerate-token%</ui-button>
 | 
			
		||||
	</section>
 | 
			
		||||
 | 
			
		||||
	<section>
 | 
			
		||||
		<header>%fa:terminal% %i18n:@console.title%</header>
 | 
			
		||||
		<header><fa icon="terminal"/> %i18n:@console.title%</header>
 | 
			
		||||
		<ui-input v-model="endpoint">
 | 
			
		||||
			<span>%i18n:@console.endpoint%</span>
 | 
			
		||||
		</ui-input>
 | 
			
		||||
		<ui-textarea v-model="body">
 | 
			
		||||
			<span>%i18n:@console.parameter% (JSON or JSON5)</span>
 | 
			
		||||
			<span slot="desc">%i18n:@console.credential-info%</span>
 | 
			
		||||
		</ui-textarea>
 | 
			
		||||
		<ui-button @click="send" :disabled="sending">
 | 
			
		||||
			<template v-if="sending">%i18n:@console.sending%</template>
 | 
			
		||||
			<template v-else>%fa:paper-plane% %i18n:@console.send%</template>
 | 
			
		||||
			<template v-else><fa icon="paper-plane"/> %i18n:@console.send%</template>
 | 
			
		||||
		</ui-button>
 | 
			
		||||
		<ui-textarea v-if="res" v-model="res" readonly tall>
 | 
			
		||||
			<span>%i18n:@console.response%</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -14,7 +14,8 @@
 | 
			
		||||
	</ol>
 | 
			
		||||
	<ol class="emojis" ref="suggests" v-if="emojis.length > 0">
 | 
			
		||||
		<li v-for="emoji in emojis" @click="complete(type, emoji.emoji)" @keydown="onKeydown" tabindex="-1">
 | 
			
		||||
			<span class="emoji" v-if="emoji.url"><img :src="emoji.url" :alt="emoji.emoji"/></span>
 | 
			
		||||
			<span class="emoji" v-if="emoji.isCustomEmoji"><img :src="emoji.url" :alt="emoji.emoji"/></span>
 | 
			
		||||
			<span class="emoji" v-else-if="!useOsDefaultEmojis"><img :src="emoji.url" :alt="emoji.emoji"/></span>
 | 
			
		||||
			<span class="emoji" v-else>{{ emoji.emoji }}</span>
 | 
			
		||||
			<span class="name" v-html="emoji.name.replace(q, `<b>${q}</b>`)"></span>
 | 
			
		||||
			<span class="alias" v-if="emoji.aliasOf">({{ emoji.aliasOf }})</span>
 | 
			
		||||
@@ -33,16 +34,24 @@ type EmojiDef = {
 | 
			
		||||
	name: string;
 | 
			
		||||
	aliasOf?: string;
 | 
			
		||||
	url?: string;
 | 
			
		||||
	isCustomEmoji?: boolean;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const lib = Object.entries(emojilib.lib).filter((x: any) => {
 | 
			
		||||
	return x[1].category != 'flags';
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const char2file = (char: string) => {
 | 
			
		||||
	let codes = [...char].map(x => x.codePointAt(0).toString(16));
 | 
			
		||||
	if (!codes.includes('200d')) codes = codes.filter(x => x != 'fe0f');
 | 
			
		||||
	return codes.join('-');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const emjdb: EmojiDef[] = lib.map((x: any) => ({
 | 
			
		||||
	emoji: x[1].char,
 | 
			
		||||
	name: x[0],
 | 
			
		||||
	aliasOf: null
 | 
			
		||||
	aliasOf: null,
 | 
			
		||||
	url: `https://twemoji.maxcdn.com/2/svg/${char2file(x[1].char)}.svg`
 | 
			
		||||
}));
 | 
			
		||||
 | 
			
		||||
lib.forEach((x: any) => {
 | 
			
		||||
@@ -51,7 +60,8 @@ lib.forEach((x: any) => {
 | 
			
		||||
			emjdb.push({
 | 
			
		||||
				emoji: x[1].char,
 | 
			
		||||
				name: k,
 | 
			
		||||
				aliasOf: x[0]
 | 
			
		||||
				aliasOf: x[0],
 | 
			
		||||
				url: `https://twemoji.maxcdn.com/2/svg/${char2file(x[1].char)}.svg`
 | 
			
		||||
			});
 | 
			
		||||
		});
 | 
			
		||||
	}
 | 
			
		||||
@@ -77,6 +87,10 @@ export default Vue.extend({
 | 
			
		||||
	computed: {
 | 
			
		||||
		items(): HTMLCollection {
 | 
			
		||||
			return (this.$refs.suggests as Element).children;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		useOsDefaultEmojis(): boolean {
 | 
			
		||||
			return this.$store.state.device.useOsDefaultEmojis;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
@@ -107,7 +121,8 @@ export default Vue.extend({
 | 
			
		||||
			emojiDefinitions.push({
 | 
			
		||||
				name: x.name,
 | 
			
		||||
				emoji: `:${x.name}:`,
 | 
			
		||||
				url: x.url
 | 
			
		||||
				url: x.url,
 | 
			
		||||
				isCustomEmoji: true
 | 
			
		||||
			});
 | 
			
		||||
 | 
			
		||||
			if (x.aliases) {
 | 
			
		||||
@@ -116,7 +131,8 @@ export default Vue.extend({
 | 
			
		||||
						name: alias,
 | 
			
		||||
						aliasOf: x.name,
 | 
			
		||||
						emoji: `:${x.name}:`,
 | 
			
		||||
						url: x.url
 | 
			
		||||
						url: x.url,
 | 
			
		||||
						isCustomEmoji: true
 | 
			
		||||
					});
 | 
			
		||||
				});
 | 
			
		||||
			}
 | 
			
		||||
@@ -205,6 +221,15 @@ export default Vue.extend({
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			} else if (this.type == 'emoji') {
 | 
			
		||||
				if (this.q == null || this.q == '') {
 | 
			
		||||
					this.emojis = this.emojiDb.filter(x => x.isCustomEmoji && !x.aliasOf).sort((a, b) => {
 | 
			
		||||
						var textA = a.name.toUpperCase();
 | 
			
		||||
						var textB = b.name.toUpperCase();
 | 
			
		||||
						return (textA < textB) ? -1 : (textA > textB) ? 1 : 0;
 | 
			
		||||
					});
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				const matched = [];
 | 
			
		||||
				const max = 30;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,35 +1,35 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="troubleshooter">
 | 
			
		||||
	<div class="body">
 | 
			
		||||
		<h1>%fa:wrench%%i18n:@title%</h1>
 | 
			
		||||
		<h1><fa icon="wrench"/>%i18n:@title%</h1>
 | 
			
		||||
		<div>
 | 
			
		||||
			<p :data-wip="network == null">
 | 
			
		||||
				<template v-if="network != null">
 | 
			
		||||
					<template v-if="network">%fa:check%</template>
 | 
			
		||||
					<template v-if="!network">%fa:times%</template>
 | 
			
		||||
					<template v-if="network"><fa icon="check"/></template>
 | 
			
		||||
					<template v-if="!network"><fa icon="times"/></template>
 | 
			
		||||
				</template>
 | 
			
		||||
				{{ network == null ? '%i18n:@checking-network%' : '%i18n:@network%' }}<mk-ellipsis v-if="network == null"/>
 | 
			
		||||
			</p>
 | 
			
		||||
			<p v-if="network == true" :data-wip="internet == null">
 | 
			
		||||
				<template v-if="internet != null">
 | 
			
		||||
					<template v-if="internet">%fa:check%</template>
 | 
			
		||||
					<template v-if="!internet">%fa:times%</template>
 | 
			
		||||
					<template v-if="internet"><fa icon="check"/></template>
 | 
			
		||||
					<template v-if="!internet"><fa icon="times"/></template>
 | 
			
		||||
				</template>
 | 
			
		||||
				{{ internet == null ? '%i18n:@checking-internet%' : '%i18n:@internet%' }}<mk-ellipsis v-if="internet == null"/>
 | 
			
		||||
			</p>
 | 
			
		||||
			<p v-if="internet == true" :data-wip="server == null">
 | 
			
		||||
				<template v-if="server != null">
 | 
			
		||||
					<template v-if="server">%fa:check%</template>
 | 
			
		||||
					<template v-if="!server">%fa:times%</template>
 | 
			
		||||
					<template v-if="server"><fa icon="check"/></template>
 | 
			
		||||
					<template v-if="!server"><fa icon="times"/></template>
 | 
			
		||||
				</template>
 | 
			
		||||
				{{ server == null ? '%i18n:@checking-server%' : '%i18n:@server%' }}<mk-ellipsis v-if="server == null"/>
 | 
			
		||||
			</p>
 | 
			
		||||
		</div>
 | 
			
		||||
		<p v-if="!end">%i18n:@finding%<mk-ellipsis/></p>
 | 
			
		||||
		<p v-if="network === false"><b>%fa:exclamation-triangle%%i18n:@no-network%</b><br>%i18n:@no-network-desc%</p>
 | 
			
		||||
		<p v-if="internet === false"><b>%fa:exclamation-triangle%%i18n:@no-internet%</b><br>%i18n:@no-internet-desc%</p>
 | 
			
		||||
		<p v-if="server === false"><b>%fa:exclamation-triangle%%i18n:@no-server%</b><br>%i18n:@no-server-desc%</p>
 | 
			
		||||
		<p v-if="server === true" class="success"><b>%fa:info-circle%%i18n:@success%</b><br>%i18n:@success-desc%</p>
 | 
			
		||||
		<p v-if="network === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-network%</b><br>%i18n:@no-network-desc%</p>
 | 
			
		||||
		<p v-if="internet === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-internet%</b><br>%i18n:@no-internet-desc%</p>
 | 
			
		||||
		<p v-if="server === false"><b><fa icon="exclamation-triangle"/>%i18n:@no-server%</b><br>%i18n:@no-server-desc%</p>
 | 
			
		||||
		<p v-if="server === true" class="success"><b><fa icon="info-circle"/>%i18n:@success%</b><br>%i18n:@success-desc%</p>
 | 
			
		||||
	</div>
 | 
			
		||||
	<footer>
 | 
			
		||||
		<a href="/assets/flush.html">%i18n:@flush%</a> | <a href="/assets/version.html">%i18n:@set-version%</a>
 | 
			
		||||
@@ -100,7 +100,7 @@ export default Vue.extend({
 | 
			
		||||
			color #444
 | 
			
		||||
			border-bottom solid 1px #eee
 | 
			
		||||
 | 
			
		||||
			> [data-fa]
 | 
			
		||||
			> [data-icon]
 | 
			
		||||
				margin-right 0.25em
 | 
			
		||||
 | 
			
		||||
		> div
 | 
			
		||||
@@ -115,7 +115,7 @@ export default Vue.extend({
 | 
			
		||||
				&[data-wip]
 | 
			
		||||
					color #888
 | 
			
		||||
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 0.25em
 | 
			
		||||
 | 
			
		||||
					&.times
 | 
			
		||||
@@ -132,7 +132,7 @@ export default Vue.extend({
 | 
			
		||||
			border-top solid 1px #eee
 | 
			
		||||
 | 
			
		||||
			> b
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 0.25em
 | 
			
		||||
 | 
			
		||||
			&.success
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-connect-failed">
 | 
			
		||||
	<img src="data:image/jpeg;base64,%base64:/assets/error.jpg%" alt=""/>
 | 
			
		||||
	<img src="https://raw.githubusercontent.com/syuilo/misskey/develop/src/client/assets/error.jpg" alt=""/>
 | 
			
		||||
	<h1>%i18n:@title%</h1>
 | 
			
		||||
	<p class="text">
 | 
			
		||||
		<span>{{ '%i18n:@description%'.substr(0, '%i18n:@description%'.indexOf('{')) }}</span>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<ui-card>
 | 
			
		||||
	<div slot="title">%fa:cloud% %i18n:common.drive%</div>
 | 
			
		||||
	<div slot="title"><fa icon="cloud"/> %i18n:common.drive%</div>
 | 
			
		||||
 | 
			
		||||
	<section v-if="!fetching" class="juakhbxthdewydyreaphkepoxgxvfogn">
 | 
			
		||||
		<div class="meter"><div :style="meterStyle"></div></div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										85
									
								
								src/client/app/common/views/components/emoji.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/client/app/common/views/components/emoji.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,85 @@
 | 
			
		||||
<template>
 | 
			
		||||
<img v-if="customEmoji" class="fvgwvorwhxigeolkkrcderjzcawqrscl custom" :src="url" :alt="alt" :title="alt"/>
 | 
			
		||||
<img v-else-if="char && !useOsDefaultEmojis" class="fvgwvorwhxigeolkkrcderjzcawqrscl" :src="url" :alt="alt" :title="alt"/>
 | 
			
		||||
<span v-else-if="char && useOsDefaultEmojis">{{ char }}</span>
 | 
			
		||||
<span v-else>:{{ name }}:</span>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { lib } from 'emojilib';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		name: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: false
 | 
			
		||||
		},
 | 
			
		||||
		emoji: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: false
 | 
			
		||||
		},
 | 
			
		||||
		customEmojis: {
 | 
			
		||||
			required: false,
 | 
			
		||||
			default: []
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			url: null,
 | 
			
		||||
			char: null,
 | 
			
		||||
			customEmoji: null
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		alt(): string {
 | 
			
		||||
			return this.customEmoji ? `:${this.customEmoji.name}:` : this.char;
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		useOsDefaultEmojis(): boolean {
 | 
			
		||||
			return this.$store.state.device.useOsDefaultEmojis;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	created() {
 | 
			
		||||
		if (this.name) {
 | 
			
		||||
			const customEmoji = this.customEmojis.find(x => x.name == this.name);
 | 
			
		||||
			if (customEmoji) {
 | 
			
		||||
				this.customEmoji = customEmoji;
 | 
			
		||||
				this.url = customEmoji.url;
 | 
			
		||||
			} else {
 | 
			
		||||
				const emoji = lib[this.name];
 | 
			
		||||
				if (emoji) {
 | 
			
		||||
					this.char = emoji.char;
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			this.char = this.emoji;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (this.char) {
 | 
			
		||||
			let codes = [...this.char].map(x => x.codePointAt(0).toString(16));
 | 
			
		||||
			if (!codes.includes('200d')) codes = codes.filter(x => x != 'fe0f');
 | 
			
		||||
 | 
			
		||||
			this.url = `https://twemoji.maxcdn.com/2/svg/${codes.join('-')}.svg`;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.fvgwvorwhxigeolkkrcderjzcawqrscl
 | 
			
		||||
	height 1.25em
 | 
			
		||||
	vertical-align -0.25em
 | 
			
		||||
 | 
			
		||||
	&.custom
 | 
			
		||||
		height 2.5em
 | 
			
		||||
		vertical-align middle
 | 
			
		||||
		transition transform 0.2s ease
 | 
			
		||||
 | 
			
		||||
		&:hover
 | 
			
		||||
			transform scale(1.2)
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="wjqjnyhzogztorhrdgcpqlkxhkmuetgj">
 | 
			
		||||
	<p>%fa:exclamation-triangle% %i18n:common.error.title%</p>
 | 
			
		||||
	<p><fa icon="exclamation-triangle"/> %i18n:common.error.title%</p>
 | 
			
		||||
	<ui-button @click="() => $emit('retry')">%i18n:common.error.retry%</ui-button>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<span class="mk-file-type-icon">
 | 
			
		||||
	<template v-if="kind == 'image'">%fa:file-image%</template>
 | 
			
		||||
	<template v-if="kind == 'image'"><fa icon="file-image"/></template>
 | 
			
		||||
</span>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="xqnhankfuuilcwvhgsopeqncafzsquya">
 | 
			
		||||
	<button class="go-index" v-if="selfNav" @click="goIndex">%fa:arrow-left%</button>
 | 
			
		||||
	<button class="go-index" v-if="selfNav" @click="goIndex"><fa icon="arrow-left"/></button>
 | 
			
		||||
	<header><b><router-link :to="blackUser | userPage">{{ blackUser | userName }}</router-link></b>(%i18n:common.reversi.black%) vs <b><router-link :to="whiteUser | userPage">{{ whiteUser | userName }}</router-link></b>(%i18n:common.reversi.white%)</header>
 | 
			
		||||
 | 
			
		||||
	<div style="overflow: hidden; line-height: 28px;">
 | 
			
		||||
@@ -51,13 +51,13 @@
 | 
			
		||||
 | 
			
		||||
	<div class="player" v-if="game.isEnded">
 | 
			
		||||
		<div>
 | 
			
		||||
			<button @click="logPos = 0" :disabled="logPos == 0">%fa:angle-double-left%</button>
 | 
			
		||||
			<button @click="logPos--" :disabled="logPos == 0">%fa:angle-left%</button>
 | 
			
		||||
			<button @click="logPos = 0" :disabled="logPos == 0"><fa icon="angle-double-left"/></button>
 | 
			
		||||
			<button @click="logPos--" :disabled="logPos == 0"><fa icon="angle-left"/></button>
 | 
			
		||||
		</div>
 | 
			
		||||
		<span>{{ logPos }} / {{ logs.length }}</span>
 | 
			
		||||
		<div>
 | 
			
		||||
			<button @click="logPos++" :disabled="logPos == logs.length">%fa:angle-right%</button>
 | 
			
		||||
			<button @click="logPos = logs.length" :disabled="logPos == logs.length">%fa:angle-double-right%</button>
 | 
			
		||||
			<button @click="logPos++" :disabled="logPos == logs.length"><fa icon="angle-right"/></button>
 | 
			
		||||
			<button @click="logPos = logs.length" :disabled="logPos == logs.length"><fa icon="angle-double-right"/></button>
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -17,13 +17,13 @@
 | 
			
		||||
			</header>
 | 
			
		||||
 | 
			
		||||
			<div>
 | 
			
		||||
				<div class="random" v-if="game.settings.map == null">%fa:dice%</div>
 | 
			
		||||
				<div class="random" v-if="game.settings.map == null"><fa icon="dice"/></div>
 | 
			
		||||
				<div class="board" v-else :style="{ 'grid-template-rows': `repeat(${ game.settings.map.length }, 1fr)`, 'grid-template-columns': `repeat(${ game.settings.map[0].length }, 1fr)` }">
 | 
			
		||||
					<div v-for="(x, i) in game.settings.map.join('')"
 | 
			
		||||
							:data-none="x == ' '"
 | 
			
		||||
							@click="onPixelClick(i, x)">
 | 
			
		||||
						<template v-if="x == 'b'"><template v-if="$store.state.device.darkmode">%fa:circle R%</template><template v-else>%fa:circle%</template></template>
 | 
			
		||||
						<template v-if="x == 'w'"><template v-if="$store.state.device.darkmode">%fa:circle%</template><template v-else>%fa:circle R%</template></template>
 | 
			
		||||
						<template v-if="x == 'b'"><template v-if="$store.state.device.darkmode"><fa :icon="['far', 'circle']"/></template><template v-else><fa icon="circle"/></template></template>
 | 
			
		||||
						<template v-if="x == 'w'"><template v-if="$store.state.device.darkmode"><fa :icon="['far', 'circle']"/></template><template v-else><fa icon="circle"/></template></template>
 | 
			
		||||
					</div>
 | 
			
		||||
				</div>
 | 
			
		||||
			</div>
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										63
									
								
								src/client/app/common/views/components/github-setting.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/client/app/common/views/components/github-setting.vue
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,63 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-github-setting">
 | 
			
		||||
	<p>%i18n:@description%<a :href="`${docsUrl}/link-to-github`" target="_blank">%i18n:@detail%</a></p>
 | 
			
		||||
	<p class="account" v-if="$store.state.i.github" :title="`GitHub ID: ${$store.state.i.github.id}`">%i18n:@connected-to%: <a :href="`https://github.com/${$store.state.i.github.login}`" target="_blank">@{{ $store.state.i.github.login }}</a></p>
 | 
			
		||||
	<p>
 | 
			
		||||
		<a :href="`${apiUrl}/connect/github`" target="_blank" @click.prevent="connect">{{ $store.state.i.github ? '%i18n:@reconnect%' : '%i18n:@connect%' }}</a>
 | 
			
		||||
		<span v-if="$store.state.i.github"> or </span>
 | 
			
		||||
		<a :href="`${apiUrl}/disconnect/github`" target="_blank" v-if="$store.state.i.github" @click.prevent="disconnect">%i18n:@disconnect%</a>
 | 
			
		||||
	</p>
 | 
			
		||||
	<p class="id" v-if="$store.state.i.github">GitHub ID: {{ $store.state.i.github.id }}</p>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { apiUrl, docsUrl } from '../../../config';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			form: null,
 | 
			
		||||
			apiUrl,
 | 
			
		||||
			docsUrl
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.$watch('$store.state.i', () => {
 | 
			
		||||
			if (this.$store.state.i.github && this.form)
 | 
			
		||||
				this.form.close();
 | 
			
		||||
		}, {
 | 
			
		||||
			deep: true
 | 
			
		||||
		});
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		connect() {
 | 
			
		||||
			this.form = window.open(apiUrl + '/connect/github',
 | 
			
		||||
				'github_connect_window',
 | 
			
		||||
				'height=570, width=520');
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		disconnect() {
 | 
			
		||||
			window.open(apiUrl + '/disconnect/github',
 | 
			
		||||
				'github_disconnect_window',
 | 
			
		||||
				'height=570, width=520');
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-github-setting
 | 
			
		||||
	.account
 | 
			
		||||
		border solid 1px #e1e8ed
 | 
			
		||||
		border-radius 4px
 | 
			
		||||
		padding 16px
 | 
			
		||||
 | 
			
		||||
		a
 | 
			
		||||
			font-weight bold
 | 
			
		||||
			color inherit
 | 
			
		||||
 | 
			
		||||
	.id
 | 
			
		||||
		color #8899a6
 | 
			
		||||
</style>
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-google">
 | 
			
		||||
	<input type="search" v-model="query" :placeholder="q">
 | 
			
		||||
	<button @click="search">%fa:search% %i18n:common.search%</button>
 | 
			
		||||
	<button @click="search"><fa icon="search"/> %i18n:common.search%</button>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -37,8 +37,9 @@ import messaging from './messaging.vue';
 | 
			
		||||
import messagingRoom from './messaging-room.vue';
 | 
			
		||||
import urlPreview from './url-preview.vue';
 | 
			
		||||
import twitterSetting from './twitter-setting.vue';
 | 
			
		||||
import githubSetting from './github-setting.vue';
 | 
			
		||||
import fileTypeIcon from './file-type-icon.vue';
 | 
			
		||||
import Reversi from './games/reversi/reversi.vue';
 | 
			
		||||
import emoji from './emoji.vue';
 | 
			
		||||
import welcomeTimeline from './welcome-timeline.vue';
 | 
			
		||||
import uiInput from './ui/input.vue';
 | 
			
		||||
import uiButton from './ui/button.vue';
 | 
			
		||||
@@ -90,8 +91,9 @@ Vue.component('mk-messaging', messaging);
 | 
			
		||||
Vue.component('mk-messaging-room', messagingRoom);
 | 
			
		||||
Vue.component('mk-url-preview', urlPreview);
 | 
			
		||||
Vue.component('mk-twitter-setting', twitterSetting);
 | 
			
		||||
Vue.component('mk-github-setting', githubSetting);
 | 
			
		||||
Vue.component('mk-file-type-icon', fileTypeIcon);
 | 
			
		||||
Vue.component('mk-reversi', Reversi);
 | 
			
		||||
Vue.component('mk-emoji', emoji);
 | 
			
		||||
Vue.component('mk-welcome-timeline', welcomeTimeline);
 | 
			
		||||
Vue.component('ui-input', uiInput);
 | 
			
		||||
Vue.component('ui-button', uiButton);
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-media-banner">
 | 
			
		||||
	<div class="sensitive" v-if="media.isSensitive && hide" @click="hide = false">
 | 
			
		||||
		<span class="icon">%fa:exclamation-triangle%</span>
 | 
			
		||||
		<span class="icon"><fa icon="exclamation-triangle"/></span>
 | 
			
		||||
		<b>%i18n:@sensitive%</b>
 | 
			
		||||
		<span>%i18n:@click-to-show%</span>
 | 
			
		||||
	</div>
 | 
			
		||||
@@ -18,7 +18,7 @@
 | 
			
		||||
		:title="media.name"
 | 
			
		||||
		:download="media.name"
 | 
			
		||||
	>
 | 
			
		||||
		<span class="icon">%fa:download%</span>
 | 
			
		||||
		<span class="icon"><fa icon="download"/></span>
 | 
			
		||||
		<b>{{ media.name }}</b>
 | 
			
		||||
	</a>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,9 @@
 | 
			
		||||
	<div class="popover" :class="{ hukidasi }" ref="popover">
 | 
			
		||||
		<template v-for="item, i in items">
 | 
			
		||||
			<div v-if="item === null"></div>
 | 
			
		||||
			<button v-if="item" @click="clicked(item.action)" v-html="item.icon ? item.icon + ' ' + item.text : item.text" :tabindex="i"></button>
 | 
			
		||||
			<button v-if="item" @click="clicked(item.action)" :tabindex="i">
 | 
			
		||||
				<fa v-if="item.icon" :icon="item.icon"/>{{ item.text }}
 | 
			
		||||
			</button>
 | 
			
		||||
		</template>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -188,6 +190,9 @@ export default Vue.extend({
 | 
			
		||||
				color var(--primaryForeground)
 | 
			
		||||
				background var(--primaryDarken10)
 | 
			
		||||
 | 
			
		||||
			> [data-icon]
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
 | 
			
		||||
		> div
 | 
			
		||||
			margin 8px 0
 | 
			
		||||
			height 1px
 | 
			
		||||
 
 | 
			
		||||
@@ -14,13 +14,13 @@
 | 
			
		||||
	<div class="file" @click="file = null" v-if="file">{{ file.name }}</div>
 | 
			
		||||
	<mk-uploader ref="uploader" @uploaded="onUploaded"/>
 | 
			
		||||
	<button class="send" @click="send" :disabled="!canSend || sending" title="%i18n:@send%">
 | 
			
		||||
		<template v-if="!sending">%fa:paper-plane%</template><template v-if="sending">%fa:spinner .spin%</template>
 | 
			
		||||
		<template v-if="!sending"><fa icon="paper-plane"/></template><template v-if="sending"><fa icon="spinner .spin"/></template>
 | 
			
		||||
	</button>
 | 
			
		||||
	<button class="attach-from-local" @click="chooseFile" title="%i18n:@attach-from-local%">
 | 
			
		||||
		%fa:upload%
 | 
			
		||||
		<fa icon="upload"/>
 | 
			
		||||
	</button>
 | 
			
		||||
	<button class="attach-from-drive" @click="chooseFileFromDrive" title="%i18n:@attach-from-drive%">
 | 
			
		||||
		%fa:R folder-open%
 | 
			
		||||
		<fa :icon="['far', 'folder-open']"/>
 | 
			
		||||
	</button>
 | 
			
		||||
	<input ref="file" type="file" @change="onChangeFile"/>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -24,7 +24,7 @@
 | 
			
		||||
		<footer>
 | 
			
		||||
			<span class="read" v-if="isMe && message.isRead">%i18n:@is-read%</span>
 | 
			
		||||
			<mk-time :time="message.createdAt"/>
 | 
			
		||||
			<template v-if="message.is_edited">%fa:pencil-alt%</template>
 | 
			
		||||
			<template v-if="message.is_edited"><fa icon="pencil-alt"/></template>
 | 
			
		||||
		</footer>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -179,7 +179,10 @@ export default Vue.extend({
 | 
			
		||||
			font-size 10px
 | 
			
		||||
			color var(--messagingRoomMessageInfo)
 | 
			
		||||
 | 
			
		||||
			> [data-fa]
 | 
			
		||||
			> .read
 | 
			
		||||
				margin 0 8px
 | 
			
		||||
 | 
			
		||||
			> [data-icon]
 | 
			
		||||
				margin-left 4px
 | 
			
		||||
 | 
			
		||||
	&:not([data-is-me])
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,11 @@
 | 
			
		||||
	@drop.prevent.stop="onDrop"
 | 
			
		||||
>
 | 
			
		||||
	<div class="body">
 | 
			
		||||
		<p class="init" v-if="init">%fa:spinner .spin%%i18n:common.loading%</p>
 | 
			
		||||
		<p class="empty" v-if="!init && messages.length == 0">%fa:info-circle%%i18n:@empty%</p>
 | 
			
		||||
		<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages">%fa:flag%%i18n:@no-history%</p>
 | 
			
		||||
		<p class="init" v-if="init"><fa icon="spinner .spin"/>%i18n:common.loading%</p>
 | 
			
		||||
		<p class="empty" v-if="!init && messages.length == 0"><fa icon="info-circle"/>%i18n:@empty%</p>
 | 
			
		||||
		<p class="no-history" v-if="!init && messages.length > 0 && !existMoreMessages"><fa icon="flag"/>%i18n:@no-history%</p>
 | 
			
		||||
		<button class="more" :class="{ fetching: fetchingMoreMessages }" v-if="existMoreMessages" @click="fetchMoreMessages" :disabled="fetchingMoreMessages">
 | 
			
		||||
			<template v-if="fetchingMoreMessages">%fa:spinner .pulse .fw%</template>{{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:@more%' }}
 | 
			
		||||
			<template v-if="fetchingMoreMessages"><fa icon="spinner .pulse" fixed-width/></template>{{ fetchingMoreMessages ? '%i18n:common.loading%' : '%i18n:@more%' }}
 | 
			
		||||
		</button>
 | 
			
		||||
		<template v-for="(message, i) in _messages">
 | 
			
		||||
			<x-message :message="message" :key="message.id"/>
 | 
			
		||||
@@ -20,7 +20,7 @@
 | 
			
		||||
	<footer>
 | 
			
		||||
		<transition name="fade">
 | 
			
		||||
			<div class="new-message" v-show="showIndicator">
 | 
			
		||||
				<button @click="onIndicatorClick">%fa:arrow-circle-down%%i18n:@new-message%</button>
 | 
			
		||||
				<button @click="onIndicatorClick"><i><fa icon="arrow-circle-down"/></i>%i18n:@new-message%</button>
 | 
			
		||||
			</div>
 | 
			
		||||
		</transition>
 | 
			
		||||
		<x-form :user="user" ref="form"/>
 | 
			
		||||
@@ -280,7 +280,7 @@ export default Vue.extend({
 | 
			
		||||
			color var(--messagingRoomInfo)
 | 
			
		||||
			opacity 0.5
 | 
			
		||||
 | 
			
		||||
			[data-fa]
 | 
			
		||||
			[data-icon]
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
 | 
			
		||||
		> .no-history
 | 
			
		||||
@@ -292,7 +292,7 @@ export default Vue.extend({
 | 
			
		||||
			color var(--messagingRoomInfo)
 | 
			
		||||
			opacity 0.5
 | 
			
		||||
 | 
			
		||||
			[data-fa]
 | 
			
		||||
			[data-icon]
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
 | 
			
		||||
		> .more
 | 
			
		||||
@@ -313,7 +313,7 @@ export default Vue.extend({
 | 
			
		||||
			&.fetching
 | 
			
		||||
				cursor wait
 | 
			
		||||
 | 
			
		||||
			> [data-fa]
 | 
			
		||||
			> [data-icon]
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
 | 
			
		||||
		> .message
 | 
			
		||||
@@ -381,7 +381,7 @@ export default Vue.extend({
 | 
			
		||||
				&:active
 | 
			
		||||
					background var(--primaryDarken10)
 | 
			
		||||
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> i
 | 
			
		||||
					position absolute
 | 
			
		||||
					top 0
 | 
			
		||||
					left 10px
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
<div class="mk-messaging" :data-compact="compact">
 | 
			
		||||
	<div class="search" v-if="!compact" :style="{ top: headerTop + 'px' }">
 | 
			
		||||
		<div class="form">
 | 
			
		||||
			<label for="search-input">%fa:search%</label>
 | 
			
		||||
			<label for="search-input"><i><fa icon="search"/></i></label>
 | 
			
		||||
			<input v-model="q" type="search" @input="search" @keydown="onSearchKeydown" placeholder="%i18n:@search-user%"/>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div class="result">
 | 
			
		||||
@@ -45,7 +45,7 @@
 | 
			
		||||
		</template>
 | 
			
		||||
	</div>
 | 
			
		||||
	<p class="no-history" v-if="!fetching && messages.length == 0">%i18n:@no-history%</p>
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -213,7 +213,7 @@ export default Vue.extend({
 | 
			
		||||
				width 38px
 | 
			
		||||
				pointer-events none
 | 
			
		||||
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> i
 | 
			
		||||
					display block
 | 
			
		||||
					position absolute
 | 
			
		||||
					top 0
 | 
			
		||||
@@ -418,7 +418,7 @@ export default Vue.extend({
 | 
			
		||||
		text-align center
 | 
			
		||||
		color #aaa
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	// TODO: element base media query
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,4 @@
 | 
			
		||||
import Vue, { VNode } from 'vue';
 | 
			
		||||
import * as emojilib from 'emojilib';
 | 
			
		||||
import { length } from 'stringz';
 | 
			
		||||
import parse from '../../../../../mfm/parse';
 | 
			
		||||
import getAcct from '../../../../../misc/acct/render';
 | 
			
		||||
@@ -188,24 +187,16 @@ export default Vue.component('misskey-flavored-markdown', {
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				case 'emoji': {
 | 
			
		||||
					//#region カスタム絵文字
 | 
			
		||||
					if (this.customEmojis != null) {
 | 
			
		||||
						const customEmoji = this.customEmojis.find(e => e.name == token.emoji || (e.aliases || []).includes(token.emoji));
 | 
			
		||||
						if (customEmoji) {
 | 
			
		||||
							return [createElement('img', {
 | 
			
		||||
								attrs: {
 | 
			
		||||
									src: customEmoji.url,
 | 
			
		||||
									alt: token.emoji,
 | 
			
		||||
									title: token.emoji,
 | 
			
		||||
									style: 'height: 2.5em; vertical-align: middle;'
 | 
			
		||||
								}
 | 
			
		||||
							})];
 | 
			
		||||
					const customEmojis = (this.os.getMetaSync() || { emojis: [] }).emojis || [];
 | 
			
		||||
					return [createElement('mk-emoji', {
 | 
			
		||||
						attrs: {
 | 
			
		||||
							emoji: token.emoji,
 | 
			
		||||
							name: token.name
 | 
			
		||||
						},
 | 
			
		||||
						props: {
 | 
			
		||||
							customEmojis: this.customEmojis || customEmojis
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
					//#endregion
 | 
			
		||||
 | 
			
		||||
					const emoji = emojilib.lib[token.emoji];
 | 
			
		||||
					return [createElement('span', emoji ? emoji.char : token.content)];
 | 
			
		||||
					})];
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				case 'search': {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<ui-card>
 | 
			
		||||
	<div slot="title">%fa:ban% %i18n:@mute-and-block%</div>
 | 
			
		||||
	<div slot="title"><fa icon="ban"/> %i18n:@mute-and-block%</div>
 | 
			
		||||
 | 
			
		||||
	<section>
 | 
			
		||||
		<header>%i18n:@mute%</header>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,11 @@
 | 
			
		||||
<span class="mk-nav">
 | 
			
		||||
	<a :href="aboutUrl">%i18n:@about%</a>
 | 
			
		||||
	<i>・</i>
 | 
			
		||||
	<a href="/stats">%i18n:@stats%</a>
 | 
			
		||||
	<i>・</i>
 | 
			
		||||
	<a :href="repositoryUrl">%i18n:@repository%</a>
 | 
			
		||||
	<i>・</i>
 | 
			
		||||
	<a :href="feedbackUrl" target="_blank">%i18n:@feedback%</a>
 | 
			
		||||
	<i>・</i>
 | 
			
		||||
	<a href="/dev">%i18n:@develop%</a>
 | 
			
		||||
	<i>・</i>
 | 
			
		||||
	<a href="https://twitter.com/misskey_xyz" target="_blank">Follow us on %fa:B twitter%</a>
 | 
			
		||||
</span>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -6,18 +6,18 @@
 | 
			
		||||
	<span class="is-bot" v-if="note.user.isBot">bot</span>
 | 
			
		||||
	<span class="is-cat" v-if="note.user.isCat">cat</span>
 | 
			
		||||
	<span class="username"><mk-acct :user="note.user"/></span>
 | 
			
		||||
	<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%">%fa:star%</span>
 | 
			
		||||
	<span class="is-verified" v-if="note.user.isVerified" title="%i18n:common.verified-user%"><fa icon="star"/></span>
 | 
			
		||||
	<div class="info">
 | 
			
		||||
		<span class="app" v-if="note.app && !mini">via <b>{{ note.app.name }}</b></span>
 | 
			
		||||
		<span class="mobile" v-if="note.viaMobile">%fa:mobile-alt%</span>
 | 
			
		||||
		<span class="mobile" v-if="note.viaMobile"><fa icon="mobile-alt"/></span>
 | 
			
		||||
		<router-link class="created-at" :to="note | notePage">
 | 
			
		||||
			<mk-time :time="note.createdAt"/>
 | 
			
		||||
		</router-link>
 | 
			
		||||
		<span class="visibility" v-if="note.visibility != 'public'">
 | 
			
		||||
			<template v-if="note.visibility == 'home'">%fa:home%</template>
 | 
			
		||||
			<template v-if="note.visibility == 'followers'">%fa:unlock%</template>
 | 
			
		||||
			<template v-if="note.visibility == 'specified'">%fa:envelope%</template>
 | 
			
		||||
			<template v-if="note.visibility == 'private'">%fa:lock%</template>
 | 
			
		||||
			<template v-if="note.visibility == 'home'"><fa icon="home"/></template>
 | 
			
		||||
			<template v-if="note.visibility == 'followers'"><fa icon="unlock"/></template>
 | 
			
		||||
			<template v-if="note.visibility == 'specified'"><fa icon="envelope"/></template>
 | 
			
		||||
			<template v-if="note.visibility == 'private'"><fa icon="lock"/></template>
 | 
			
		||||
		</span>
 | 
			
		||||
	</div>
 | 
			
		||||
</header>
 | 
			
		||||
 
 | 
			
		||||
@@ -15,18 +15,18 @@ export default Vue.extend({
 | 
			
		||||
	computed: {
 | 
			
		||||
		items() {
 | 
			
		||||
			const items = [{
 | 
			
		||||
				icon: '%fa:info-circle%',
 | 
			
		||||
				icon: 'info-circle',
 | 
			
		||||
				text: '%i18n:@detail%',
 | 
			
		||||
				action: this.detail
 | 
			
		||||
			}, {
 | 
			
		||||
				icon: '%fa:link%',
 | 
			
		||||
				icon: 'link',
 | 
			
		||||
				text: '%i18n:@copy-link%',
 | 
			
		||||
				action: this.copyLink
 | 
			
		||||
			}];
 | 
			
		||||
 | 
			
		||||
			if (this.note.uri) {
 | 
			
		||||
				items.push({
 | 
			
		||||
					icon: '%fa:external-link-square-alt%',
 | 
			
		||||
					icon: 'external-link-square-alt',
 | 
			
		||||
					text: '%i18n:@remote%',
 | 
			
		||||
					action: () => {
 | 
			
		||||
						window.open(this.note.uri, '_blank');
 | 
			
		||||
@@ -38,13 +38,13 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
			if (this.note.isFavorited) {
 | 
			
		||||
				items.push({
 | 
			
		||||
					icon: '%fa:star%',
 | 
			
		||||
					icon: 'star',
 | 
			
		||||
					text: '%i18n:@unfavorite%',
 | 
			
		||||
					action: this.unfavorite
 | 
			
		||||
				});
 | 
			
		||||
			} else {
 | 
			
		||||
				items.push({
 | 
			
		||||
					icon: '%fa:star%',
 | 
			
		||||
					icon: 'star',
 | 
			
		||||
					text: '%i18n:@favorite%',
 | 
			
		||||
					action: this.favorite
 | 
			
		||||
				});
 | 
			
		||||
@@ -53,13 +53,13 @@ export default Vue.extend({
 | 
			
		||||
			if (this.note.userId == this.$store.state.i.id) {
 | 
			
		||||
				if ((this.$store.state.i.pinnedNoteIds || []).includes(this.note.id)) {
 | 
			
		||||
					items.push({
 | 
			
		||||
						icon: '%fa:thumbtack%',
 | 
			
		||||
						icon: 'thumbtack',
 | 
			
		||||
						text: '%i18n:@unpin%',
 | 
			
		||||
						action: this.unpin
 | 
			
		||||
					});
 | 
			
		||||
				} else {
 | 
			
		||||
					items.push({
 | 
			
		||||
						icon: '%fa:thumbtack%',
 | 
			
		||||
						icon: 'thumbtack',
 | 
			
		||||
						text: '%i18n:@pin%',
 | 
			
		||||
						action: this.pin
 | 
			
		||||
					});
 | 
			
		||||
@@ -69,7 +69,7 @@ export default Vue.extend({
 | 
			
		||||
			if (this.note.userId == this.$store.state.i.id || this.$store.state.i.isAdmin) {
 | 
			
		||||
				items.push(null);
 | 
			
		||||
				items.push({
 | 
			
		||||
					icon: '%fa:trash-alt R%',
 | 
			
		||||
					icon: ['far', 'trash-alt'],
 | 
			
		||||
					text: '%i18n:@delete%',
 | 
			
		||||
					action: this.del
 | 
			
		||||
				});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,19 +1,19 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-poll-editor">
 | 
			
		||||
	<p class="caution" v-if="choices.length < 2">
 | 
			
		||||
		%fa:exclamation-triangle%%i18n:@no-only-one-choice%
 | 
			
		||||
		<fa icon="exclamation-triangle"/>%i18n:@no-only-one-choice%
 | 
			
		||||
	</p>
 | 
			
		||||
	<ul ref="choices">
 | 
			
		||||
		<li v-for="(choice, i) in choices">
 | 
			
		||||
			<input :value="choice" @input="onInput(i, $event)" :placeholder="'%i18n:@choice-n%'.replace('{}', i + 1)">
 | 
			
		||||
			<button @click="remove(i)" title="%i18n:@remove%">
 | 
			
		||||
				%fa:times%
 | 
			
		||||
				<fa icon="times"/>
 | 
			
		||||
			</button>
 | 
			
		||||
		</li>
 | 
			
		||||
	</ul>
 | 
			
		||||
	<button class="add" v-if="choices.length < 10" @click="add">%i18n:@add%</button>
 | 
			
		||||
	<button class="destroy" @click="destroy" title="%i18n:@destroy%">
 | 
			
		||||
		%fa:times%
 | 
			
		||||
		<fa icon="times"/>
 | 
			
		||||
	</button>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -76,7 +76,7 @@ export default Vue.extend({
 | 
			
		||||
		font-size 0.8em
 | 
			
		||||
		color #f00
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> ul
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@
 | 
			
		||||
		<li v-for="choice in poll.choices" :key="choice.id" @click="vote(choice.id)" :class="{ voted: choice.voted }" :title="!isVoted ? '%i18n:@vote-to%'.replace('{}', choice.text) : ''">
 | 
			
		||||
			<div class="backdrop" :style="{ 'width': (showResult ? (choice.votes / total * 100) : 0) + '%' }"></div>
 | 
			
		||||
			<span>
 | 
			
		||||
				<template v-if="choice.isVoted">%fa:check%</template>
 | 
			
		||||
				<template v-if="choice.isVoted"><fa icon="check"/></template>
 | 
			
		||||
				<span>{{ choice.text }}</span>
 | 
			
		||||
				<span class="votes" v-if="showResult">({{ '%i18n:@vote-count%'.replace('{}', choice.votes) }})</span>
 | 
			
		||||
			</span>
 | 
			
		||||
@@ -100,7 +100,7 @@ export default Vue.extend({
 | 
			
		||||
				transition width 1s ease
 | 
			
		||||
 | 
			
		||||
			> span
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 4px
 | 
			
		||||
 | 
			
		||||
				> .votes
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<ui-card>
 | 
			
		||||
	<div slot="title">%fa:user% %i18n:@title%</div>
 | 
			
		||||
	<div slot="title"><fa icon="user"/> %i18n:@title%</div>
 | 
			
		||||
 | 
			
		||||
	<section class="fit-top">
 | 
			
		||||
		<ui-form :disabled="saving">
 | 
			
		||||
@@ -16,12 +16,12 @@
 | 
			
		||||
 | 
			
		||||
			<ui-input v-model="location">
 | 
			
		||||
				<span>%i18n:@location%</span>
 | 
			
		||||
				<span slot="prefix">%fa:map-marker-alt%</span>
 | 
			
		||||
				<span slot="prefix"><fa icon="map-marker-alt"/></span>
 | 
			
		||||
			</ui-input>
 | 
			
		||||
 | 
			
		||||
			<ui-input v-model="birthday" type="date">
 | 
			
		||||
				<span>%i18n:@birthday%</span>
 | 
			
		||||
				<span slot="prefix">%fa:birthday-cake%</span>
 | 
			
		||||
				<span slot="prefix"><fa icon="birthday-cake"/></span>
 | 
			
		||||
			</ui-input>
 | 
			
		||||
 | 
			
		||||
			<ui-textarea v-model="description" :max="500">
 | 
			
		||||
@@ -30,14 +30,14 @@
 | 
			
		||||
 | 
			
		||||
			<ui-input type="file" @change="onAvatarChange">
 | 
			
		||||
				<span>%i18n:@avatar%</span>
 | 
			
		||||
				<span slot="icon">%fa:image%</span>
 | 
			
		||||
				<span slot="text" v-if="avatarUploading">%i18n:@uploading%<mk-ellipsis/></span>
 | 
			
		||||
				<span slot="icon"><fa icon="image"/></span>
 | 
			
		||||
				<span slot="desc" v-if="avatarUploading">%i18n:@uploading%<mk-ellipsis/></span>
 | 
			
		||||
			</ui-input>
 | 
			
		||||
 | 
			
		||||
			<ui-input type="file" @change="onBannerChange">
 | 
			
		||||
				<span>%i18n:@banner%</span>
 | 
			
		||||
				<span slot="icon">%fa:image%</span>
 | 
			
		||||
				<span slot="text" v-if="bannerUploading">%i18n:@uploading%<mk-ellipsis/></span>
 | 
			
		||||
				<span slot="icon"><fa icon="image"/></span>
 | 
			
		||||
				<span slot="desc" v-if="bannerUploading">%i18n:@uploading%<mk-ellipsis/></span>
 | 
			
		||||
			</ui-input>
 | 
			
		||||
 | 
			
		||||
			<ui-button @click="save(true)">%i18n:@save%</ui-button>
 | 
			
		||||
 
 | 
			
		||||
@@ -8,11 +8,12 @@
 | 
			
		||||
	</ui-input>
 | 
			
		||||
	<ui-input v-model="password" type="password" required styl="fill">
 | 
			
		||||
		<span>%i18n:@password%</span>
 | 
			
		||||
		<span slot="prefix">%fa:lock%</span>
 | 
			
		||||
		<span slot="prefix"><fa icon="lock"/></span>
 | 
			
		||||
	</ui-input>
 | 
			
		||||
	<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required styl="fill"/>
 | 
			
		||||
	<ui-button type="submit" :disabled="signing">{{ signing ? '%i18n:@signing-in%' : '%i18n:@signin%' }}</ui-button>
 | 
			
		||||
	<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/twitter`">%i18n:@signin-with-twitter%</a></p>
 | 
			
		||||
	<p style="margin: 8px 0;">%i18n:@or% <a :href="`${apiUrl}/signin/github`">%i18n:@signin-with-github%</a></p>
 | 
			
		||||
</form>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,39 +3,39 @@
 | 
			
		||||
	<template v-if="meta">
 | 
			
		||||
		<ui-input v-if="meta.disableRegistration" v-model="invitationCode" type="text" :autocomplete="Math.random()" spellcheck="false" required styl="fill">
 | 
			
		||||
			<span>%i18n:@invitation-code%</span>
 | 
			
		||||
			<span slot="prefix">%fa:id-card-alt%</span>
 | 
			
		||||
			<p slot="text" v-html="'%i18n:@invitation-info%'.replace('{}', meta.maintainer.url)"></p>
 | 
			
		||||
			<span slot="prefix"><fa icon="id-card-alt"/></span>
 | 
			
		||||
			<p slot="desc" v-html="'%i18n:@invitation-info%'.replace('{}', 'mailto:' + meta.maintainer.email)"></p>
 | 
			
		||||
		</ui-input>
 | 
			
		||||
		<ui-input v-model="username" type="text" pattern="^[a-zA-Z0-9_]{1,20}$" :autocomplete="Math.random()" spellcheck="false" required @input="onChangeUsername" styl="fill">
 | 
			
		||||
			<span>%i18n:@username%</span>
 | 
			
		||||
			<span slot="prefix">@</span>
 | 
			
		||||
			<span slot="suffix">@{{ host }}</span>
 | 
			
		||||
			<p slot="text" v-if="usernameState == 'wait'" style="color:#999">%fa:spinner .pulse .fw% %i18n:@checking%</p>
 | 
			
		||||
			<p slot="text" v-if="usernameState == 'ok'" style="color:#3CB7B5">%fa:check .fw% %i18n:@available%</p>
 | 
			
		||||
			<p slot="text" v-if="usernameState == 'unavailable'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@unavailable%</p>
 | 
			
		||||
			<p slot="text" v-if="usernameState == 'error'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@error%</p>
 | 
			
		||||
			<p slot="text" v-if="usernameState == 'invalid-format'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@invalid-format%</p>
 | 
			
		||||
			<p slot="text" v-if="usernameState == 'min-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-short%</p>
 | 
			
		||||
			<p slot="text" v-if="usernameState == 'max-range'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@too-long%</p>
 | 
			
		||||
			<p slot="desc" v-if="usernameState == 'wait'" style="color:#999"><fa icon="spinner .pulse" fixed-width/> %i18n:@checking%</p>
 | 
			
		||||
			<p slot="desc" v-if="usernameState == 'ok'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@available%</p>
 | 
			
		||||
			<p slot="desc" v-if="usernameState == 'unavailable'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@unavailable%</p>
 | 
			
		||||
			<p slot="desc" v-if="usernameState == 'error'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@error%</p>
 | 
			
		||||
			<p slot="desc" v-if="usernameState == 'invalid-format'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@invalid-format%</p>
 | 
			
		||||
			<p slot="desc" v-if="usernameState == 'min-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@too-short%</p>
 | 
			
		||||
			<p slot="desc" v-if="usernameState == 'max-range'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@too-long%</p>
 | 
			
		||||
		</ui-input>
 | 
			
		||||
		<ui-input v-model="password" type="password" :autocomplete="Math.random()" required @input="onChangePassword" :with-password-meter="true" styl="fill">
 | 
			
		||||
			<span>%i18n:@password%</span>
 | 
			
		||||
			<span slot="prefix">%fa:lock%</span>
 | 
			
		||||
			<div slot="text">
 | 
			
		||||
				<p slot="text" v-if="passwordStrength == 'low'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@weak-password%</p>
 | 
			
		||||
				<p slot="text" v-if="passwordStrength == 'medium'" style="color:#3CB7B5">%fa:check .fw% %i18n:@normal-password%</p>
 | 
			
		||||
				<p slot="text" v-if="passwordStrength == 'high'" style="color:#3CB7B5">%fa:check .fw% %i18n:@strong-password%</p>
 | 
			
		||||
			<span slot="prefix"><fa icon="lock"/></span>
 | 
			
		||||
			<div slot="desc">
 | 
			
		||||
				<p v-if="passwordStrength == 'low'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@weak-password%</p>
 | 
			
		||||
				<p v-if="passwordStrength == 'medium'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@normal-password%</p>
 | 
			
		||||
				<p v-if="passwordStrength == 'high'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@strong-password%</p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</ui-input>
 | 
			
		||||
		<ui-input v-model="retypedPassword" type="password" :autocomplete="Math.random()" required @input="onChangePasswordRetype" styl="fill">
 | 
			
		||||
			<span>%i18n:@password% (%i18n:@retype%)</span>
 | 
			
		||||
			<span slot="prefix">%fa:lock%</span>
 | 
			
		||||
			<div slot="text">
 | 
			
		||||
				<p slot="text" v-if="passwordRetypeState == 'match'" style="color:#3CB7B5">%fa:check .fw% %i18n:@password-matched%</p>
 | 
			
		||||
				<p slot="text" v-if="passwordRetypeState == 'not-match'" style="color:#FF1161">%fa:exclamation-triangle .fw% %i18n:@password-not-matched%</p>
 | 
			
		||||
			<span slot="prefix"><fa icon="lock"/></span>
 | 
			
		||||
			<div slot="desc">
 | 
			
		||||
				<p v-if="passwordRetypeState == 'match'" style="color:#3CB7B5"><fa icon="check" fixed-width/> %i18n:@password-matched%</p>
 | 
			
		||||
				<p v-if="passwordRetypeState == 'not-match'" style="color:#FF1161"><fa icon="exclamation-triangle" fixed-width/> %i18n:@password-not-matched%</p>
 | 
			
		||||
			</div>
 | 
			
		||||
		</ui-input>
 | 
			
		||||
		<div v-if="meta.recaptchaSitekey != null" class="g-recaptcha" :data-sitekey="meta.recaptchaSitekey" style="margin: 16px 0;"></div>
 | 
			
		||||
		<div v-if="meta.enableRecaptcha" class="g-recaptcha" :data-sitekey="meta.recaptchaSiteKey" style="margin: 16px 0;"></div>
 | 
			
		||||
		<ui-button type="submit">%i18n:@create%</ui-button>
 | 
			
		||||
	</template>
 | 
			
		||||
</form>
 | 
			
		||||
@@ -130,7 +130,7 @@ export default Vue.extend({
 | 
			
		||||
				username: this.username,
 | 
			
		||||
				password: this.password,
 | 
			
		||||
				invitationCode: this.invitationCode,
 | 
			
		||||
				'g-recaptcha-response': this.meta.recaptchaSitekey != null ? (window as any).grecaptcha.getResponse() : null
 | 
			
		||||
				'g-recaptcha-response': this.meta.enableRecaptcha ? (window as any).grecaptcha.getResponse() : null
 | 
			
		||||
			}, true).then(() => {
 | 
			
		||||
				(this as any).api('signin', {
 | 
			
		||||
					username: this.username,
 | 
			
		||||
@@ -141,7 +141,7 @@ export default Vue.extend({
 | 
			
		||||
			}).catch(() => {
 | 
			
		||||
				alert('%i18n:@some-error%');
 | 
			
		||||
 | 
			
		||||
				if (this.meta.recaptchaSitekey != null) {
 | 
			
		||||
				if (this.meta.enableRecaptcha) {
 | 
			
		||||
					(window as any).grecaptcha.reset();
 | 
			
		||||
				}
 | 
			
		||||
			});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,15 +1,15 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-stream-indicator">
 | 
			
		||||
	<p v-if="stream.state == 'initializing'">
 | 
			
		||||
		%fa:spinner .pulse%
 | 
			
		||||
		<fa icon="spinner .pulse"/>
 | 
			
		||||
		<span>%i18n:@connecting%<mk-ellipsis/></span>
 | 
			
		||||
	</p>
 | 
			
		||||
	<p v-if="stream.state == 'reconnecting'">
 | 
			
		||||
		%fa:spinner .pulse%
 | 
			
		||||
		<fa icon="spinner .pulse"/>
 | 
			
		||||
		<span>%i18n:@reconnecting%<mk-ellipsis/></span>
 | 
			
		||||
	</p>
 | 
			
		||||
	<p v-if="stream.state == 'connected'">
 | 
			
		||||
		%fa:check%
 | 
			
		||||
		<fa icon="check"/>
 | 
			
		||||
		<span>%i18n:@connected%</span>
 | 
			
		||||
	</p>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -80,7 +80,7 @@ export default Vue.extend({
 | 
			
		||||
		display block
 | 
			
		||||
		margin 0
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 0.25em
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="jtivnzhfwquxpsfidertopbmwmchmnmo">
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<p class="empty" v-else-if="tags.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
 | 
			
		||||
	<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<p class="empty" v-else-if="tags.length == 0"><fa icon="exclamation-circle"/>%i18n:@empty%</p>
 | 
			
		||||
	<div v-else>
 | 
			
		||||
		<vue-word-cloud
 | 
			
		||||
				:words="tags.slice(0, 20).map(x => [x.name, x.count])"
 | 
			
		||||
@@ -74,7 +74,7 @@ export default Vue.extend({
 | 
			
		||||
		text-align center
 | 
			
		||||
		color #aaa
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> div
 | 
			
		||||
 
 | 
			
		||||
@@ -25,7 +25,7 @@
 | 
			
		||||
	</label>
 | 
			
		||||
 | 
			
		||||
	<details class="creator">
 | 
			
		||||
		<summary>%fa:palette% %i18n:@create-a-theme%</summary>
 | 
			
		||||
		<summary><fa icon="palette"/> %i18n:@create-a-theme%</summary>
 | 
			
		||||
		<div>
 | 
			
		||||
			<span>%i18n:@base-theme%:</span>
 | 
			
		||||
			<ui-radio v-model="myThemeBase" value="light">%i18n:@base-theme-light%</ui-radio>
 | 
			
		||||
@@ -51,23 +51,23 @@
 | 
			
		||||
			<div style="padding-bottom:8px;">%i18n:@text-color%:</div>
 | 
			
		||||
			<color-picker v-model="myThemeText"/>
 | 
			
		||||
		</div>
 | 
			
		||||
		<ui-button @click="preview()">%fa:eye% %i18n:@preview-created-theme%</ui-button>
 | 
			
		||||
		<ui-button primary @click="gen()">%fa:save R% %i18n:@save-created-theme%</ui-button>
 | 
			
		||||
		<ui-button @click="preview()"><fa icon="eye"/> %i18n:@preview-created-theme%</ui-button>
 | 
			
		||||
		<ui-button primary @click="gen()"><fa :icon="['far', 'save']"/> %i18n:@save-created-theme%</ui-button>
 | 
			
		||||
	</details>
 | 
			
		||||
 | 
			
		||||
	<details>
 | 
			
		||||
		<summary>%fa:download% %i18n:@install-a-theme%</summary>
 | 
			
		||||
		<ui-button @click="import_()">%fa:file-import% %i18n:@import%</ui-button>
 | 
			
		||||
		<summary><fa icon="download"/> %i18n:@install-a-theme%</summary>
 | 
			
		||||
		<ui-button @click="import_()"><fa icon="file-import"/> %i18n:@import%</ui-button>
 | 
			
		||||
		<input ref="file" type="file" accept=".misskeytheme" style="display:none;" @change="onUpdateImportFile"/>
 | 
			
		||||
		<p>%i18n:@import-by-code%:</p>
 | 
			
		||||
		<ui-textarea v-model="installThemeCode">
 | 
			
		||||
			<span>%i18n:@theme-code%</span>
 | 
			
		||||
		</ui-textarea>
 | 
			
		||||
		<ui-button @click="() => install(this.installThemeCode)">%fa:check% %i18n:@install%</ui-button>
 | 
			
		||||
		<ui-button @click="() => install(this.installThemeCode)"><fa icon="check"/> %i18n:@install%</ui-button>
 | 
			
		||||
	</details>
 | 
			
		||||
 | 
			
		||||
	<details>
 | 
			
		||||
		<summary>%fa:folder-open% %i18n:@manage-themes%</summary>
 | 
			
		||||
		<summary><fa icon="folder-open"/> %i18n:@manage-themes%</summary>
 | 
			
		||||
		<ui-select v-model="selectedThemeId" placeholder="%i18n:@select-theme%">
 | 
			
		||||
			<optgroup label="%i18n:@builtin-themes%">
 | 
			
		||||
				<option v-for="x in builtinThemes" :value="x.id" :key="x.id">{{ x.name }}</option>
 | 
			
		||||
@@ -89,8 +89,8 @@
 | 
			
		||||
			<ui-textarea readonly :value="selectedThemeCode">
 | 
			
		||||
				<span>%i18n:@theme-code%</span>
 | 
			
		||||
			</ui-textarea>
 | 
			
		||||
			<ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export">%fa:box% %i18n:@export%</ui-button>
 | 
			
		||||
			<ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)">%fa:trash-alt R% %i18n:@uninstall%</ui-button>
 | 
			
		||||
			<ui-button @click="export_()" link :download="`${selectedTheme.name}.misskeytheme`" ref="export"><fa icon="box"/> %i18n:@export%</ui-button>
 | 
			
		||||
			<ui-button @click="uninstall()" v-if="!builtinThemes.some(t => t.id == selectedTheme.id)"><fa :icon="['far', 'trash-alt']"/> %i18n:@uninstall%</ui-button>
 | 
			
		||||
		</template>
 | 
			
		||||
	</details>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="csqvmxybqbycalfhkxvyfrgbrdalkaoc">
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<p class="empty" v-else-if="stats.length == 0">%fa:exclamation-circle%%i18n:@empty%</p>
 | 
			
		||||
	<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
	<p class="empty" v-else-if="stats.length == 0"><fa icon="exclamation-circle"/>%i18n:@empty%</p>
 | 
			
		||||
	<!-- トランジションを有効にするとなぜかメモリリークする -->
 | 
			
		||||
	<transition-group v-else tag="div" name="chart">
 | 
			
		||||
		<div v-for="stat in stats" :key="stat.tag">
 | 
			
		||||
@@ -58,7 +58,7 @@ export default Vue.extend({
 | 
			
		||||
		color var(--text)
 | 
			
		||||
		opacity 0.7
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> div
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,11 @@
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	inject: ['horizonGrouped'],
 | 
			
		||||
	inject: {
 | 
			
		||||
		horizonGrouped: {
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		type: {
 | 
			
		||||
			type: String,
 | 
			
		||||
 
 | 
			
		||||
@@ -48,6 +48,9 @@ export default Vue.extend({
 | 
			
		||||
		&.fit-top
 | 
			
		||||
			padding-top 0
 | 
			
		||||
 | 
			
		||||
		&.fit-bottom
 | 
			
		||||
			padding-bottom 0
 | 
			
		||||
 | 
			
		||||
		> header
 | 
			
		||||
			margin-bottom 16px
 | 
			
		||||
			font-weight bold
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="ymxyweixqwsxauxldgpvecjepnwxbylu" :class="{ warn }">
 | 
			
		||||
	<i v-if="warn">%fa:exclamation-triangle%</i>
 | 
			
		||||
	<i v-else>%fa:info-circle%</i>
 | 
			
		||||
	<i v-if="warn"><fa icon="exclamation-triangle"/></i>
 | 
			
		||||
	<i v-else><fa icon="info-circle"/></i>
 | 
			
		||||
	<slot></slot>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="ui-input" :class="[{ focused, filled, inline }, styl]">
 | 
			
		||||
<div class="ui-input" :class="[{ focused, filled, inline, disabled }, styl]">
 | 
			
		||||
	<div class="icon" ref="icon"><slot name="icon"></slot></div>
 | 
			
		||||
	<div class="input">
 | 
			
		||||
		<div class="password-meter" v-if="withPasswordMeter" v-show="passwordStrength != ''" :data-strength="passwordStrength">
 | 
			
		||||
@@ -11,6 +11,7 @@
 | 
			
		||||
			<input ref="input"
 | 
			
		||||
					:type="type"
 | 
			
		||||
					v-model="v"
 | 
			
		||||
					:disabled="disabled"
 | 
			
		||||
					:required="required"
 | 
			
		||||
					:readonly="readonly"
 | 
			
		||||
					:pattern="pattern"
 | 
			
		||||
@@ -32,7 +33,7 @@
 | 
			
		||||
		</template>
 | 
			
		||||
		<div class="suffix" ref="suffix"><slot name="suffix"></slot></div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="text"><slot name="text"></slot></div>
 | 
			
		||||
	<div class="desc"><slot name="desc"></slot></div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -41,7 +42,11 @@ import Vue from 'vue';
 | 
			
		||||
const getPasswordStrength = require('syuilo-password-strength');
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	inject: ['horizonGrouped'],
 | 
			
		||||
	inject: {
 | 
			
		||||
		horizonGrouped: {
 | 
			
		||||
			default: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	props: {
 | 
			
		||||
		value: {
 | 
			
		||||
			required: false
 | 
			
		||||
@@ -58,6 +63,10 @@ export default Vue.extend({
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false
 | 
			
		||||
		},
 | 
			
		||||
		disabled: {
 | 
			
		||||
			type: Boolean,
 | 
			
		||||
			required: false
 | 
			
		||||
		},
 | 
			
		||||
		pattern: {
 | 
			
		||||
			type: String,
 | 
			
		||||
			required: false
 | 
			
		||||
@@ -312,7 +321,7 @@ root(fill)
 | 
			
		||||
			if fill
 | 
			
		||||
				padding-right 12px
 | 
			
		||||
 | 
			
		||||
	> .text
 | 
			
		||||
	> .desc
 | 
			
		||||
		margin 6px 0
 | 
			
		||||
		font-size 13px
 | 
			
		||||
 | 
			
		||||
@@ -349,4 +358,10 @@ root(fill)
 | 
			
		||||
		display inline-block
 | 
			
		||||
		margin 0
 | 
			
		||||
 | 
			
		||||
	&.disabled
 | 
			
		||||
		opacity 0.7
 | 
			
		||||
 | 
			
		||||
		&, *
 | 
			
		||||
			cursor not-allowed !important
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -129,5 +129,6 @@ export default Vue.extend({
 | 
			
		||||
		> p
 | 
			
		||||
			margin 0
 | 
			
		||||
			opacity 0.7
 | 
			
		||||
			font-size 90%
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -13,7 +13,7 @@
 | 
			
		||||
			@blur="focused = false"
 | 
			
		||||
		></textarea>
 | 
			
		||||
	</div>
 | 
			
		||||
	<div class="text"><slot name="text"></slot></div>
 | 
			
		||||
	<div class="desc"><slot name="desc"></slot></div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -139,7 +139,7 @@ root(fill)
 | 
			
		||||
			outline none
 | 
			
		||||
			box-shadow none
 | 
			
		||||
 | 
			
		||||
	> .text
 | 
			
		||||
	> .desc
 | 
			
		||||
		margin 6px 0
 | 
			
		||||
		font-size 13px
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -3,7 +3,7 @@
 | 
			
		||||
	<ol v-if="uploads.length > 0">
 | 
			
		||||
		<li v-for="ctx in uploads" :key="ctx.id">
 | 
			
		||||
			<div class="img" :style="{ backgroundImage: `url(${ ctx.img })` }"></div>
 | 
			
		||||
			<p class="name">%fa:spinner .pulse%{{ ctx.name }}</p>
 | 
			
		||||
			<p class="name"><fa icon="spinner .pulse"/>{{ ctx.name }}</p>
 | 
			
		||||
			<p class="status">
 | 
			
		||||
				<span class="initing" v-if="ctx.progress == undefined">%i18n:@waiting%<mk-ellipsis/></span>
 | 
			
		||||
				<span class="kb" v-if="ctx.progress != undefined">{{ String(Math.floor(ctx.progress.value / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i> / {{ String(Math.floor(ctx.progress.max / 1024)).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,') }}<i>KB</i></span>
 | 
			
		||||
@@ -155,7 +155,7 @@ export default Vue.extend({
 | 
			
		||||
				text-overflow ellipsis
 | 
			
		||||
				overflow hidden
 | 
			
		||||
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 4px
 | 
			
		||||
 | 
			
		||||
			> .status
 | 
			
		||||
 
 | 
			
		||||
@@ -6,7 +6,7 @@
 | 
			
		||||
	<span class="pathname" v-if="pathname != ''">{{ pathname }}</span>
 | 
			
		||||
	<span class="query">{{ query }}</span>
 | 
			
		||||
	<span class="hash">{{ hash }}</span>
 | 
			
		||||
	%fa:external-link-square-alt%
 | 
			
		||||
	<fa icon="external-link-square-alt"/>
 | 
			
		||||
</a>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -40,7 +40,7 @@ export default Vue.extend({
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.mk-url
 | 
			
		||||
	word-break break-all
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		padding-left 2px
 | 
			
		||||
		font-size .9em
 | 
			
		||||
		font-weight 400
 | 
			
		||||
 
 | 
			
		||||
@@ -3,34 +3,34 @@
 | 
			
		||||
	<div class="backdrop" ref="backdrop" @click="close"></div>
 | 
			
		||||
	<div class="popover" :class="{ compact }" ref="popover">
 | 
			
		||||
		<div @click="choose('public')" :class="{ active: v == 'public' }">
 | 
			
		||||
			<div>%fa:globe%</div>
 | 
			
		||||
			<div><fa icon="globe"/></div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span>%i18n:@public%</span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div @click="choose('home')" :class="{ active: v == 'home' }">
 | 
			
		||||
			<div>%fa:home%</div>
 | 
			
		||||
			<div><fa icon="home"/></div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span>%i18n:@home%</span>
 | 
			
		||||
				<span>%i18n:@home-desc%</span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div @click="choose('followers')" :class="{ active: v == 'followers' }">
 | 
			
		||||
			<div>%fa:unlock%</div>
 | 
			
		||||
			<div><fa icon="unlock"/></div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span>%i18n:@followers%</span>
 | 
			
		||||
				<span>%i18n:@followers-desc%</span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div @click="choose('specified')" :class="{ active: v == 'specified' }">
 | 
			
		||||
			<div>%fa:envelope%</div>
 | 
			
		||||
			<div><fa icon="envelope"/></div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span>%i18n:@specified%</span>
 | 
			
		||||
				<span>%i18n:@specified-desc%</span>
 | 
			
		||||
			</div>
 | 
			
		||||
		</div>
 | 
			
		||||
		<div @click="choose('private')" :class="{ active: v == 'private' }">
 | 
			
		||||
			<div>%fa:lock%</div>
 | 
			
		||||
			<div><fa icon="lock"/></div>
 | 
			
		||||
			<div>
 | 
			
		||||
				<span>%i18n:@private%</span>
 | 
			
		||||
			</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -109,7 +109,7 @@ class Autocomplete {
 | 
			
		||||
 | 
			
		||||
		if (isEmoji && opened == false) {
 | 
			
		||||
			const emoji = text.substr(emojiIndex + 1);
 | 
			
		||||
			if (emoji != '' && emoji.match(/^[\+\-a-z0-9_]+$/)) {
 | 
			
		||||
			if (!emoji.includes(' ')) {
 | 
			
		||||
				this.open('emoji', emoji);
 | 
			
		||||
				opened = true;
 | 
			
		||||
			}
 | 
			
		||||
@@ -145,6 +145,7 @@ class Autocomplete {
 | 
			
		||||
		} else {
 | 
			
		||||
			// サジェスト要素作成
 | 
			
		||||
			this.suggestion = new MkAutocomplete({
 | 
			
		||||
				parent: this.vm,
 | 
			
		||||
				propsData: {
 | 
			
		||||
					textarea: this.textarea,
 | 
			
		||||
					complete: this.complete,
 | 
			
		||||
@@ -222,8 +223,6 @@ class Autocomplete {
 | 
			
		||||
			const trimmedBefore = before.substring(0, before.lastIndexOf(':'));
 | 
			
		||||
			const after = source.substr(caret);
 | 
			
		||||
 | 
			
		||||
			if (value.startsWith(':')) value = value + ' ';
 | 
			
		||||
 | 
			
		||||
			// 挿入
 | 
			
		||||
			this.text = trimmedBefore + value + after;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -19,13 +19,13 @@
 | 
			
		||||
			@click="onClick"
 | 
			
		||||
			:disabled="followWait">
 | 
			
		||||
		<template v-if="!followWait">
 | 
			
		||||
			<template v-if="user.hasPendingFollowRequestFromYou && user.isLocked">%fa:hourglass-half% %i18n:@request-pending%</template>
 | 
			
		||||
			<template v-else-if="user.hasPendingFollowRequestFromYou && !user.isLocked">%fa:hourglass-start% %i18n:@follow-processing%</template>
 | 
			
		||||
			<template v-else-if="user.isFollowing">%fa:minus% %i18n:@following%</template>
 | 
			
		||||
			<template v-else-if="!user.isFollowing && user.isLocked">%fa:plus% %i18n:@follow-request%</template>
 | 
			
		||||
			<template v-else-if="!user.isFollowing && !user.isLocked">%fa:plus% %i18n:@follow%</template>
 | 
			
		||||
			<template v-if="user.hasPendingFollowRequestFromYou && user.isLocked"><fa icon="hourglass-half"/> %i18n:@request-pending%</template>
 | 
			
		||||
			<template v-else-if="user.hasPendingFollowRequestFromYou && !user.isLocked"><fa icon="hourglass-start"/> %i18n:@follow-processing%</template>
 | 
			
		||||
			<template v-else-if="user.isFollowing"><fa icon="minus"/> %i18n:@following%</template>
 | 
			
		||||
			<template v-else-if="!user.isFollowing && user.isLocked"><fa icon="plus"/> %i18n:@follow-request%</template>
 | 
			
		||||
			<template v-else-if="!user.isFollowing && !user.isLocked"><fa icon="plus"/> %i18n:@follow%</template>
 | 
			
		||||
		</template>
 | 
			
		||||
		<template v-else>%fa:spinner .pulse .fw%</template>
 | 
			
		||||
		<template v-else><fa icon="spinner .pulse" fixed-width/></template>
 | 
			
		||||
	</button>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,10 +2,10 @@
 | 
			
		||||
<div>
 | 
			
		||||
	<mk-widget-container :show-header="false">
 | 
			
		||||
		<article class="dolfvtibguprpxxhfndqaosjitixjohx">
 | 
			
		||||
			<h1>%fa:heart%%i18n:@title%</h1>
 | 
			
		||||
			<h1><fa icon="heart"/>%i18n:@title%</h1>
 | 
			
		||||
			<p v-if="meta">
 | 
			
		||||
				{{ '%i18n:@text%'.substr(0, '%i18n:@text%'.indexOf('{')) }}
 | 
			
		||||
				<a :href="meta.maintainer.url">{{ meta.maintainer.name }}</a>
 | 
			
		||||
				<a :href="'mailto:' + meta.maintainer.email">{{ meta.maintainer.name }}</a>
 | 
			
		||||
				{{ '%i18n:@text%'.substr('%i18n:@text%'.indexOf('}') + 1) }}
 | 
			
		||||
			</p>
 | 
			
		||||
		</article>
 | 
			
		||||
@@ -41,7 +41,7 @@ export default define({
 | 
			
		||||
		margin 0 0 5px 0
 | 
			
		||||
		font-size 1em
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 0.25em
 | 
			
		||||
 | 
			
		||||
	> p
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-hashtags">
 | 
			
		||||
	<mk-widget-container :show-header="!props.compact">
 | 
			
		||||
		<template slot="header">%fa:hashtag%%i18n:@title%</template>
 | 
			
		||||
		<template slot="header"><fa icon="hashtag"/>%i18n:@title%</template>
 | 
			
		||||
 | 
			
		||||
		<div class="mkw-hashtags--body" :data-mobile="platform == 'mobile'">
 | 
			
		||||
			<mk-trends/>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-memo">
 | 
			
		||||
	<mk-widget-container :show-header="!props.compact">
 | 
			
		||||
		<template slot="header">%fa:R sticky-note%%i18n:@title%</template>
 | 
			
		||||
		<template slot="header"><fa :icon="['far', 'sticky-note']"/>%i18n:@title%</template>
 | 
			
		||||
 | 
			
		||||
		<div class="mkw-memo--body">
 | 
			
		||||
			<textarea v-model="text" placeholder="%i18n:@memo%" @input="onChange"></textarea>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-photo-stream" :class="$style.root" :data-melt="props.design == 2">
 | 
			
		||||
	<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
 | 
			
		||||
		<template slot="header">%fa:camera%%i18n:@title%</template>
 | 
			
		||||
		<template slot="header"><fa icon="camera"/>%i18n:@title%</template>
 | 
			
		||||
 | 
			
		||||
		<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
		<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
		<div :class="$style.stream" v-if="!fetching && images.length > 0">
 | 
			
		||||
			<div v-for="image in images" :class="$style.img" :style="`background-image: url(${image.thumbnailUrl || image.url})`"></div>
 | 
			
		||||
		</div>
 | 
			
		||||
@@ -94,7 +94,7 @@ export default define({
 | 
			
		||||
	text-align center
 | 
			
		||||
	color #aaa
 | 
			
		||||
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-posts-monitor">
 | 
			
		||||
	<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
 | 
			
		||||
		<template slot="header">%fa:chart-line%%i18n:@title%</template>
 | 
			
		||||
		<button slot="func" @click="toggle" title="%i18n:@toggle%">%fa:sort%</button>
 | 
			
		||||
		<template slot="header"><fa icon="chart-line"/>%i18n:@title%</template>
 | 
			
		||||
		<button slot="func" @click="toggle" title="%i18n:@toggle%"><fa icon="sort"/></button>
 | 
			
		||||
 | 
			
		||||
		<div class="qpdmibaztplkylerhdbllwcokyrfxeyj" :class="{ dual: props.view == 0 }">
 | 
			
		||||
			<svg :viewBox="`0 0 ${ viewBoxX } ${ viewBoxY }`" v-show="props.view != 2">
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-rss">
 | 
			
		||||
	<mk-widget-container :show-header="!props.compact">
 | 
			
		||||
		<template slot="header">%fa:rss-square%RSS</template>
 | 
			
		||||
		<button slot="func" title="設定" @click="setting">%fa:cog%</button>
 | 
			
		||||
		<template slot="header"><fa icon="rss-square"/>RSS</template>
 | 
			
		||||
		<button slot="func" title="設定" @click="setting"><fa icon="cog"/></button>
 | 
			
		||||
 | 
			
		||||
		<div class="mkw-rss--body" :data-mobile="platform == 'mobile'">
 | 
			
		||||
			<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
			<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
			<div class="feed" v-else>
 | 
			
		||||
				<a v-for="item in items" :href="item.link" target="_blank">{{ item.title }}</a>
 | 
			
		||||
			</div>
 | 
			
		||||
@@ -85,7 +85,7 @@ export default define({
 | 
			
		||||
			text-align center
 | 
			
		||||
			color #aaa
 | 
			
		||||
 | 
			
		||||
			> [data-fa]
 | 
			
		||||
			> [data-icon]
 | 
			
		||||
				margin-right 4px
 | 
			
		||||
 | 
			
		||||
		&[data-mobile]
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
<div class="cpu">
 | 
			
		||||
	<x-pie class="pie" :value="usage"/>
 | 
			
		||||
	<div>
 | 
			
		||||
		<p>%fa:microchip%CPU</p>
 | 
			
		||||
		<p><fa icon="microchip"/>CPU</p>
 | 
			
		||||
		<p>{{ meta.cpu.cores }} Cores</p>
 | 
			
		||||
		<p>{{ meta.cpu.model }}</p>
 | 
			
		||||
	</div>
 | 
			
		||||
@@ -57,7 +57,7 @@ export default Vue.extend({
 | 
			
		||||
			&:first-child
 | 
			
		||||
				font-weight bold
 | 
			
		||||
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 4px
 | 
			
		||||
 | 
			
		||||
	&:after
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
<div class="disk">
 | 
			
		||||
	<x-pie class="pie" :value="usage"/>
 | 
			
		||||
	<div>
 | 
			
		||||
		<p>%fa:R hdd%Storage</p>
 | 
			
		||||
		<p><fa :icon="['far', 'hdd']"/>Storage</p>
 | 
			
		||||
		<p>Total: {{ total | bytes(1) }}</p>
 | 
			
		||||
		<p>Free: {{ available | bytes(1) }}</p>
 | 
			
		||||
		<p>Used: {{ used | bytes(1) }}</p>
 | 
			
		||||
@@ -65,7 +65,7 @@ export default Vue.extend({
 | 
			
		||||
			&:first-child
 | 
			
		||||
				font-weight bold
 | 
			
		||||
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 4px
 | 
			
		||||
 | 
			
		||||
	&:after
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="info">
 | 
			
		||||
	<p>Maintainer: <b><a :href="meta.maintainer.url" target="_blank">{{ meta.maintainer.name }}</a></b></p>
 | 
			
		||||
	<p>Maintainer: <b><a :href="'mailto:' + meta.maintainer.email" target="_blank">{{ meta.maintainer.name }}</a></b></p>
 | 
			
		||||
	<p>Machine: {{ meta.machine }}</p>
 | 
			
		||||
	<p>Node: {{ meta.node }}</p>
 | 
			
		||||
</div>
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
<div class="memory">
 | 
			
		||||
	<x-pie class="pie" :value="usage"/>
 | 
			
		||||
	<div>
 | 
			
		||||
		<p>%fa:flask%Memory</p>
 | 
			
		||||
		<p><fa icon="flask"/>Memory</p>
 | 
			
		||||
		<p>Total: {{ total | bytes(1) }}</p>
 | 
			
		||||
		<p>Used: {{ used | bytes(1) }}</p>
 | 
			
		||||
		<p>Free: {{ free | bytes(1) }}</p>
 | 
			
		||||
@@ -65,7 +65,7 @@ export default Vue.extend({
 | 
			
		||||
			&:first-child
 | 
			
		||||
				font-weight bold
 | 
			
		||||
 | 
			
		||||
				> [data-fa]
 | 
			
		||||
				> [data-icon]
 | 
			
		||||
					margin-right 4px
 | 
			
		||||
 | 
			
		||||
	&:after
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-server">
 | 
			
		||||
	<mk-widget-container :show-header="props.design == 0" :naked="props.design == 2">
 | 
			
		||||
		<template slot="header">%fa:server%%i18n:@title%</template>
 | 
			
		||||
		<button slot="func" @click="toggle" title="%i18n:@toggle%">%fa:sort%</button>
 | 
			
		||||
		<template slot="header"><fa icon="server"/>%i18n:@title%</template>
 | 
			
		||||
		<button slot="func" @click="toggle" title="%i18n:@toggle%"><fa icon="sort"/></button>
 | 
			
		||||
 | 
			
		||||
		<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
		<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
		<template v-if="!fetching">
 | 
			
		||||
			<x-cpu-memory v-show="props.view == 0" :connection="connection"/>
 | 
			
		||||
			<x-cpu v-show="props.view == 1" :connection="connection" :meta="meta"/>
 | 
			
		||||
@@ -87,7 +87,7 @@ export default define({
 | 
			
		||||
	text-align center
 | 
			
		||||
	color #aaa
 | 
			
		||||
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mkw-tips">
 | 
			
		||||
	<p ref="tip">%fa:R lightbulb%<span v-html="tip"></span></p>
 | 
			
		||||
	<p ref="tip"><fa :icon="['far', 'lightbulb']"/><span v-html="tip"></span></p>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -88,7 +88,7 @@ export default define({
 | 
			
		||||
		font-size 0.7em
 | 
			
		||||
		color #999
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
		kbd
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@ import updateBanner from './api/update-banner';
 | 
			
		||||
import MkIndex from './views/pages/index.vue';
 | 
			
		||||
import MkHome from './views/pages/home.vue';
 | 
			
		||||
import MkDeck from './views/pages/deck/deck.vue';
 | 
			
		||||
import MkStats from './views/pages/stats/stats.vue';
 | 
			
		||||
import MkUser from './views/pages/user/user.vue';
 | 
			
		||||
import MkFavorites from './views/pages/favorites.vue';
 | 
			
		||||
import MkSelectDrive from './views/pages/selectdrive.vue';
 | 
			
		||||
@@ -56,7 +55,6 @@ init(async (launch) => {
 | 
			
		||||
			{ path: '/', name: 'index', component: MkIndex },
 | 
			
		||||
			{ path: '/home', name: 'home', component: MkHome },
 | 
			
		||||
			{ path: '/deck', name: 'deck', component: MkDeck },
 | 
			
		||||
			{ path: '/stats', name: 'stats', component: MkStats },
 | 
			
		||||
			{ path: '/i/customize-home', component: MkHomeCustomize },
 | 
			
		||||
			{ path: '/i/favorites', component: MkFavorites },
 | 
			
		||||
			{ path: '/i/messaging/:user', component: MkMessagingRoom },
 | 
			
		||||
 
 | 
			
		||||
@@ -1,10 +1,10 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-activity">
 | 
			
		||||
	<mk-widget-container :show-header="design == 0" :naked="design == 2">
 | 
			
		||||
		<template slot="header">%fa:chart-bar%%i18n:@title%</template>
 | 
			
		||||
		<button slot="func" title="%i18n:@toggle%" @click="toggle">%fa:sort%</button>
 | 
			
		||||
		<template slot="header"><fa icon="chart-bar"/>%i18n:@title%</template>
 | 
			
		||||
		<button slot="func" title="%i18n:@toggle%" @click="toggle"><fa icon="sort"/></button>
 | 
			
		||||
 | 
			
		||||
		<p :class="$style.fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
		<p :class="$style.fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:common.loading%<mk-ellipsis/></p>
 | 
			
		||||
		<template v-else>
 | 
			
		||||
			<x-calendar v-show="view == 0" :data="[].concat(activity)"/>
 | 
			
		||||
			<x-chart v-show="view == 1" :data="[].concat(activity)"/>
 | 
			
		||||
@@ -78,7 +78,7 @@ export default Vue.extend({
 | 
			
		||||
	text-align center
 | 
			
		||||
	color #aaa
 | 
			
		||||
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-calendar" :data-melt="design == 4 || design == 5">
 | 
			
		||||
	<template v-if="design == 0 || design == 1">
 | 
			
		||||
		<button @click="prev" title="%i18n:@prev%">%fa:chevron-circle-left%</button>
 | 
			
		||||
		<button @click="prev" title="%i18n:@prev%"><fa icon="chevron-circle-left"/></button>
 | 
			
		||||
		<p class="title">{{ '%i18n:@title%'.replace('{1}', year).replace('{2}', month) }}</p>
 | 
			
		||||
		<button @click="next" title="%i18n:@next%">%fa:chevron-circle-right%</button>
 | 
			
		||||
		<button @click="next" title="%i18n:@next%"><fa icon="chevron-circle-right"/></button>
 | 
			
		||||
	</template>
 | 
			
		||||
 | 
			
		||||
	<div class="calendar">
 | 
			
		||||
@@ -151,7 +151,7 @@ export default Vue.extend({
 | 
			
		||||
		background var(--faceHeader)
 | 
			
		||||
		box-shadow 0 1px rgba(#000, 0.07)
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> button
 | 
			
		||||
 
 | 
			
		||||
@@ -1,42 +0,0 @@
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import { Line } from 'vue-chartjs';
 | 
			
		||||
import * as mergeOptions from 'merge-options';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	extends: Line,
 | 
			
		||||
	props: {
 | 
			
		||||
		data: {
 | 
			
		||||
			required: true
 | 
			
		||||
		},
 | 
			
		||||
		opts: {
 | 
			
		||||
			required: false
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	watch: {
 | 
			
		||||
		data() {
 | 
			
		||||
			this.render();
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	mounted() {
 | 
			
		||||
		this.render();
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
		render() {
 | 
			
		||||
			this.renderChart(this.data, mergeOptions({
 | 
			
		||||
				responsive: true,
 | 
			
		||||
				maintainAspectRatio: false,
 | 
			
		||||
				scales: {
 | 
			
		||||
					xAxes: [{
 | 
			
		||||
						type: 'time',
 | 
			
		||||
						distribution: 'series'
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					intersect: false,
 | 
			
		||||
					mode: 'index',
 | 
			
		||||
					position: 'nearest'
 | 
			
		||||
				}
 | 
			
		||||
			}, this.opts || {}));
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
@@ -1,723 +0,0 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="gkgckalzgidaygcxnugepioremxvxvpt">
 | 
			
		||||
	<header>
 | 
			
		||||
		<b>%i18n:@title%:</b>
 | 
			
		||||
		<select v-model="chartType">
 | 
			
		||||
			<optgroup label="%i18n:@federation%">
 | 
			
		||||
				<option value="federation-instances">%i18n:@charts.federation-instances%</option>
 | 
			
		||||
				<option value="federation-instances-total">%i18n:@charts.federation-instances-total%</option>
 | 
			
		||||
			</optgroup>
 | 
			
		||||
			<optgroup label="%i18n:@users%">
 | 
			
		||||
				<option value="users">%i18n:@charts.users%</option>
 | 
			
		||||
				<option value="users-total">%i18n:@charts.users-total%</option>
 | 
			
		||||
			</optgroup>
 | 
			
		||||
			<optgroup label="%i18n:@notes%">
 | 
			
		||||
				<option value="notes">%i18n:@charts.notes%</option>
 | 
			
		||||
				<option value="local-notes">%i18n:@charts.local-notes%</option>
 | 
			
		||||
				<option value="remote-notes">%i18n:@charts.remote-notes%</option>
 | 
			
		||||
				<option value="notes-total">%i18n:@charts.notes-total%</option>
 | 
			
		||||
			</optgroup>
 | 
			
		||||
			<optgroup label="%i18n:@drive%">
 | 
			
		||||
				<option value="drive-files">%i18n:@charts.drive-files%</option>
 | 
			
		||||
				<option value="drive-files-total">%i18n:@charts.drive-files-total%</option>
 | 
			
		||||
				<option value="drive">%i18n:@charts.drive%</option>
 | 
			
		||||
				<option value="drive-total">%i18n:@charts.drive-total%</option>
 | 
			
		||||
			</optgroup>
 | 
			
		||||
			<optgroup label="%i18n:@network%">
 | 
			
		||||
				<option value="network-requests">%i18n:@charts.network-requests%</option>
 | 
			
		||||
				<option value="network-time">%i18n:@charts.network-time%</option>
 | 
			
		||||
				<option value="network-usage">%i18n:@charts.network-usage%</option>
 | 
			
		||||
			</optgroup>
 | 
			
		||||
		</select>
 | 
			
		||||
		<div>
 | 
			
		||||
			<span @click="span = 'day'" :class="{ active: span == 'day' }">%i18n:@per-day%</span> | <span @click="span = 'hour'" :class="{ active: span == 'hour' }">%i18n:@per-hour%</span>
 | 
			
		||||
		</div>
 | 
			
		||||
	</header>
 | 
			
		||||
	<div>
 | 
			
		||||
		<x-chart v-if="chart" :data="data[0]" :opts="data[1]"/>
 | 
			
		||||
	</div>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<script lang="ts">
 | 
			
		||||
import Vue from 'vue';
 | 
			
		||||
import XChart from './charts.chart.ts';
 | 
			
		||||
 | 
			
		||||
const colors = {
 | 
			
		||||
	local: 'rgb(246, 88, 79)',
 | 
			
		||||
	remote: 'rgb(65, 221, 222)',
 | 
			
		||||
 | 
			
		||||
	localPlus: 'rgb(52, 178, 118)',
 | 
			
		||||
	remotePlus: 'rgb(158, 255, 209)',
 | 
			
		||||
	localMinus: 'rgb(255, 97, 74)',
 | 
			
		||||
	remoteMinus: 'rgb(255, 149, 134)',
 | 
			
		||||
 | 
			
		||||
	incoming: 'rgb(52, 178, 118)',
 | 
			
		||||
	outgoing: 'rgb(255, 97, 74)',
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const rgba = (color: string): string => {
 | 
			
		||||
	return color.replace('rgb', 'rgba').replace(')', ', 0.1)');
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const limit = 35;
 | 
			
		||||
 | 
			
		||||
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
 | 
			
		||||
const negate = arr => arr.map(x => -x);
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XChart
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			now: null,
 | 
			
		||||
			chart: null,
 | 
			
		||||
			chartType: 'notes',
 | 
			
		||||
			span: 'hour'
 | 
			
		||||
		};
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	computed: {
 | 
			
		||||
		data(): any {
 | 
			
		||||
			if (this.chart == null) return null;
 | 
			
		||||
			switch (this.chartType) {
 | 
			
		||||
				case 'federation-instances': return this.federationInstancesChart(false);
 | 
			
		||||
				case 'federation-instances-total': return this.federationInstancesChart(true);
 | 
			
		||||
				case 'users': return this.usersChart(false);
 | 
			
		||||
				case 'users-total': return this.usersChart(true);
 | 
			
		||||
				case 'notes': return this.notesChart('combined');
 | 
			
		||||
				case 'local-notes': return this.notesChart('local');
 | 
			
		||||
				case 'remote-notes': return this.notesChart('remote');
 | 
			
		||||
				case 'notes-total': return this.notesTotalChart();
 | 
			
		||||
				case 'drive': return this.driveChart();
 | 
			
		||||
				case 'drive-total': return this.driveTotalChart();
 | 
			
		||||
				case 'drive-files': return this.driveFilesChart();
 | 
			
		||||
				case 'drive-files-total': return this.driveFilesTotalChart();
 | 
			
		||||
				case 'network-requests': return this.networkRequestsChart();
 | 
			
		||||
				case 'network-time': return this.networkTimeChart();
 | 
			
		||||
				case 'network-usage': return this.networkUsageChart();
 | 
			
		||||
			}
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		stats(): any[] {
 | 
			
		||||
			const stats =
 | 
			
		||||
				this.span == 'day' ? this.chart.perDay :
 | 
			
		||||
				this.span == 'hour' ? this.chart.perHour :
 | 
			
		||||
				null;
 | 
			
		||||
 | 
			
		||||
			return stats;
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	async created() {
 | 
			
		||||
		this.now = new Date();
 | 
			
		||||
 | 
			
		||||
		const [perHour, perDay] = await Promise.all([Promise.all([
 | 
			
		||||
			(this as any).api('charts/federation', { limit: limit, span: 'hour' }),
 | 
			
		||||
			(this as any).api('charts/users', { limit: limit, span: 'hour' }),
 | 
			
		||||
			(this as any).api('charts/notes', { limit: limit, span: 'hour' }),
 | 
			
		||||
			(this as any).api('charts/drive', { limit: limit, span: 'hour' }),
 | 
			
		||||
			(this as any).api('charts/network', { limit: limit, span: 'hour' })
 | 
			
		||||
		]), Promise.all([
 | 
			
		||||
			(this as any).api('charts/federation', { limit: limit, span: 'day' }),
 | 
			
		||||
			(this as any).api('charts/users', { limit: limit, span: 'day' }),
 | 
			
		||||
			(this as any).api('charts/notes', { limit: limit, span: 'day' }),
 | 
			
		||||
			(this as any).api('charts/drive', { limit: limit, span: 'day' }),
 | 
			
		||||
			(this as any).api('charts/network', { limit: limit, span: 'day' })
 | 
			
		||||
		])]);
 | 
			
		||||
 | 
			
		||||
		const chart = {
 | 
			
		||||
			perHour: {
 | 
			
		||||
				federation: perHour[0],
 | 
			
		||||
				users: perHour[1],
 | 
			
		||||
				notes: perHour[2],
 | 
			
		||||
				drive: perHour[3],
 | 
			
		||||
				network: perHour[4]
 | 
			
		||||
			},
 | 
			
		||||
			perDay: {
 | 
			
		||||
				federation: perDay[0],
 | 
			
		||||
				users: perDay[1],
 | 
			
		||||
				notes: perDay[2],
 | 
			
		||||
				drive: perDay[3],
 | 
			
		||||
				network: perDay[4]
 | 
			
		||||
			}
 | 
			
		||||
		};
 | 
			
		||||
 | 
			
		||||
		this.chart = chart;
 | 
			
		||||
	},
 | 
			
		||||
 | 
			
		||||
	methods: {
 | 
			
		||||
		getDate(i: number) {
 | 
			
		||||
			const y = this.now.getFullYear();
 | 
			
		||||
			const m = this.now.getMonth();
 | 
			
		||||
			const d = this.now.getDate();
 | 
			
		||||
			const h = this.now.getHours();
 | 
			
		||||
 | 
			
		||||
			return (
 | 
			
		||||
				this.span == 'day' ? new Date(y, m, d - i) :
 | 
			
		||||
				this.span == 'hour' ? new Date(y, m, d, h - i) :
 | 
			
		||||
				null
 | 
			
		||||
			);
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		format(arr) {
 | 
			
		||||
			return arr.map((v, i) => ({ t: this.getDate(i).getTime(), y: v }));
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		federationInstancesChart(total: boolean): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Instances',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.localPlus),
 | 
			
		||||
					borderColor: colors.localPlus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(total
 | 
			
		||||
						? this.stats.federation.instance.total
 | 
			
		||||
						: sum(this.stats.federation.instance.inc, negate(this.stats.federation.instance.dec)))
 | 
			
		||||
				}]
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		notesChart(type: string): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'All',
 | 
			
		||||
					fill: false,
 | 
			
		||||
					borderColor: '#555',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					borderDash: [4, 4],
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(type == 'combined'
 | 
			
		||||
						? sum(this.stats.notes.local.inc, negate(this.stats.notes.local.dec), this.stats.notes.remote.inc, negate(this.stats.notes.remote.dec))
 | 
			
		||||
						: sum(this.stats.notes[type].inc, negate(this.stats.notes[type].dec))
 | 
			
		||||
					)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Renotes',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: 'rgba(161, 222, 65, 0.1)',
 | 
			
		||||
					borderColor: '#a1de41',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(type == 'combined'
 | 
			
		||||
						? sum(this.stats.notes.local.diffs.renote, this.stats.notes.remote.diffs.renote)
 | 
			
		||||
						: this.stats.notes[type].diffs.renote
 | 
			
		||||
					)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Replies',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: 'rgba(247, 121, 108, 0.1)',
 | 
			
		||||
					borderColor: '#f7796c',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(type == 'combined'
 | 
			
		||||
						? sum(this.stats.notes.local.diffs.reply, this.stats.notes.remote.diffs.reply)
 | 
			
		||||
						: this.stats.notes[type].diffs.reply
 | 
			
		||||
					)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Normal',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: 'rgba(65, 221, 222, 0.1)',
 | 
			
		||||
					borderColor: '#41ddde',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(type == 'combined'
 | 
			
		||||
						? sum(this.stats.notes.local.diffs.normal, this.stats.notes.remote.diffs.normal)
 | 
			
		||||
						: this.stats.notes[type].diffs.normal
 | 
			
		||||
					)
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				scales: {
 | 
			
		||||
					yAxes: [{
 | 
			
		||||
						ticks: {
 | 
			
		||||
							callback: value => {
 | 
			
		||||
								return Vue.filter('number')(value);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					callbacks: {
 | 
			
		||||
						label: (tooltipItem, data) => {
 | 
			
		||||
							const label = data.datasets[tooltipItem.datasetIndex].label || '';
 | 
			
		||||
							return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		notesTotalChart(): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Combined',
 | 
			
		||||
					fill: false,
 | 
			
		||||
					borderColor: '#555',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					borderDash: [4, 4],
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(sum(this.stats.notes.local.total, this.stats.notes.remote.total))
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Local',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.local),
 | 
			
		||||
					borderColor: colors.local,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.notes.local.total)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Remote',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.remote),
 | 
			
		||||
					borderColor: colors.remote,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.notes.remote.total)
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				scales: {
 | 
			
		||||
					yAxes: [{
 | 
			
		||||
						ticks: {
 | 
			
		||||
							callback: value => {
 | 
			
		||||
								return Vue.filter('number')(value);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					callbacks: {
 | 
			
		||||
						label: (tooltipItem, data) => {
 | 
			
		||||
							const label = data.datasets[tooltipItem.datasetIndex].label || '';
 | 
			
		||||
							return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		usersChart(total: boolean): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Combined',
 | 
			
		||||
					fill: false,
 | 
			
		||||
					borderColor: '#555',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					borderDash: [4, 4],
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(total
 | 
			
		||||
						? sum(this.stats.users.local.total, this.stats.users.remote.total)
 | 
			
		||||
						: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec), this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
 | 
			
		||||
					)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Local',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.local),
 | 
			
		||||
					borderColor: colors.local,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(total
 | 
			
		||||
						? this.stats.users.local.total
 | 
			
		||||
						: sum(this.stats.users.local.inc, negate(this.stats.users.local.dec))
 | 
			
		||||
					)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Remote',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.remote),
 | 
			
		||||
					borderColor: colors.remote,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(total
 | 
			
		||||
						? this.stats.users.remote.total
 | 
			
		||||
						: sum(this.stats.users.remote.inc, negate(this.stats.users.remote.dec))
 | 
			
		||||
					)
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				scales: {
 | 
			
		||||
					yAxes: [{
 | 
			
		||||
						ticks: {
 | 
			
		||||
							callback: value => {
 | 
			
		||||
								return Vue.filter('number')(value);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					callbacks: {
 | 
			
		||||
						label: (tooltipItem, data) => {
 | 
			
		||||
							const label = data.datasets[tooltipItem.datasetIndex].label || '';
 | 
			
		||||
							return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		driveChart(): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'All',
 | 
			
		||||
					fill: false,
 | 
			
		||||
					borderColor: '#555',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					borderDash: [4, 4],
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(sum(this.stats.drive.local.incSize, negate(this.stats.drive.local.decSize), this.stats.drive.remote.incSize, negate(this.stats.drive.remote.decSize)))
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Local +',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.localPlus),
 | 
			
		||||
					borderColor: colors.localPlus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.drive.local.incSize)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Local -',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.localMinus),
 | 
			
		||||
					borderColor: colors.localMinus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(negate(this.stats.drive.local.decSize))
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Remote +',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.remotePlus),
 | 
			
		||||
					borderColor: colors.remotePlus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.drive.remote.incSize)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Remote -',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.remoteMinus),
 | 
			
		||||
					borderColor: colors.remoteMinus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(negate(this.stats.drive.remote.decSize))
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				scales: {
 | 
			
		||||
					yAxes: [{
 | 
			
		||||
						ticks: {
 | 
			
		||||
							callback: value => {
 | 
			
		||||
								return Vue.filter('bytes')(value, 1);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					callbacks: {
 | 
			
		||||
						label: (tooltipItem, data) => {
 | 
			
		||||
							const label = data.datasets[tooltipItem.datasetIndex].label || '';
 | 
			
		||||
							return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		driveTotalChart(): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Combined',
 | 
			
		||||
					fill: false,
 | 
			
		||||
					borderColor: '#555',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					borderDash: [4, 4],
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(sum(this.stats.drive.local.totalSize, this.stats.drive.remote.totalSize))
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Local',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.local),
 | 
			
		||||
					borderColor: colors.local,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.drive.local.totalSize)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Remote',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.remote),
 | 
			
		||||
					borderColor: colors.remote,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.drive.remote.totalSize)
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				scales: {
 | 
			
		||||
					yAxes: [{
 | 
			
		||||
						ticks: {
 | 
			
		||||
							callback: value => {
 | 
			
		||||
								return Vue.filter('bytes')(value, 1);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					callbacks: {
 | 
			
		||||
						label: (tooltipItem, data) => {
 | 
			
		||||
							const label = data.datasets[tooltipItem.datasetIndex].label || '';
 | 
			
		||||
							return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		driveFilesChart(): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'All',
 | 
			
		||||
					fill: false,
 | 
			
		||||
					borderColor: '#555',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					borderDash: [4, 4],
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(sum(this.stats.drive.local.incCount, negate(this.stats.drive.local.decCount), this.stats.drive.remote.incCount, negate(this.stats.drive.remote.decCount)))
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Local +',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.localPlus),
 | 
			
		||||
					borderColor: colors.localPlus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.drive.local.incCount)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Local -',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.localMinus),
 | 
			
		||||
					borderColor: colors.localMinus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(negate(this.stats.drive.local.decCount))
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Remote +',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.remotePlus),
 | 
			
		||||
					borderColor: colors.remotePlus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.drive.remote.incCount)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Remote -',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.remoteMinus),
 | 
			
		||||
					borderColor: colors.remoteMinus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(negate(this.stats.drive.remote.decCount))
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				scales: {
 | 
			
		||||
					yAxes: [{
 | 
			
		||||
						ticks: {
 | 
			
		||||
							callback: value => {
 | 
			
		||||
								return Vue.filter('number')(value);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					callbacks: {
 | 
			
		||||
						label: (tooltipItem, data) => {
 | 
			
		||||
							const label = data.datasets[tooltipItem.datasetIndex].label || '';
 | 
			
		||||
							return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		driveFilesTotalChart(): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Combined',
 | 
			
		||||
					fill: false,
 | 
			
		||||
					borderColor: '#555',
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					borderDash: [4, 4],
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(sum(this.stats.drive.local.totalCount, this.stats.drive.remote.totalCount))
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Local',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.local),
 | 
			
		||||
					borderColor: colors.local,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.drive.local.totalCount)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Remote',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.remote),
 | 
			
		||||
					borderColor: colors.remote,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.drive.remote.totalCount)
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				scales: {
 | 
			
		||||
					yAxes: [{
 | 
			
		||||
						ticks: {
 | 
			
		||||
							callback: value => {
 | 
			
		||||
								return Vue.filter('number')(value);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					callbacks: {
 | 
			
		||||
						label: (tooltipItem, data) => {
 | 
			
		||||
							const label = data.datasets[tooltipItem.datasetIndex].label || '';
 | 
			
		||||
							return `${label}: ${Vue.filter('number')(tooltipItem.yLabel)}`;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		networkRequestsChart(): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Incoming',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.localPlus),
 | 
			
		||||
					borderColor: colors.localPlus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.network.incomingRequests)
 | 
			
		||||
				}]
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		networkTimeChart(): any {
 | 
			
		||||
			const data = [];
 | 
			
		||||
 | 
			
		||||
			for (let i = 0; i < limit; i++) {
 | 
			
		||||
				data.push(this.stats.network.incomingRequests[i] != 0 ? (this.stats.network.totalTime[i] / this.stats.network.incomingRequests[i]) : 0);
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Avg time (ms)',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.localPlus),
 | 
			
		||||
					borderColor: colors.localPlus,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(data)
 | 
			
		||||
				}]
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
 | 
			
		||||
		networkUsageChart(): any {
 | 
			
		||||
			return [{
 | 
			
		||||
				datasets: [{
 | 
			
		||||
					label: 'Incoming',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.incoming),
 | 
			
		||||
					borderColor: colors.incoming,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.network.incomingBytes)
 | 
			
		||||
				}, {
 | 
			
		||||
					label: 'Outgoing',
 | 
			
		||||
					fill: true,
 | 
			
		||||
					backgroundColor: rgba(colors.outgoing),
 | 
			
		||||
					borderColor: colors.outgoing,
 | 
			
		||||
					borderWidth: 2,
 | 
			
		||||
					pointBackgroundColor: '#fff',
 | 
			
		||||
					lineTension: 0,
 | 
			
		||||
					data: this.format(this.stats.network.outgoingBytes)
 | 
			
		||||
				}]
 | 
			
		||||
			}, {
 | 
			
		||||
				scales: {
 | 
			
		||||
					yAxes: [{
 | 
			
		||||
						ticks: {
 | 
			
		||||
							callback: value => {
 | 
			
		||||
								return Vue.filter('bytes')(value, 1);
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
					}]
 | 
			
		||||
				},
 | 
			
		||||
				tooltips: {
 | 
			
		||||
					callbacks: {
 | 
			
		||||
						label: (tooltipItem, data) => {
 | 
			
		||||
							const label = data.datasets[tooltipItem.datasetIndex].label || '';
 | 
			
		||||
							return `${label}: ${Vue.filter('bytes')(tooltipItem.yLabel, 1)}`;
 | 
			
		||||
						}
 | 
			
		||||
					}
 | 
			
		||||
				}
 | 
			
		||||
			}];
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
.gkgckalzgidaygcxnugepioremxvxvpt
 | 
			
		||||
	padding 32px
 | 
			
		||||
	background #fff
 | 
			
		||||
	box-shadow 0 2px 8px rgba(#000, 0.1)
 | 
			
		||||
 | 
			
		||||
	*
 | 
			
		||||
		user-select none
 | 
			
		||||
 | 
			
		||||
	> header
 | 
			
		||||
		display flex
 | 
			
		||||
		margin 0 0 1em 0
 | 
			
		||||
		padding 0 0 8px 0
 | 
			
		||||
		font-size 1em
 | 
			
		||||
		color #555
 | 
			
		||||
		border-bottom solid 1px #eee
 | 
			
		||||
 | 
			
		||||
		> b
 | 
			
		||||
			margin-right 8px
 | 
			
		||||
 | 
			
		||||
		> *:last-child
 | 
			
		||||
			margin-left auto
 | 
			
		||||
 | 
			
		||||
			*
 | 
			
		||||
				&:not(.active)
 | 
			
		||||
					color var(--primary)
 | 
			
		||||
					cursor pointer
 | 
			
		||||
 | 
			
		||||
	> div
 | 
			
		||||
		> *
 | 
			
		||||
			display block
 | 
			
		||||
			height 350px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
@@ -13,7 +13,7 @@
 | 
			
		||||
		@change-selection="onChangeSelection"
 | 
			
		||||
	/>
 | 
			
		||||
	<div :class="$style.footer">
 | 
			
		||||
		<button :class="$style.upload" title="%i18n:@upload%" @click="upload">%fa:upload%</button>
 | 
			
		||||
		<button :class="$style.upload" title="%i18n:@upload%" @click="upload"><fa icon="upload"/></button>
 | 
			
		||||
		<button :class="$style.cancel" @click="cancel">%i18n:@cancel%</button>
 | 
			
		||||
		<button :class="$style.ok" :disabled="multiple && files.length == 0" @click="ok">%i18n:@ok%</button>
 | 
			
		||||
	</div>
 | 
			
		||||
@@ -28,7 +28,7 @@ export default Vue.extend({
 | 
			
		||||
			default: false
 | 
			
		||||
		},
 | 
			
		||||
		title: {
 | 
			
		||||
			default: '%fa:R file%%i18n:@choose-prompt%'
 | 
			
		||||
			default: '<fa :icon="['far', 'file']"/>%i18n:@choose-prompt%'
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
@@ -62,7 +62,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.title
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
.count
 | 
			
		||||
 
 | 
			
		||||
@@ -21,7 +21,7 @@ import Vue from 'vue';
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	props: {
 | 
			
		||||
		title: {
 | 
			
		||||
			default: '%fa:R folder%%i18n:@choose-prompt%'
 | 
			
		||||
			default: '<fa :icon="['far', 'folder']"/>%i18n:@choose-prompt%'
 | 
			
		||||
		}
 | 
			
		||||
	},
 | 
			
		||||
	methods: {
 | 
			
		||||
@@ -40,7 +40,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.title
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
.browser
 | 
			
		||||
 
 | 
			
		||||
@@ -3,13 +3,13 @@
 | 
			
		||||
	<li v-for="(item, i) in menu" :class="item ? item.type : item === null ? 'divider' : null">
 | 
			
		||||
		<template v-if="item">
 | 
			
		||||
			<template v-if="item.type == null || item.type == 'item'">
 | 
			
		||||
				<p @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</p>
 | 
			
		||||
				<p @click="click(item)"><i v-if="item.icon" :class="$style.icon"><fa :icon="item.icon"/></i>{{ item.text }}</p>
 | 
			
		||||
			</template>
 | 
			
		||||
			<template v-else-if="item.type == 'link'">
 | 
			
		||||
				<a :href="item.href" :target="item.target" @click="click(item)"><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}</a>
 | 
			
		||||
				<a :href="item.href" :target="item.target" @click="click(item)"><i v-if="item.icon" :class="$style.icon"><fa :icon="item.icon"/></i>{{ item.text }}</a>
 | 
			
		||||
			</template>
 | 
			
		||||
			<template v-else-if="item.type == 'nest'">
 | 
			
		||||
				<p><span :class="$style.icon" v-if="item.icon" v-html="item.icon"></span>{{ item.text }}...<span class="caret">%fa:caret-right%</span></p>
 | 
			
		||||
				<p><i v-if="item.icon" :class="$style.icon"><fa :icon="item.icon"/></i>{{ item.text }}...<span class="caret"><fa icon="caret-right"/></span></p>
 | 
			
		||||
				<me-nu :menu="item.menu" @x="click"/>
 | 
			
		||||
			</template>
 | 
			
		||||
		</template>
 | 
			
		||||
@@ -113,9 +113,9 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.icon
 | 
			
		||||
	> *
 | 
			
		||||
		width 28px
 | 
			
		||||
		margin-left -28px
 | 
			
		||||
		text-align center
 | 
			
		||||
	display inline-block
 | 
			
		||||
	width 28px
 | 
			
		||||
	margin-left -28px
 | 
			
		||||
	text-align center
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
<template>
 | 
			
		||||
	<mk-window ref="window" is-modal width="800px" :can-close="false">
 | 
			
		||||
		<span slot="header">%fa:crop%{{ title }}</span>
 | 
			
		||||
		<span slot="header"><fa icon="crop"/>{{ title }}</span>
 | 
			
		||||
		<div class="body">
 | 
			
		||||
			<vue-cropper ref="cropper"
 | 
			
		||||
				:src="image.url"
 | 
			
		||||
@@ -64,7 +64,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.header
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
.img
 | 
			
		||||
 
 | 
			
		||||
@@ -91,8 +91,6 @@ export default Vue.extend({
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" scoped>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.mk-dialog
 | 
			
		||||
	> .bg
 | 
			
		||||
		display block
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
<mk-window ref="window" @closed="destroyDom" width="800px" height="500px" :popout-url="popout">
 | 
			
		||||
	<template slot="header">
 | 
			
		||||
		<p v-if="usage" :class="$style.info"><b>{{ usage.toFixed(1) }}%</b> %i18n:@used%</p>
 | 
			
		||||
		<span :class="$style.title">%fa:cloud%%i18n:common.drive%</span>
 | 
			
		||||
		<span :class="$style.title"><fa icon="cloud"/>%i18n:common.drive%</span>
 | 
			
		||||
	</template>
 | 
			
		||||
	<mk-drive :class="$style.browser" multiple :init-folder="folder" ref="browser"/>
 | 
			
		||||
</mk-window>
 | 
			
		||||
@@ -39,7 +39,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.title
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
.info
 | 
			
		||||
 
 | 
			
		||||
@@ -71,27 +71,27 @@ export default Vue.extend({
 | 
			
		||||
			contextmenu((this as any).os)(e, [{
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:@contextmenu.rename%',
 | 
			
		||||
				icon: '%fa:i-cursor%',
 | 
			
		||||
				icon: 'i-cursor',
 | 
			
		||||
				action: this.rename
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: this.file.isSensitive ? '%i18n:@contextmenu.unmark-as-sensitive%' : '%i18n:@contextmenu.mark-as-sensitive%',
 | 
			
		||||
				icon: this.file.isSensitive ? '%fa:R eye%' : '%fa:R eye-slash%',
 | 
			
		||||
				icon: this.file.isSensitive ? ['far', 'eye'] : ['far', 'eye-slash'],
 | 
			
		||||
				action: this.toggleSensitive
 | 
			
		||||
			}, null, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:@contextmenu.copy-url%',
 | 
			
		||||
				icon: '%fa:link%',
 | 
			
		||||
				icon: 'link',
 | 
			
		||||
				action: this.copyUrl
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'link',
 | 
			
		||||
				href: `${this.file.url}?download`,
 | 
			
		||||
				text: '%i18n:@contextmenu.download%',
 | 
			
		||||
				icon: '%fa:download%',
 | 
			
		||||
				icon: 'download',
 | 
			
		||||
			}, null, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:common.delete%',
 | 
			
		||||
				icon: '%fa:R trash-alt%',
 | 
			
		||||
				icon: ['far', 'trash-alt'],
 | 
			
		||||
				action: this.deleteFile
 | 
			
		||||
			}, null, {
 | 
			
		||||
				type: 'nest',
 | 
			
		||||
@@ -170,7 +170,7 @@ export default Vue.extend({
 | 
			
		||||
		copyUrl() {
 | 
			
		||||
			copyToClipboard(this.file.url);
 | 
			
		||||
			(this as any).apis.dialog({
 | 
			
		||||
				title: '%fa:check%%i18n:@contextmenu.copied%',
 | 
			
		||||
				title: '<fa icon="check"/>%i18n:@contextmenu.copied%',
 | 
			
		||||
				text: '%i18n:@contextmenu.copied-url-to-clipboard%',
 | 
			
		||||
				actions: [{
 | 
			
		||||
					text: '%i18n:common.ok%'
 | 
			
		||||
 
 | 
			
		||||
@@ -16,8 +16,8 @@
 | 
			
		||||
	:title="title"
 | 
			
		||||
>
 | 
			
		||||
	<p class="name">
 | 
			
		||||
		<template v-if="hover">%fa:R folder-open .fw%</template>
 | 
			
		||||
		<template v-if="!hover">%fa:R folder .fw%</template>
 | 
			
		||||
		<template v-if="hover"><fa :icon="['far', 'folder-open']" fixed-width/></template>
 | 
			
		||||
		<template v-if="!hover"><fa :icon="['far', 'folder']" fixed-width/></template>
 | 
			
		||||
		{{ folder.name }}
 | 
			
		||||
	</p>
 | 
			
		||||
</div>
 | 
			
		||||
@@ -55,22 +55,22 @@ export default Vue.extend({
 | 
			
		||||
			contextmenu((this as any).os)(e, [{
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:@contextmenu.move-to-this-folder%',
 | 
			
		||||
				icon: '%fa:arrow-right%',
 | 
			
		||||
				icon: 'arrow-right',
 | 
			
		||||
				action: this.go
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:@contextmenu.show-in-new-window%',
 | 
			
		||||
				icon: '%fa:R window-restore%',
 | 
			
		||||
				icon: ['far', 'window-restore'],
 | 
			
		||||
				action: this.newWindow
 | 
			
		||||
			}, null, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:@contextmenu.rename%',
 | 
			
		||||
				icon: '%fa:i-cursor%',
 | 
			
		||||
				icon: 'i-cursor',
 | 
			
		||||
				action: this.rename
 | 
			
		||||
			}, null, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:common.delete%',
 | 
			
		||||
				icon: '%fa:R trash-alt%',
 | 
			
		||||
				icon: ['far', 'trash-alt'],
 | 
			
		||||
				action: this.deleteFolder
 | 
			
		||||
			}], {
 | 
			
		||||
					closed: () => {
 | 
			
		||||
@@ -155,7 +155,7 @@ export default Vue.extend({
 | 
			
		||||
					switch (err) {
 | 
			
		||||
						case 'detected-circular-definition':
 | 
			
		||||
							(this as any).apis.dialog({
 | 
			
		||||
								title: '%fa:exclamation-triangle%%i18n:@unable-to-process%',
 | 
			
		||||
								title: '<fa icon="exclamation-triangle"/>%i18n:@unable-to-process%',
 | 
			
		||||
								text: '%i18n:@circular-reference-detected%',
 | 
			
		||||
								actions: [{
 | 
			
		||||
									text: '%i18n:common.ok%'
 | 
			
		||||
@@ -255,7 +255,7 @@ export default Vue.extend({
 | 
			
		||||
		font-size 0.9em
 | 
			
		||||
		color var(--desktopDriveFolderFg)
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
			margin-left 2px
 | 
			
		||||
			text-align left
 | 
			
		||||
 
 | 
			
		||||
@@ -7,7 +7,7 @@
 | 
			
		||||
	@dragleave="onDragleave"
 | 
			
		||||
	@drop.stop="onDrop"
 | 
			
		||||
>
 | 
			
		||||
	<template v-if="folder == null">%fa:cloud%</template>
 | 
			
		||||
	<i v-if="folder == null" class="cloud"><fa icon="cloud"/></i>
 | 
			
		||||
	<span>{{ folder == null ? '%i18n:common.drive%' : folder.name }}</span>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
@@ -110,7 +110,7 @@ export default Vue.extend({
 | 
			
		||||
	&[data-draghover]
 | 
			
		||||
		background #eee
 | 
			
		||||
 | 
			
		||||
	[data-fa].cloud
 | 
			
		||||
	i.cloud
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -4,10 +4,10 @@
 | 
			
		||||
		<div class="path" @contextmenu.prevent.stop="() => {}">
 | 
			
		||||
			<x-nav-folder :class="{ current: folder == null }"/>
 | 
			
		||||
			<template v-for="folder in hierarchyFolders">
 | 
			
		||||
				<span class="separator">%fa:angle-right%</span>
 | 
			
		||||
				<span class="separator"><fa icon="angle-right"/></span>
 | 
			
		||||
				<x-nav-folder :folder="folder" :key="folder.id"/>
 | 
			
		||||
			</template>
 | 
			
		||||
			<span class="separator" v-if="folder != null">%fa:angle-right%</span>
 | 
			
		||||
			<span class="separator" v-if="folder != null"><fa icon="angle-right"/></span>
 | 
			
		||||
			<span class="folder current" v-if="folder != null">{{ folder.name }}</span>
 | 
			
		||||
		</div>
 | 
			
		||||
		<!--
 | 
			
		||||
@@ -138,17 +138,17 @@ export default Vue.extend({
 | 
			
		||||
			contextmenu((this as any).os)(e, [{
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:@contextmenu.create-folder%',
 | 
			
		||||
				icon: '%fa:R folder%',
 | 
			
		||||
				icon: ['far', 'folder'],
 | 
			
		||||
				action: this.createFolder
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:@contextmenu.upload%',
 | 
			
		||||
				icon: '%fa:upload%',
 | 
			
		||||
				icon: 'upload',
 | 
			
		||||
				action: this.selectLocalFile
 | 
			
		||||
			}, {
 | 
			
		||||
				type: 'item',
 | 
			
		||||
				text: '%i18n:@contextmenu.url-upload%',
 | 
			
		||||
				icon: '%fa:cloud-upload-alt%',
 | 
			
		||||
				icon: 'cloud-upload-alt',
 | 
			
		||||
				action: this.urlUpload
 | 
			
		||||
			}]);
 | 
			
		||||
		},
 | 
			
		||||
@@ -313,7 +313,7 @@ export default Vue.extend({
 | 
			
		||||
					switch (err) {
 | 
			
		||||
						case 'detected-circular-definition':
 | 
			
		||||
							(this as any).apis.dialog({
 | 
			
		||||
								title: '%fa:exclamation-triangle%%i18n:@unable-to-process%',
 | 
			
		||||
								title: '<fa icon="exclamation-triangle"/>%i18n:@unable-to-process%',
 | 
			
		||||
								text: '%i18n:@circular-reference-detected%',
 | 
			
		||||
								actions: [{
 | 
			
		||||
									text: '%i18n:common.ok%'
 | 
			
		||||
@@ -343,7 +343,7 @@ export default Vue.extend({
 | 
			
		||||
				});
 | 
			
		||||
 | 
			
		||||
				(this as any).apis.dialog({
 | 
			
		||||
					title: '%fa:check%%i18n:@url-upload-requested%',
 | 
			
		||||
					title: '<fa icon="check"/>%i18n:@url-upload-requested%',
 | 
			
		||||
					text: '%i18n:@may-take-time%',
 | 
			
		||||
					actions: [{
 | 
			
		||||
						text: '%i18n:common.ok%'
 | 
			
		||||
@@ -359,7 +359,7 @@ export default Vue.extend({
 | 
			
		||||
			}).then(name => {
 | 
			
		||||
				(this as any).api('drive/folders/create', {
 | 
			
		||||
					name: name,
 | 
			
		||||
					folderId: this.folder ? this.folder.id : undefined
 | 
			
		||||
					parentId: this.folder ? this.folder.id : undefined
 | 
			
		||||
				}).then(folder => {
 | 
			
		||||
					this.addFolder(folder, true);
 | 
			
		||||
				});
 | 
			
		||||
@@ -613,9 +613,6 @@ export default Vue.extend({
 | 
			
		||||
				line-height 38px
 | 
			
		||||
				cursor pointer
 | 
			
		||||
 | 
			
		||||
				i
 | 
			
		||||
					margin-right 4px
 | 
			
		||||
 | 
			
		||||
				*
 | 
			
		||||
					pointer-events none
 | 
			
		||||
 | 
			
		||||
@@ -635,7 +632,7 @@ export default Vue.extend({
 | 
			
		||||
					opacity 0.5
 | 
			
		||||
					cursor default
 | 
			
		||||
 | 
			
		||||
					> [data-fa]
 | 
			
		||||
					> [data-icon]
 | 
			
		||||
						margin 0
 | 
			
		||||
 | 
			
		||||
		> .search
 | 
			
		||||
 
 | 
			
		||||
@@ -5,13 +5,13 @@
 | 
			
		||||
	:disabled="wait"
 | 
			
		||||
>
 | 
			
		||||
	<template v-if="!wait">
 | 
			
		||||
		<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked">%fa:hourglass-half%<template v-if="size == 'big'"> %i18n:@request-pending%</template></template>
 | 
			
		||||
		<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked">%fa:hourglass-start%<template v-if="size == 'big'"> %i18n:@follow-processing%</template></template>
 | 
			
		||||
		<template v-else-if="u.isFollowing">%fa:minus%<template v-if="size == 'big'"> %i18n:@following%</template></template>
 | 
			
		||||
		<template v-else-if="!u.isFollowing && u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow-request%</template></template>
 | 
			
		||||
		<template v-else-if="!u.isFollowing && !u.isLocked">%fa:plus%<template v-if="size == 'big'"> %i18n:@follow%</template></template>
 | 
			
		||||
		<template v-if="u.hasPendingFollowRequestFromYou && u.isLocked"><fa icon="hourglass-half"/><template v-if="size == 'big'"> %i18n:@request-pending%</template></template>
 | 
			
		||||
		<template v-else-if="u.hasPendingFollowRequestFromYou && !u.isLocked"><fa icon="hourglass-start"/><template v-if="size == 'big'"> %i18n:@follow-processing%</template></template>
 | 
			
		||||
		<template v-else-if="u.isFollowing"><fa icon="minus"/><template v-if="size == 'big'"> %i18n:@following%</template></template>
 | 
			
		||||
		<template v-else-if="!u.isFollowing && u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> %i18n:@follow-request%</template></template>
 | 
			
		||||
		<template v-else-if="!u.isFollowing && !u.isLocked"><fa icon="plus"/><template v-if="size == 'big'"> %i18n:@follow%</template></template>
 | 
			
		||||
	</template>
 | 
			
		||||
	<template v-else>%fa:spinner .pulse .fw%</template>
 | 
			
		||||
	<template v-else><fa icon="spinner .pulse" fixed-width/></template>
 | 
			
		||||
</button>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,9 @@
 | 
			
		||||
		</div>
 | 
			
		||||
	</div>
 | 
			
		||||
	<p class="empty" v-if="!fetching && users.length == 0">%i18n:@empty%</p>
 | 
			
		||||
	<p class="fetching" v-if="fetching">%fa:spinner .pulse .fw%%i18n:@fetching%<mk-ellipsis/></p>
 | 
			
		||||
	<p class="fetching" v-if="fetching"><fa icon="spinner .pulse" fixed-width/>%i18n:@fetching%<mk-ellipsis/></p>
 | 
			
		||||
	<a class="refresh" @click="refresh">%i18n:@refresh%</a>
 | 
			
		||||
	<button class="close" @click="destroyDom()" title="%i18n:@close%">%fa:times%</button>
 | 
			
		||||
	<button class="close" @click="destroyDom()" title="%i18n:@close%"><fa icon="times"/></button>
 | 
			
		||||
</div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -124,7 +124,7 @@ export default Vue.extend({
 | 
			
		||||
		text-align center
 | 
			
		||||
		color #aaa
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			margin-right 4px
 | 
			
		||||
 | 
			
		||||
	> .refresh
 | 
			
		||||
@@ -155,7 +155,7 @@ export default Vue.extend({
 | 
			
		||||
		&:active
 | 
			
		||||
			color #222
 | 
			
		||||
 | 
			
		||||
		> [data-fa]
 | 
			
		||||
		> [data-icon]
 | 
			
		||||
			padding 14px
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" width="500px" height="560px" :popout-url="popout" @closed="destroyDom">
 | 
			
		||||
	<span slot="header" :class="$style.header">%fa:gamepad%%i18n:@game%</span>
 | 
			
		||||
	<mk-reversi :class="$style.content" @gamed="g => game = g"/>
 | 
			
		||||
	<span slot="header" :class="$style.header"><fa icon="gamepad"/>%i18n:@game%</span>
 | 
			
		||||
	<x-reversi :class="$style.content" @gamed="g => game = g"/>
 | 
			
		||||
</mk-window>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
@@ -10,6 +10,9 @@ import Vue from 'vue';
 | 
			
		||||
import { url } from '../../../config';
 | 
			
		||||
 | 
			
		||||
export default Vue.extend({
 | 
			
		||||
	components: {
 | 
			
		||||
		XReversi: () => import('../../../common/views/components/games/reversi/reversi.vue')
 | 
			
		||||
	},
 | 
			
		||||
	data() {
 | 
			
		||||
		return {
 | 
			
		||||
			game: null
 | 
			
		||||
@@ -27,7 +30,7 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
.header
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
.content
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<div class="mk-home" :data-customize="customize">
 | 
			
		||||
	<div class="customize" v-if="customize">
 | 
			
		||||
		<router-link to="/">%fa:check%%i18n:@done%</router-link>
 | 
			
		||||
		<router-link to="/"><fa icon="check"/>%i18n:@done%</router-link>
 | 
			
		||||
		<div>
 | 
			
		||||
			<div class="adder">
 | 
			
		||||
				<p>%i18n:@add-widget%</p>
 | 
			
		||||
@@ -185,7 +185,7 @@ export default Vue.extend({
 | 
			
		||||
	methods: {
 | 
			
		||||
		hint() {
 | 
			
		||||
			(this as any).apis.dialog({
 | 
			
		||||
				title: '%fa:info-circle%%i18n:common.customization-tips.title%',
 | 
			
		||||
				title: '<fa icon="info-circle"/>%i18n:common.customization-tips.title%',
 | 
			
		||||
				text: '<p>%i18n:common.customization-tips.paragraph1%</p>' +
 | 
			
		||||
					'<p>%i18n:common.customization-tips.paragraph2%</p>' +
 | 
			
		||||
					'<p>%i18n:common.customization-tips.paragraph3%</p>' +
 | 
			
		||||
@@ -299,7 +299,7 @@ export default Vue.extend({
 | 
			
		||||
				background var(--primaryDarken10)
 | 
			
		||||
				transition background 0s ease
 | 
			
		||||
 | 
			
		||||
			> [data-fa]
 | 
			
		||||
			> [data-icon]
 | 
			
		||||
				margin-right 8px
 | 
			
		||||
 | 
			
		||||
		> div
 | 
			
		||||
 
 | 
			
		||||
@@ -1,7 +1,7 @@
 | 
			
		||||
<template>
 | 
			
		||||
<mk-window ref="window" is-modal width="500px" @before-close="beforeClose" @closed="destroyDom">
 | 
			
		||||
	<span slot="header" :class="$style.header">
 | 
			
		||||
		%fa:i-cursor%{{ title }}
 | 
			
		||||
		<fa icon="i-cursor"/>{{ title }}
 | 
			
		||||
	</span>
 | 
			
		||||
 | 
			
		||||
	<div :class="$style.body">
 | 
			
		||||
@@ -76,10 +76,8 @@ export default Vue.extend({
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<style lang="stylus" module>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.header
 | 
			
		||||
	> [data-fa]
 | 
			
		||||
	> [data-icon]
 | 
			
		||||
		margin-right 4px
 | 
			
		||||
 | 
			
		||||
.body
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user