Compare commits
102 Commits
Author | SHA1 | Date | |
---|---|---|---|
796252357e | |||
b0344d52e9 | |||
8e6da3a0d9 | |||
756e4eaeec | |||
3126d0730a | |||
748e9f15df | |||
7c714f5788 | |||
d3c3ad839b | |||
168de3c316 | |||
9e20fc5c88 | |||
1ff5151786 | |||
a56738a331 | |||
2d04a3d6d2 | |||
e0a55b9100 | |||
0a57eecb3a | |||
51f98020f6 | |||
70123805e1 | |||
1448b11d00 | |||
490b81ed05 | |||
96f675abed | |||
4cd451c613 | |||
187792dfc4 | |||
3dde561fe5 | |||
a75ec45172 | |||
3732ddf75f | |||
b8d8097734 | |||
1092532292 | |||
8a79ba0e2b | |||
ca2949fbb4 | |||
17b373ac07 | |||
7aa66f438f | |||
73641fd78d | |||
64aac9d6ad | |||
2be13736c8 | |||
ff4f5fec1d | |||
7d64f8abe4 | |||
88e6929e9f | |||
5fb0a995dd | |||
58a04ce1a5 | |||
f74e0d9123 | |||
c6249b82d4 | |||
d6131c0b09 | |||
e62c810b7c | |||
2851a1a7ef | |||
12cd2709d6 | |||
bf54e58873 | |||
6b473e3a5c | |||
4b68abd963 | |||
0e764a2b3e | |||
9d1ed1eb0d | |||
a09a3465a2 | |||
001969efaf | |||
e7c515da9a | |||
8367c7dd49 | |||
55e6cae240 | |||
5553c3fb17 | |||
026265cb1e | |||
289c76a802 | |||
7c1bc1d6bc | |||
90cf0d32b5 | |||
88d934f922 | |||
abf11bb03c | |||
2d1f50303d | |||
9fb7c4091f | |||
2fdec27ab0 | |||
aff56469ed | |||
419cb7fbad | |||
7882851539 | |||
358299cf0e | |||
5a2af24869 | |||
6d35872af5 | |||
559dfdaa80 | |||
79c49bc926 | |||
76c538ad25 | |||
e76e358d98 | |||
76f37671b4 | |||
a9fc176c3c | |||
8b13e3c327 | |||
c3cd6ad2d2 | |||
3444b9c9c8 | |||
ca6fc9cd79 | |||
4747ae8b61 | |||
b6c50d63a0 | |||
52ebf2055e | |||
d0af2c2a98 | |||
10216af48a | |||
d0c8d537f5 | |||
1903aaf351 | |||
e6cdf1b995 | |||
2900d22cdc | |||
dc072d4706 | |||
8ae14f146b | |||
57444c6c3f | |||
759719d124 | |||
59782973be | |||
6c647ea91c | |||
c9763dabe1 | |||
da82754659 | |||
a3cc0ad18b | |||
2e8e5c2751 | |||
a60d83b101 | |||
6d45265763 |
@ -84,40 +84,54 @@ redis:
|
||||
drive:
|
||||
storage: 'fs'
|
||||
|
||||
# OR
|
||||
# OR
|
||||
|
||||
# storage: 'minio'
|
||||
# bucket:
|
||||
# prefix:
|
||||
# config:
|
||||
# endPoint:
|
||||
# port:
|
||||
# useSSL:
|
||||
# accessKey:
|
||||
# secretKey:
|
||||
#drive:
|
||||
# storage: 'minio'
|
||||
# bucket:
|
||||
# prefix:
|
||||
# config:
|
||||
# endPoint:
|
||||
# port:
|
||||
# useSSL:
|
||||
# accessKey:
|
||||
# secretKey:
|
||||
|
||||
# S3 example
|
||||
# storage: 'minio'
|
||||
# bucket: bucket-name
|
||||
# prefix: files
|
||||
# config:
|
||||
# endPoint: s3-us-west-2.amazonaws.com
|
||||
# region: us-west-2
|
||||
# useSSL: true
|
||||
# accessKey: XXX
|
||||
# secretKey: YYY
|
||||
# S3/GCS example
|
||||
#
|
||||
# * Replace <endpoint> to
|
||||
# S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
|
||||
# GCS: use 'storage.googleapis.com'
|
||||
#
|
||||
# * Replace <region> to
|
||||
# S3: see https://docs.aws.amazon.com/general/latest/gr/rande.html#s3_region
|
||||
# GCS: not needed (just delete the region line)
|
||||
#
|
||||
#drive:
|
||||
# storage: 'minio'
|
||||
# bucket: bucket-name
|
||||
# prefix: files
|
||||
# baseUrl: https://bucket-name.<endpoint>
|
||||
# config:
|
||||
# endPoint: <endpoint>
|
||||
# region: <region>
|
||||
# useSSL: true
|
||||
# accessKey: XXX
|
||||
# secretKey: YYY
|
||||
|
||||
# S3 example (with CDN, custom domain)
|
||||
# storage: 'minio'
|
||||
# bucket: drive.example.com
|
||||
# prefix: files
|
||||
# baseUrl: https://drive.example.com
|
||||
# config:
|
||||
# endPoint: s3-us-west-2.amazonaws.com
|
||||
# region: us-west-2
|
||||
# useSSL: true
|
||||
# accessKey: XXX
|
||||
# secretKey: YYY
|
||||
# S3/GCS example (with CDN, custom domain)
|
||||
#
|
||||
#drive:
|
||||
# storage: 'minio'
|
||||
# bucket: drive.example.com
|
||||
# prefix: files
|
||||
# baseUrl: https://drive.example.com
|
||||
# config:
|
||||
# endPoint: <endpoint>
|
||||
# region: <region>
|
||||
# useSSL: true
|
||||
# accessKey: XXX
|
||||
# secretKey: YYY
|
||||
|
||||
# ┌───────────────┐
|
||||
#───┘ ID generation └───────────────────────────────────────────
|
||||
@ -149,3 +163,6 @@ autoAdmin: true
|
||||
|
||||
# Clustering
|
||||
#clusterLimit: 1
|
||||
|
||||
# IP address family used for outgoing request (ipv4, ipv6 or dual)
|
||||
#outgoingAddressFamily: ipv4
|
||||
|
@ -1 +1 @@
|
||||
v11.7.0
|
||||
v12.1.0
|
||||
|
178
CHANGELOG.md
178
CHANGELOG.md
@ -5,6 +5,37 @@ If you encounter any problems with updating, please try the following:
|
||||
1. `npm run clean` or `npm run cleanall`
|
||||
2. Retry update (Don't forget `npm i`)
|
||||
|
||||
Migration
|
||||
------------------------------
|
||||
#### 1
|
||||
`ormconfig.json`という名前で、Misskeyのインストール場所(package.jsonとかがあるディレクトリ)に新たなファイルを作る。中身は次のようにします:
|
||||
``` json
|
||||
{
|
||||
"type": "postgres",
|
||||
"host": "PostgreSQLのホスト",
|
||||
"port": 5432,
|
||||
"username": "PostgreSQLのユーザー名",
|
||||
"password": "PostgreSQLのパスワード",
|
||||
"database": "PostgreSQLのデータベース名",
|
||||
"entities": ["src/models/entities/*.ts"],
|
||||
"migrations": ["migration/*.ts"],
|
||||
"cli": {
|
||||
"migrationsDir": "migration"
|
||||
}
|
||||
}
|
||||
```
|
||||
上記の各種PostgreSQLの設定(ポートも)は、設定ファイルに書いてあるものをコピーしてください。
|
||||
|
||||
#### 2
|
||||
```
|
||||
npm i -g ts-node
|
||||
```
|
||||
|
||||
#### 3
|
||||
```
|
||||
ts-node ./node_modules/typeorm/cli.js migration:run
|
||||
```
|
||||
|
||||
How to migrate to v11 from v10
|
||||
------------------------------
|
||||
### 移行の注意点
|
||||
@ -42,6 +73,122 @@ mongodb:
|
||||
8. master ブランチに戻す
|
||||
9. enjoy
|
||||
|
||||
11.12.0 (2019/05/10)
|
||||
--------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
### Improvements
|
||||
* インスタンス運営者がおすすめアカウントを設定できるように
|
||||
* MisskeyPagesでNAME環境変数がNULLにならないように
|
||||
* MisskeyPagesにNULL環境変数を追加
|
||||
* MisskeyPagesで変数を並べ替えられるように
|
||||
* MisskeyPagesのテキストのリスト内で変数埋め込みできるように
|
||||
* 自分の指定した投稿のRenoteを全て解除するAPIを追加
|
||||
|
||||
### Fixes
|
||||
* Noteをpull取得した時にhost名がvalidateされていない問題を修正
|
||||
* みつけるで人気のタグが表示されない問題を修正
|
||||
|
||||
### その他
|
||||
* アカウントのisVerifiedフラグを廃止
|
||||
|
||||
11.11.2 (2019/05/07)
|
||||
--------------------
|
||||
### Fixes
|
||||
* IPv4 onlyホストからDualstackホストにAP deliverできない問題を修正
|
||||
* ストリーミングに接続するまでラグがある問題を修正
|
||||
* 2段階認証のコードが0から始まる時正しく入力できない問題を修正
|
||||
* ユーザーの更新日時が新しい順で更新日時がnullのユーザーが先頭に来る問題を修正
|
||||
* 値選択時の問題を修正
|
||||
* リバーシでマップの変更が反映されない問題を修正
|
||||
* リバーシで対局終了直後に盤面を巻き戻してもすぐ最終ターンまでリセットされる問題を修正
|
||||
|
||||
11.11.1 (2019/05/05)
|
||||
--------------------
|
||||
### Fixes
|
||||
* MisskeyPagesのリストから選択関数が使えない問題を修正
|
||||
|
||||
11.11.0 (2019/05/05)
|
||||
--------------------
|
||||
### Improvements
|
||||
* MisskeyPagesにリストから選択関数を追加
|
||||
* MisskeyPagesに確率を指定できるテキストランダム選択関数を追加
|
||||
* 外部サービス連携ログインリンクにアイコン追加
|
||||
|
||||
### Fixes
|
||||
* MisskeyPagesでifを入れ子にできなくなっていた問題を修正
|
||||
* MisskeyPagesで数値入力を作成するとテキスト入力になる問題を修正
|
||||
* 外部サービス連携に関する問題を修正
|
||||
|
||||
11.10.1 (2019/05/04)
|
||||
--------------------
|
||||
### Fixes
|
||||
* MisskeyPagesでページブロックを削除できなくなっていた問題を修正
|
||||
|
||||
### その他
|
||||
* Node.js v12対応
|
||||
|
||||
11.10.0 (2019/05/03)
|
||||
--------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](#migration)の手順を実行してください
|
||||
|
||||
### Improvements
|
||||
* MisskeyPagesに割った余りを求める関数を追加
|
||||
* Mastodon v2.8.0 のフォローリストをインポートできるように
|
||||
* エクスポートリクエストに失敗したらエラーを表示するように
|
||||
* エクスポートファイルでは同一ハッシュチェックをしないように
|
||||
|
||||
### Fixes
|
||||
* 2段階認証を設定するとログインできなくなる問題を修正
|
||||
* ファイルをアップロードできないことがある問題を修正
|
||||
* リモートファイルをキャッシュしない設定だとサムネイル時にオリジナル画像が表示されない問題を修正
|
||||
* 外部サービス連携の不具合を修正
|
||||
|
||||
11.9.0 (2019/05/02)
|
||||
-------------------
|
||||
### Improvements
|
||||
* MisskeyPagesで編集時にページブロックをドラッグで並べ替えられるように
|
||||
* MisskeyPagesにカウンターボタンブロックを追加
|
||||
|
||||
11.8.1 (2019/05/02)
|
||||
-------------------
|
||||
### Fixes
|
||||
* リモートファイルをキャッシュしないオプション有効時にファイルが作成できない問題を修正
|
||||
|
||||
11.8.0-2 (2019/05/01)
|
||||
-------------------
|
||||
* 11.8.0 のリリース内容が 11.7.0 と同一だったのを修正
|
||||
|
||||
11.8.0 (2019/05/01)
|
||||
-------------------
|
||||
### Improvements
|
||||
* MisskeyPagesで関数を作成できるように
|
||||
* MisskeyPagesでソースを表示できるように
|
||||
* MisskeyPagesにシードを与えるランダム関数を追加
|
||||
* MisskeyPagesに複数行テキストをテキストのリストに変換する関数を追加
|
||||
|
||||
### Fixes
|
||||
* APIドキュメントが見れなくなっていたのを修正
|
||||
* mention (あなた宛て) streaming にミュートが効かない問題を修正
|
||||
* デザインの調整
|
||||
|
||||
11.7.0 (2019/04/30)
|
||||
-------------------
|
||||
### Improvements
|
||||
* MisskeyPagesに ifブロック を追加
|
||||
* MisskeyPagesに テキストエリア を追加
|
||||
* MisskeyPagesに 複数行テキスト入力 を追加
|
||||
* MisskeyPagesに 投稿フォーム を追加
|
||||
* MisskeyPagesに 変換系関数 を追加
|
||||
* MisskeyPagesに 環境変数 URL を追加
|
||||
* MisskeyPagesでボタンやスイッチなどのテキストに変数使えるように
|
||||
|
||||
### Fixes
|
||||
* OGPのサイト名を修正
|
||||
* デザインの調整
|
||||
|
||||
11.6.0 (2019/04/29)
|
||||
-------------------
|
||||
### Improvements
|
||||
@ -61,36 +208,7 @@ mongodb:
|
||||
11.5.0 (2019/04/29)
|
||||
-------------------
|
||||
### 注意
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前にまず以下の手順を実行してください
|
||||
|
||||
#### 1
|
||||
`ormconfig.json`という名前で、Misskeyのインストール場所(package.jsonとかがあるディレクトリ)に新たなファイルを作る。中身は次のようにします:
|
||||
``` json
|
||||
{
|
||||
"type": "postgres",
|
||||
"host": "PostgreSQLのホスト",
|
||||
"port": 5432,
|
||||
"username": "PostgreSQLのユーザー名",
|
||||
"password": "PostgreSQLのパスワード",
|
||||
"database": "PostgreSQLのデータベース名",
|
||||
"entities": ["src/models/entities/*.ts"],
|
||||
"migrations": ["migration/*.ts"],
|
||||
"cli": {
|
||||
"migrationsDir": "migration"
|
||||
}
|
||||
}
|
||||
```
|
||||
上記の各種PostgreSQLの設定(ポートも)は、設定ファイルに書いてあるものをコピーしてください。
|
||||
|
||||
#### 2
|
||||
```
|
||||
npm i -g ts-node
|
||||
```
|
||||
|
||||
#### 3
|
||||
```
|
||||
ts-node ./node_modules/typeorm/cli.js migration:run
|
||||
```
|
||||
このアップデートを適用した後、プロセスを起動(もしくは再起動)する前に[マイグレーション](migration)の手順を実行してください
|
||||
|
||||
### New features
|
||||
#### MisskeyPages
|
||||
|
@ -1,4 +1,4 @@
|
||||
FROM node:11-alpine AS base
|
||||
FROM node:12.1-alpine AS base
|
||||
|
||||
ENV NODE_ENV=production
|
||||
|
||||
|
22
README.md
22
README.md
@ -105,6 +105,7 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5888816/36da0f7c15954df0ab13f9abdf227f66/1.jpeg?token-time=2145916800&token-hash=at8QpJXJ8C0zINY_NmoMKv-MhXVoUK-YzTgaJPJzJYU%3D" alt="Hiroshi Seki" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12190916/fb7fa7983c14425f890369535b1506a4/3.png?token-time=2145916800&token-hash=oH_i7gJjNT7Ot6j9JiVwy7ZJIBqACVnzLqlz4YrDAZA%3D" alt="weepjp" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19045173/cb91c0f345c24d4ebfd05f19906d5e26/1.png?token-time=2145916800&token-hash=o_zKBytJs_AxHwSYw_5R8eD0eSJe3RoTR3kR3Q0syN0%3D" alt="kiritan" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13099460/43cecdbaa63a40d79bf50a96b9910b9d/1.jpe?token-time=2145916800&token-hash=bqwLTk0Wo0hUJJ8J5y7ii05bLzz-_CDA7Bo0Mp4RFU0%3D" alt="ne_moni" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12913507/f7181eacafe8469a93033d85f5969c29/4.jpe?token-time=2145916800&token-hash=zEyJqVM7u9d8Ri-65fJYSJcWF1jBH1nJ5a3taRzrTmw%3D" alt="Melilot" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5670915/ee175f0bfb6347ffa4ea101a8c097bff/1.jpg?token-time=2145916800&token-hash=mPLM9CA-riFHx-myr3bLZJuH2xBRHA9se5VbHhLIOuA%3D" alt="osapon" width="100"></td>
|
||||
@ -112,62 +113,65 @@ Please see the [Contribution Guide](./CONTRIBUTING.md).
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/rane_hs">Hiroshi Seki</a></td>
|
||||
<td><a href="https://www.patreon.com/weepjp">weepjp</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=19045173">kiritan</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13099460">ne_moni</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12913507">Melilot</a></td>
|
||||
<td><a href="https://www.patreon.com/osapon">osapon</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=16869916">見当かなみ</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18899730/6a22797f68254034a854d69ea2445fc8/1.png?token-time=2145916800&token-hash=b_uj57yxo5VzkSOUS7oXE_762dyOTB_oxzbO6lFNG3k%3D" alt="YuzuRyo61" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12021162/963128bb8d14476dbd8407943db8f31a/1.png?token-time=2145916800&token-hash=FMV7cPKBD1TU2WTbl1jg6AcdKSvTb2BSFcDhgc-EO8w%3D" alt="gutfuckllc" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/11357794/923ce94cd8c44ba788ee931907881839/1.png?token-time=2145916800&token-hash=9nEQje_eMvUjq9a7L3uBqW-MQbS-rRMaMgd7UYVoFNM%3D" alt="mydarkstar" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/12718187" alt="Peter G." width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/18833336" alt="itiradi" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13039004/509d0c412eb14ae08d6a812a3054f7d6/1.jpe?token-time=2145916800&token-hash=UQRWf01TwHDV4Cls1K0YAOAjM29ssif7hLVq0ESQ0hs%3D" alt="nemu" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/17866454" alt="sikyosyounin" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5881381/6235ca5d3fb04c8e95ef5b4ff2abcc18/3.png?token-time=2145916800&token-hash=KjfQL8nf3AIf6WqzLshBYAyX44piAqOAZiYXgZS_H6A%3D" alt="YUKIMOCHI" width="100"></td>
|
||||
<td><img src="https://c8.patreon.com/2/200/17463605" alt="Sampot" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/19356899/496b4681d33b4520bd7688e0fd19c04d/2.jpeg?token-time=2145916800&token-hash=_sTj3dUBOhn9qwiJ7F19Qd-yWWfUqJC_0jG1h0agEqQ%3D" alt="sheeta.s" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/Yuzulia">YuzuRyo61</a></td>
|
||||
<td><a href="https://www.patreon.com/gutfuckllc">gutfuckllc</a></td>
|
||||
<td><a href="https://www.patreon.com/mydarkstar">mydarkstar</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12718187">Peter G.</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=18833336">itiradi</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13039004">nemu</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=17866454">sikyosyounin</a></td>
|
||||
<td><a href="https://www.patreon.com/yukimochi">YUKIMOCHI</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=17463605">Sampot</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=19356899">sheeta.s</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13737140/1adf7835017d479280d90fe8d30aade2/1.png?token-time=2145916800&token-hash=0pdle8h5pDZrww0BDOjdz6zO-HudeGTh36a3qi1biVU%3D" alt="Satsuki Yanagi" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17880724/311738c8a48f4a6b9443c2445a75adde/1.jpe?token-time=2145916800&token-hash=CPxGQhKIlEaa6WUcgbyHixyKEhakiw9RFdOhsIJBQ_o%3D" alt="takimura" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/17195955/be45e5e14c3e48b2bee0456c84e19df4/4.jpe?token-time=2145916800&token-hash=UslrPVM-8TXOe8AapuNiaFYjcIJgPNcU-fKpGbfGJNI%3D" alt="Damillora" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/16900731/83884b38afc74d4cbe83c30a13b10edd/1.png?token-time=2145916800&token-hash=R5Tog8RWg0rguRoCIoir3lThokrdPvs8Utfikhc0nhY%3D" alt="Atsuko Tominaga" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4389829/9f709180ac714651a70f74a82f3ffdb9/3.png?token-time=2145916800&token-hash=FTm3WVom4dJ9NwWMU4OpCL_8Yc13WiwEbKrDPyTZTPs%3D" alt="natalie" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/13034746/c711c7f58e204ecfbc2fd646bc8a4eee/1.jpe?token-time=2145916800&token-hash=EWxXhVbZYH7KB4IDT3joc8TbIg8zPO40x1r5IDn3R7c%3D" alt="Hiratake" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/2384390/5681180e1efb46a8b28e0e8d4c8b9037/1.jpg?token-time=2145916800&token-hash=SJcMy-Q1BcS940-LFUVOMfR7-5SgrzsEQGhYb3yowFk%3D" alt="CG" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/18072312/98e894d960314fa7bc236a72a39488fe/1.jpe?token-time=2145916800&token-hash=qA8j97lIZNc-74AuZ0p4F3ms6sKPeKjtNt2vEuwpsyo%3D" alt="Hekovic" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/4503830/ccf2cc867ea64de0b524bb2e24b9a1cb/1.jpeg?token-time=2145916800&token-hash=L55UhJ0rcuNAH3w_ryeeGN4hC6taoOixyAhraEi0bzw%3D" alt="dansup" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1.jpeg?token-time=2145916800&token-hash=d8jBQLMOHD87KtXs5C9fk1o58DMF73pQ-dYH3uZJPBE%3D" alt="Gargron" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/user?u=13737140">Satsuki Yanagi</a></td>
|
||||
<td><a href="https://www.patreon.com/takimura">takimura</a></td>
|
||||
<td><a href="https://www.patreon.com/damillora">Damillora</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=16900731">Atsuko Tominaga</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=4389829">natalie</a></td>
|
||||
<td><a href="https://www.patreon.com/hiratake">Hiratake</a></td>
|
||||
<td><a href="https://www.patreon.com/Corset">CG</a></td>
|
||||
<td><a href="https://www.patreon.com/hekovic">Hekovic</a></td>
|
||||
<td><a href="https://www.patreon.com/dansup">dansup</a></td>
|
||||
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
|
||||
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
|
||||
</tr></table>
|
||||
<table><tr>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/619786/32cf01444db24e578cd1982c197f6fc6/1.jpeg?token-time=2145916800&token-hash=d8jBQLMOHD87KtXs5C9fk1o58DMF73pQ-dYH3uZJPBE%3D" alt="Gargron" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/5731881/4b6038e6cda34c04b83a5fcce3806a93/1.png?token-time=2145916800&token-hash=hBayGfOmQH3kRMdNnDe4oCZD_9fsJWSt29xXR3KRMVk%3D" alt="Nokotaro Takeda" width="100"></td>
|
||||
<td><img src="https://c10.patreonusercontent.com/3/eyJ3IjoyMDB9/patreon-media/p/user/12531784/93a45137841849329ba692da92ac7c60/1.jpeg?token-time=2145916800&token-hash=vGe7wXGqmA8Q7m-kDNb6fyGdwk-Dxk4F-ut8ZZu51RM%3D" alt="Takashi Shibuya" width="100"></td>
|
||||
</tr><tr>
|
||||
<td><a href="https://www.patreon.com/mastodon">Gargron</a></td>
|
||||
<td><a href="https://www.patreon.com/takenoko">Nokotaro Takeda</a></td>
|
||||
<td><a href="https://www.patreon.com/user?u=12531784">Takashi Shibuya</a></td>
|
||||
</tr></table>
|
||||
|
||||
**Last updated:** Wed, 24 Apr 2019 05:56:07 UTC
|
||||
**Last updated:** Tue, 07 May 2019 11:55:07 UTC
|
||||
<!-- PATREON_END -->
|
||||
|
||||
:four_leaf_clover: Copyright
|
||||
|
@ -61,6 +61,7 @@ common:
|
||||
month-and-day: "{day}. {month}."
|
||||
trash: "Koš"
|
||||
drive: "Disk"
|
||||
pages: "Stránky"
|
||||
messaging: "Konverzace"
|
||||
home: "Domů"
|
||||
deck: "Deck"
|
||||
@ -71,12 +72,20 @@ common:
|
||||
favorites: "Oblíbené"
|
||||
permissions:
|
||||
"read:account": "Zobrazit informace o účtu"
|
||||
"write:account": "Narábět s účtem"
|
||||
"read:blocks": "Prohlížet blokování"
|
||||
"write:blocks": "Narábět s blokováním"
|
||||
"read:drive": "Prohlížet Disk"
|
||||
"write:drive": "Pracovat s Diskem"
|
||||
"read:favorites": "Prohlížet oblíbené"
|
||||
"write:favorites": "Narábět s oblíbeními"
|
||||
"read:following": "Prohlížet následování"
|
||||
"write:following": "Pracovat s následováním"
|
||||
"read:messaging": "Prohlížet konverzaci"
|
||||
"write:messaging": "Pracovat s konverzaci"
|
||||
"read:mutes": "Prohlížet ztlumené"
|
||||
"write:mutes": "Narábět s utíšeními"
|
||||
"write:notes": "Narábět s poznámkami"
|
||||
"read:notifications": "Prohlížet oznámení"
|
||||
"write:notifications": "Pracovat s oznámeními"
|
||||
"read:reactions": "Prohlížet reakce"
|
||||
@ -377,6 +386,7 @@ common/views/components/theme.vue:
|
||||
installed: "\"{}\" byl nainstalován"
|
||||
create-a-theme: "Vytvořit motiv"
|
||||
save-created-theme: "Uložit motiv"
|
||||
text-color: "Barva textu"
|
||||
base-theme: "Základní vzhled"
|
||||
base-theme-light: "Světlý"
|
||||
base-theme-dark: "Tmavý"
|
||||
@ -454,6 +464,7 @@ common/views/components/user-menu.vue:
|
||||
suspend: "Zmrazit"
|
||||
common/views/components/poll.vue:
|
||||
vote-count: "{} hlasů"
|
||||
total-votes: "{} hlasů celkem"
|
||||
vote: "Hlasovat"
|
||||
show-result: "Podívat se na výsledky"
|
||||
voted: "Už jste hlasovaly"
|
||||
@ -464,10 +475,21 @@ common/views/components/poll.vue:
|
||||
remaining-seconds: "zbývá {s} sekund"
|
||||
common/views/components/poll-editor.vue:
|
||||
no-only-one-choice: "Musíte vybrat alespoň dvě možnosti"
|
||||
choice-n: "Volba {}"
|
||||
remove: "Odstranit tuto možnost"
|
||||
add: "+ Přidat možnost"
|
||||
destroy: "Zahodit dotazník"
|
||||
expiration: "Termín"
|
||||
infinite: "Nekonečne"
|
||||
at: "Výběr data a času"
|
||||
deadline-date: "Termín ukončení"
|
||||
interval: "Trvání"
|
||||
second: "Sekunda"
|
||||
minute: "Minuta"
|
||||
hour: "Hodina"
|
||||
day: "Ne"
|
||||
common/views/components/reaction-picker.vue:
|
||||
choose-reaction: "Vyberte svoji reakci"
|
||||
common/views/components/emoji-picker.vue:
|
||||
custom-emoji: "Emoji"
|
||||
people: "Lidé"
|
||||
@ -1198,3 +1220,42 @@ deck/deck.user-column.vue:
|
||||
activity: "Aktivita"
|
||||
dev/views/new-app.vue:
|
||||
app-name-desc: "Jméno vaší aplikace"
|
||||
pages:
|
||||
title: "Titulek"
|
||||
blocks:
|
||||
post: "Formulář pro psaní"
|
||||
_post:
|
||||
text: "Obsah"
|
||||
_textInput:
|
||||
text: "Titulek"
|
||||
_textareaInput:
|
||||
text: "Titulek"
|
||||
_numberInput:
|
||||
text: "Titulek"
|
||||
_switch:
|
||||
text: "Titulek"
|
||||
_counter:
|
||||
text: "Titulek"
|
||||
_button:
|
||||
text: "Titulek"
|
||||
_action:
|
||||
_dialog:
|
||||
content: "Obsah"
|
||||
script:
|
||||
categories:
|
||||
random: "Náhodně"
|
||||
list: "Seznamy"
|
||||
blocks:
|
||||
_join:
|
||||
arg1: "Seznamy"
|
||||
random: "Náhodně"
|
||||
_randomPick:
|
||||
arg1: "Seznamy"
|
||||
_dailyRandomPick:
|
||||
arg1: "Seznamy"
|
||||
_seedRandomPick:
|
||||
arg2: "Seznamy"
|
||||
_pick:
|
||||
arg1: "Seznamy"
|
||||
types:
|
||||
array: "Seznamy"
|
||||
|
@ -53,6 +53,7 @@ common:
|
||||
month-and-day: "{day}/{month}"
|
||||
trash: "Papierkorb"
|
||||
drive: "Drive"
|
||||
pages: "Seite"
|
||||
messaging: "Unterhaltungen"
|
||||
home: "Home"
|
||||
deck: "Stapel"
|
||||
@ -64,6 +65,7 @@ common:
|
||||
permissions:
|
||||
"read:account": "Accountinformationen anzeigen."
|
||||
"write:account": "Accountinformationen bearbeiten."
|
||||
"read:blocks": "Blöcke anzeigen"
|
||||
"read:drive": "Dateien anzeigen"
|
||||
"write:drive": "Dateien bearbeiten"
|
||||
"read:favorites": "Favoriten anzeigen"
|
||||
@ -718,25 +720,6 @@ deck/deck.user-column.vue:
|
||||
docs:
|
||||
edit-this-page-on-github: "Hast Du einen Fehler gefunden oder Lust, diese Dokumentation zu verbessern?"
|
||||
edit-this-page-on-github-link: "Seite auf GitHub bearbeiten!"
|
||||
api:
|
||||
entities:
|
||||
properties: "Eigenschaften"
|
||||
endpoints:
|
||||
params: "Parameter"
|
||||
no-params: "Keine Parameter."
|
||||
res: "Antwort"
|
||||
require-credential: "Dieser Endpunkt erfordert eine Authentifizierung."
|
||||
require-permission: "Dieser Endpunkt erfordert die {permission} Berechtigung."
|
||||
has-limit: "Es gibt eine Ratenbegrenzung."
|
||||
duration-limit: "Es sind maximal {max} Anfragen pro {duration} Millisekunden möglich."
|
||||
min-interval-limit: "Es ist nur eine Anfrage alle {interval} Millisekunden möglich."
|
||||
show-src: "Quellcode anzeigen."
|
||||
show-src-link: "Quellcode auf GitHub anzeigen"
|
||||
generated: "Dieses Dokument wird automatisch anhand der API-Definition generiert."
|
||||
props:
|
||||
name: "Name"
|
||||
type: "Typ"
|
||||
description: "Beschreibung"
|
||||
dev/views/index.vue:
|
||||
manage-apps: "Anwendungen verwalten"
|
||||
dev/views/apps.vue:
|
||||
@ -753,3 +736,24 @@ dev/views/new-app.vue:
|
||||
authority: "Berechtigungen"
|
||||
authority-desc: "Nur die hier eingetragenen Berechtigungen, werden per API zur Verfügung stehen."
|
||||
authority-warning: "Dies kann auch nach dem erstellen der Anwendung geändert werden, allerdings werden dann alle bisher generierten Token ungültig."
|
||||
pages:
|
||||
blocks:
|
||||
post: "\"Neuer Beitrag\"-Formular"
|
||||
script:
|
||||
categories:
|
||||
random: "Zufällige Auswahl"
|
||||
list: "Listen"
|
||||
blocks:
|
||||
_join:
|
||||
arg1: "Listen"
|
||||
random: "Zufällige Auswahl"
|
||||
_randomPick:
|
||||
arg1: "Listen"
|
||||
_dailyRandomPick:
|
||||
arg1: "Listen"
|
||||
_seedRandomPick:
|
||||
arg2: "Listen"
|
||||
_pick:
|
||||
arg1: "Listen"
|
||||
types:
|
||||
array: "Listen"
|
||||
|
@ -34,6 +34,7 @@ common:
|
||||
signup: "Sign up"
|
||||
signout: "Logout"
|
||||
reload-to-apply-the-setting: "You'll need to reload the page to reflect this setting. Do you want to reload it now?"
|
||||
fetching-as-ap-object: "Inquiring to union"
|
||||
got-it: "Got it!"
|
||||
customization-tips:
|
||||
title: "Customization tips"
|
||||
@ -61,6 +62,7 @@ common:
|
||||
month-and-day: "{month}/{day}"
|
||||
trash: "Trash"
|
||||
drive: "Drive"
|
||||
pages: "Pages"
|
||||
messaging: "Talk"
|
||||
home: "Home"
|
||||
deck: "Deck"
|
||||
@ -1621,25 +1623,6 @@ deck/deck.user-column.vue:
|
||||
docs:
|
||||
edit-this-page-on-github: "Found an error, or do you want to contribute to the documentation?"
|
||||
edit-this-page-on-github-link: "Edit this page at GitHub!"
|
||||
api:
|
||||
entities:
|
||||
properties: "Properties"
|
||||
endpoints:
|
||||
params: "Parameters"
|
||||
no-params: "No parameter."
|
||||
res: "Response"
|
||||
require-credential: "This endpoint requires the authentication information."
|
||||
require-permission: "This endpoint requires {permission} permission."
|
||||
has-limit: "There is a rate limit."
|
||||
duration-limit: "If you have sent your requests more than {max} times in {duration} milliseconds, you will be unable to send more requests."
|
||||
min-interval-limit: "If {interval} milliseconds haven't passed since the last request, you can't send a request."
|
||||
show-src: "You can view the source code for this endpoint."
|
||||
show-src-link: "See the code on GitHub"
|
||||
generated: "This document is generated by the API definition."
|
||||
props:
|
||||
name: "Name"
|
||||
type: "Type"
|
||||
description: "Description"
|
||||
dev/views/index.vue:
|
||||
manage-apps: "Manage apps"
|
||||
dev/views/apps.vue:
|
||||
@ -1648,13 +1631,252 @@ dev/views/apps.vue:
|
||||
app-missing: "No apps"
|
||||
dev/views/new-app.vue:
|
||||
new-app: "New Application"
|
||||
new-app-info: "You can also create an application with the API. (app/create)"
|
||||
create-app: "Creating application"
|
||||
app-name: "Application name"
|
||||
app-name-placeholder: "ex) Misskey for iOS"
|
||||
app-name-desc: "The name of your app"
|
||||
app-overview: "Application summary"
|
||||
app-overview-placeholder: " ex) Misskey iOS Client."
|
||||
app-overview-desc: "A brief description, or an introduction of your app."
|
||||
callback-url: "The callback URL (optional)"
|
||||
callback-url-placeholder: "ex) https://your.app.example.com/callback.php"
|
||||
callback-url-desc: "The URL to redirect to after the user is authenticated via the authentication form."
|
||||
authority: "Permissions"
|
||||
authority-desc: "Only the functions requested here can be accessed via the API."
|
||||
authority-warning: "You can change it even after creating the application, but if you give different permissions, all user keys associated at that time will be invalidated."
|
||||
pages:
|
||||
new-page: "Create a page"
|
||||
edit-page: "Edit a page"
|
||||
read-page: "Viewing the source"
|
||||
page-created: "Created the page!"
|
||||
page-updated: "Updated the page"
|
||||
are-you-sure-delete: "Do you want to delete this page?"
|
||||
page-deleted: "The page has been deleted"
|
||||
edit-this-page: "Edit this page"
|
||||
view-source: "View Source"
|
||||
view-page: "View page"
|
||||
inspector: "Inspector"
|
||||
content: "Page block"
|
||||
variables: "Variables"
|
||||
more-details: "Description"
|
||||
title: "Title"
|
||||
url: "Page URL"
|
||||
summary: "Summary of page"
|
||||
align-center: "Center align"
|
||||
font: "Font"
|
||||
fontSerif: "Serif"
|
||||
fontSansSerif: "Sans Serif"
|
||||
set-eye-catching-image: "Set an eye-catching image"
|
||||
remove-eye-catching-image: "Delete an eye-catching image"
|
||||
choose-block: "Add a block"
|
||||
select-type: "Select a type"
|
||||
enter-variable-name: "Please choose a variable name"
|
||||
the-variable-name-is-already-used: "This variable name is already used"
|
||||
content-blocks: "Content"
|
||||
input-blocks: "Input"
|
||||
special-blocks: "Special"
|
||||
post-from-post-form: "Post this content"
|
||||
posted-from-post-form: "Posted!"
|
||||
blocks:
|
||||
text: "Text"
|
||||
textarea: "Text area"
|
||||
section: "Section"
|
||||
image: "Images"
|
||||
button: "Button"
|
||||
if: "If"
|
||||
_if:
|
||||
variable: "Variables"
|
||||
post: "Post form"
|
||||
_post:
|
||||
text: "Content"
|
||||
textInput: "Text input"
|
||||
_textInput:
|
||||
name: "Variable name"
|
||||
text: "Title"
|
||||
default: "Default value"
|
||||
textareaInput: "Multiple type text input"
|
||||
_textareaInput:
|
||||
name: "Variable name"
|
||||
text: "Title"
|
||||
default: "Default value"
|
||||
numberInput: "Numeric input"
|
||||
_numberInput:
|
||||
name: "Variable name"
|
||||
text: "Title"
|
||||
default: "Default value"
|
||||
switch: "Switch"
|
||||
_switch:
|
||||
name: "Variable name"
|
||||
text: "Title"
|
||||
default: "Default value"
|
||||
counter: "Counter"
|
||||
_counter:
|
||||
name: "Variable name"
|
||||
text: "Title"
|
||||
inc: "Increase number"
|
||||
_button:
|
||||
text: "Title"
|
||||
action: "Operation when the button pressed"
|
||||
_action:
|
||||
dialog: "Show a dialog"
|
||||
_dialog:
|
||||
content: "Content"
|
||||
resetRandom: "Reset a random number"
|
||||
script:
|
||||
categories:
|
||||
flow: "Control"
|
||||
logical: "Logical operation"
|
||||
operation: "Compute"
|
||||
comparison: "Compare"
|
||||
random: "Random"
|
||||
value: "Value"
|
||||
fn: "Function"
|
||||
text: "Text operation"
|
||||
convert: "Variable"
|
||||
list: "Lists"
|
||||
blocks:
|
||||
text: "Text"
|
||||
multiLineText: "Text (Multiple lines)"
|
||||
textList: "List of text"
|
||||
_textList:
|
||||
info: "Separate each one with a newline"
|
||||
strLen: "Length of text"
|
||||
_strLen:
|
||||
arg1: "Text"
|
||||
strPick: "Extract character"
|
||||
_strPick:
|
||||
arg1: "Text"
|
||||
arg2: "Position of character"
|
||||
strReplace: "Replace string(s)"
|
||||
_strReplace:
|
||||
arg1: "Text"
|
||||
arg2: "Before replacement"
|
||||
arg3: "After replacement"
|
||||
strReverse: "Flip text"
|
||||
_strReverse:
|
||||
arg1: "Text"
|
||||
_join:
|
||||
arg1: "Lists"
|
||||
arg2: "Separator"
|
||||
add: "+ Plus"
|
||||
_add:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
subtract: "- Minus"
|
||||
_subtract:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
multiply: "× Multiply"
|
||||
_multiply:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
divide: "÷ Divide"
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_remind:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A and B are equal"
|
||||
_eq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
notEq: "A and B are different"
|
||||
_notEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
and: "A and B"
|
||||
_and:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
or: "A or B"
|
||||
_or:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
lt: "A is smaller than B"
|
||||
_lt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
gt: "A is bigger than B"
|
||||
_gt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
ltEq: "A is smaller or same than B"
|
||||
_ltEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
gtEq: "A is bigger or same than B"
|
||||
_gtEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
if: "Branch"
|
||||
_if:
|
||||
arg1: "If"
|
||||
arg2: "then"
|
||||
arg3: "else"
|
||||
not: "denial"
|
||||
_not:
|
||||
arg1: "denial"
|
||||
random: "Random"
|
||||
_random:
|
||||
arg1: "Probability"
|
||||
rannum: "Random number"
|
||||
_rannum:
|
||||
arg1: "Minimum"
|
||||
arg2: "Maximum"
|
||||
randomPick: "Choose at random from the list"
|
||||
_randomPick:
|
||||
arg1: "Lists"
|
||||
dailyRandom: "Random (Daily for each user)"
|
||||
_dailyRandom:
|
||||
arg1: "Probability"
|
||||
dailyRannum: "Random number (Daily for each user)"
|
||||
_dailyRannum:
|
||||
arg1: "Minimum"
|
||||
arg2: "Maximum"
|
||||
dailyRandomPick: "Choose at random from the list (Daily for each user)"
|
||||
_dailyRandomPick:
|
||||
arg1: "Lists"
|
||||
_seedRandom:
|
||||
arg1: "Seed"
|
||||
arg2: "Probability"
|
||||
_seedRannum:
|
||||
arg1: "Seed"
|
||||
arg2: "Minimum"
|
||||
arg3: "Maximum"
|
||||
_seedRandomPick:
|
||||
arg1: "Seed"
|
||||
arg2: "Lists"
|
||||
_DRPWPM:
|
||||
arg1: "List of text"
|
||||
_pick:
|
||||
arg1: "Lists"
|
||||
arg2: "Position"
|
||||
number: "Number"
|
||||
stringToNumber: "Text to number"
|
||||
_stringToNumber:
|
||||
arg1: "Text"
|
||||
numberToString: "Number to text"
|
||||
_numberToString:
|
||||
arg1: "Number"
|
||||
splitStrByLine: "Split the text by lines"
|
||||
_splitStrByLine:
|
||||
arg1: "Text"
|
||||
ref: "Variables"
|
||||
fn: "Function"
|
||||
_fn:
|
||||
slots: "Slots"
|
||||
arg1: "Output"
|
||||
for: "Repeat"
|
||||
thereIsEmptySlot: "Slot {slot} is empty!"
|
||||
types:
|
||||
string: "Text"
|
||||
number: "Number"
|
||||
boolean: "Flag"
|
||||
array: "Lists"
|
||||
stringArray: "List of text"
|
||||
emptySlot: "Empty slot"
|
||||
enviromentVariables: "Environment variable"
|
||||
pageVariables: "Page element"
|
||||
argVariables: "Input slot"
|
||||
|
@ -969,3 +969,24 @@ deck:
|
||||
rename: "Renombrar"
|
||||
deck/deck.user-column.vue:
|
||||
activity: "Actividad"
|
||||
pages:
|
||||
blocks:
|
||||
post: "Formulario"
|
||||
script:
|
||||
categories:
|
||||
random: "Aleatorio"
|
||||
list: "Listas"
|
||||
blocks:
|
||||
_join:
|
||||
arg1: "Listas"
|
||||
random: "Aleatorio"
|
||||
_randomPick:
|
||||
arg1: "Listas"
|
||||
_dailyRandomPick:
|
||||
arg1: "Listas"
|
||||
_seedRandomPick:
|
||||
arg2: "Listas"
|
||||
_pick:
|
||||
arg1: "Listas"
|
||||
types:
|
||||
array: "Listas"
|
||||
|
@ -60,6 +60,7 @@ common:
|
||||
month-and-day: "{day}-{month}"
|
||||
trash: "Corbeille"
|
||||
drive: "Drive"
|
||||
pages: "Pages"
|
||||
messaging: "Conversations"
|
||||
home: "Principal"
|
||||
deck: "Deck"
|
||||
@ -1469,6 +1470,7 @@ mobile/views/pages/drive.vue:
|
||||
contextmenu:
|
||||
upload: "Téléverser un fichier"
|
||||
create-folder: "Créer un dossier"
|
||||
rename-folder: "Renommer le dossier"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "Listes"
|
||||
mobile/views/pages/signup.vue:
|
||||
@ -1570,25 +1572,6 @@ deck/deck.user-column.vue:
|
||||
docs:
|
||||
edit-this-page-on-github: "Vous avez trouvé une erreur ou vous voulez contribuer à la documentation ?"
|
||||
edit-this-page-on-github-link: "Éditez cette page sur GitHub !"
|
||||
api:
|
||||
entities:
|
||||
properties: "Propriétés"
|
||||
endpoints:
|
||||
params: "Paramètres"
|
||||
no-params: "Aucun paramètre"
|
||||
res: "Réponse"
|
||||
require-credential: "Ce point de communication nécessite une authentification."
|
||||
require-permission: "Ce point de communication nécessite la permission {permission}."
|
||||
has-limit: "Il y’a un taux limite."
|
||||
duration-limit: "Si vous avez envoyé plus de {max} requêtes en {duration} millisecondes, vous ne serez pas en mesure d'envoyer d'autres requêtes."
|
||||
min-interval-limit: "Vous ne pourrez pas effectuer une nouvelle requête si {interval} millisecondes ne se sont pas écoulées depuis la dernière demande."
|
||||
show-src: "Vous pouvez voir le code source ce point de communication."
|
||||
show-src-link: "Consulter le code sur GitHub"
|
||||
generated: "Ce document est généré à partir de la définition de l’API."
|
||||
props:
|
||||
name: "Nom"
|
||||
type: "Type"
|
||||
description: "Description"
|
||||
dev/views/index.vue:
|
||||
manage-apps: "Gestion des applications"
|
||||
dev/views/apps.vue:
|
||||
@ -1605,3 +1588,142 @@ dev/views/new-app.vue:
|
||||
authority: "Autorisations "
|
||||
authority-desc: "Sont accessibles via l’API, uniquement les fonctionnalités demandées ici."
|
||||
authority-warning: "Vous pouvez le changer même après avoir créé l'application, mais si vous attribuez une nouvelle permission, toutes les clés utilisateur associées seront dès lors invalides."
|
||||
pages:
|
||||
title: "Titre"
|
||||
blocks:
|
||||
text: "Texte"
|
||||
textarea: "Zone de texte"
|
||||
section: "Section"
|
||||
image: "Images"
|
||||
button: "Bouton"
|
||||
if: "Si"
|
||||
_if:
|
||||
variable: "Variables"
|
||||
post: "Champs de publication"
|
||||
_post:
|
||||
text: "Contenu"
|
||||
_textInput:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
_textareaInput:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
_numberInput:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
switch: "Basculer"
|
||||
_switch:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
default: "Valeur par défaut"
|
||||
counter: "Compteur"
|
||||
_counter:
|
||||
name: "Nom de la variable"
|
||||
text: "Titre"
|
||||
_button:
|
||||
text: "Titre"
|
||||
_action:
|
||||
_dialog:
|
||||
content: "Contenu"
|
||||
script:
|
||||
categories:
|
||||
flow: "Contrôle"
|
||||
logical: "Opération logique"
|
||||
operation: "Calculer"
|
||||
comparison: "Comparer"
|
||||
random: "Aléatoire"
|
||||
value: "Valeur"
|
||||
fn: "Fonction"
|
||||
list: "Listes"
|
||||
blocks:
|
||||
text: "Texte"
|
||||
strLen: "Longueur du texte"
|
||||
_strLen:
|
||||
arg1: "Texte"
|
||||
strPick: "Extraire un caractère"
|
||||
_strPick:
|
||||
arg1: "Texte"
|
||||
_strReplace:
|
||||
arg1: "Texte"
|
||||
arg2: "Avant le remplacement"
|
||||
arg3: "Après le remplacement"
|
||||
strReverse: "Inverser le texte"
|
||||
_strReverse:
|
||||
arg1: "Texte"
|
||||
_join:
|
||||
arg1: "Listes"
|
||||
arg2: "Séparateur"
|
||||
add: "+ Plus"
|
||||
_add:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
subtract: "- Moins"
|
||||
_subtract:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
multiply: "× Multiplier par"
|
||||
_multiply:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
divide: "÷ Diviser par"
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_remind:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A et B sont équivalents"
|
||||
_eq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
notEq: "A et B sont différents"
|
||||
_notEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_and:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_or:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_lt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_gt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_ltEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
_gtEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
random: "Aléatoire"
|
||||
_randomPick:
|
||||
arg1: "Listes"
|
||||
_dailyRandomPick:
|
||||
arg1: "Listes"
|
||||
_seedRandomPick:
|
||||
arg2: "Listes"
|
||||
_pick:
|
||||
arg1: "Listes"
|
||||
number: "Numérique"
|
||||
_stringToNumber:
|
||||
arg1: "Texte"
|
||||
_numberToString:
|
||||
arg1: "Numérique"
|
||||
_splitStrByLine:
|
||||
arg1: "Texte"
|
||||
fn: "Fonction"
|
||||
_fn:
|
||||
arg1: "Sortie"
|
||||
for: "Répéter"
|
||||
types:
|
||||
string: "Texte"
|
||||
number: "Numérique"
|
||||
array: "Listes"
|
||||
stringArray: "Liste de texte"
|
||||
enviromentVariables: "Variables d'environnement"
|
||||
|
@ -263,7 +263,6 @@ common:
|
||||
update-available-title: "更新があります"
|
||||
update-available: "Misskeyの新しいバージョンがあります({newer}。現在{current}を利用中)。ページを再度読み込みすると更新が適用されます。"
|
||||
my-token-regenerated: "あなたのトークンが更新されたのでサインアウトします。"
|
||||
verified-user: "公式アカウント"
|
||||
hide-password: "パスワードを隠す"
|
||||
show-password: "パスワードを表示する"
|
||||
|
||||
@ -339,7 +338,7 @@ auth/views/index.vue:
|
||||
sign-in: "サインインしてください"
|
||||
|
||||
common/views/pages/explore.vue:
|
||||
verified-users: "公式アカウント"
|
||||
pinned-users: "ピン留めされたユーザー"
|
||||
popular-users: "人気のユーザー"
|
||||
recently-updated-users: "最近投稿したユーザー"
|
||||
recently-registered-users: "新規ユーザー"
|
||||
@ -1264,7 +1263,7 @@ admin/views/instance.vue:
|
||||
invite: "招待"
|
||||
save: "保存"
|
||||
saved: "保存しました"
|
||||
user-recommendation-config: "おすすめユーザー"
|
||||
pinned-users: "ピン留めユーザー"
|
||||
email-config: "メールサーバーの設定"
|
||||
email-config-info: "メールアドレス確認やパスワードリセットの際に使われます。"
|
||||
enable-email: "メール配信を有効にする"
|
||||
@ -1351,12 +1350,6 @@ admin/views/users.vue:
|
||||
silence-confirm: "サイレンスしますか?"
|
||||
unmake-silence: "サイレンスの解除"
|
||||
unsilence-confirm: "サイレンスを解除しますか?"
|
||||
verify: "公式アカウントにする"
|
||||
verify-confirm: "公式アカウントにしますか?"
|
||||
verified: "公式アカウントにしました"
|
||||
unverify: "公式アカウントを解除する"
|
||||
unverify-confirm: "公式アカウントを解除しますか?"
|
||||
unverified: "公式アカウントを解除しました"
|
||||
update-remote-user: "リモートユーザー情報の更新"
|
||||
remote-user-updated: "リモートユーザー情報を更新しました"
|
||||
users:
|
||||
@ -1373,7 +1366,6 @@ admin/views/users.vue:
|
||||
admin: "管理者"
|
||||
moderator: "モデレーター"
|
||||
adminOrModerator: "管理者+モデレーター"
|
||||
verified: "公式アカウント"
|
||||
silenced: "サイレンス済み"
|
||||
suspended: "凍結済み"
|
||||
origin:
|
||||
@ -1842,16 +1834,21 @@ dev/views/new-app.vue:
|
||||
pages:
|
||||
new-page: "ページの作成"
|
||||
edit-page: "ページの編集"
|
||||
read-page: "ソースを表示中"
|
||||
page-created: "ページを作成しました"
|
||||
page-updated: "ページを更新しました"
|
||||
are-you-sure-delete: "このページを削除しますか?"
|
||||
page-deleted: "ページを削除しました"
|
||||
edit-this-page: "このページを編集"
|
||||
view-source: "ソースを表示"
|
||||
view-page: "ページを見る"
|
||||
inspector: "インスペクター"
|
||||
content: "ページブロック"
|
||||
variables: "変数"
|
||||
variables-info: "変数を使うことで動的なページを作成できます。テキスト内で <b>{ 変数名 }</b> と書くとそこに変数の値を埋め込めます。例えば <b>Hello { thing } world!</b> というテキストで、変数(thing)の値が <b>ai</b> だった場合、テキストは <b>Hello ai world!</b> になります。"
|
||||
variables-info2: "変数の評価(値を算出すること)は上から下に行われるので、ある変数の中で自分より下の変数を参照することはできません。例えば上から <b>A、B、C</b> と3つの変数を定義したとき、<b>C</b>の中で<b>A</b>や<b>B</b>を参照することはできますが、<b>A</b>の中で<b>B</b>や<b>C</b>を参照することはできません。"
|
||||
variables-info3: "ユーザーからの入力を受け取るには、ページに「ユーザー入力」ブロックを設置し、「変数名」に入力を格納したい変数名を設定します(変数は自動で作成されます)。その変数を使ってユーザー入力に応じた動作を行えます。"
|
||||
variables-info4: "関数を使うと、値の算出処理を再利用可能な形にまとめることができます。関数を作るには、「関数」タイプの変数を作成します。関数にはスロット(引数)を設定することができ、スロットの値は関数内で変数として利用可能です。また、AiScript標準で関数を引数に取る関数(高階関数と呼ばれます)も存在します。関数は予め定義しておくほかに、このような高階関数のスロットに即席でセットすることもできます。"
|
||||
more-details: "詳しい説明"
|
||||
title: "タイトル"
|
||||
url: "ページURL"
|
||||
@ -1860,31 +1857,62 @@ pages:
|
||||
font: "フォント"
|
||||
fontSerif: "セリフ"
|
||||
fontSansSerif: "サンセリフ"
|
||||
set-eye-catchig-image: "アイキャッチ画像を設定"
|
||||
remove-eye-catchig-image: "アイキャッチ画像を削除"
|
||||
set-eye-catching-image: "アイキャッチ画像を設定"
|
||||
remove-eye-catching-image: "アイキャッチ画像を削除"
|
||||
choose-block: "ブロックを追加"
|
||||
select-type: "種類を選択"
|
||||
enter-variable-name: "変数名を決めてください"
|
||||
the-variable-name-is-already-used: "その変数名は既に使われています"
|
||||
content-blocks: "コンテンツ"
|
||||
input-blocks: "入力"
|
||||
special-blocks: "特殊"
|
||||
post-from-post-form: "この内容を投稿"
|
||||
posted-from-post-form: "投稿しました"
|
||||
blocks:
|
||||
text: "テキスト"
|
||||
textarea: "テキストエリア"
|
||||
section: "セクション"
|
||||
image: "画像"
|
||||
button: "ボタン"
|
||||
input: "ユーザー入力"
|
||||
_input:
|
||||
|
||||
if: "もし"
|
||||
_if:
|
||||
variable: "変数"
|
||||
|
||||
post: "投稿フォーム"
|
||||
_post:
|
||||
text: "内容"
|
||||
|
||||
textInput: "テキスト入力"
|
||||
_textInput:
|
||||
name: "変数名"
|
||||
text: "タイトル"
|
||||
default: "デフォルト値"
|
||||
inputType: "入力の種類"
|
||||
_inputType:
|
||||
string: "テキスト"
|
||||
number: "数値"
|
||||
|
||||
textareaInput: "複数行テキスト入力"
|
||||
_textareaInput:
|
||||
name: "変数名"
|
||||
text: "タイトル"
|
||||
default: "デフォルト値"
|
||||
|
||||
numberInput: "数値入力"
|
||||
_numberInput:
|
||||
name: "変数名"
|
||||
text: "タイトル"
|
||||
default: "デフォルト値"
|
||||
|
||||
switch: "スイッチ"
|
||||
_switch:
|
||||
name: "変数名"
|
||||
text: "タイトル"
|
||||
default: "デフォルト値"
|
||||
|
||||
counter: "カウンター"
|
||||
_counter:
|
||||
name: "変数名"
|
||||
text: "タイトル"
|
||||
inc: "増加値"
|
||||
|
||||
_button:
|
||||
text: "タイトル"
|
||||
action: "ボタンを押したときの動作"
|
||||
@ -1893,6 +1921,7 @@ pages:
|
||||
_dialog:
|
||||
content: "内容"
|
||||
resetRandom: "乱数をリセット"
|
||||
|
||||
script:
|
||||
categories:
|
||||
flow: "制御"
|
||||
@ -1903,6 +1932,8 @@ pages:
|
||||
value: "値"
|
||||
fn: "関数"
|
||||
text: "テキスト操作"
|
||||
convert: "変換"
|
||||
list: "リスト"
|
||||
blocks:
|
||||
text: "テキスト"
|
||||
multiLineText: "テキスト(複数行)"
|
||||
@ -1924,6 +1955,10 @@ pages:
|
||||
strReverse: "テキストを反転"
|
||||
_strReverse:
|
||||
arg1: "テキスト"
|
||||
join: "テキストを連結"
|
||||
_join:
|
||||
arg1: "リスト"
|
||||
arg2: "区切り"
|
||||
add: "+ 足す"
|
||||
_add:
|
||||
arg1: "A"
|
||||
@ -1940,6 +1975,10 @@ pages:
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ 割った余り"
|
||||
_remind:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "AとBが同じ"
|
||||
_eq:
|
||||
arg1: "A"
|
||||
@ -2000,14 +2039,46 @@ pages:
|
||||
dailyRandomPick: "リストからランダムに選択 (ユーザーごとに日替わり)"
|
||||
_dailyRandomPick:
|
||||
arg1: "リスト"
|
||||
number: "数"
|
||||
seedRandom: "ランダム (シード)"
|
||||
_seedRandom:
|
||||
arg1: "シード"
|
||||
arg2: "確率"
|
||||
seedRannum: "乱数 (シード)"
|
||||
_seedRannum:
|
||||
arg1: "シード"
|
||||
arg2: "最小"
|
||||
arg3: "最大"
|
||||
seedRandomPick: "リストからランダムに選択 (シード)"
|
||||
_seedRandomPick:
|
||||
arg1: "シード"
|
||||
arg2: "リスト"
|
||||
DRPWPM: "確率付きリストからランダムに選択 (ユーザーごとに日替わり)"
|
||||
_DRPWPM:
|
||||
arg1: "テキストのリスト"
|
||||
pick: "リストから選択"
|
||||
_pick:
|
||||
arg1: "リスト"
|
||||
arg2: "位置"
|
||||
number: "数値"
|
||||
stringToNumber: "テキストを数値に"
|
||||
_stringToNumber:
|
||||
arg1: "テキスト"
|
||||
numberToString: "数値をテキストに"
|
||||
_numberToString:
|
||||
arg1: "数値"
|
||||
splitStrByLine: "テキストを行で分割"
|
||||
_splitStrByLine:
|
||||
arg1: "テキスト"
|
||||
ref: "変数"
|
||||
in: "入力"
|
||||
_in:
|
||||
arg1: "スロット番号"
|
||||
fn: "関数"
|
||||
_fn:
|
||||
slots: "スロット"
|
||||
slots-info: "スロットひとつひとつを改行で区切ってください"
|
||||
arg1: "出力"
|
||||
for: "繰り返し"
|
||||
_for:
|
||||
arg1: "回数"
|
||||
arg2: "処理"
|
||||
typeError: "スロット{slot}は\"{expect}\"を受け付けますが、\"{actual}\"が入れられています!"
|
||||
thereIsEmptySlot: "スロット{slot}が空です!"
|
||||
types:
|
||||
@ -2019,3 +2090,4 @@ pages:
|
||||
emptySlot: "空のスロット"
|
||||
enviromentVariables: "環境変数"
|
||||
pageVariables: "ページ要素"
|
||||
argVariables: "入力スロット"
|
||||
|
@ -1246,25 +1246,6 @@ deck/deck.user-column.vue:
|
||||
docs:
|
||||
edit-this-page-on-github: "間違いや改善点を見つけましたか?"
|
||||
edit-this-page-on-github-link: "このページをGitHubで編集"
|
||||
api:
|
||||
entities:
|
||||
properties: "プロパティ"
|
||||
endpoints:
|
||||
params: "パラメータ"
|
||||
no-params: "パラメータはありません"
|
||||
res: "レスポンス"
|
||||
require-credential: "このエンドポイントは認証情報が必須です。"
|
||||
require-permission: "このエンドポイントは{permission}の権限を必要とします。"
|
||||
has-limit: "レートリミットがあります。"
|
||||
duration-limit: "直近{duration}ミリ秒の間のこのエンドポイントへのリクエスト数の合計が{max}を超える場合はリクエストできません。"
|
||||
min-interval-limit: "前回のリクエストから{interval}ミリ秒経っていない場合はリクエストできません。"
|
||||
show-src: "このエンドポイントのソースコードも閲覧できます。"
|
||||
show-src-link: "コードをGitHubで見る"
|
||||
generated: "このドキュメントはAPI定義に基づき自動生成されています。"
|
||||
props:
|
||||
name: "名前"
|
||||
type: "型"
|
||||
description: "説明"
|
||||
dev/views/index.vue:
|
||||
manage-apps: "アプリの管理"
|
||||
dev/views/apps.vue:
|
||||
@ -1281,3 +1262,25 @@ dev/views/new-app.vue:
|
||||
authority: "権限"
|
||||
authority-desc: "ここにチェックした機能しかAPIからアクセスできひんから気ぃつけてな"
|
||||
authority-warning: "アプリ作った後でも変えれるけど、新しいやつ追加したらそん時関連付いてるユーザーキーは全部ほかされるで。"
|
||||
pages:
|
||||
blocks:
|
||||
image: "画像"
|
||||
post: "投稿フォーム"
|
||||
script:
|
||||
categories:
|
||||
random: "いんじゃんほい"
|
||||
list: "リスト"
|
||||
blocks:
|
||||
_join:
|
||||
arg1: "リスト"
|
||||
random: "いんじゃんほい"
|
||||
_randomPick:
|
||||
arg1: "リスト"
|
||||
_dailyRandomPick:
|
||||
arg1: "リスト"
|
||||
_seedRandomPick:
|
||||
arg2: "リスト"
|
||||
_pick:
|
||||
arg1: "リスト"
|
||||
types:
|
||||
array: "リスト"
|
||||
|
@ -34,6 +34,7 @@ common:
|
||||
signup: "신규 등록"
|
||||
signout: "로그아웃"
|
||||
reload-to-apply-the-setting: "이 설정을 적용하려면 페이지를 새로고침해야 합니다. 바로 새로고침하시겠습니까?"
|
||||
fetching-as-ap-object: "연합에서 조회 중"
|
||||
got-it: "알겠습니다"
|
||||
customization-tips:
|
||||
title: "커스터마이징 도움말"
|
||||
@ -61,6 +62,7 @@ common:
|
||||
month-and-day: "{month}월 {day}일"
|
||||
trash: "휴지통"
|
||||
drive: "드라이브"
|
||||
pages: "페이지"
|
||||
messaging: "대화"
|
||||
home: "홈"
|
||||
deck: "덱"
|
||||
@ -72,8 +74,23 @@ common:
|
||||
permissions:
|
||||
"read:account": "계정 정보 보기"
|
||||
"write:account": "계정 정보 변경"
|
||||
"read:blocks": "차단 보기"
|
||||
"write:blocks": "차단 수정"
|
||||
"read:drive": "드라이브 보기"
|
||||
"write:drive": "드라이브 수정"
|
||||
"read:favorites": "즐겨찾기 보기"
|
||||
"write:favorites": "즐겨찾기 수정"
|
||||
"read:following": "팔로우 정보 보기"
|
||||
"write:following": "팔로잉, 팔로우 수정"
|
||||
"read:messaging": "대화 보기"
|
||||
"write:messaging": "대화 수정"
|
||||
"read:mutes": "뮤트 보기"
|
||||
"write:mutes": "뮤트 수정"
|
||||
"write:notes": "글 작성, 삭제"
|
||||
"read:notifications": "글 보기"
|
||||
"write:notifications": "알림 수정"
|
||||
"read:reactions": "리액션 보기"
|
||||
"write:reactions": "리액션 수정"
|
||||
"write:votes": "투표하기"
|
||||
empty-timeline-info:
|
||||
follow-users-to-make-your-timeline: "사용자를 팔로우하면 글이 타임라인에 표시됩니다."
|
||||
@ -293,12 +310,12 @@ auth/views/form.vue:
|
||||
accept: "접근 권한 허용"
|
||||
auth/views/index.vue:
|
||||
loading: "로드 중"
|
||||
denied: "어플리케이션의 연계를 취소하였습니다."
|
||||
denied: "애플리케이션의 연계를 취소하였습니다."
|
||||
denied-paragraph: "이 앱이 당신의 계정에 액세스할 수 없습니다."
|
||||
already-authorized: "이 앱은 이미 연결되어 있습니다."
|
||||
allowed: "어플리케이션의 연동을 허용하였습니다."
|
||||
callback-url: "어플리케이션으로 돌아갑니다."
|
||||
please-go-back: "어플리케이션으로 돌아가여 시도하여 주십시오."
|
||||
allowed: "애플리케이션의 연동을 허용하였습니다."
|
||||
callback-url: "애플리케이션으로 돌아갑니다."
|
||||
please-go-back: "애플리케이션으로 돌아가여 시도하여 주십시오."
|
||||
error: "세션이 존재하지 않습니다."
|
||||
sign-in: "로그인 해주시기 바랍니다"
|
||||
common/views/pages/explore.vue:
|
||||
@ -474,8 +491,12 @@ common/views/components/user-menu.vue:
|
||||
mention: "멘션"
|
||||
mute: "뮤트"
|
||||
unmute: "뮤트 해제"
|
||||
mute-confirm: "이 사용자를 뮤트하시겠습니까?"
|
||||
unmute-confirm: "이 사용자를 뮤트 해제하시겠습니까?"
|
||||
block: "차단"
|
||||
unblock: "차단 해제"
|
||||
block-confirm: "이 사용자를 차단하시겠습니까?"
|
||||
unblock-confirm: "이 사용자를 차단 해제하시겠습니까?"
|
||||
push-to-list: "리스트에 추가"
|
||||
select-list: "리스트를 선택하여 주십시오"
|
||||
report-abuse: "스팸 신고"
|
||||
@ -483,8 +504,12 @@ common/views/components/user-menu.vue:
|
||||
report-abuse-reported: "관리자에게 보고되었습니다. 협조해주셔서 감사합니다."
|
||||
silence: "침묵"
|
||||
unsilence: "침묵 해제"
|
||||
silence-confirm: "이 사용자를 침묵하시겠습니까?"
|
||||
unsilence-confirm: "이 사용자를 침묵 해제하시겠습니까?"
|
||||
suspend: "정지"
|
||||
unsuspend: "정지 해제"
|
||||
suspend-confirm: "이 사용자를 정지하시겠습니까?"
|
||||
unsuspend-confirm: "이 사용자를 정지 해제하시겠습니까?"
|
||||
common/views/components/poll.vue:
|
||||
vote-to: "\"{}\"에 투표하기"
|
||||
vote-count: "{}표"
|
||||
@ -668,6 +693,7 @@ common/views/components/user-list-editor.vue:
|
||||
delete-are-you-sure: "리스트 \"$1\"을 삭제하시겠습니까?"
|
||||
deleted: "삭제하였습니다"
|
||||
common/views/components/user-lists.vue:
|
||||
create-list: "리스트 만들기"
|
||||
list-name: "리스트 이름"
|
||||
common/views/widgets/broadcast.vue:
|
||||
fetching: "확인중"
|
||||
@ -946,7 +972,7 @@ common/views/components/api-settings.vue:
|
||||
sending: "응답을 기다리는 중"
|
||||
response: "결과"
|
||||
desktop/views/components/settings.apps.vue:
|
||||
no-apps: "연결된 어플리케이션이 없습니다"
|
||||
no-apps: "연결된 애플리케이션이 없습니다"
|
||||
common/views/components/drive-settings.vue:
|
||||
max: "최대 용량"
|
||||
in-use: "사용중"
|
||||
@ -1194,7 +1220,9 @@ admin/views/users.vue:
|
||||
unsuspend-confirm: "정지를 해제하시겠습니까?"
|
||||
unsuspended: "정지를 해제하였습니다"
|
||||
make-silence: "침묵"
|
||||
silence-confirm: "침묵으로 설정합니까?"
|
||||
unmake-silence: "침묵 해제"
|
||||
unsilence-confirm: "침묵 해제하시겠습니까?"
|
||||
verify: "공식 계정으로 설정"
|
||||
verify-confirm: "공식 계정으로 설정하시겠습니까?"
|
||||
verified: "공식 계정으로 설정하였습니다"
|
||||
@ -1388,6 +1416,7 @@ desktop/views/widgets/polls.vue:
|
||||
desktop/views/widgets/post-form.vue:
|
||||
title: "글쓰기"
|
||||
note: "글쓰기"
|
||||
something-happened: "알 수 없는 문제로 글을 게시할 수 없습니다."
|
||||
desktop/views/widgets/profile.vue:
|
||||
update-banner: "클릭하여 배너 변경"
|
||||
update-avatar: "클릭하여 아바타 변경"
|
||||
@ -1410,6 +1439,7 @@ mobile/views/components/drive.vue:
|
||||
here-is-root: "현재 경로는 루트 경로로 폴더가 아닙니다."
|
||||
url-prompt: "업로드 하려는 파일의 URL"
|
||||
uploading: "업로드를 요청하였습니다. 업로드가 완료될 때까지 시간이 소요될 수 있습니다."
|
||||
folder-name-cannot-empty: "폴더 이름은 비워둘 수 없습니다."
|
||||
mobile/views/components/drive-file-chooser.vue:
|
||||
select-file: "파일 선택"
|
||||
mobile/views/components/drive-folder-chooser.vue:
|
||||
@ -1486,7 +1516,11 @@ mobile/views/components/ui.nav.vue:
|
||||
mobile/views/pages/drive.vue:
|
||||
contextmenu:
|
||||
upload: "파일 업로드"
|
||||
url-upload: "파일을 URL로부터 업로드"
|
||||
create-folder: "폴더 만들기"
|
||||
rename-folder: "폴더 이름 바꾸기"
|
||||
move-folder: "이 폴더를 이동"
|
||||
delete-folder: "이 폴더를 삭제"
|
||||
mobile/views/pages/user-lists.vue:
|
||||
title: "리스트"
|
||||
mobile/views/pages/signup.vue:
|
||||
@ -1589,25 +1623,6 @@ deck/deck.user-column.vue:
|
||||
docs:
|
||||
edit-this-page-on-github: "틀린 점이나 개선할 점을 찾으셨나요?"
|
||||
edit-this-page-on-github-link: "이 페이지를 GitHub에서 편집"
|
||||
api:
|
||||
entities:
|
||||
properties: "프로퍼티"
|
||||
endpoints:
|
||||
params: "매개변수"
|
||||
no-params: "매개변수가 없습니다"
|
||||
res: "응답"
|
||||
require-credential: "이 엔드포인트는 인증 정보가 필수적입니다."
|
||||
require-permission: "이 엔드포인트는 {permission} 권한을 필요로 합니다."
|
||||
has-limit: "Rate limit(요청 비율 제한)이 있습니다."
|
||||
duration-limit: "최근 {duration} 밀리초 사이에 엔드포인트로의 요청 수의 합계가 {max}를 넘는 경우 요청이 불가능합니다."
|
||||
min-interval-limit: "이전 요청으로부터 {interval} 밀리초가 지나지 않은 경우 요청할 수 없습니다."
|
||||
show-src: "이 엔드포인트에 대한 소스코드를 확인할 수 있습니다."
|
||||
show-src-link: "코드를 Github에서 보기"
|
||||
generated: "이 문서는 API 정의를 기반으로 자동으로 생성됩니다."
|
||||
props:
|
||||
name: "이름"
|
||||
type: "자료형"
|
||||
description: "설명"
|
||||
dev/views/index.vue:
|
||||
manage-apps: "앱 관리"
|
||||
dev/views/apps.vue:
|
||||
@ -1615,12 +1630,64 @@ dev/views/apps.vue:
|
||||
create-app: "앱 생성"
|
||||
app-missing: "앱 없음"
|
||||
dev/views/new-app.vue:
|
||||
create-app: "어플리케이션 생성"
|
||||
app-name: "어플리케이션 이름"
|
||||
new-app: "새 애플리케이션"
|
||||
new-app-info: "애플리케이션은 API에서도 생성할 수 있습니다. (app/create)"
|
||||
create-app: "애플리케이션 생성"
|
||||
app-name: "애플리케이션 이름"
|
||||
app-name-placeholder: "ex) Misskey for iOS"
|
||||
app-name-desc: "앱의 이름."
|
||||
app-overview: "앱 개요"
|
||||
app-overview-placeholder: "ex) Misskey iOS 클라이언트."
|
||||
app-overview-desc: "애플리케이션에 대한 간단한 설명이나 소개"
|
||||
callback-url: "콜백 URL (옵션)"
|
||||
callback-url-placeholder: "ex) https://your.app.example.com/callback.php"
|
||||
callback-url-desc: "사용자가 인증 폼에서 인증한 뒤 리다이렉트할 URL을 설정합니다."
|
||||
authority: "권한"
|
||||
authority-desc: "이곳에서 요청한 권한에 한정하여 API로 액세스할 수 있습니다."
|
||||
authority-warning: "앱을 생성한 뒤에도 변경할 수 있지만, 새로운 권한을 설정하는 경우 그 시점부터 예전에 발급받았던 유저 키는 모두 무효화됩니다."
|
||||
pages:
|
||||
new-page: "페이지 만들기"
|
||||
edit-page: "페이지 수정"
|
||||
read-page: "소스 표시중"
|
||||
page-created: "페이지를 만들었습니다"
|
||||
page-updated: "페이지를 수정했습니다"
|
||||
are-you-sure-delete: "이 페이지를 삭제하시겠습니까?"
|
||||
title: "제목"
|
||||
blocks:
|
||||
image: "이미지"
|
||||
post: "게시 양식"
|
||||
_post:
|
||||
text: "내용"
|
||||
_textInput:
|
||||
text: "제목"
|
||||
_textareaInput:
|
||||
text: "제목"
|
||||
_numberInput:
|
||||
text: "제목"
|
||||
_switch:
|
||||
text: "제목"
|
||||
_counter:
|
||||
text: "제목"
|
||||
_button:
|
||||
text: "제목"
|
||||
_action:
|
||||
_dialog:
|
||||
content: "내용"
|
||||
script:
|
||||
categories:
|
||||
random: "랜덤"
|
||||
list: "리스트"
|
||||
blocks:
|
||||
_join:
|
||||
arg1: "리스트"
|
||||
random: "랜덤"
|
||||
_randomPick:
|
||||
arg1: "리스트"
|
||||
_dailyRandomPick:
|
||||
arg1: "리스트"
|
||||
_seedRandomPick:
|
||||
arg2: "리스트"
|
||||
_pick:
|
||||
arg1: "리스트"
|
||||
types:
|
||||
array: "리스트"
|
||||
|
@ -619,13 +619,22 @@ deck/deck.user-column.vue:
|
||||
docs:
|
||||
edit-this-page-on-github: "Heb je een fout ontdekt of wil je bijdragen aan de documentatie? "
|
||||
edit-this-page-on-github-link: "Bewerk deze pagina op GitHub!"
|
||||
api:
|
||||
entities:
|
||||
properties: "Eigenschappen"
|
||||
endpoints:
|
||||
params: "Parameters"
|
||||
res: "Antwoord"
|
||||
props:
|
||||
name: "Naam"
|
||||
type: "Type"
|
||||
description: "Omschrijving"
|
||||
pages:
|
||||
blocks:
|
||||
image: "Afbeeldingen"
|
||||
script:
|
||||
categories:
|
||||
list: "Lijsten"
|
||||
blocks:
|
||||
_join:
|
||||
arg1: "Lijsten"
|
||||
_randomPick:
|
||||
arg1: "Lijsten"
|
||||
_dailyRandomPick:
|
||||
arg1: "Lijsten"
|
||||
_seedRandomPick:
|
||||
arg2: "Lijsten"
|
||||
_pick:
|
||||
arg1: "Lijsten"
|
||||
types:
|
||||
array: "Lijsten"
|
||||
|
@ -489,14 +489,24 @@ deck/deck.user-column.vue:
|
||||
following: "Følger"
|
||||
followers: "Følgere"
|
||||
images: "Bilder"
|
||||
docs:
|
||||
api:
|
||||
entities:
|
||||
properties: "Egenskaper"
|
||||
endpoints:
|
||||
params: "Parametere"
|
||||
res: "Respons"
|
||||
props:
|
||||
name: "Navn"
|
||||
type: "Type"
|
||||
description: "Beskrivelse"
|
||||
pages:
|
||||
blocks:
|
||||
image: "Bilder"
|
||||
script:
|
||||
categories:
|
||||
random: "Tilfeldig"
|
||||
list: "Lister"
|
||||
blocks:
|
||||
_join:
|
||||
arg1: "Lister"
|
||||
random: "Tilfeldig"
|
||||
_randomPick:
|
||||
arg1: "Lister"
|
||||
_dailyRandomPick:
|
||||
arg1: "Lister"
|
||||
_seedRandomPick:
|
||||
arg2: "Lister"
|
||||
_pick:
|
||||
arg1: "Lister"
|
||||
types:
|
||||
array: "Lister"
|
||||
|
@ -54,6 +54,7 @@ common:
|
||||
month-and-day: "{month}-{day}"
|
||||
trash: "Kosz"
|
||||
drive: "Dysk"
|
||||
pages: "Strony"
|
||||
messaging: "Rozmowy"
|
||||
home: "Strona główna"
|
||||
deck: "Tablice"
|
||||
@ -127,6 +128,7 @@ common:
|
||||
behavior: "Zachowanie"
|
||||
fetch-on-scroll: "Automatycznie ładuj po przeciągnięciu w dół"
|
||||
note-visibility: "Widoczność wpisów"
|
||||
remember-note-visibility: "Zapamiętaj widoczność wpisów"
|
||||
web-search-engine: "Wyszukiwarka internetowa"
|
||||
line-width: "Szerokości linii"
|
||||
line-width-thin: "Cienka"
|
||||
@ -1190,23 +1192,6 @@ deck/deck.user-column.vue:
|
||||
docs:
|
||||
edit-this-page-on-github: "Znalazłeś błąd lub chcesz pomóc w tworzeniu dokumentacji?"
|
||||
edit-this-page-on-github-link: "Edytuj stronę na GitHubie!"
|
||||
api:
|
||||
entities:
|
||||
properties: "Właściwości"
|
||||
endpoints:
|
||||
params: "Parametry"
|
||||
no-params: "Brak parametrów."
|
||||
res: "Odpowiedź"
|
||||
require-credential: "Punkt końcowy wymaga informacji o uwierzytelnieniu."
|
||||
require-permission: "Ten punkt końcowy wymaga uprawnienia {permission}."
|
||||
has-limit: "Istnieje limit częstotliwości."
|
||||
min-interval-limit: "Nie możesz wykonać żądania przed upłynięciem {interval} od ostatniego żądania."
|
||||
show-src: "Możesz zobaczyć kod źródłowy tego punktu końcowego."
|
||||
show-src-link: "Zobacz kod na GitHubie"
|
||||
props:
|
||||
name: "Nazwa"
|
||||
type: "Rodzaj"
|
||||
description: "Opis"
|
||||
dev/views/index.vue:
|
||||
manage-apps: "Zarządzaj aplikacjami"
|
||||
dev/views/apps.vue:
|
||||
@ -1215,3 +1200,38 @@ dev/views/apps.vue:
|
||||
dev/views/new-app.vue:
|
||||
app-name: "Nazwa Aplikacji"
|
||||
authority: "Uprawnienia"
|
||||
pages:
|
||||
title: "Tytuł"
|
||||
blocks:
|
||||
image: "Zdjęcia"
|
||||
post: "Formularz tworzenia"
|
||||
_textInput:
|
||||
text: "Tytuł"
|
||||
_textareaInput:
|
||||
text: "Tytuł"
|
||||
_numberInput:
|
||||
text: "Tytuł"
|
||||
_switch:
|
||||
text: "Tytuł"
|
||||
_counter:
|
||||
text: "Tytuł"
|
||||
_button:
|
||||
text: "Tytuł"
|
||||
script:
|
||||
categories:
|
||||
random: "Losowy"
|
||||
list: "Listy"
|
||||
blocks:
|
||||
_join:
|
||||
arg1: "Listy"
|
||||
random: "Losowy"
|
||||
_randomPick:
|
||||
arg1: "Listy"
|
||||
_dailyRandomPick:
|
||||
arg1: "Listy"
|
||||
_seedRandomPick:
|
||||
arg2: "Listy"
|
||||
_pick:
|
||||
arg1: "Listy"
|
||||
types:
|
||||
array: "Listy"
|
||||
|
@ -274,18 +274,9 @@ deck/deck.user-column.vue:
|
||||
timeline: "Linha do tempo"
|
||||
docs:
|
||||
edit-this-page-on-github-link: "Edite esta página no GitHub!"
|
||||
api:
|
||||
entities:
|
||||
properties: "Propriedades"
|
||||
endpoints:
|
||||
params: "Parâmetros"
|
||||
no-params: "Sem parâmetros"
|
||||
res: "Resposta"
|
||||
show-src-link: "Veja o código no GitHub"
|
||||
generated: "Este documento foi gerado pelas definições da API."
|
||||
props:
|
||||
name: "Nome"
|
||||
type: "Tipo"
|
||||
description: "Descrição"
|
||||
dev/views/index.vue:
|
||||
manage-apps: "Gerenciar aplicativos"
|
||||
pages:
|
||||
blocks:
|
||||
image: "Imagens"
|
||||
post: "Formulário de publicação"
|
||||
|
@ -146,3 +146,9 @@ mobile/views/components/sub-note-content.vue:
|
||||
poll: "Голосования"
|
||||
mobile/views/pages/widgets.vue:
|
||||
customization-tips: "Советы по настройке"
|
||||
pages:
|
||||
script:
|
||||
categories:
|
||||
random: "Случайно"
|
||||
blocks:
|
||||
random: "Случайно"
|
||||
|
@ -62,6 +62,7 @@ common:
|
||||
month-and-day: "{month}月 {day}日"
|
||||
trash: "垃圾箱"
|
||||
drive: "网盘"
|
||||
pages: "页面"
|
||||
messaging: "聊天"
|
||||
home: "首页"
|
||||
deck: "Deck"
|
||||
@ -1622,25 +1623,6 @@ deck/deck.user-column.vue:
|
||||
docs:
|
||||
edit-this-page-on-github: "发现错误或想要为文档做出贡献?"
|
||||
edit-this-page-on-github-link: "在GitHub上编辑这个页面。"
|
||||
api:
|
||||
entities:
|
||||
properties: "属性"
|
||||
endpoints:
|
||||
params: "参数"
|
||||
no-params: "没有参数"
|
||||
res: "回应"
|
||||
require-credential: "此端点需要身份验证信息。"
|
||||
require-permission: "此端点需要{permission}权限。"
|
||||
has-limit: "有一个速率限制。"
|
||||
duration-limit: "如果您发送请求在{duration}毫秒内多于{max}次,则无法发送更多请求。"
|
||||
min-interval-limit: "如果自上次请求后未传递{interval}毫秒,则无法发送请求。"
|
||||
show-src: "您可以查看此端点的源代码。"
|
||||
show-src-link: "查阅GitHub上的代码"
|
||||
generated: "该文档由API定义生成。"
|
||||
props:
|
||||
name: "名称"
|
||||
type: "型号"
|
||||
description: "描述"
|
||||
dev/views/index.vue:
|
||||
manage-apps: "管理应用"
|
||||
dev/views/apps.vue:
|
||||
@ -1663,3 +1645,247 @@ dev/views/new-app.vue:
|
||||
authority: "权限"
|
||||
authority-desc: "只能通过API访问此处请求的功能。"
|
||||
authority-warning: "您可以在创建应用程序后对其进行更改,但如果您授予不同的权限,则当时关联的所有用户密钥都将失效。"
|
||||
pages:
|
||||
new-page: "创建页面"
|
||||
edit-page: "编辑页面"
|
||||
read-page: "查看源"
|
||||
page-created: "页面已创建"
|
||||
page-updated: "页面已更新"
|
||||
are-you-sure-delete: "是否删除此页面?"
|
||||
page-deleted: "该页面已被删除。"
|
||||
edit-this-page: "编辑此页面"
|
||||
view-source: "查看源代码"
|
||||
view-page: "查看页面"
|
||||
inspector: "检查器"
|
||||
content: "页面内容"
|
||||
variables: "变量"
|
||||
variables-info: "您可以使用变量创建动态页面。在文本中通过<b>{变量名}</b>的写法来嵌入变量值。例如在文本<b>Hello { thing } world!</b>中,如果变量(thing)的值为<b>ai</b>,那么该文本会成为<b>Hello ai world!</b>。"
|
||||
variables-info2: "因为变量的计算(计算变量值)是从上到下执行的,所以不能在变量中引用下面的变量。例如从上到下依次定义了<b>A,B,C</b>3个变量,那么<b>C</b>中可以引用<b>A</b>或<b>B</b>,但是<b>A</b>无法引用<b>B</b>或<b>C</b>。"
|
||||
variables-info3: "为了接收来自用户的输入,页面上设有“用户输入”块,在“变量名称”中设置要在其中保存输入值的变量名(变量会自动创建)。您可以使用该变量执行操作以响应用户输入。"
|
||||
variables-info4: "通过使用函数,您可以将数值计算过程组合成可重用的形式。要创建函数,需要创建一个“函数”类型的变量。你可以将函数设定为槽函数(参数)的格式,槽函数的值可作为函数中的变量使用。另外,AiScript标准中还有一些函数会将函数作为参数(称为高阶函数)。\n除了已经预先定义的函数外,您也可以将它们设置为这些高阶函数的槽函数。"
|
||||
more-details: "详细说明"
|
||||
title: "标题"
|
||||
url: "页面URL"
|
||||
summary: "页面摘要"
|
||||
align-center: "居中"
|
||||
font: "字体"
|
||||
fontSerif: "衬线字体"
|
||||
fontSansSerif: "无衬线字体"
|
||||
choose-block: "添加块"
|
||||
select-type: "类型选择"
|
||||
enter-variable-name: "请确定变量名"
|
||||
the-variable-name-is-already-used: "变量名已使用"
|
||||
content-blocks: "内容"
|
||||
input-blocks: "输入"
|
||||
special-blocks: "特殊"
|
||||
post-from-post-form: "发布此内容"
|
||||
posted-from-post-form: "已发布"
|
||||
blocks:
|
||||
text: "文本"
|
||||
textarea: "文本区域"
|
||||
section: "章节"
|
||||
image: "图片"
|
||||
button: "按钮"
|
||||
if: "如果"
|
||||
_if:
|
||||
variable: "变量"
|
||||
post: "投稿形式"
|
||||
_post:
|
||||
text: "内容"
|
||||
textInput: "文本输入"
|
||||
_textInput:
|
||||
name: "变量名"
|
||||
text: "标题"
|
||||
default: "默认值"
|
||||
textareaInput: "多行文本输入"
|
||||
_textareaInput:
|
||||
name: "变量名"
|
||||
text: "标题"
|
||||
default: "默认值"
|
||||
numberInput: "数值输入"
|
||||
_numberInput:
|
||||
name: "变量名"
|
||||
text: "标题"
|
||||
default: "默认值"
|
||||
switch: "开关"
|
||||
_switch:
|
||||
name: "变量名"
|
||||
text: "标题"
|
||||
default: "默认值"
|
||||
counter: "计数器"
|
||||
_counter:
|
||||
name: "变量名"
|
||||
text: "标题"
|
||||
inc: "增加值"
|
||||
_button:
|
||||
text: "标题"
|
||||
action: "按下按钮时的行为"
|
||||
_action:
|
||||
dialog: "显示对话框"
|
||||
_dialog:
|
||||
content: "内容"
|
||||
resetRandom: "随机值重置"
|
||||
script:
|
||||
categories:
|
||||
flow: "控制"
|
||||
logical: "逻辑运算"
|
||||
operation: "计算"
|
||||
comparison: "比较"
|
||||
random: "随机"
|
||||
value: "值"
|
||||
fn: "函数"
|
||||
text: "文本操作"
|
||||
list: "列表"
|
||||
blocks:
|
||||
text: "文本"
|
||||
multiLineText: "文本 (多行)"
|
||||
textList: "文本列表"
|
||||
_textList:
|
||||
info: "情使用换行符分隔每行"
|
||||
strLen: "文本长度"
|
||||
_strLen:
|
||||
arg1: "文本"
|
||||
strPick: "字符提取"
|
||||
_strPick:
|
||||
arg1: "文本"
|
||||
arg2: "字符位置"
|
||||
strReplace: "文本替换"
|
||||
_strReplace:
|
||||
arg1: "文本"
|
||||
arg2: "替换之前"
|
||||
arg3: "替换之后"
|
||||
strReverse: "文本反向"
|
||||
_strReverse:
|
||||
arg1: "文本"
|
||||
join: "合并文本"
|
||||
_join:
|
||||
arg1: "列表"
|
||||
arg2: "分隔符"
|
||||
add: "+ 加"
|
||||
_add:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
subtract: "- 减"
|
||||
_subtract:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
multiply: "× 乘"
|
||||
_multiply:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
divide: "÷ 除"
|
||||
_divide:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
remind: "÷ 取模"
|
||||
_remind:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
eq: "A和B相等"
|
||||
_eq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
notEq: "A和B不等"
|
||||
_notEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
and: "A和B"
|
||||
_and:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
or: "A或B"
|
||||
_or:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
lt: "< A小于B"
|
||||
_lt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
gt: "> A大于B"
|
||||
_gt:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
ltEq: "<= A小于等于B"
|
||||
_ltEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
gtEq: ">= A大于等于B"
|
||||
_gtEq:
|
||||
arg1: "A"
|
||||
arg2: "B"
|
||||
if: "分支"
|
||||
_if:
|
||||
arg1: "如果"
|
||||
arg2: "的话"
|
||||
arg3: "否则"
|
||||
random: "随机"
|
||||
_random:
|
||||
arg1: "概率"
|
||||
rannum: "随机"
|
||||
_rannum:
|
||||
arg1: "最小"
|
||||
arg2: "最大"
|
||||
randomPick: "从列表中随机选择"
|
||||
_randomPick:
|
||||
arg1: "列表"
|
||||
dailyRandom: "随机(每个用户每日)"
|
||||
_dailyRandom:
|
||||
arg1: "概率"
|
||||
dailyRannum: "随机数(每个用户每日)"
|
||||
_dailyRannum:
|
||||
arg1: "最小"
|
||||
arg2: "最大"
|
||||
dailyRandomPick: "从列表中随机选择(每个用户每日)"
|
||||
_dailyRandomPick:
|
||||
arg1: "列表"
|
||||
seedRandom: "随机 (种子)"
|
||||
_seedRandom:
|
||||
arg1: "种子"
|
||||
arg2: "概率"
|
||||
seedRannum: "随机数(种子)"
|
||||
_seedRannum:
|
||||
arg1: "种子"
|
||||
arg2: "最小"
|
||||
arg3: "最大"
|
||||
seedRandomPick: "从列表中随机选择 (种子)"
|
||||
_seedRandomPick:
|
||||
arg1: "种子"
|
||||
arg2: "列表"
|
||||
_DRPWPM:
|
||||
arg1: "文本列表"
|
||||
pick: "从列表中选择"
|
||||
_pick:
|
||||
arg1: "列表"
|
||||
arg2: "位置"
|
||||
number: "数值"
|
||||
stringToNumber: "文本到数字"
|
||||
_stringToNumber:
|
||||
arg1: "文本"
|
||||
numberToString: "数字到文本"
|
||||
_numberToString:
|
||||
arg1: "数值"
|
||||
splitStrByLine: "将文本按行拆分"
|
||||
_splitStrByLine:
|
||||
arg1: "文本"
|
||||
ref: "变量"
|
||||
fn: "函数"
|
||||
_fn:
|
||||
slots: "槽函数"
|
||||
slots-info: "请使用换行符分隔每个槽函数"
|
||||
arg1: "输出"
|
||||
for: "重复"
|
||||
_for:
|
||||
arg1: "次数"
|
||||
arg2: "处理"
|
||||
typeError: "槽函数{slot}需要传入“{expect}”,但是实际传入为“{actual}”!"
|
||||
thereIsEmptySlot: "槽函数{slot}为空!"
|
||||
types:
|
||||
string: "文本"
|
||||
number: "数值"
|
||||
boolean: "布尔值"
|
||||
array: "列表"
|
||||
stringArray: "文本列表"
|
||||
emptySlot: "空白槽函数"
|
||||
enviromentVariables: "环境变量"
|
||||
pageVariables: "页面元素"
|
||||
argVariables: "输入槽函数"
|
||||
|
23
migration/1556746559567-UserProfile.ts
Normal file
23
migration/1556746559567-UserProfile.ts
Normal file
@ -0,0 +1,23 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class UserProfile1556746559567 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`UPDATE "user_profile" SET github = FALSE`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "githubId"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "githubId" VARCHAR(64)`);
|
||||
await queryRunner.query(`UPDATE "user_profile" SET discord = FALSE`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordExpiresDate"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "discordExpiresDate" VARCHAR(64)`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`UPDATE "user_profile" SET github = FALSE`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "githubId"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "githubId" INTEGER`);
|
||||
await queryRunner.query(`UPDATE "user_profile" SET discord = FALSE`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" DROP COLUMN "discordExpiresDate"`);
|
||||
await queryRunner.query(`ALTER TABLE "user_profile" ADD COLUMN "discordExpiresDate" INTEGER`);
|
||||
}
|
||||
|
||||
}
|
13
migration/1557476068003-PinnedUsers.ts
Normal file
13
migration/1557476068003-PinnedUsers.ts
Normal file
@ -0,0 +1,13 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class PinnedUsers1557476068003 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "pinnedUsers" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "pinnedUsers"`);
|
||||
}
|
||||
|
||||
}
|
29
package.json
29
package.json
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <i@syuilo.com>",
|
||||
"version": "11.6.0",
|
||||
"version": "11.12.0",
|
||||
"codename": "daybreak",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -63,8 +63,8 @@
|
||||
"@types/lolex": "3.1.1",
|
||||
"@types/minio": "7.0.1",
|
||||
"@types/mocha": "5.2.6",
|
||||
"@types/node": "11.13.4",
|
||||
"@types/nodemailer": "4.6.7",
|
||||
"@types/node": "11.13.8",
|
||||
"@types/nodemailer": "4.6.8",
|
||||
"@types/nprogress": "0.0.29",
|
||||
"@types/oauth": "0.9.1",
|
||||
"@types/parse5": "5.0.0",
|
||||
@ -77,9 +77,10 @@
|
||||
"@types/redis": "2.8.12",
|
||||
"@types/rename": "1.0.1",
|
||||
"@types/request": "2.48.1",
|
||||
"@types/request-promise-native": "1.0.15",
|
||||
"@types/request-promise-native": "1.0.16",
|
||||
"@types/request-stats": "3.0.0",
|
||||
"@types/rimraf": "2.0.2",
|
||||
"@types/seedrandom": "2.4.28",
|
||||
"@types/sharp": "0.22.1",
|
||||
"@types/showdown": "1.9.2",
|
||||
"@types/speakeasy": "2.0.4",
|
||||
@ -88,7 +89,7 @@
|
||||
"@types/tmp": "0.1.0",
|
||||
"@types/uuid": "3.4.4",
|
||||
"@types/web-push": "3.3.0",
|
||||
"@types/webpack": "4.4.27",
|
||||
"@types/webpack": "4.4.29",
|
||||
"@types/webpack-stream": "3.2.10",
|
||||
"@types/websocket": "0.0.40",
|
||||
"@types/ws": "6.0.1",
|
||||
@ -111,12 +112,12 @@
|
||||
"cssnano": "4.1.10",
|
||||
"dateformat": "3.0.3",
|
||||
"deep-equal": "1.0.1",
|
||||
"diskusage": "1.1.0",
|
||||
"diskusage": "1.1.1",
|
||||
"double-ended-queue": "2.1.0-0",
|
||||
"emojilib": "2.4.0",
|
||||
"eslint": "5.16.0",
|
||||
"eslint-plugin-vue": "5.2.2",
|
||||
"eventemitter3": "3.1.0",
|
||||
"eventemitter3": "3.1.2",
|
||||
"feed": "2.0.4",
|
||||
"file-type": "10.11.0",
|
||||
"fuckadblock": "3.2.1",
|
||||
@ -159,13 +160,13 @@
|
||||
"loader-utils": "1.2.3",
|
||||
"lolex": "3.1.0",
|
||||
"lookup-dns-cache": "2.1.0",
|
||||
"minio": "7.0.7",
|
||||
"mocha": "6.1.3",
|
||||
"minio": "7.0.8",
|
||||
"mocha": "6.1.4",
|
||||
"moji": "0.5.1",
|
||||
"moment": "2.24.0",
|
||||
"ms": "2.1.1",
|
||||
"nested-property": "0.0.7",
|
||||
"node-fetch": "2.3.0",
|
||||
"node-fetch": "2.5.0",
|
||||
"nodemailer": "6.1.1",
|
||||
"nprogress": "0.2.0",
|
||||
"object-assign-deep": "0.4.0",
|
||||
@ -233,8 +234,8 @@
|
||||
"vue-color": "2.7.0",
|
||||
"vue-content-loading": "1.6.0",
|
||||
"vue-cropperjs": "3.0.0",
|
||||
"vue-i18n": "8.11.1",
|
||||
"vue-js-modal": "1.3.28",
|
||||
"vue-i18n": "8.11.2",
|
||||
"vue-js-modal": "1.3.31",
|
||||
"vue-json-pretty": "1.6.0",
|
||||
"vue-loader": "15.7.0",
|
||||
"vue-marquee-text-component": "1.1.1",
|
||||
@ -250,9 +251,9 @@
|
||||
"vuex-persistedstate": "2.5.4",
|
||||
"web-push": "3.3.3",
|
||||
"webpack": "4.30.0",
|
||||
"webpack-cli": "3.3.0",
|
||||
"webpack-cli": "3.3.1",
|
||||
"websocket": "1.0.28",
|
||||
"ws": "6.2.1",
|
||||
"ws": "7.0.0",
|
||||
"xev": "2.0.1"
|
||||
}
|
||||
}
|
||||
|
@ -82,6 +82,14 @@
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title>{{ $t('pinned-users') }}</template>
|
||||
<section>
|
||||
<ui-textarea v-model="pinnedUsers"></ui-textarea>
|
||||
<ui-button @click="updateMeta">{{ $t('save') }}</ui-button>
|
||||
</section>
|
||||
</ui-card>
|
||||
|
||||
<ui-card>
|
||||
<template #title>{{ $t('invite') }}</template>
|
||||
<section>
|
||||
@ -190,6 +198,7 @@ export default Vue.extend({
|
||||
enableServiceWorker: false,
|
||||
swPublicKey: null,
|
||||
swPrivateKey: null,
|
||||
pinnedUsers: [],
|
||||
faHeadset, faShieldAlt, faGhost, faUserPlus, farEnvelope, faBolt
|
||||
};
|
||||
},
|
||||
@ -239,6 +248,7 @@ export default Vue.extend({
|
||||
this.enableServiceWorker = meta.enableServiceWorker;
|
||||
this.swPublicKey = meta.swPublickey;
|
||||
this.swPrivateKey = meta.swPrivateKey;
|
||||
this.pinnedUsers = meta.pinnedUsers.join('\n');
|
||||
});
|
||||
},
|
||||
|
||||
@ -297,7 +307,8 @@ export default Vue.extend({
|
||||
smtpPass: this.smtpAuth ? this.smtpPass : '',
|
||||
enableServiceWorker: this.enableServiceWorker,
|
||||
swPublicKey: this.swPublicKey,
|
||||
swPrivateKey: this.swPrivateKey
|
||||
swPrivateKey: this.swPrivateKey,
|
||||
pinnedUsers: this.pinnedUsers.split('\n')
|
||||
}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
|
@ -11,7 +11,6 @@
|
||||
<span class="username">@{{ user | acct }}</span>
|
||||
<span class="is-admin" v-if="user.isAdmin">admin</span>
|
||||
<span class="is-moderator" v-if="user.isModerator">moderator</span>
|
||||
<span class="is-verified" v-if="user.isVerified" :title="$t('@.verified-user')"><fa icon="star"/></span>
|
||||
<span class="is-silenced" v-if="user.isSilenced" :title="$t('@.silenced-user')"><fa :icon="faMicrophoneSlash"/></span>
|
||||
<span class="is-suspended" v-if="user.isSuspended" :title="$t('@.suspended-user')"><fa :icon="faSnowflake"/></span>
|
||||
</header>
|
||||
@ -77,7 +76,6 @@ export default Vue.extend({
|
||||
background var(--noteHeaderAdminBg)
|
||||
color var(--noteHeaderAdminFg)
|
||||
|
||||
> .is-verified
|
||||
> .is-silenced
|
||||
> .is-suspended
|
||||
margin 0 0 0 .5em
|
||||
|
@ -12,10 +12,6 @@
|
||||
<x-user :user='user'/>
|
||||
<div class="actions">
|
||||
<ui-button @click="resetPassword"><fa :icon="faKey"/> {{ $t('reset-password') }}</ui-button>
|
||||
<ui-horizon-group>
|
||||
<ui-button @click="verifyUser" :disabled="verifying"><fa :icon="faCertificate"/> {{ $t('verify') }}</ui-button>
|
||||
<ui-button @click="unverifyUser" :disabled="unverifying">{{ $t('unverify') }}</ui-button>
|
||||
</ui-horizon-group>
|
||||
<ui-horizon-group>
|
||||
<ui-button @click="silenceUser"><fa :icon="faMicrophoneSlash"/> {{ $t('make-silence') }}</ui-button>
|
||||
<ui-button @click="unsilenceUser">{{ $t('unmake-silence') }}</ui-button>
|
||||
@ -47,7 +43,6 @@
|
||||
<option value="all">{{ $t('users.state.all') }}</option>
|
||||
<option value="admin">{{ $t('users.state.admin') }}</option>
|
||||
<option value="moderator">{{ $t('users.state.moderator') }}</option>
|
||||
<option value="verified">{{ $t('users.state.verified') }}</option>
|
||||
<option value="silenced">{{ $t('users.state.silenced') }}</option>
|
||||
<option value="suspended">{{ $t('users.state.suspended') }}</option>
|
||||
</ui-select>
|
||||
@ -71,7 +66,7 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../i18n';
|
||||
import parseAcct from "../../../../misc/acct/parse";
|
||||
import { faCertificate, faUsers, faTerminal, faSearch, faKey, faSync, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faUsers, faTerminal, faSearch, faKey, faSync, faMicrophoneSlash } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faSnowflake } from '@fortawesome/free-regular-svg-icons';
|
||||
import XUser from './users.user.vue';
|
||||
|
||||
@ -84,8 +79,6 @@ export default Vue.extend({
|
||||
return {
|
||||
user: null,
|
||||
target: null,
|
||||
verifying: false,
|
||||
unverifying: false,
|
||||
suspending: false,
|
||||
unsuspending: false,
|
||||
sort: '+createdAt',
|
||||
@ -95,7 +88,7 @@ export default Vue.extend({
|
||||
offset: 0,
|
||||
users: [],
|
||||
existMore: false,
|
||||
faTerminal, faCertificate, faUsers, faSnowflake, faSearch, faKey, faSync, faMicrophoneSlash
|
||||
faTerminal, faUsers, faSnowflake, faSearch, faKey, faSync, faMicrophoneSlash
|
||||
};
|
||||
},
|
||||
|
||||
@ -181,56 +174,6 @@ export default Vue.extend({
|
||||
});
|
||||
},
|
||||
|
||||
async verifyUser() {
|
||||
if (!await this.getConfirmed(this.$t('verify-confirm'))) return;
|
||||
|
||||
this.verifying = true;
|
||||
|
||||
const process = async () => {
|
||||
await this.$root.api('admin/verify-user', { userId: this.user.id });
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
text: this.$t('verified')
|
||||
});
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: e.toString()
|
||||
});
|
||||
});
|
||||
|
||||
this.verifying = false;
|
||||
|
||||
this.refreshUser();
|
||||
},
|
||||
|
||||
async unverifyUser() {
|
||||
if (!await this.getConfirmed(this.$t('unverify-confirm'))) return;
|
||||
|
||||
this.unverifying = true;
|
||||
|
||||
const process = async () => {
|
||||
await this.$root.api('admin/unverify-user', { userId: this.user.id });
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
text: this.$t('unverified')
|
||||
});
|
||||
};
|
||||
|
||||
await process().catch(e => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: e.toString()
|
||||
});
|
||||
});
|
||||
|
||||
this.unverifying = false;
|
||||
|
||||
this.refreshUser();
|
||||
},
|
||||
|
||||
async silenceUser() {
|
||||
if (!await this.getConfirmed(this.$t('silence-confirm'))) return;
|
||||
|
||||
|
@ -34,7 +34,7 @@ body
|
||||
.peg
|
||||
display block
|
||||
position absolute
|
||||
right 0px
|
||||
right 0
|
||||
width 100px
|
||||
height 100%
|
||||
box-shadow 0 0 10px var(--primary), 0 0 5px var(--primary)
|
||||
|
@ -98,7 +98,7 @@ export default Vue.extend({
|
||||
margin 0 auto
|
||||
text-align center
|
||||
background #fff
|
||||
box-shadow 0px 4px 16px rgba(#000, 0.2)
|
||||
box-shadow 0 4px 16px rgba(#000, 0.2)
|
||||
|
||||
> .fetching
|
||||
margin 0
|
||||
|
@ -52,7 +52,7 @@ function match(e: KeyboardEvent, patterns: action['patterns']): boolean {
|
||||
pattern.ctrl == e.ctrlKey &&
|
||||
pattern.shift == e.shiftKey &&
|
||||
pattern.alt == e.altKey &&
|
||||
e.metaKey == false
|
||||
!e.metaKey
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -1,480 +0,0 @@
|
||||
/**
|
||||
* AiScript
|
||||
* evaluator & type checker
|
||||
*/
|
||||
|
||||
import autobind from 'autobind-decorator';
|
||||
import * as seedrandom from 'seedrandom';
|
||||
|
||||
import {
|
||||
faSuperscript,
|
||||
faAlignLeft,
|
||||
faShareAlt,
|
||||
faSquareRootAlt,
|
||||
faPlus,
|
||||
faMinus,
|
||||
faTimes,
|
||||
faDivide,
|
||||
faList,
|
||||
faQuoteRight,
|
||||
faEquals,
|
||||
faGreaterThan,
|
||||
faLessThan,
|
||||
faGreaterThanEqual,
|
||||
faLessThanEqual,
|
||||
faExclamation,
|
||||
faNotEqual,
|
||||
faDice,
|
||||
faSortNumericUp,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { faFlag } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
import { version } from '../../config';
|
||||
|
||||
export type Block = {
|
||||
id: string;
|
||||
type: string;
|
||||
args: Block[];
|
||||
value: any;
|
||||
};
|
||||
|
||||
export type Variable = Block & {
|
||||
name: string;
|
||||
};
|
||||
|
||||
type Type = 'string' | 'number' | 'boolean' | 'stringArray';
|
||||
|
||||
type TypeError = {
|
||||
arg: number;
|
||||
expect: Type;
|
||||
actual: Type;
|
||||
};
|
||||
|
||||
const funcDefs = {
|
||||
if: { in: ['boolean', 0, 0], out: 0, category: 'flow', icon: faShareAlt, },
|
||||
not: { in: ['boolean'], out: 'boolean', category: 'logical', icon: faFlag, },
|
||||
or: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, },
|
||||
and: { in: ['boolean', 'boolean'], out: 'boolean', category: 'logical', icon: faFlag, },
|
||||
add: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faPlus, },
|
||||
subtract: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faMinus, },
|
||||
multiply: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faTimes, },
|
||||
divide: { in: ['number', 'number'], out: 'number', category: 'operation', icon: faDivide, },
|
||||
eq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faEquals, },
|
||||
notEq: { in: [0, 0], out: 'boolean', category: 'comparison', icon: faNotEqual, },
|
||||
gt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThan, },
|
||||
lt: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThan, },
|
||||
gtEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faGreaterThanEqual, },
|
||||
ltEq: { in: ['number', 'number'], out: 'boolean', category: 'comparison', icon: faLessThanEqual, },
|
||||
strLen: { in: ['string'], out: 'number', category: 'text', icon: faQuoteRight, },
|
||||
strPick: { in: ['string', 'number'], out: 'string', category: 'text', icon: faQuoteRight, },
|
||||
strReplace: { in: ['string', 'string', 'string'], out: 'string', category: 'text', icon: faQuoteRight, },
|
||||
strReverse: { in: ['string'], out: 'string', category: 'text', icon: faQuoteRight, },
|
||||
rannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
|
||||
random: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, },
|
||||
randomPick: { in: [0], out: 0, category: 'random', icon: faDice, },
|
||||
dailyRannum: { in: ['number', 'number'], out: 'number', category: 'random', icon: faDice, },
|
||||
dailyRandom: { in: ['number'], out: 'boolean', category: 'random', icon: faDice, },
|
||||
dailyRandomPick: { in: [0], out: 0, category: 'random', icon: faDice, },
|
||||
};
|
||||
|
||||
const blockDefs = [
|
||||
{ type: 'text', out: 'string', category: 'value', icon: faQuoteRight, },
|
||||
{ type: 'multiLineText', out: 'string', category: 'value', icon: faAlignLeft, },
|
||||
{ type: 'textList', out: 'stringArray', category: 'value', icon: faList, },
|
||||
{ type: 'number', out: 'number', category: 'value', icon: faSortNumericUp, },
|
||||
{ type: 'ref', out: null, category: 'value', icon: faSuperscript, },
|
||||
{ type: 'in', out: null, category: 'value', icon: faSuperscript, },
|
||||
{ type: 'fn', out: 'function', category: 'value', icon: faSuperscript, },
|
||||
...Object.entries(funcDefs).map(([k, v]) => ({
|
||||
type: k, out: v.out || null, category: v.category, icon: v.icon
|
||||
}))
|
||||
];
|
||||
|
||||
type PageVar = { name: string; value: any; type: Type; };
|
||||
|
||||
const envVarsDef = {
|
||||
AI: 'string',
|
||||
VERSION: 'string',
|
||||
LOGIN: 'boolean',
|
||||
NAME: 'string',
|
||||
USERNAME: 'string',
|
||||
USERID: 'string',
|
||||
NOTES_COUNT: 'number',
|
||||
FOLLOWERS_COUNT: 'number',
|
||||
FOLLOWING_COUNT: 'number',
|
||||
IS_CAT: 'boolean',
|
||||
MY_NOTES_COUNT: 'number',
|
||||
MY_FOLLOWERS_COUNT: 'number',
|
||||
MY_FOLLOWING_COUNT: 'number',
|
||||
};
|
||||
|
||||
export class AiScript {
|
||||
private variables: Variable[];
|
||||
private pageVars: PageVar[];
|
||||
private envVars: Record<keyof typeof envVarsDef, any>;
|
||||
|
||||
public static envVarsDef = envVarsDef;
|
||||
public static blockDefs = blockDefs;
|
||||
public static funcDefs = funcDefs;
|
||||
private opts: {
|
||||
randomSeed?: string; user?: any; visitor?: any;
|
||||
};
|
||||
|
||||
constructor(variables: Variable[] = [], pageVars: PageVar[] = [], opts: AiScript['opts'] = {}) {
|
||||
this.variables = variables;
|
||||
this.pageVars = pageVars;
|
||||
this.opts = opts;
|
||||
|
||||
this.envVars = {
|
||||
AI: 'kawaii',
|
||||
VERSION: version,
|
||||
LOGIN: opts.visitor != null,
|
||||
NAME: opts.visitor ? opts.visitor.name : '',
|
||||
USERNAME: opts.visitor ? opts.visitor.username : '',
|
||||
USERID: opts.visitor ? opts.visitor.id : '',
|
||||
NOTES_COUNT: opts.visitor ? opts.visitor.notesCount : 0,
|
||||
FOLLOWERS_COUNT: opts.visitor ? opts.visitor.followersCount : 0,
|
||||
FOLLOWING_COUNT: opts.visitor ? opts.visitor.followingCount : 0,
|
||||
IS_CAT: opts.visitor ? opts.visitor.isCat : false,
|
||||
MY_NOTES_COUNT: opts.user ? opts.user.notesCount : 0,
|
||||
MY_FOLLOWERS_COUNT: opts.user ? opts.user.followersCount : 0,
|
||||
MY_FOLLOWING_COUNT: opts.user ? opts.user.followingCount : 0,
|
||||
};
|
||||
}
|
||||
|
||||
@autobind
|
||||
public injectVars(vars: Variable[]) {
|
||||
this.variables = vars;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public injectPageVars(pageVars: PageVar[]) {
|
||||
this.pageVars = pageVars;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public updatePageVar(name: string, value: any) {
|
||||
this.pageVars.find(v => v.name === name).value = value;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public updateRandomSeed(seed: string) {
|
||||
this.opts.randomSeed = seed;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public static isLiteralBlock(v: Block) {
|
||||
if (v.type === null) return true;
|
||||
if (v.type === 'text') return true;
|
||||
if (v.type === 'multiLineText') return true;
|
||||
if (v.type === 'textList') return true;
|
||||
if (v.type === 'number') return true;
|
||||
if (v.type === 'ref') return true;
|
||||
if (v.type === 'fn') return true;
|
||||
if (v.type === 'in') return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public typeCheck(v: Block): TypeError | null {
|
||||
if (AiScript.isLiteralBlock(v)) return null;
|
||||
|
||||
const def = AiScript.funcDefs[v.type];
|
||||
if (def == null) {
|
||||
throw new Error('Unknown type: ' + v.type);
|
||||
}
|
||||
|
||||
const generic: Type[] = [];
|
||||
|
||||
for (let i = 0; i < def.in.length; i++) {
|
||||
const arg = def.in[i];
|
||||
const type = this.typeInference(v.args[i]);
|
||||
if (type === null) continue;
|
||||
|
||||
if (typeof arg === 'number') {
|
||||
if (generic[arg] === undefined) {
|
||||
generic[arg] = type;
|
||||
} else if (type !== generic[arg]) {
|
||||
return {
|
||||
arg: i,
|
||||
expect: generic[arg],
|
||||
actual: type
|
||||
};
|
||||
}
|
||||
} else if (type !== arg) {
|
||||
return {
|
||||
arg: i,
|
||||
expect: arg,
|
||||
actual: type
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@autobind
|
||||
public getExpectedType(v: Block, slot: number): Type | null {
|
||||
const def = AiScript.funcDefs[v.type];
|
||||
if (def == null) {
|
||||
throw new Error('Unknown type: ' + v.type);
|
||||
}
|
||||
|
||||
const generic: Type[] = [];
|
||||
|
||||
for (let i = 0; i < def.in.length; i++) {
|
||||
const arg = def.in[i];
|
||||
const type = this.typeInference(v.args[i]);
|
||||
if (type === null) continue;
|
||||
|
||||
if (typeof arg === 'number') {
|
||||
if (generic[arg] === undefined) {
|
||||
generic[arg] = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof def.in[slot] === 'number') {
|
||||
return generic[def.in[slot]] || null;
|
||||
} else {
|
||||
return def.in[slot];
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
public typeInference(v: Block): Type | null {
|
||||
if (v.type === null) return null;
|
||||
if (v.type === 'text') return 'string';
|
||||
if (v.type === 'multiLineText') return 'string';
|
||||
if (v.type === 'textList') return 'stringArray';
|
||||
if (v.type === 'number') return 'number';
|
||||
if (v.type === 'ref') {
|
||||
const variable = this.variables.find(va => va.name === v.value);
|
||||
if (variable) {
|
||||
return this.typeInference(variable);
|
||||
}
|
||||
|
||||
const pageVar = this.pageVars.find(va => va.name === v.value);
|
||||
if (pageVar) {
|
||||
return pageVar.type;
|
||||
}
|
||||
|
||||
const envVar = AiScript.envVarsDef[v.value];
|
||||
if (envVar) {
|
||||
return envVar;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
if (v.type === 'fn') return null; // todo
|
||||
if (v.type === 'in') return null; // todo
|
||||
|
||||
const generic: Type[] = [];
|
||||
|
||||
const def = AiScript.funcDefs[v.type];
|
||||
|
||||
for (let i = 0; i < def.in.length; i++) {
|
||||
const arg = def.in[i];
|
||||
if (typeof arg === 'number') {
|
||||
const type = this.typeInference(v.args[i]);
|
||||
|
||||
if (generic[arg] === undefined) {
|
||||
generic[arg] = type;
|
||||
} else {
|
||||
if (type !== generic[arg]) {
|
||||
generic[arg] = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof def.out === 'number') {
|
||||
return generic[def.out];
|
||||
} else {
|
||||
return def.out;
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
public getVarsByType(type: Type | null): Variable[] {
|
||||
if (type == null) return this.variables;
|
||||
return this.variables.filter(x => (this.typeInference(x) === null) || (this.typeInference(x) === type));
|
||||
}
|
||||
|
||||
@autobind
|
||||
public getVarByName(name: string): Variable {
|
||||
return this.variables.find(x => x.name === name);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public getEnvVarsByType(type: Type | null): string[] {
|
||||
if (type == null) return Object.keys(AiScript.envVarsDef);
|
||||
return Object.entries(AiScript.envVarsDef).filter(([k, v]) => type === v).map(([k, v]) => k);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public getPageVarsByType(type: Type | null): string[] {
|
||||
if (type == null) return this.pageVars.map(v => v.name);
|
||||
return this.pageVars.filter(v => type === v.type).map(v => v.name);
|
||||
}
|
||||
|
||||
@autobind
|
||||
private interpolate(str: string, values: { name: string, value: any }[]) {
|
||||
return str.replace(/\{(.+?)\}/g, match => {
|
||||
const v = this.getVariableValue(match.slice(1, -1).trim(), values);
|
||||
return v == null ? 'NULL' : v.toString();
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
public evaluateVars() {
|
||||
const values: { name: string, value: any }[] = [];
|
||||
|
||||
for (const v of this.variables) {
|
||||
values.push({
|
||||
name: v.name,
|
||||
value: this.evaluate(v, values)
|
||||
});
|
||||
}
|
||||
|
||||
for (const v of this.pageVars) {
|
||||
values.push({
|
||||
name: v.name,
|
||||
value: v.value
|
||||
});
|
||||
}
|
||||
|
||||
for (const [k, v] of Object.entries(this.envVars)) {
|
||||
values.push({
|
||||
name: k,
|
||||
value: v
|
||||
});
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@autobind
|
||||
private evaluate(block: Block, values: { name: string, value: any }[], slotArg: Record<string, any> = {}): any {
|
||||
if (block.type === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (block.type === 'number') {
|
||||
return parseInt(block.value, 10);
|
||||
}
|
||||
|
||||
if (block.type === 'text' || block.type === 'multiLineText') {
|
||||
return this.interpolate(block.value, values);
|
||||
}
|
||||
|
||||
if (block.type === 'textList') {
|
||||
return block.value.trim().split('\n');
|
||||
}
|
||||
|
||||
if (block.type === 'ref') {
|
||||
return this.getVariableValue(block.value, values);
|
||||
}
|
||||
|
||||
if (block.type === 'in') {
|
||||
return slotArg[block.value];
|
||||
}
|
||||
|
||||
if (block.type === 'fn') { // ユーザー関数定義
|
||||
return {
|
||||
slots: block.value.slots,
|
||||
exec: slotArg => this.evaluate(block.value.expression, values, slotArg)
|
||||
};
|
||||
}
|
||||
|
||||
if (block.type.startsWith('fn:')) { // ユーザー関数呼び出し
|
||||
const fnName = block.type.split(':')[1];
|
||||
const fn = this.getVariableValue(fnName, values);
|
||||
for (let i = 0; i < fn.slots.length; i++) {
|
||||
const name = fn.slots[i];
|
||||
slotArg[name] = this.evaluate(block.args[i], values);
|
||||
}
|
||||
return fn.exec(slotArg);
|
||||
}
|
||||
|
||||
if (block.args === undefined) return null;
|
||||
|
||||
const date = new Date();
|
||||
const day = `${this.opts.visitor ? this.opts.visitor.id : ''} ${date.getFullYear()}/${date.getMonth()}/${date.getDate()}`;
|
||||
|
||||
const funcs: { [p in keyof typeof funcDefs]: any } = {
|
||||
not: (a) => !a,
|
||||
eq: (a, b) => a === b,
|
||||
notEq: (a, b) => a !== b,
|
||||
gt: (a, b) => a > b,
|
||||
lt: (a, b) => a < b,
|
||||
gtEq: (a, b) => a >= b,
|
||||
ltEq: (a, b) => a <= b,
|
||||
or: (a, b) => a || b,
|
||||
and: (a, b) => a && b,
|
||||
if: (bool, a, b) => bool ? a : b,
|
||||
add: (a, b) => a + b,
|
||||
subtract: (a, b) => a - b,
|
||||
multiply: (a, b) => a * b,
|
||||
divide: (a, b) => a / b,
|
||||
strLen: (a) => a.length,
|
||||
strPick: (a, b) => a[b - 1],
|
||||
strReplace: (a, b, c) => a.split(b).join(c),
|
||||
strReverse: (a) => a.split('').reverse().join(''),
|
||||
random: (probability) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability,
|
||||
rannum: (min, max) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)),
|
||||
randomPick: (list) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)],
|
||||
dailyRandom: (probability) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability,
|
||||
dailyRannum: (min, max) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)),
|
||||
dailyRandomPick: (list) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)],
|
||||
};
|
||||
|
||||
const fnName = block.type;
|
||||
|
||||
const fn = funcs[fnName];
|
||||
if (fn == null) {
|
||||
console.error('Unknown function: ' + fnName);
|
||||
throw new Error('Unknown function: ' + fnName);
|
||||
}
|
||||
|
||||
const args = block.args.map(x => this.evaluate(x, values, slotArg));
|
||||
|
||||
return fn(...args);
|
||||
}
|
||||
|
||||
@autobind
|
||||
private getVariableValue(name: string, values: { name: string, value: any }[]): any {
|
||||
const v = values.find(v => v.name === name);
|
||||
if (v) {
|
||||
return v.value;
|
||||
}
|
||||
|
||||
const pageVar = this.pageVars.find(v => v.name === name);
|
||||
if (pageVar) {
|
||||
return pageVar.value;
|
||||
}
|
||||
|
||||
if (AiScript.envVarsDef[name]) {
|
||||
return this.envVars[name];
|
||||
}
|
||||
|
||||
throw new Error(`Script: No such variable '${name}'`);
|
||||
}
|
||||
|
||||
@autobind
|
||||
public isUsedName(name: string) {
|
||||
if (this.variables.some(v => v.name === name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this.pageVars.some(v => v.name === name)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (AiScript.envVarsDef[name]) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -2,17 +2,35 @@ export function collectPageVars(content) {
|
||||
const pageVars = [];
|
||||
const collect = (xs: any[]) => {
|
||||
for (const x of xs) {
|
||||
if (x.type === 'input') {
|
||||
if (x.type === 'textInput') {
|
||||
pageVars.push({
|
||||
name: x.name,
|
||||
type: x.inputType,
|
||||
value: x.default
|
||||
type: 'string',
|
||||
value: x.default || ''
|
||||
});
|
||||
} else if (x.type === 'textareaInput') {
|
||||
pageVars.push({
|
||||
name: x.name,
|
||||
type: 'string',
|
||||
value: x.default || ''
|
||||
});
|
||||
} else if (x.type === 'numberInput') {
|
||||
pageVars.push({
|
||||
name: x.name,
|
||||
type: 'number',
|
||||
value: x.default || 0
|
||||
});
|
||||
} else if (x.type === 'switch') {
|
||||
pageVars.push({
|
||||
name: x.name,
|
||||
type: 'boolean',
|
||||
value: x.default
|
||||
value: x.default || false
|
||||
});
|
||||
} else if (x.type === 'counter') {
|
||||
pageVars.push({
|
||||
name: x.name,
|
||||
type: 'number',
|
||||
value: 0
|
||||
});
|
||||
} else if (x.children) {
|
||||
collect(x.children);
|
||||
|
@ -11,9 +11,9 @@ export default function(me, settings, note) {
|
||||
return (
|
||||
(!isMyNote && note.reply && includesMutedWords(note.reply.text)) ||
|
||||
(!isMyNote && note.renote && includesMutedWords(note.renote.text)) ||
|
||||
(settings.showMyRenotes === false && isMyNote && isPureRenote) ||
|
||||
(settings.showRenotedMyNotes === false && isPureRenote && note.renote.userId == me.id) ||
|
||||
(settings.showLocalRenotes === false && isPureRenote && note.renote.user.host == null) ||
|
||||
(!settings.showMyRenotes && isMyNote && isPureRenote) ||
|
||||
(!settings.showRenotedMyNotes && isPureRenote && note.renote.userId == me.id) ||
|
||||
(!settings.showLocalRenotes && isPureRenote && note.renote.user.host == null) ||
|
||||
(!isMyNote && includesMutedWords(note.text))
|
||||
);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ export default class Stream extends EventEmitter {
|
||||
|
||||
const user = os.store.state.i;
|
||||
|
||||
this.stream = new ReconnectingWebsocket(wsUrl + (user ? `?i=${user.token}` : ''));
|
||||
this.stream = new ReconnectingWebsocket(wsUrl + (user ? `?i=${user.token}` : ''), '', { minReconnectionDelay: 1 }); // https://github.com/pladaria/reconnecting-websocket/issues/91
|
||||
this.stream.addEventListener('open', this.onOpen);
|
||||
this.stream.addEventListener('close', this.onClose);
|
||||
this.stream.addEventListener('message', this.onMessage);
|
||||
|
@ -80,7 +80,7 @@ export default Vue.extend({
|
||||
|
||||
ms(): number {
|
||||
return this.now.getMilliseconds() * this.smooth;
|
||||
}
|
||||
},
|
||||
s(): number {
|
||||
return this.now.getSeconds();
|
||||
},
|
||||
|
@ -200,6 +200,7 @@ export default Vue.extend({
|
||||
// 通信を取りこぼしてもいいように定期的にポーリングさせる
|
||||
if (this.game.isStarted && !this.game.isEnded) {
|
||||
this.pollingClock = setInterval(() => {
|
||||
if (this.game.isEnded) return;
|
||||
const crc32 = CRC32.str(this.logs.map(x => x.pos.toString()).join(''));
|
||||
this.connection.send('check', {
|
||||
crc32: crc32
|
||||
|
@ -230,7 +230,7 @@ export default Vue.extend({
|
||||
this.game.map = Object.values(maps).find(x => x.name == this.mapName).data;
|
||||
}
|
||||
this.$forceUpdate();
|
||||
this.updateSettings();
|
||||
this.updateSettings('map');
|
||||
},
|
||||
|
||||
onPixelClick(pos, pixel) {
|
||||
|
@ -202,7 +202,7 @@ export default Vue.extend({
|
||||
left 0
|
||||
z-index 1
|
||||
width 100%
|
||||
box-shadow 0 0px 2px rgba(#000, 0.2)
|
||||
box-shadow 0 0 2px rgba(#000, 0.2)
|
||||
|
||||
> .form
|
||||
background rgba(0, 0, 0, 0.02)
|
||||
|
@ -8,7 +8,6 @@
|
||||
<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="$t('@.verified-user')"><fa icon="star"/></span>
|
||||
<div class="info">
|
||||
<span class="app" v-if="note.app && !mini && $store.state.settings.showVia">via <b>{{ note.app.name }}</b></span>
|
||||
<span class="mobile" v-if="note.viaMobile"><fa icon="mobile-alt"/></span>
|
||||
@ -95,10 +94,6 @@ export default Vue.extend({
|
||||
color var(--noteHeaderAcct)
|
||||
flex-shrink 2147483647
|
||||
|
||||
> .is-verified
|
||||
margin 0 .5em 0 0
|
||||
color #4dabf7
|
||||
|
||||
> .info
|
||||
margin-left auto
|
||||
font-size 0.9em
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')">
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faBolt"/> {{ $t('blocks.button') }}</template>
|
||||
|
||||
<section class="xfhsjczc">
|
||||
@ -16,9 +16,9 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { faBolt } from '@fortawesome/free-solid-svg-icons';
|
||||
import XContainer from './page-editor.container.vue';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faBolt"/> {{ $t('blocks.counter') }}</template>
|
||||
|
||||
<section style="padding: 0 16px 0 16px;">
|
||||
<ui-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('blocks._counter.name') }}</span></ui-input>
|
||||
<ui-input v-model="value.text"><span>{{ $t('blocks._counter.text') }}</span></ui-input>
|
||||
<ui-input v-model="value.inc" type="number"><span>{{ $t('blocks._counter.increment') }}</span></ui-input>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
components: {
|
||||
XContainer
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faBolt, faMagic
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.value.name == null) Vue.set(this.value, 'name', '');
|
||||
},
|
||||
});
|
||||
</script>
|
@ -0,0 +1,90 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faQuestion"/> {{ $t('blocks.if') }}</template>
|
||||
<template #func>
|
||||
<button @click="add()">
|
||||
<fa :icon="faPlus"/>
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<section class="romcojzs">
|
||||
<ui-select v-model="value.var">
|
||||
<template #label>{{ $t('blocks._if.variable') }}</template>
|
||||
<option v-for="v in aiScript.getVarsByType('boolean')" :value="v.name">{{ v.name }}</option>
|
||||
<optgroup :label="$t('script.pageVariables')">
|
||||
<option v-for="v in aiScript.getPageVarsByType('boolean')" :value="v">{{ v }}</option>
|
||||
</optgroup>
|
||||
<optgroup :label="$t('script.enviromentVariables')">
|
||||
<option v-for="v in aiScript.getEnvVarsByType('boolean')" :value="v">{{ v }}</option>
|
||||
</optgroup>
|
||||
</ui-select>
|
||||
|
||||
<x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as uuid from 'uuid';
|
||||
import { faPlus, faQuestion } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
components: {
|
||||
XContainer
|
||||
},
|
||||
|
||||
inject: ['getPageBlockList'],
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
aiScript: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faPlus, faQuestion
|
||||
};
|
||||
},
|
||||
|
||||
beforeCreate() {
|
||||
this.$options.components.XBlocks = require('../page-editor.blocks.vue').default
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.value.children == null) Vue.set(this.value, 'children', []);
|
||||
if (this.value.var === undefined) Vue.set(this.value, 'var', null);
|
||||
},
|
||||
|
||||
methods: {
|
||||
async add() {
|
||||
const { canceled, result: type } = await this.$root.dialog({
|
||||
type: null,
|
||||
title: this.$t('choose-block'),
|
||||
select: {
|
||||
groupedItems: this.getPageBlockList()
|
||||
},
|
||||
showCancelButton: true
|
||||
});
|
||||
if (canceled) return;
|
||||
|
||||
const id = uuid.v4();
|
||||
this.value.children.push({ id, type });
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.romcojzs
|
||||
padding 0 16px 16px 16px
|
||||
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')">
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faImage"/> {{ $t('blocks.image') }}</template>
|
||||
<template #func>
|
||||
<button @click="choose()">
|
||||
@ -15,11 +15,11 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { faPencilAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faImage, faFolderOpen } from '@fortawesome/free-regular-svg-icons';
|
||||
import XContainer from './page-editor.container.vue';
|
||||
import XFileThumbnail from '../drive-file-thumbnail.vue';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
import XFileThumbnail from '../../drive-file-thumbnail.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faBolt"/> {{ $t('blocks.numberInput') }}</template>
|
||||
|
||||
<section style="padding: 0 16px 0 16px;">
|
||||
<ui-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('blocks._numberInput.name') }}</span></ui-input>
|
||||
<ui-input v-model="value.text"><span>{{ $t('blocks._numberInput.text') }}</span></ui-input>
|
||||
<ui-input v-model="value.default" type="number"><span>{{ $t('blocks._numberInput.default') }}</span></ui-input>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
components: {
|
||||
XContainer
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faBolt, faMagic
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.value.name == null) Vue.set(this.value, 'name', '');
|
||||
},
|
||||
});
|
||||
</script>
|
@ -0,0 +1,40 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faPaperPlane"/> {{ $t('blocks.post') }}</template>
|
||||
|
||||
<section style="padding: 0 16px 16px 16px;">
|
||||
<ui-textarea v-model="value.text">{{ $t('blocks._post.text') }}</ui-textarea>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faPaperPlane } from '@fortawesome/free-regular-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
components: {
|
||||
XContainer
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faPaperPlane
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.value.text == null) Vue.set(this.value, 'text', '');
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')">
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faStickyNote"/> {{ value.title }}</template>
|
||||
<template #func>
|
||||
<button @click="rename()">
|
||||
@ -11,20 +11,18 @@
|
||||
</template>
|
||||
|
||||
<section class="ilrvjyvi">
|
||||
<div class="children">
|
||||
<x-block v-for="child in value.children" :value="child" @input="v => updateItem(v)" @remove="() => remove(child)" :key="child.id"/>
|
||||
</div>
|
||||
<x-blocks class="children" v-model="value.children" :ai-script="aiScript"/>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import * as uuid from 'uuid';
|
||||
import { faPlus, faPencilAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faStickyNote } from '@fortawesome/free-regular-svg-icons';
|
||||
import XContainer from './page-editor.container.vue';
|
||||
import * as uuid from 'uuid';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
@ -33,10 +31,15 @@ export default Vue.extend({
|
||||
XContainer
|
||||
},
|
||||
|
||||
inject: ['getPageBlockList'],
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
aiScript: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
@ -46,7 +49,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
beforeCreate() {
|
||||
this.$options.components.XBlock = require('./page-editor.block.vue').default
|
||||
this.$options.components.XBlocks = require('../page-editor.blocks.vue').default
|
||||
},
|
||||
|
||||
created() {
|
||||
@ -79,19 +82,7 @@ export default Vue.extend({
|
||||
type: null,
|
||||
title: this.$t('choose-block'),
|
||||
select: {
|
||||
items: [{
|
||||
value: 'section', text: this.$t('blocks.section')
|
||||
}, {
|
||||
value: 'text', text: this.$t('blocks.text')
|
||||
}, {
|
||||
value: 'image', text: this.$t('blocks.image')
|
||||
}, {
|
||||
value: 'button', text: this.$t('blocks.button')
|
||||
}, {
|
||||
value: 'input', text: this.$t('blocks.input')
|
||||
}, {
|
||||
value: 'switch', text: this.$t('blocks.switch')
|
||||
}]
|
||||
groupedItems: this.getPageBlockList()
|
||||
},
|
||||
showCancelButton: true
|
||||
});
|
||||
@ -100,27 +91,6 @@ export default Vue.extend({
|
||||
const id = uuid.v4();
|
||||
this.value.children.push({ id, type });
|
||||
},
|
||||
|
||||
updateItem(v) {
|
||||
const i = this.value.children.findIndex(x => x.id === v.id);
|
||||
const newValue = [
|
||||
...this.value.children.slice(0, i),
|
||||
v,
|
||||
...this.value.children.slice(i + 1)
|
||||
];
|
||||
this.value.children = newValue;
|
||||
this.$emit('input', this.value);
|
||||
},
|
||||
|
||||
remove(el) {
|
||||
const i = this.value.children.findIndex(x => x.id === el.id);
|
||||
const newValue = [
|
||||
...this.value.children.slice(0, i),
|
||||
...this.value.children.slice(i + 1)
|
||||
];
|
||||
this.value.children = newValue;
|
||||
this.$emit('input', this.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
@ -1,9 +1,9 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')">
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faBolt"/> {{ $t('blocks.switch') }}</template>
|
||||
|
||||
<section class="kjuadyyj">
|
||||
<ui-input v-model="value.name"><template #prefix><fa :icon="faSquareRootAlt"/></template><span>{{ $t('blocks._switch.name') }}</span></ui-input>
|
||||
<ui-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('blocks._switch.name') }}</span></ui-input>
|
||||
<ui-input v-model="value.text"><span>{{ $t('blocks._switch.text') }}</span></ui-input>
|
||||
<ui-switch v-model="value.default"><span>{{ $t('blocks._switch.default') }}</span></ui-switch>
|
||||
</section>
|
||||
@ -12,9 +12,9 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { faBolt, faSquareRootAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import XContainer from './page-editor.container.vue';
|
||||
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
@ -31,7 +31,7 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
faBolt, faSquareRootAlt
|
||||
faBolt, faMagic
|
||||
};
|
||||
},
|
||||
|
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faBolt"/> {{ $t('blocks.textInput') }}</template>
|
||||
|
||||
<section style="padding: 0 16px 0 16px;">
|
||||
<ui-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('blocks._textInput.name') }}</span></ui-input>
|
||||
<ui-input v-model="value.text"><span>{{ $t('blocks._textInput.text') }}</span></ui-input>
|
||||
<ui-input v-model="value.default" type="text"><span>{{ $t('blocks._textInput.default') }}</span></ui-input>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
components: {
|
||||
XContainer
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faBolt, faMagic
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.value.name == null) Vue.set(this.value, 'name', '');
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')">
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faAlignLeft"/> {{ $t('blocks.text') }}</template>
|
||||
|
||||
<section class="ihymsbbe">
|
||||
@ -10,9 +10,9 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { faAlignLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import XContainer from './page-editor.container.vue';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
@ -54,4 +54,5 @@ export default Vue.extend({
|
||||
padding 16px
|
||||
background transparent
|
||||
color var(--text)
|
||||
font-size 14px
|
||||
</style>
|
@ -0,0 +1,42 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faBolt"/> {{ $t('blocks.textareaInput') }}</template>
|
||||
|
||||
<section style="padding: 0 16px 16px 16px;">
|
||||
<ui-input v-model="value.name"><template #prefix><fa :icon="faMagic"/></template><span>{{ $t('blocks._textareaInput.name') }}</span></ui-input>
|
||||
<ui-input v-model="value.text"><span>{{ $t('blocks._textareaInput.text') }}</span></ui-input>
|
||||
<ui-textarea v-model="value.default"><span>{{ $t('blocks._textareaInput.default') }}</span></ui-textarea>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faBolt, faMagic } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
components: {
|
||||
XContainer
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faBolt, faMagic
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.value.name == null) Vue.set(this.value, 'name', '');
|
||||
},
|
||||
});
|
||||
</script>
|
@ -0,0 +1,58 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')" :draggable="true">
|
||||
<template #header><fa :icon="faAlignLeft"/> {{ $t('blocks.textarea') }}</template>
|
||||
|
||||
<section class="ihymsbbe">
|
||||
<textarea v-model="value.text"></textarea>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faAlignLeft } from '@fortawesome/free-solid-svg-icons';
|
||||
import i18n from '../../../../../i18n';
|
||||
import XContainer from '../page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
components: {
|
||||
XContainer
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faAlignLeft,
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.value.text == null) Vue.set(this.value, 'text', '');
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.ihymsbbe
|
||||
> textarea
|
||||
display block
|
||||
-webkit-appearance none
|
||||
-moz-appearance none
|
||||
appearance none
|
||||
width 100%
|
||||
min-width 100%
|
||||
min-height 150px
|
||||
border none
|
||||
box-shadow none
|
||||
padding 16px
|
||||
background transparent
|
||||
color var(--text)
|
||||
font-size 14px
|
||||
</style>
|
@ -1,25 +0,0 @@
|
||||
<template>
|
||||
<component :is="'x-' + value.type" :value="value" @input="v => updateItem(v)" @remove="() => $emit('remove', value)" :key="value.id"/>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import XSection from './page-editor.section.vue';
|
||||
import XText from './page-editor.text.vue';
|
||||
import XImage from './page-editor.image.vue';
|
||||
import XButton from './page-editor.button.vue';
|
||||
import XInput from './page-editor.input.vue';
|
||||
import XSwitch from './page-editor.switch.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XSection, XText, XImage, XButton, XInput, XSwitch
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
});
|
||||
</script>
|
@ -0,0 +1,65 @@
|
||||
<template>
|
||||
<x-draggable tag="div" :list="blocks" handle=".drag-handle" :group="{ name: 'blocks' }" animation="150" swap-threshold="0.5">
|
||||
<component v-for="block in blocks" :is="'x-' + block.type" :value="block" @input="updateItem" @remove="() => removeItem(block)" :key="block.id" :ai-script="aiScript"/>
|
||||
</x-draggable>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import * as XDraggable from 'vuedraggable';
|
||||
import XSection from './els/page-editor.el.section.vue';
|
||||
import XText from './els/page-editor.el.text.vue';
|
||||
import XTextarea from './els/page-editor.el.textarea.vue';
|
||||
import XImage from './els/page-editor.el.image.vue';
|
||||
import XButton from './els/page-editor.el.button.vue';
|
||||
import XTextInput from './els/page-editor.el.text-input.vue';
|
||||
import XTextareaInput from './els/page-editor.el.textarea-input.vue';
|
||||
import XNumberInput from './els/page-editor.el.number-input.vue';
|
||||
import XSwitch from './els/page-editor.el.switch.vue';
|
||||
import XIf from './els/page-editor.el.if.vue';
|
||||
import XPost from './els/page-editor.el.post.vue';
|
||||
import XCounter from './els/page-editor.el.counter.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XDraggable, XSection, XText, XImage, XButton, XTextarea, XTextInput, XTextareaInput, XNumberInput, XSwitch, XIf, XPost, XCounter
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
type: Array,
|
||||
required: true
|
||||
},
|
||||
aiScript: {
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
|
||||
computed: {
|
||||
blocks() {
|
||||
return this.value;
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
updateItem(v) {
|
||||
const i = this.blocks.findIndex(x => x.id === v.id);
|
||||
const newValue = [
|
||||
...this.blocks.slice(0, i),
|
||||
v,
|
||||
...this.blocks.slice(i + 1)
|
||||
];
|
||||
this.$emit('input', newValue);
|
||||
},
|
||||
|
||||
removeItem(el) {
|
||||
const i = this.blocks.findIndex(x => x.id === el.id);
|
||||
const newValue = [
|
||||
...this.blocks.slice(0, i),
|
||||
...this.blocks.slice(i + 1)
|
||||
];
|
||||
this.$emit('input', newValue);
|
||||
},
|
||||
}
|
||||
});
|
||||
</script>
|
@ -7,6 +7,9 @@
|
||||
<button v-if="removable" @click="remove()">
|
||||
<fa :icon="faTrashAlt"/>
|
||||
</button>
|
||||
<button v-if="draggable" class="drag-handle">
|
||||
<fa :icon="faBars"/>
|
||||
</button>
|
||||
<button @click="toggleContent(!showBody)">
|
||||
<template v-if="showBody"><fa icon="angle-up"/></template>
|
||||
<template v-else><fa icon="angle-down"/></template>
|
||||
@ -23,6 +26,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import { faBars } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faTrashAlt } from '@fortawesome/free-regular-svg-icons';
|
||||
import i18n from '../../../../i18n';
|
||||
|
||||
@ -38,6 +42,10 @@ export default Vue.extend({
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
draggable: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
error: {
|
||||
required: false,
|
||||
default: null
|
||||
@ -50,7 +58,7 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
showBody: this.expanded,
|
||||
faTrashAlt
|
||||
faTrashAlt, faBars
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
@ -120,6 +128,9 @@ export default Vue.extend({
|
||||
&:active
|
||||
color var(--faceTextButtonActive)
|
||||
|
||||
.drag-handle
|
||||
cursor move
|
||||
|
||||
> .warn
|
||||
color #b19e49
|
||||
margin 0
|
||||
|
@ -1,61 +0,0 @@
|
||||
<template>
|
||||
<x-container @remove="() => $emit('remove')">
|
||||
<template #header><fa :icon="faBolt"/> {{ $t('blocks.input') }}</template>
|
||||
|
||||
<section class="dnvasjon">
|
||||
<ui-input v-model="value.name"><template #prefix><fa :icon="faSquareRootAlt"/></template><span>{{ $t('blocks._input.name') }}</span></ui-input>
|
||||
<ui-input v-model="value.text"><span>{{ $t('blocks._input.text') }}</span></ui-input>
|
||||
<ui-select v-model="value.inputType">
|
||||
<template #label>{{ $t('blocks._input.inputType') }}</template>
|
||||
<option value="string">{{ $t('blocks._input._inputType.string') }}</option>
|
||||
<option value="number">{{ $t('blocks._input._inputType.number') }}</option>
|
||||
</ui-select>
|
||||
<ui-input v-model="value.default" :type="value.inputType"><span>{{ $t('blocks._input.default') }}</span></ui-input>
|
||||
</section>
|
||||
</x-container>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { faBolt, faSquareRootAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import XContainer from './page-editor.container.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
components: {
|
||||
XContainer
|
||||
},
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
faBolt, faSquareRootAlt
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.value.name == null) Vue.set(this.value, 'name', '');
|
||||
if (this.value.inputType == null) Vue.set(this.value, 'inputType', 'string');
|
||||
|
||||
this.$watch('value.inputType', t => {
|
||||
if (this.value.default != null) {
|
||||
if (t === 'number') this.value.default = parseInt(this.value.default, 10);
|
||||
if (t === 'string') this.value.default = this.value.default.toString();
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.dnvasjon
|
||||
padding 0 16px 0 16px
|
||||
|
||||
</style>
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<x-container :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn">
|
||||
<x-container :removable="removable" @remove="() => $emit('remove')" :error="error" :warn="warn" :draggable="draggable">
|
||||
<template #header><fa v-if="icon" :icon="icon"/> <template v-if="title">{{ title }} <span class="turmquns" v-if="typeText">({{ typeText }})</span></template><template v-else-if="typeText">{{ typeText }}</template></template>
|
||||
<template #func>
|
||||
<button @click="changeType()">
|
||||
@ -25,6 +25,9 @@
|
||||
<section v-else-if="value.type === 'ref'" class="hpdwcrvs">
|
||||
<select v-model="value.value">
|
||||
<option v-for="v in aiScript.getVarsByType(getExpectedType ? getExpectedType() : null).filter(x => x.name !== name)" :value="v.name">{{ v.name }}</option>
|
||||
<optgroup :label="$t('script.argVariables')">
|
||||
<option v-for="v in fnSlots" :value="v.name">{{ v.name }}</option>
|
||||
</optgroup>
|
||||
<optgroup :label="$t('script.pageVariables')">
|
||||
<option v-for="v in aiScript.getPageVarsByType(getExpectedType ? getExpectedType() : null)" :value="v">{{ v }}</option>
|
||||
</optgroup>
|
||||
@ -33,17 +36,15 @@
|
||||
</optgroup>
|
||||
</select>
|
||||
</section>
|
||||
<section v-else-if="value.type === 'in'" class="hpdwcrvs">
|
||||
<select v-model="value.value">
|
||||
<option v-for="v in fnSlots" :value="v">{{ v }}</option>
|
||||
</select>
|
||||
</section>
|
||||
<section v-else-if="value.type === 'fn'" class="" style="padding:16px;">
|
||||
<ui-textarea v-model="slots"></ui-textarea>
|
||||
<section v-else-if="value.type === 'fn'" class="" style="padding:0 16px 16px 16px;">
|
||||
<ui-textarea v-model="slots">
|
||||
<span>{{ $t('script.blocks._fn.slots') }}</span>
|
||||
<template #desc>{{ $t('script.blocks._fn.slots-info') }}</template>
|
||||
</ui-textarea>
|
||||
<x-v v-if="value.value.expression" v-model="value.value.expression" :title="$t(`script.blocks._fn.arg1`)" :get-expected-type="() => null" :ai-script="aiScript" :fn-slots="value.value.slots" :name="name"/>
|
||||
</section>
|
||||
<section v-else-if="value.type.startsWith('fn:')" class="" style="padding:16px;">
|
||||
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="aiScript.getVarByName(value.type.split(':')[1]).value.slots[i]" :get-expected-type="() => null" :ai-script="aiScript" :name="name" :key="i"/>
|
||||
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="aiScript.getVarByName(value.type.split(':')[1]).value.slots[i].name" :get-expected-type="() => null" :ai-script="aiScript" :name="name" :key="i"/>
|
||||
</section>
|
||||
<section v-else class="" style="padding:16px;">
|
||||
<x-v v-for="(x, i) in value.args" v-model="value.args[i]" :title="$t(`script.blocks._${value.type}.arg${i + 1}`)" :get-expected-type="() => _getExpectedType(i)" :ai-script="aiScript" :name="name" :fn-slots="fnSlots" :key="i"/>
|
||||
@ -55,8 +56,8 @@
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import XContainer from './page-editor.container.vue';
|
||||
import { faSuperscript, faPencilAlt, faSquareRootAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { AiScript } from '../../../scripts/aiscript';
|
||||
import { faPencilAlt, faPlug } from '@fortawesome/free-solid-svg-icons';
|
||||
import { isLiteralBlock, funcDefs, blockDefs } from '../../../../../../misc/aiscript/index';
|
||||
import * as uuid from 'uuid';
|
||||
|
||||
export default Vue.extend({
|
||||
@ -92,33 +93,40 @@ export default Vue.extend({
|
||||
fnSlots: {
|
||||
required: false,
|
||||
},
|
||||
draggable: {
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
AiScript,
|
||||
error: null,
|
||||
warn: null,
|
||||
slots: '',
|
||||
faSuperscript, faPencilAlt, faSquareRootAlt
|
||||
faPencilAlt
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
icon(): any {
|
||||
if (this.value.type === null) return null;
|
||||
if (this.value.type.startsWith('fn:')) return null;
|
||||
return AiScript.blockDefs.find(x => x.type === this.value.type).icon;
|
||||
if (this.value.type.startsWith('fn:')) return faPlug;
|
||||
return blockDefs.find(x => x.type === this.value.type).icon;
|
||||
},
|
||||
typeText(): any {
|
||||
if (this.value.type === null) return null;
|
||||
if (this.value.type.startsWith('fn:')) return this.value.type.split(':')[1];
|
||||
return this.$t(`script.blocks.${this.value.type}`);
|
||||
},
|
||||
},
|
||||
|
||||
watch: {
|
||||
slots() {
|
||||
this.value.value.slots = this.slots.split('\n');
|
||||
this.value.value.slots = this.slots.split('\n').map(x => ({
|
||||
name: x,
|
||||
type: null
|
||||
}));
|
||||
}
|
||||
},
|
||||
|
||||
@ -129,7 +137,7 @@ export default Vue.extend({
|
||||
created() {
|
||||
if (this.value.value == null) Vue.set(this.value, 'value', null);
|
||||
|
||||
if (this.value.value && this.value.value.slots) this.slots = this.value.value.slots.join('\n');
|
||||
if (this.value.value && this.value.value.slots) this.slots = this.value.value.slots.map(x => x.name).join('\n');
|
||||
|
||||
this.$watch('value.type', (t) => {
|
||||
this.warn = null;
|
||||
@ -155,17 +163,17 @@ export default Vue.extend({
|
||||
return;
|
||||
}
|
||||
|
||||
if (AiScript.isLiteralBlock(this.value)) return;
|
||||
if (isLiteralBlock(this.value)) return;
|
||||
|
||||
const empties = [];
|
||||
for (let i = 0; i < AiScript.funcDefs[this.value.type].in.length; i++) {
|
||||
for (let i = 0; i < funcDefs[this.value.type].in.length; i++) {
|
||||
const id = uuid.v4();
|
||||
empties.push({ id, type: null });
|
||||
}
|
||||
Vue.set(this.value, 'args', empties);
|
||||
|
||||
for (let i = 0; i < AiScript.funcDefs[this.value.type].in.length; i++) {
|
||||
const inType = AiScript.funcDefs[this.value.type].in[i];
|
||||
for (let i = 0; i < funcDefs[this.value.type].in.length; i++) {
|
||||
const inType = funcDefs[this.value.type].in[i];
|
||||
if (typeof inType !== 'number') {
|
||||
if (inType === 'number') this.value.args[i].type = 'number';
|
||||
if (inType === 'string') this.value.args[i].type = 'text';
|
||||
|
@ -2,16 +2,16 @@
|
||||
<div>
|
||||
<div class="gwbmwxkm" :class="{ shadow: $store.state.device.useShadow, round: $store.state.device.roundedCorners }">
|
||||
<header>
|
||||
<div class="title"><fa :icon="faStickyNote"/> {{ pageId ? $t('edit-page') : $t('new-page') }}</div>
|
||||
<div class="title"><fa :icon="faStickyNote"/> {{ readonly ? $t('read-page') : pageId ? $t('edit-page') : $t('new-page') }}</div>
|
||||
<div class="buttons">
|
||||
<button @click="del()"><fa :icon="faTrashAlt"/></button>
|
||||
<button @click="del()" v-if="!readonly"><fa :icon="faTrashAlt"/></button>
|
||||
<button @click="() => showOptions = !showOptions"><fa :icon="faCog"/></button>
|
||||
<button @click="save()"><fa :icon="faSave"/></button>
|
||||
<button @click="save()" v-if="!readonly"><fa :icon="faSave"/></button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<section>
|
||||
<a class="view" v-if="pageId" :href="`/@${ $store.state.i.username }/pages/${ currentName }`" target="_blank"><fa :icon="faExternalLinkSquareAlt"/> {{ $t('view-page') }}</a>
|
||||
<a class="view" v-if="pageId" :href="`/@${ author.username }/pages/${ currentName }`" target="_blank"><fa :icon="faExternalLinkSquareAlt"/> {{ $t('view-page') }}</a>
|
||||
|
||||
<ui-input v-model="title">
|
||||
<span>{{ $t('title') }}</span>
|
||||
@ -23,7 +23,7 @@
|
||||
</ui-input>
|
||||
|
||||
<ui-input v-model="name">
|
||||
<template #prefix>{{ url }}/@{{ $store.state.i.username }}/pages/</template>
|
||||
<template #prefix>{{ url }}/@{{ author.username }}/pages/</template>
|
||||
<span>{{ $t('url') }}</span>
|
||||
</ui-input>
|
||||
|
||||
@ -36,62 +36,70 @@
|
||||
</ui-select>
|
||||
|
||||
<div class="eyeCatch">
|
||||
<ui-button v-if="eyeCatchingImageId == null" @click="setEyeCatchingImage()"><fa :icon="faPlus"/> {{ $t('set-eye-catchig-image') }}</ui-button>
|
||||
<ui-button v-if="eyeCatchingImageId == null && !readonly" @click="setEyeCatchingImage()"><fa :icon="faPlus"/> {{ $t('set-eye-catching-image') }}</ui-button>
|
||||
<div v-else-if="eyeCatchingImage">
|
||||
<img :src="eyeCatchingImage.url" :alt="eyeCatchingImage.name"/>
|
||||
<ui-button @click="removeEyeCatchingImage()"><fa :icon="faTrashAlt"/> {{ $t('remove-eye-catchig-image') }}</ui-button>
|
||||
<ui-button @click="removeEyeCatchingImage()" v-if="!readonly"><fa :icon="faTrashAlt"/> {{ $t('remove-eye-catching-image') }}</ui-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="content" v-for="child in content">
|
||||
<x-block :value="child" @input="v => updateItem(v)" @remove="() => remove(child)" :key="child.id"/>
|
||||
</div>
|
||||
<x-blocks class="content" v-model="content" :ai-script="aiScript"/>
|
||||
|
||||
<ui-button @click="add()"><fa :icon="faPlus"/></ui-button>
|
||||
<ui-button @click="add()" v-if="!readonly"><fa :icon="faPlus"/></ui-button>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<ui-container :body-togglable="true">
|
||||
<template #header><fa :icon="faSquareRootAlt"/> {{ $t('variables') }}</template>
|
||||
<template #header><fa :icon="faMagic"/> {{ $t('variables') }}</template>
|
||||
<div class="qmuvgica">
|
||||
<div class="variables" v-show="variables.length > 0">
|
||||
<template v-for="variable in variables">
|
||||
<x-variable
|
||||
:value="variable"
|
||||
:removable="true"
|
||||
@input="v => updateVariable(v)"
|
||||
@remove="() => removeVariable(variable)"
|
||||
:key="variable.name"
|
||||
:ai-script="aiScript"
|
||||
:name="variable.name"
|
||||
:title="variable.name"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
<x-draggable tag="div" class="variables" v-show="variables.length > 0" :list="variables" handle=".drag-handle" :group="{ name: 'variables' }" animation="150" swap-threshold="0.5">
|
||||
<x-variable v-for="variable in variables"
|
||||
:value="variable"
|
||||
:removable="true"
|
||||
@input="v => updateVariable(v)"
|
||||
@remove="() => removeVariable(variable)"
|
||||
:key="variable.name"
|
||||
:ai-script="aiScript"
|
||||
:name="variable.name"
|
||||
:title="variable.name"
|
||||
:draggable="true"
|
||||
/>
|
||||
</x-draggable>
|
||||
|
||||
<ui-button @click="addVariable()" class="add"><fa :icon="faPlus"/></ui-button>
|
||||
<ui-button @click="addVariable()" class="add" v-if="!readonly"><fa :icon="faPlus"/></ui-button>
|
||||
|
||||
<ui-info><span v-html="$t('variables-info')"></span><a @click="() => moreDetails = true" style="display:block;">{{ $t('more-details') }}</a></ui-info>
|
||||
|
||||
<template v-if="moreDetails">
|
||||
<ui-info><span v-html="$t('variables-info2')"></span></ui-info>
|
||||
<ui-info><span v-html="$t('variables-info3')"></span></ui-info>
|
||||
<ui-info><span v-html="$t('variables-info4')"></span></ui-info>
|
||||
</template>
|
||||
</div>
|
||||
</ui-container>
|
||||
|
||||
<ui-container :body-togglable="true" :expanded="false">
|
||||
<template #header><fa :icon="faCode"/> {{ $t('inspector') }}</template>
|
||||
<div style="padding:0 32px 32px 32px;">
|
||||
<ui-textarea :value="JSON.stringify(content, null, 2)" readonly tall>{{ $t('content') }}</ui-textarea>
|
||||
<ui-textarea :value="JSON.stringify(variables, null, 2)" readonly tall>{{ $t('variables') }}</ui-textarea>
|
||||
</div>
|
||||
</ui-container>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { faICursor, faPlus, faSquareRootAlt, faCog, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as XDraggable from 'vuedraggable';
|
||||
import { faICursor, faPlus, faMagic, faCog, faCode, faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faSave, faStickyNote, faTrashAlt } from '@fortawesome/free-regular-svg-icons';
|
||||
import i18n from '../../../../i18n';
|
||||
import XVariable from './page-editor.script-block.vue';
|
||||
import XBlock from './page-editor.block.vue';
|
||||
import XBlocks from './page-editor.blocks.vue';
|
||||
import * as uuid from 'uuid';
|
||||
import { AiScript } from '../../../scripts/aiscript';
|
||||
import { blockDefs } from '../../../../../../misc/aiscript/index';
|
||||
import { ASTypeChecker } from '../../../../../../misc/aiscript/type-checker';
|
||||
import { url } from '../../../../config';
|
||||
import { collectPageVars } from '../../../scripts/collect-page-vars';
|
||||
|
||||
@ -99,18 +107,24 @@ export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
components: {
|
||||
XVariable, XBlock
|
||||
XDraggable, XVariable, XBlocks
|
||||
},
|
||||
|
||||
props: {
|
||||
page: {
|
||||
type: String,
|
||||
type: Object,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
readonly: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
author: this.$store.state.i,
|
||||
pageId: null,
|
||||
currentName: null,
|
||||
title: '',
|
||||
@ -126,7 +140,7 @@ export default Vue.extend({
|
||||
showOptions: false,
|
||||
moreDetails: false,
|
||||
url,
|
||||
faPlus, faICursor, faSave, faStickyNote, faSquareRootAlt, faCog, faTrashAlt, faExternalLinkSquareAlt
|
||||
faPlus, faICursor, faSave, faStickyNote, faMagic, faCog, faTrashAlt, faExternalLinkSquareAlt, faCode
|
||||
};
|
||||
},
|
||||
|
||||
@ -143,31 +157,28 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
created() {
|
||||
this.aiScript = new AiScript();
|
||||
this.aiScript = new ASTypeChecker();
|
||||
|
||||
this.$watch('variables', () => {
|
||||
this.aiScript.injectVars(this.variables);
|
||||
this.aiScript.variables = this.variables;
|
||||
}, { deep: true });
|
||||
|
||||
this.$watch('content', () => {
|
||||
this.aiScript.injectPageVars(collectPageVars(this.content));
|
||||
this.aiScript.pageVars = collectPageVars(this.content);
|
||||
}, { deep: true });
|
||||
|
||||
if (this.page) {
|
||||
this.$root.api('pages/show', {
|
||||
pageId: this.page,
|
||||
}).then(page => {
|
||||
this.pageId = page.id;
|
||||
this.title = page.title;
|
||||
this.name = page.name;
|
||||
this.currentName = page.name;
|
||||
this.summary = page.summary;
|
||||
this.font = page.font;
|
||||
this.alignCenter = page.alignCenter;
|
||||
this.content = page.content;
|
||||
this.variables = page.variables;
|
||||
this.eyeCatchingImageId = page.eyeCatchingImageId;
|
||||
});
|
||||
this.author = this.page.user;
|
||||
this.pageId = this.page.id;
|
||||
this.title = this.page.title;
|
||||
this.name = this.page.name;
|
||||
this.currentName = this.page.name;
|
||||
this.summary = this.page.summary;
|
||||
this.font = this.page.font;
|
||||
this.alignCenter = this.page.alignCenter;
|
||||
this.content = this.page.content;
|
||||
this.variables = this.page.variables;
|
||||
this.eyeCatchingImageId = this.page.eyeCatchingImageId;
|
||||
} else {
|
||||
const id = uuid.v4();
|
||||
this.content = [{
|
||||
@ -180,7 +191,9 @@ export default Vue.extend({
|
||||
|
||||
provide() {
|
||||
return {
|
||||
getScriptBlockList: this.getScriptBlockList
|
||||
readonly: this.readonly,
|
||||
getScriptBlockList: this.getScriptBlockList,
|
||||
getPageBlockList: this.getPageBlockList
|
||||
}
|
||||
},
|
||||
|
||||
@ -250,19 +263,7 @@ export default Vue.extend({
|
||||
type: null,
|
||||
title: this.$t('choose-block'),
|
||||
select: {
|
||||
items: [{
|
||||
value: 'section', text: this.$t('blocks.section')
|
||||
}, {
|
||||
value: 'text', text: this.$t('blocks.text')
|
||||
}, {
|
||||
value: 'image', text: this.$t('blocks.image')
|
||||
}, {
|
||||
value: 'button', text: this.$t('blocks.button')
|
||||
}, {
|
||||
value: 'input', text: this.$t('blocks.input')
|
||||
}, {
|
||||
value: 'switch', text: this.$t('blocks.switch')
|
||||
}]
|
||||
groupedItems: this.getPageBlockList()
|
||||
},
|
||||
showCancelButton: true
|
||||
});
|
||||
@ -296,25 +297,6 @@ export default Vue.extend({
|
||||
this.variables.push({ id, name, type: null });
|
||||
},
|
||||
|
||||
updateItem(v) {
|
||||
const i = this.content.findIndex(x => x.id === v.id);
|
||||
const newValue = [
|
||||
...this.content.slice(0, i),
|
||||
v,
|
||||
...this.content.slice(i + 1)
|
||||
];
|
||||
this.content = newValue;
|
||||
},
|
||||
|
||||
remove(el) {
|
||||
const i = this.content.findIndex(x => x.id === el.id);
|
||||
const newValue = [
|
||||
...this.content.slice(0, i),
|
||||
...this.content.slice(i + 1)
|
||||
];
|
||||
this.content = newValue;
|
||||
},
|
||||
|
||||
removeVariable(v) {
|
||||
const i = this.variables.findIndex(x => x.name === v.name);
|
||||
const newValue = [
|
||||
@ -324,10 +306,38 @@ export default Vue.extend({
|
||||
this.variables = newValue;
|
||||
},
|
||||
|
||||
getPageBlockList() {
|
||||
return [{
|
||||
label: this.$t('content-blocks'),
|
||||
items: [
|
||||
{ value: 'section', text: this.$t('blocks.section') },
|
||||
{ value: 'text', text: this.$t('blocks.text') },
|
||||
{ value: 'image', text: this.$t('blocks.image') },
|
||||
{ value: 'textarea', text: this.$t('blocks.textarea') },
|
||||
]
|
||||
}, {
|
||||
label: this.$t('input-blocks'),
|
||||
items: [
|
||||
{ value: 'button', text: this.$t('blocks.button') },
|
||||
{ value: 'textInput', text: this.$t('blocks.textInput') },
|
||||
{ value: 'textareaInput', text: this.$t('blocks.textareaInput') },
|
||||
{ value: 'numberInput', text: this.$t('blocks.numberInput') },
|
||||
{ value: 'switch', text: this.$t('blocks.switch') },
|
||||
{ value: 'counter', text: this.$t('blocks.counter') }
|
||||
]
|
||||
}, {
|
||||
label: this.$t('special-blocks'),
|
||||
items: [
|
||||
{ value: 'if', text: this.$t('blocks.if') },
|
||||
{ value: 'post', text: this.$t('blocks.post') }
|
||||
]
|
||||
}];
|
||||
},
|
||||
|
||||
getScriptBlockList(type: string = null) {
|
||||
const list = [];
|
||||
|
||||
const blocks = AiScript.blockDefs.filter(block => type === null || block.out === null || block.out === type);
|
||||
const blocks = blockDefs.filter(block => type === null || block.out === null || block.out === type || typeof block.out === 'number');
|
||||
|
||||
for (const block of blocks) {
|
||||
const category = list.find(x => x.category === block.category);
|
||||
@ -436,6 +446,7 @@ export default Vue.extend({
|
||||
> .view
|
||||
display inline-block
|
||||
margin 16px 0 0 0
|
||||
font-size 14px
|
||||
|
||||
> .content
|
||||
margin-bottom 16px
|
||||
|
@ -54,7 +54,11 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
mounted() {
|
||||
document.cookie = `i=${this.$store.state.i.token}`;
|
||||
if (!document.cookie.match(/i=(\w+)/)) {
|
||||
document.cookie = `i=${this.$store.state.i.token}; path=/;` +
|
||||
` domain=${document.location.hostname}; max-age=31536000;` +
|
||||
(document.location.protocol.startsWith('https') ? ' secure' : '');
|
||||
}
|
||||
this.$watch('$store.state.i', () => {
|
||||
if (this.$store.state.i.twitter) {
|
||||
if (this.twitterForm) this.twitterForm.close();
|
||||
|
@ -290,12 +290,17 @@ export default Vue.extend({
|
||||
this.exportTarget == 'mute' ? 'i/export-mute' :
|
||||
this.exportTarget == 'blocking' ? 'i/export-blocking' :
|
||||
this.exportTarget == 'user-lists' ? 'i/export-user-lists' :
|
||||
null, {});
|
||||
|
||||
this.$root.dialog({
|
||||
type: 'info',
|
||||
text: this.$t('export-requested')
|
||||
});
|
||||
null, {}).then(() => {
|
||||
this.$root.dialog({
|
||||
type: 'info',
|
||||
text: this.$t('export-requested')
|
||||
});
|
||||
}).catch((e: any) => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
text: e.message
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
doImport() {
|
||||
|
@ -273,7 +273,7 @@ export default Vue.extend({
|
||||
|
||||
import_() {
|
||||
(this.$refs.file as any).click();
|
||||
}
|
||||
},
|
||||
|
||||
export_() {
|
||||
const blob = new Blob([this.selectedThemeCode], {
|
||||
|
@ -10,14 +10,14 @@
|
||||
<span>{{ $t('password') }}</span>
|
||||
<template #prefix><fa icon="lock"/></template>
|
||||
</ui-input>
|
||||
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="number" required>
|
||||
<ui-input v-if="user && user.twoFactorEnabled" v-model="token" type="text" pattern="^[0-9]{6}$" autocomplete="off" spellcheck="false" required>
|
||||
<span>{{ $t('@.2fa') }}</span>
|
||||
<template #prefix><fa icon="gavel"/></template>
|
||||
</ui-input>
|
||||
<ui-button type="submit" :disabled="signing">{{ signing ? $t('signing-in') : $t('@.signin') }}</ui-button>
|
||||
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`">{{ $t('signin-with-twitter') }}</a></p>
|
||||
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`">{{ $t('signin-with-github') }}</a></p>
|
||||
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`">{{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p>
|
||||
<p v-if="meta && meta.enableTwitterIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/twitter`"><fa :icon="['fab', 'twitter']"/> {{ $t('signin-with-twitter') }}</a></p>
|
||||
<p v-if="meta && meta.enableGithubIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/github`"><fa :icon="['fab', 'github']"/> {{ $t('signin-with-github') }}</a></p>
|
||||
<p v-if="meta && meta.enableDiscordIntegration" style="margin: 8px 0;"><a :href="`${apiUrl}/signin/discord`"><fa :icon="['fab', 'discord']"/> {{ $t('signin-with-discord') /* TODO: Make these layouts better */ }}</a></p>
|
||||
</form>
|
||||
</template>
|
||||
|
||||
|
@ -322,7 +322,7 @@ root(fill)
|
||||
|
||||
> .value
|
||||
display block
|
||||
width 0%
|
||||
width 0
|
||||
height 100%
|
||||
background transparent
|
||||
border-radius 6px
|
||||
|
@ -5,10 +5,9 @@
|
||||
<span class="label" ref="label"><slot name="label"></slot></span>
|
||||
<div class="prefix" ref="prefix"><slot name="prefix"></slot></div>
|
||||
<select ref="input"
|
||||
:value="v"
|
||||
v-model="v"
|
||||
:required="required"
|
||||
:disabled="disabled"
|
||||
@input="$emit('input', $event.target.value)"
|
||||
@focus="focused = true"
|
||||
@blur="focused = false"
|
||||
>
|
||||
@ -56,20 +55,22 @@ export default Vue.extend({
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
v: this.value,
|
||||
focused: false
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
v: {
|
||||
get() {
|
||||
return this.value;
|
||||
},
|
||||
set(v) {
|
||||
this.$emit('input', v);
|
||||
}
|
||||
},
|
||||
filled(): boolean {
|
||||
return this.v != '' && this.v != null;
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value(v) {
|
||||
this.v = v;
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (this.$refs.prefix) {
|
||||
this.$refs.label.style.left = (this.$refs.prefix.offsetLeft + this.$refs.prefix.offsetWidth) + 'px';
|
||||
|
@ -166,7 +166,7 @@ export default Vue.extend({
|
||||
> .follow-button
|
||||
position absolute
|
||||
top 8px
|
||||
right 0px
|
||||
right 0
|
||||
|
||||
> .more
|
||||
display block
|
||||
|
@ -160,7 +160,7 @@ export default Vue.extend({
|
||||
this.$emit('top');
|
||||
}
|
||||
|
||||
if (this.$store.state.settings.fetchOnScroll !== false) {
|
||||
if (this.$store.state.settings.fetchOnScroll) {
|
||||
const current = this.$refs.body.scrollTop + this.$refs.body.clientHeight;
|
||||
if (current > this.$refs.body.scrollHeight - 1) this.$emit('bottom');
|
||||
}
|
||||
|
@ -205,7 +205,7 @@ export default Vue.extend({
|
||||
top -32px
|
||||
left 0
|
||||
right 0
|
||||
width 0px
|
||||
width 0
|
||||
margin 0 auto
|
||||
border-top solid 16px transparent
|
||||
border-left solid 16px transparent
|
||||
|
@ -102,7 +102,7 @@ class Autocomplete {
|
||||
}
|
||||
}
|
||||
|
||||
if (isHashtag && opened == false) {
|
||||
if (isHashtag && !opened) {
|
||||
const hashtag = text.substr(hashtagIndex + 1);
|
||||
if (!hashtag.includes(' ')) {
|
||||
this.open('hashtag', hashtag);
|
||||
@ -110,7 +110,7 @@ class Autocomplete {
|
||||
}
|
||||
}
|
||||
|
||||
if (isEmoji && opened == false) {
|
||||
if (isEmoji && !opened) {
|
||||
const emoji = text.substr(emojiIndex + 1);
|
||||
if (!emoji.includes(' ')) {
|
||||
this.open('emoji', emoji);
|
||||
|
@ -13,8 +13,8 @@
|
||||
<template #header><fa :icon="faHashtag" fixed-width/>{{ $t('popular-tags') }}</template>
|
||||
|
||||
<div class="vxjfqztj">
|
||||
<router-link v-for="tag in tagsLocal" :to="`/explore/tags/${tag.name}`" :key="'local:' + tag.name" class="local">{{ tag.name }}</router-link>
|
||||
<router-link v-for="tag in tagsRemote" :to="`/explore/tags/${tag.name}`" :key="'remote:' + tag.name">{{ tag.name }}</router-link>
|
||||
<router-link v-for="tag in tagsLocal" :to="`/explore/tags/${tag.tag}`" :key="'local:' + tag.tag" class="local">{{ tag.tag }}</router-link>
|
||||
<router-link v-for="tag in tagsRemote" :to="`/explore/tags/${tag.tag}`" :key="'remote:' + tag.tag">{{ tag.tag }}</router-link>
|
||||
</div>
|
||||
</ui-container>
|
||||
|
||||
@ -26,8 +26,8 @@
|
||||
</mk-user-list>
|
||||
|
||||
<template v-if="tag == null">
|
||||
<mk-user-list :make-promise="verifiedUsers">
|
||||
<fa :icon="faBookmark" fixed-width/>{{ $t('verified-users') }}
|
||||
<mk-user-list :make-promise="pinnedUsers">
|
||||
<fa :icon="faBookmark" fixed-width/>{{ $t('pinned-users') }}
|
||||
</mk-user-list>
|
||||
<mk-user-list :make-promise="popularUsers">
|
||||
<fa :icon="faChartLine" fixed-width/>{{ $t('popular-users') }}
|
||||
@ -60,12 +60,7 @@ export default Vue.extend({
|
||||
|
||||
data() {
|
||||
return {
|
||||
verifiedUsers: () => this.$root.api('users', {
|
||||
state: 'verified',
|
||||
origin: 'local',
|
||||
sort: '+follower',
|
||||
limit: 10
|
||||
}),
|
||||
pinnedUsers: () => this.$root.api('pinned-users'),
|
||||
popularUsers: () => this.$root.api('users', {
|
||||
state: 'alive',
|
||||
origin: 'local',
|
||||
|
@ -8,12 +8,18 @@ import XText from './page.text.vue';
|
||||
import XSection from './page.section.vue';
|
||||
import XImage from './page.image.vue';
|
||||
import XButton from './page.button.vue';
|
||||
import XInput from './page.input.vue';
|
||||
import XNumberInput from './page.number-input.vue';
|
||||
import XTextInput from './page.text-input.vue';
|
||||
import XTextareaInput from './page.textarea-input.vue';
|
||||
import XSwitch from './page.switch.vue';
|
||||
import XIf from './page.if.vue';
|
||||
import XTextarea from './page.textarea.vue';
|
||||
import XPost from './page.post.vue';
|
||||
import XCounter from './page.counter.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
components: {
|
||||
XText, XSection, XImage, XButton, XInput, XSwitch
|
||||
XText, XSection, XImage, XButton, XNumberInput, XTextInput, XTextareaInput, XTextarea, XPost, XSwitch, XIf, XCounter
|
||||
},
|
||||
|
||||
props: {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-button class="kudkigyw" @click="click()">{{ value.text }}</ui-button>
|
||||
<ui-button class="kudkigyw" @click="click()">{{ script.interpolate(value.text) }}</ui-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -20,13 +20,13 @@ export default Vue.extend({
|
||||
methods: {
|
||||
click() {
|
||||
if (this.value.action === 'dialog') {
|
||||
this.script.reEval();
|
||||
this.script.eval();
|
||||
this.$root.dialog({
|
||||
text: this.script.interpolate(this.value.content)
|
||||
});
|
||||
} else if (this.value.action === 'resetRandom') {
|
||||
this.script.aiScript.updateRandomSeed(Math.random());
|
||||
this.script.reEval();
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
47
src/client/app/common/views/pages/page/page.counter.vue
Normal file
47
src/client/app/common/views/pages/page/page.counter.vue
Normal file
@ -0,0 +1,47 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-button class="llumlmnx" @click="click()">{{ script.interpolate(value.text) }}</ui-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
script: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
v: 0,
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
click() {
|
||||
this.v = this.v + (this.value.inc || 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.llumlmnx
|
||||
display inline-block
|
||||
min-width 300px
|
||||
max-width 450px
|
||||
margin 8px 0
|
||||
</style>
|
30
src/client/app/common/views/pages/page/page.if.vue
Normal file
30
src/client/app/common/views/pages/page/page.if.vue
Normal file
@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div v-show="script.vars[value.var]">
|
||||
<x-block v-for="child in value.children" :value="child" :page="page" :script="script" :key="child.id" :h="h"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
script: {
|
||||
required: true
|
||||
},
|
||||
page: {
|
||||
required: true
|
||||
},
|
||||
h: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
beforeCreate() {
|
||||
this.$options.components.XBlock = require('./page.block.vue').default
|
||||
},
|
||||
});
|
||||
</script>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-input class="kudkigyw" v-model="v" :type="value.inputType">{{ value.text }}</ui-input>
|
||||
<ui-input class="kudkigyw" v-model="v" type="number">{{ script.interpolate(value.text) }}</ui-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -25,10 +25,8 @@ export default Vue.extend({
|
||||
|
||||
watch: {
|
||||
v() {
|
||||
let v = this.v;
|
||||
if (this.value.inputType === 'number') v = parseInt(v, 10);
|
||||
this.script.aiScript.updatePageVar(this.value.name, v);
|
||||
this.script.reEval();
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
});
|
68
src/client/app/common/views/pages/page/page.post.vue
Normal file
68
src/client/app/common/views/pages/page/page.post.vue
Normal file
@ -0,0 +1,68 @@
|
||||
<template>
|
||||
<div class="ngbfujlo">
|
||||
<ui-textarea class="textarea" :value="text" readonly></ui-textarea>
|
||||
<ui-button primary @click="post()" :disabled="posting || posted">{{ posted ? $t('posted-from-post-form') : $t('post-from-post-form') }}</ui-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
|
||||
export default Vue.extend({
|
||||
i18n: i18n('pages'),
|
||||
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
script: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
text: this.script.interpolate(this.value.text),
|
||||
posted: false,
|
||||
posting: false,
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$watch('script.vars', () => {
|
||||
this.text = this.script.interpolate(this.value.text);
|
||||
}, { deep: true });
|
||||
},
|
||||
|
||||
methods: {
|
||||
post() {
|
||||
this.posting = true;
|
||||
this.$root.api('notes/create', {
|
||||
text: this.text,
|
||||
}).then(() => {
|
||||
this.posted = true;
|
||||
this.$root.dialog({
|
||||
type: 'success',
|
||||
splash: true
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.ngbfujlo
|
||||
padding 0 32px 32px 32px
|
||||
border solid 2px var(--pageBlockBorder)
|
||||
border-radius 6px
|
||||
|
||||
@media (max-width 600px)
|
||||
padding 0 16px 16px 16px
|
||||
|
||||
> .textarea
|
||||
margin-top 16px
|
||||
margin-bottom 16px
|
||||
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="hkcxmtwj">
|
||||
<ui-switch v-model="v">{{ value.text }}</ui-switch>
|
||||
<ui-switch v-model="v">{{ script.interpolate(value.text) }}</ui-switch>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -26,7 +26,7 @@ export default Vue.extend({
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.reEval();
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -36,4 +36,8 @@ export default Vue.extend({
|
||||
.hkcxmtwj
|
||||
display inline-block
|
||||
margin 16px auto
|
||||
|
||||
& + .hkcxmtwj
|
||||
margin-left 16px
|
||||
|
||||
</style>
|
||||
|
41
src/client/app/common/views/pages/page/page.text-input.vue
Normal file
41
src/client/app/common/views/pages/page/page.text-input.vue
Normal file
@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-input class="kudkigyw" v-model="v" type="text">{{ script.interpolate(value.text) }}</ui-input>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
script: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
v: this.value.default,
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
.kudkigyw
|
||||
display inline-block
|
||||
min-width 300px
|
||||
max-width 450px
|
||||
margin 8px 0
|
||||
</style>
|
@ -0,0 +1,36 @@
|
||||
<template>
|
||||
<div>
|
||||
<ui-textarea class="" v-model="v">{{ script.interpolate(value.text) }}</ui-textarea>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
script: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
v: this.value.default,
|
||||
};
|
||||
},
|
||||
|
||||
watch: {
|
||||
v() {
|
||||
this.script.aiScript.updatePageVar(this.value.name, this.v);
|
||||
this.script.eval();
|
||||
}
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
</style>
|
33
src/client/app/common/views/pages/page/page.textarea.vue
Normal file
33
src/client/app/common/views/pages/page/page.textarea.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<ui-textarea class="" :value="text" readonly></ui-textarea>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
|
||||
export default Vue.extend({
|
||||
props: {
|
||||
value: {
|
||||
required: true
|
||||
},
|
||||
script: {
|
||||
required: true
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
text: this.script.interpolate(this.value.text),
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
this.$watch('script.vars', () => {
|
||||
this.text = this.script.interpolate(this.value.text);
|
||||
}, { deep: true });
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="stylus" scoped>
|
||||
</style>
|
@ -11,6 +11,7 @@
|
||||
<footer>
|
||||
<small>@{{ page.user.username }}</small>
|
||||
<router-link v-if="$store.getters.isSignedIn && $store.state.i.id === page.userId" :to="`/i/pages/edit/${page.id}`">{{ $t('edit-this-page') }}</router-link>
|
||||
<router-link :to="`./${page.name}/view-source`">{{ $t('view-source') }}</router-link>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
@ -18,28 +19,36 @@
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import i18n from '../../../../i18n';
|
||||
import { faICursor, faPlus, faSquareRootAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faICursor, faPlus } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faSave, faStickyNote } from '@fortawesome/free-regular-svg-icons';
|
||||
import XBlock from './page.block.vue';
|
||||
import { AiScript } from '../../../scripts/aiscript';
|
||||
import { ASEvaluator } from '../../../../../../misc/aiscript/evaluator';
|
||||
import { collectPageVars } from '../../../scripts/collect-page-vars';
|
||||
import { url } from '../../../../config';
|
||||
|
||||
class Script {
|
||||
public aiScript: AiScript;
|
||||
public vars: any;
|
||||
public aiScript: ASEvaluator;
|
||||
private onError: any;
|
||||
public vars: Record<string, any>;
|
||||
|
||||
constructor(aiScript) {
|
||||
constructor(aiScript, onError) {
|
||||
this.aiScript = aiScript;
|
||||
this.vars = this.aiScript.evaluateVars();
|
||||
this.onError = onError;
|
||||
this.eval();
|
||||
}
|
||||
|
||||
public reEval() {
|
||||
this.vars = this.aiScript.evaluateVars();
|
||||
public eval() {
|
||||
try {
|
||||
this.vars = this.aiScript.evaluateVars();
|
||||
} catch (e) {
|
||||
this.onError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public interpolate(str: string) {
|
||||
return str.replace(/\{(.+?)\}/g, match => {
|
||||
const v = this.vars.find(x => x.name === match.slice(1, -1).trim()).value;
|
||||
if (str == null) return null;
|
||||
return str.replace(/{(.+?)}/g, match => {
|
||||
const v = this.vars[match.slice(1, -1).trim()];
|
||||
return v == null ? 'NULL' : v.toString();
|
||||
});
|
||||
}
|
||||
@ -67,7 +76,7 @@ export default Vue.extend({
|
||||
return {
|
||||
page: null,
|
||||
script: null,
|
||||
faPlus, faICursor, faSave, faStickyNote, faSquareRootAlt
|
||||
faPlus, faICursor, faSave, faStickyNote
|
||||
};
|
||||
},
|
||||
|
||||
@ -78,11 +87,15 @@ export default Vue.extend({
|
||||
}).then(page => {
|
||||
this.page = page;
|
||||
const pageVars = this.getPageVars();
|
||||
this.script = new Script(new AiScript(this.page.variables, pageVars, {
|
||||
this.script = new Script(new ASEvaluator(this.page.variables, pageVars, {
|
||||
randomSeed: Math.random(),
|
||||
user: page.user,
|
||||
visitor: this.$store.state.i
|
||||
}));
|
||||
visitor: this.$store.state.i,
|
||||
page: page,
|
||||
url: url
|
||||
}), e => {
|
||||
console.dir(e);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@ -142,4 +155,10 @@ export default Vue.extend({
|
||||
display block
|
||||
opacity 0.5
|
||||
|
||||
> a
|
||||
font-size 14px
|
||||
|
||||
> a + a
|
||||
margin-left 8px
|
||||
|
||||
</style>
|
||||
|
@ -159,8 +159,9 @@ init(async (launch, os) => {
|
||||
{ path: '/i/pages', component: () => import('./views/home/pages.vue').then(m => m.default) },
|
||||
]},
|
||||
{ path: '/@:user/pages/:page', props: true, component: () => import('./views/pages/page.vue').then(m => m.default) },
|
||||
{ path: '/@:user/pages/:pageName/view-source', props: true, component: () => import('./views/pages/page-editor.vue').then(m => m.default) },
|
||||
{ path: '/i/pages/new', component: () => import('./views/pages/page-editor.vue').then(m => m.default) },
|
||||
{ path: '/i/pages/edit/:page', props: true, component: () => import('./views/pages/page-editor.vue').then(m => m.default) },
|
||||
{ path: '/i/pages/edit/:pageId', props: true, component: () => import('./views/pages/page-editor.vue').then(m => m.default) },
|
||||
{ path: '/i/messaging/:user', component: MkMessagingRoom },
|
||||
{ path: '/i/drive', component: MkDrive },
|
||||
{ path: '/i/drive/folder/:folder', component: MkDrive },
|
||||
|
@ -750,12 +750,17 @@ export default Vue.extend({
|
||||
bottom 0
|
||||
animation-delay -1.0s
|
||||
|
||||
@keyframes sk-rotate { 100% { transform: rotate(360deg); }}
|
||||
@keyframes sk-rotate {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-bounce {
|
||||
0%, 100% {
|
||||
transform: scale(0.0);
|
||||
} 50% {
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
|
@ -181,7 +181,7 @@ export default Vue.extend({
|
||||
this.releaseQueue();
|
||||
}
|
||||
|
||||
if (this.$store.state.settings.fetchOnScroll !== false) {
|
||||
if (this.$store.state.settings.fetchOnScroll) {
|
||||
const current = window.scrollY + window.innerHeight;
|
||||
if (current > document.body.offsetHeight - 8) this.fetchMore();
|
||||
}
|
||||
|
@ -377,7 +377,7 @@ export default Vue.extend({
|
||||
}, err => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
title: this.$t('error')
|
||||
title: this.$t('error'),
|
||||
text: err.message
|
||||
});
|
||||
}, {
|
||||
|
@ -480,7 +480,7 @@ export default Vue.extend({
|
||||
&:focus
|
||||
&:not([data-is-modal])
|
||||
> .body
|
||||
box-shadow 0 0 0px 1px var(--primaryAlpha05), 0 2px 12px 0 var(--desktopWindowShadow)
|
||||
box-shadow 0 0 0 1px var(--primaryAlpha05), 0 2px 12px 0 var(--desktopWindowShadow)
|
||||
|
||||
> .handle
|
||||
$size = 8px
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<mk-ui>
|
||||
<main>
|
||||
<x-page-editor :page="page"/>
|
||||
<x-page-editor v-if="page !== undefined" :page="page" :readonly="readonly"/>
|
||||
</main>
|
||||
</mk-ui>
|
||||
</template>
|
||||
@ -15,9 +15,44 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
props: {
|
||||
page: {
|
||||
pageId: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
pageName: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
user: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
page: undefined,
|
||||
readonly: false
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.pageId) {
|
||||
this.$root.api('pages/show', {
|
||||
pageId: this.pageId,
|
||||
}).then(page => {
|
||||
this.page = page;
|
||||
});
|
||||
} else if (this.pageName && this.user) {
|
||||
this.$root.api('pages/show', {
|
||||
name: this.pageName,
|
||||
username: this.user,
|
||||
}).then(page => {
|
||||
this.readonly = true;
|
||||
this.page = page;
|
||||
});
|
||||
} else {
|
||||
this.page = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -352,7 +352,7 @@ export default Vue.extend({
|
||||
padding 0 16px
|
||||
line-height 48px
|
||||
background var(--faceHeader)
|
||||
box-shadow 0 1px 0px rgba(0, 0, 0, 0.1)
|
||||
box-shadow 0 1px 0 rgba(0, 0, 0, 0.1)
|
||||
|
||||
& + div
|
||||
max-height calc(100% - 48px)
|
||||
|
@ -173,9 +173,10 @@ export default class MiOS extends EventEmitter {
|
||||
|
||||
// Init service worker
|
||||
if (this.shouldRegisterSw) {
|
||||
this.getMeta().then(data => {
|
||||
this.registerSw(data.swPublickey);
|
||||
});
|
||||
// #4813
|
||||
//this.getMeta().then(data => {
|
||||
// this.registerSw(data.swPublickey);
|
||||
//});
|
||||
}
|
||||
};
|
||||
|
||||
@ -195,7 +196,7 @@ export default class MiOS extends EventEmitter {
|
||||
});
|
||||
} else {
|
||||
// Get token from cookie or localStorage
|
||||
const i = (document.cookie.match(/i=(!\w+)/) || [null, null])[1] || localStorage.getItem('i');
|
||||
const i = (document.cookie.match(/i=(\w+)/) || [null, null])[1] || localStorage.getItem('i');
|
||||
|
||||
fetchme(i, me => {
|
||||
if (me) {
|
||||
@ -504,7 +505,7 @@ class WindowSystem extends EventEmitter {
|
||||
function urlBase64ToUint8Array(base64String: string): Uint8Array {
|
||||
const padding = '='.repeat((4 - base64String.length % 4) % 4);
|
||||
const base64 = (base64String + padding)
|
||||
.replace(/\-/g, '+')
|
||||
.replace(/-/g, '+')
|
||||
.replace(/_/g, '/');
|
||||
|
||||
const rawData = window.atob(base64);
|
||||
|
@ -146,7 +146,7 @@ init((launch, os) => {
|
||||
{ path: '/i/drive/folder/:folder', component: MkDrive },
|
||||
{ path: '/i/drive/file/:file', component: MkDrive },
|
||||
{ path: '/i/pages/new', component: () => import('./views/pages/page-editor.vue').then(m => m.default) },
|
||||
{ path: '/i/pages/edit/:page', props: true, component: () => import('./views/pages/page-editor.vue').then(m => m.default) },
|
||||
{ path: '/i/pages/edit/:pageId', props: true, component: () => import('./views/pages/page-editor.vue').then(m => m.default) },
|
||||
{ path: '/selectdrive', component: MkSelectDrive },
|
||||
{ path: '/search', component: MkSearch },
|
||||
{ path: '/tags/:tag', component: MkTag },
|
||||
@ -160,6 +160,7 @@ init((launch, os) => {
|
||||
{ path: 'followers', component: () => import('../common/views/pages/followers.vue').then(m => m.default) },
|
||||
]},
|
||||
{ path: '/@:user/pages/:page', props: true, component: () => import('./views/pages/page.vue').then(m => m.default) },
|
||||
{ path: '/@:user/pages/:pageName/view-source', props: true, component: () => import('./views/pages/page-editor.vue').then(m => m.default) },
|
||||
{ path: '/notes/:note', component: MkNote },
|
||||
{ path: '/authorize-follow', component: MkFollow },
|
||||
{ path: '*', component: MkNotFound }
|
||||
|
@ -83,7 +83,7 @@ export default Vue.extend({
|
||||
hierarchyFolders: [],
|
||||
selectedFiles: [],
|
||||
info: null,
|
||||
connection: null
|
||||
connection: null,
|
||||
|
||||
fetching: true,
|
||||
fetchingMoreFiles: false,
|
||||
@ -385,7 +385,7 @@ export default Vue.extend({
|
||||
|
||||
createFolder() {
|
||||
this.$root.dialog({
|
||||
title: this.$t('folder-name')
|
||||
title: this.$t('folder-name'),
|
||||
input: {
|
||||
default: this.folder.name
|
||||
}
|
||||
@ -415,7 +415,7 @@ export default Vue.extend({
|
||||
return;
|
||||
}
|
||||
this.$root.dialog({
|
||||
title: this.$t('folder-name')
|
||||
title: this.$t('folder-name'),
|
||||
input: {
|
||||
default: this.folder.name
|
||||
}
|
||||
@ -597,12 +597,17 @@ export default Vue.extend({
|
||||
bottom 0
|
||||
animation-delay -1.0s
|
||||
|
||||
@keyframes sk-rotate { 100% { transform: rotate(360deg); }}
|
||||
@keyframes sk-rotate {
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes sk-bounce {
|
||||
0%, 100% {
|
||||
transform: scale(0.0);
|
||||
} 50% {
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.0);
|
||||
}
|
||||
}
|
||||
|
@ -175,7 +175,7 @@ export default Vue.extend({
|
||||
this.releaseQueue();
|
||||
}
|
||||
|
||||
if (this.$store.state.settings.fetchOnScroll !== false) {
|
||||
if (this.$store.state.settings.fetchOnScroll) {
|
||||
// 親要素が display none だったら弾く
|
||||
// https://github.com/syuilo/misskey/issues/1569
|
||||
// http://d.hatena.ne.jp/favril/20091105/1257403319
|
||||
|
@ -115,7 +115,7 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
onScroll() {
|
||||
if (this.$store.state.settings.fetchOnScroll !== false) {
|
||||
if (this.$store.state.settings.fetchOnScroll) {
|
||||
// 親要素が display none だったら弾く
|
||||
// https://github.com/syuilo/misskey/issues/1569
|
||||
// http://d.hatena.ne.jp/favril/20091105/1257403319
|
||||
|
@ -295,7 +295,7 @@ export default Vue.extend({
|
||||
}, err => {
|
||||
this.$root.dialog({
|
||||
type: 'error',
|
||||
title: this.$t('error')
|
||||
title: this.$t('error'),
|
||||
text: err.message
|
||||
});
|
||||
}, {
|
||||
@ -341,7 +341,7 @@ export default Vue.extend({
|
||||
|
||||
post() {
|
||||
this.posting = true;
|
||||
const viaMobile = this.$store.state.settings.disableViaMobile !== true;
|
||||
const viaMobile = !this.$store.state.settings.disableViaMobile;
|
||||
this.$root.api('notes/create', {
|
||||
text: this.text == '' ? undefined : this.text,
|
||||
fileIds: this.files.length > 0 ? this.files.map(f => f.id) : undefined,
|
||||
|
@ -49,7 +49,7 @@ export default Vue.extend({
|
||||
padding 0 8px
|
||||
|
||||
&.shadow
|
||||
box-shadow 0 0px 8px rgba(0, 0, 0, 0.25)
|
||||
box-shadow 0 0 8px rgba(0, 0, 0, 0.25)
|
||||
|
||||
&, *
|
||||
user-select none
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<mk-ui>
|
||||
<main>
|
||||
<x-page-editor :page="page"/>
|
||||
<x-page-editor v-if="page !== undefined" :page="page" :readonly="readonly"/>
|
||||
</main>
|
||||
</mk-ui>
|
||||
</template>
|
||||
@ -15,9 +15,44 @@ export default Vue.extend({
|
||||
},
|
||||
|
||||
props: {
|
||||
page: {
|
||||
pageId: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
pageName: {
|
||||
type: String,
|
||||
required: false
|
||||
},
|
||||
user: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
|
||||
data() {
|
||||
return {
|
||||
page: undefined,
|
||||
readonly: false
|
||||
};
|
||||
},
|
||||
|
||||
created() {
|
||||
if (this.pageId) {
|
||||
this.$root.api('pages/show', {
|
||||
pageId: this.pageId,
|
||||
}).then(page => {
|
||||
this.page = page;
|
||||
});
|
||||
} else if (this.pageName && this.user) {
|
||||
this.$root.api('pages/show', {
|
||||
name: this.pageName,
|
||||
username: this.user,
|
||||
}).then(page => {
|
||||
this.readonly = true;
|
||||
this.page = page;
|
||||
});
|
||||
} else {
|
||||
this.page = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
@ -33,4 +33,7 @@ main
|
||||
padding 16px
|
||||
max-width 1000px
|
||||
|
||||
@media (min-width 600px)
|
||||
padding 32px
|
||||
|
||||
</style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
input
|
||||
min-width 0px
|
||||
min-width 0
|
||||
|
||||
input:not([type])
|
||||
input[type='text']
|
||||
|
@ -126,7 +126,7 @@ export default (os: MiOS) => new Vuex.Store({
|
||||
|
||||
logout(ctx) {
|
||||
ctx.commit('updateI', null);
|
||||
document.cookie = 'i=;';
|
||||
document.cookie = `i=; max-age=0; domain=${document.location.hostname}`;
|
||||
localStorage.removeItem('i');
|
||||
},
|
||||
|
||||
|
@ -3,12 +3,6 @@
|
||||
*/
|
||||
|
||||
import composeNotification from './common/scripts/compose-notification';
|
||||
import { erase } from '../../prelude/array';
|
||||
|
||||
// キャッシュするリソース
|
||||
const cachee = [
|
||||
'/'
|
||||
];
|
||||
|
||||
// インストールされたとき
|
||||
self.addEventListener('install', ev => {
|
||||
@ -16,31 +10,9 @@ self.addEventListener('install', ev => {
|
||||
|
||||
ev.waitUntil(Promise.all([
|
||||
self.skipWaiting(), // Force activate
|
||||
caches.open(_VERSION_).then(cache => cache.addAll(cachee)) // Cache
|
||||
]));
|
||||
});
|
||||
|
||||
// アクティベートされたとき
|
||||
self.addEventListener('activate', ev => {
|
||||
// Clean up old caches
|
||||
ev.waitUntil(
|
||||
caches.keys().then(keys => Promise.all(
|
||||
erase(_VERSION_, keys)
|
||||
.map(key => caches.delete(key))
|
||||
))
|
||||
);
|
||||
});
|
||||
|
||||
// リクエストが発生したとき
|
||||
self.addEventListener('fetch', ev => {
|
||||
ev.respondWith(
|
||||
// キャッシュがあるか確認してあればそれを返す
|
||||
caches.match(ev.request).then(response =>
|
||||
response || fetch(ev.request)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
// プッシュ通知を受け取ったとき
|
||||
self.addEventListener('push', ev => {
|
||||
// クライアント取得
|
||||
@ -59,11 +31,3 @@ self.addEventListener('push', ev => {
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
self.addEventListener('message', ev => {
|
||||
if (ev.data == 'clear') {
|
||||
caches.keys().then(keys => {
|
||||
for (const key of keys) caches.delete(key);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -44,6 +44,8 @@ export type Source = {
|
||||
clusterLimit?: number;
|
||||
|
||||
id: string;
|
||||
|
||||
outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual';
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -129,7 +129,7 @@ export const mfmLanguage = P.createLanguage({
|
||||
mention: () => {
|
||||
return P((input, i) => {
|
||||
const text = input.substr(i);
|
||||
const match = text.match(/^@\w([\w-]*\w)?(?:@[\w\.\-]+\w)?/);
|
||||
const match = text.match(/^@\w([\w-]*\w)?(?:@[\w.\-]+\w)?/);
|
||||
if (!match) return P.makeFailure(i, 'not a mention');
|
||||
if (input[i - 1] != null && input[i - 1].match(/[a-z0-9]/i)) return P.makeFailure(i, 'not a mention');
|
||||
return P.makeSuccess(i + match[0].length, match[0]);
|
||||
@ -141,7 +141,7 @@ export const mfmLanguage = P.createLanguage({
|
||||
},
|
||||
hashtag: () => P((input, i) => {
|
||||
const text = input.substr(i);
|
||||
const match = text.match(/^#([^\s\.,!\?'"#:\/\[\]【】]+)/i);
|
||||
const match = text.match(/^#([^\s.,!?'"#:\/\[\]【】]+)/i);
|
||||
if (!match) return P.makeFailure(i, 'not a hashtag');
|
||||
let hashtag = match[1];
|
||||
hashtag = removeOrphanedBrackets(hashtag);
|
||||
|
@ -36,4 +36,4 @@ export function createTree(type: string, children: MfmForest, props: any): MfmTr
|
||||
return T.createTree({ type, props }, children);
|
||||
}
|
||||
|
||||
export const urlRegex = /^https?:\/\/[\w\/:%#@\$&\?!\(\)\[\]~\.,=\+\-]+/;
|
||||
export const urlRegex = /^https?:\/\/[\w\/:%#@$&?!()\[\]~.,=+\-]+/;
|
||||
|
264
src/misc/aiscript/evaluator.ts
Normal file
264
src/misc/aiscript/evaluator.ts
Normal file
@ -0,0 +1,264 @@
|
||||
import autobind from 'autobind-decorator';
|
||||
import * as seedrandom from 'seedrandom';
|
||||
import { Variable, PageVar, envVarsDef, funcDefs, Block, isFnBlock } from '.';
|
||||
|
||||
type Fn = {
|
||||
slots: string[];
|
||||
exec: (args: Record<string, any>) => ReturnType<ASEvaluator['evaluate']>;
|
||||
};
|
||||
|
||||
/**
|
||||
* AiScript evaluator
|
||||
*/
|
||||
export class ASEvaluator {
|
||||
private variables: Variable[];
|
||||
private pageVars: PageVar[];
|
||||
private envVars: Record<keyof typeof envVarsDef, any>;
|
||||
|
||||
private opts: {
|
||||
randomSeed: string; user?: any; visitor?: any; page?: any; url?: string; version: string;
|
||||
};
|
||||
|
||||
constructor(variables: Variable[], pageVars: PageVar[], opts: ASEvaluator['opts']) {
|
||||
this.variables = variables;
|
||||
this.pageVars = pageVars;
|
||||
this.opts = opts;
|
||||
|
||||
const date = new Date();
|
||||
|
||||
this.envVars = {
|
||||
AI: 'kawaii',
|
||||
VERSION: opts.version,
|
||||
URL: opts.page ? `${opts.url}/@${opts.page.user.username}/pages/${opts.page.name}` : '',
|
||||
LOGIN: opts.visitor != null,
|
||||
NAME: opts.visitor ? opts.visitor.name || opts.visitor.username : '',
|
||||
USERNAME: opts.visitor ? opts.visitor.username : '',
|
||||
USERID: opts.visitor ? opts.visitor.id : '',
|
||||
NOTES_COUNT: opts.visitor ? opts.visitor.notesCount : 0,
|
||||
FOLLOWERS_COUNT: opts.visitor ? opts.visitor.followersCount : 0,
|
||||
FOLLOWING_COUNT: opts.visitor ? opts.visitor.followingCount : 0,
|
||||
IS_CAT: opts.visitor ? opts.visitor.isCat : false,
|
||||
MY_NOTES_COUNT: opts.user ? opts.user.notesCount : 0,
|
||||
MY_FOLLOWERS_COUNT: opts.user ? opts.user.followersCount : 0,
|
||||
MY_FOLLOWING_COUNT: opts.user ? opts.user.followingCount : 0,
|
||||
SEED: opts.randomSeed ? opts.randomSeed : '',
|
||||
YMD: `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`,
|
||||
NULL: null
|
||||
};
|
||||
}
|
||||
|
||||
@autobind
|
||||
public updatePageVar(name: string, value: any) {
|
||||
const pageVar = this.pageVars.find(v => v.name === name);
|
||||
if (pageVar !== undefined) {
|
||||
pageVar.value = value;
|
||||
} else {
|
||||
throw new AiScriptError(`No such page var '${name}'`);
|
||||
}
|
||||
}
|
||||
|
||||
@autobind
|
||||
public updateRandomSeed(seed: string) {
|
||||
this.opts.randomSeed = seed;
|
||||
this.envVars.SEED = seed;
|
||||
}
|
||||
|
||||
@autobind
|
||||
private interpolate(str: string, scope: Scope) {
|
||||
return str.replace(/{(.+?)}/g, match => {
|
||||
const v = scope.getState(match.slice(1, -1).trim());
|
||||
return v == null ? 'NULL' : v.toString();
|
||||
});
|
||||
}
|
||||
|
||||
@autobind
|
||||
public evaluateVars(): Record<string, any> {
|
||||
const values: Record<string, any> = {};
|
||||
|
||||
for (const [k, v] of Object.entries(this.envVars)) {
|
||||
values[k] = v;
|
||||
}
|
||||
|
||||
for (const v of this.pageVars) {
|
||||
values[v.name] = v.value;
|
||||
}
|
||||
|
||||
for (const v of this.variables) {
|
||||
values[v.name] = this.evaluate(v, new Scope([values]));
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
||||
@autobind
|
||||
private evaluate(block: Block, scope: Scope): any {
|
||||
if (block.type === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (block.type === 'number') {
|
||||
return parseInt(block.value, 10);
|
||||
}
|
||||
|
||||
if (block.type === 'text' || block.type === 'multiLineText') {
|
||||
return this.interpolate(block.value || '', scope);
|
||||
}
|
||||
|
||||
if (block.type === 'textList') {
|
||||
return this.interpolate(block.value || '', scope).trim().split('\n');
|
||||
}
|
||||
|
||||
if (block.type === 'ref') {
|
||||
return scope.getState(block.value);
|
||||
}
|
||||
|
||||
if (isFnBlock(block)) { // ユーザー関数定義
|
||||
return {
|
||||
slots: block.value.slots.map(x => x.name),
|
||||
exec: (slotArg: Record<string, any>) => {
|
||||
return this.evaluate(block.value.expression, scope.createChildScope(slotArg, block.id));
|
||||
}
|
||||
} as Fn;
|
||||
}
|
||||
|
||||
if (block.type.startsWith('fn:')) { // ユーザー関数呼び出し
|
||||
const fnName = block.type.split(':')[1];
|
||||
const fn = scope.getState(fnName);
|
||||
const args = {} as Record<string, any>;
|
||||
for (let i = 0; i < fn.slots.length; i++) {
|
||||
const name = fn.slots[i];
|
||||
args[name] = this.evaluate(block.args[i], scope);
|
||||
}
|
||||
return fn.exec(args);
|
||||
}
|
||||
|
||||
if (block.args === undefined) return null;
|
||||
|
||||
const date = new Date();
|
||||
const day = `${this.opts.visitor ? this.opts.visitor.id : ''} ${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`;
|
||||
|
||||
const funcs: { [p in keyof typeof funcDefs]: Function } = {
|
||||
not: (a: boolean) => !a,
|
||||
or: (a: boolean, b: boolean) => a || b,
|
||||
and: (a: boolean, b: boolean) => a && b,
|
||||
eq: (a: any, b: any) => a === b,
|
||||
notEq: (a: any, b: any) => a !== b,
|
||||
gt: (a: number, b: number) => a > b,
|
||||
lt: (a: number, b: number) => a < b,
|
||||
gtEq: (a: number, b: number) => a >= b,
|
||||
ltEq: (a: number, b: number) => a <= b,
|
||||
if: (bool: boolean, a: any, b: any) => bool ? a : b,
|
||||
for: (times: number, fn: Fn) => {
|
||||
const result = [];
|
||||
for (let i = 0; i < times; i++) {
|
||||
result.push(fn.exec({
|
||||
[fn.slots[0]]: i + 1
|
||||
}));
|
||||
}
|
||||
return result;
|
||||
},
|
||||
add: (a: number, b: number) => a + b,
|
||||
subtract: (a: number, b: number) => a - b,
|
||||
multiply: (a: number, b: number) => a * b,
|
||||
divide: (a: number, b: number) => a / b,
|
||||
remind: (a: number, b: number) => a % b,
|
||||
strLen: (a: string) => a.length,
|
||||
strPick: (a: string, b: number) => a[b - 1],
|
||||
strReplace: (a: string, b: string, c: string) => a.split(b).join(c),
|
||||
strReverse: (a: string) => a.split('').reverse().join(''),
|
||||
join: (texts: string[], separator: string) => texts.join(separator || ''),
|
||||
stringToNumber: (a: string) => parseInt(a),
|
||||
numberToString: (a: number) => a.toString(),
|
||||
splitStrByLine: (a: string) => a.split('\n'),
|
||||
pick: (list: any[], i: number) => list[i - 1],
|
||||
random: (probability: number) => Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * 100) < probability,
|
||||
rannum: (min: number, max: number) => min + Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * (max - min + 1)),
|
||||
randomPick: (list: any[]) => list[Math.floor(seedrandom(`${this.opts.randomSeed}:${block.id}`)() * list.length)],
|
||||
dailyRandom: (probability: number) => Math.floor(seedrandom(`${day}:${block.id}`)() * 100) < probability,
|
||||
dailyRannum: (min: number, max: number) => min + Math.floor(seedrandom(`${day}:${block.id}`)() * (max - min + 1)),
|
||||
dailyRandomPick: (list: any[]) => list[Math.floor(seedrandom(`${day}:${block.id}`)() * list.length)],
|
||||
seedRandom: (seed: any, probability: number) => Math.floor(seedrandom(seed)() * 100) < probability,
|
||||
seedRannum: (seed: any, min: number, max: number) => min + Math.floor(seedrandom(seed)() * (max - min + 1)),
|
||||
seedRandomPick: (seed: any, list: any[]) => list[Math.floor(seedrandom(seed)() * list.length)],
|
||||
DRPWPM: (list: string[]) => {
|
||||
const xs = [];
|
||||
let totalFactor = 0;
|
||||
for (const x of list) {
|
||||
const parts = x.split(' ');
|
||||
const factor = parseInt(parts.pop()!, 10);
|
||||
const text = parts.join(' ');
|
||||
totalFactor += factor;
|
||||
xs.push({ factor, text });
|
||||
}
|
||||
const r = seedrandom(`${day}:${block.id}`)() * totalFactor;
|
||||
let stackedFactor = 0;
|
||||
for (const x of xs) {
|
||||
if (r >= stackedFactor && r <= x.factor) {
|
||||
return x.text;
|
||||
} else {
|
||||
stackedFactor += x.factor;
|
||||
}
|
||||
}
|
||||
return xs[0].text;
|
||||
},
|
||||
};
|
||||
|
||||
const fnName = block.type;
|
||||
const fn = (funcs as any)[fnName];
|
||||
if (fn == null) {
|
||||
throw new AiScriptError(`No such function '${fnName}'`);
|
||||
} else {
|
||||
return fn(...block.args.map(x => this.evaluate(x, scope)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class AiScriptError extends Error {
|
||||
public info?: any;
|
||||
|
||||
constructor(message: string, info?: any) {
|
||||
super(message);
|
||||
|
||||
this.info = info;
|
||||
|
||||
// Maintains proper stack trace for where our error was thrown (only available on V8)
|
||||
if (Error.captureStackTrace) {
|
||||
Error.captureStackTrace(this, AiScriptError);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Scope {
|
||||
private layerdStates: Record<string, any>[];
|
||||
public name: string;
|
||||
|
||||
constructor(layerdStates: Scope['layerdStates'], name?: Scope['name']) {
|
||||
this.layerdStates = layerdStates;
|
||||
this.name = name || 'anonymous';
|
||||
}
|
||||
|
||||
@autobind
|
||||
public createChildScope(states: Record<string, any>, name?: Scope['name']): Scope {
|
||||
const layer = [states, ...this.layerdStates];
|
||||
return new Scope(layer, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定した名前の変数の値を取得します
|
||||
* @param name 変数名
|
||||
*/
|
||||
@autobind
|
||||
public getState(name: string): any {
|
||||
for (const later of this.layerdStates) {
|
||||
const state = later[name];
|
||||
if (state !== undefined) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
throw new AiScriptError(
|
||||
`No such variable '${name}' in scope '${this.name}'`, {
|
||||
scope: this.layerdStates
|
||||
});
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user