Compare commits
108 Commits
Author | SHA1 | Date | |
---|---|---|---|
938fcb3e5e | |||
3553f3be4e | |||
8c4662e6e5 | |||
e7610b2467 | |||
79e60afd42 | |||
3da93e7bf9 | |||
1700154f00 | |||
68571d8f57 | |||
61461b7f59 | |||
c27c3817a9 | |||
08e1db45a9 | |||
27c373ddf4 | |||
e62d7bc1ba | |||
0d680b060e | |||
0f054aed88 | |||
b4308ecb91 | |||
e68ff28649 | |||
cb946772b4 | |||
f169585a55 | |||
e5fbc68e0e | |||
f32cad2667 | |||
503f23ad3b | |||
481b46ac9e | |||
9c34ef7d74 | |||
18fbaee9df | |||
94b59e8b6f | |||
e9c5cd543e | |||
5e1d17dff2 | |||
526838c77c | |||
ef6b370d0e | |||
38896205c8 | |||
7221684337 | |||
99e936bf2b | |||
138fee9b52 | |||
a19c1535e8 | |||
05ba1d0fd4 | |||
c2b3436770 | |||
a0fd3aef98 | |||
b112341d91 | |||
0dba5607a8 | |||
7378c4a9d8 | |||
89e7ef36dc | |||
39ed02bc53 | |||
449dc17df8 | |||
5cb3d86a1b | |||
a3687dd653 | |||
fbc5b8ceb7 | |||
4c545fbba5 | |||
7fda726bbe | |||
e404e5e2de | |||
930127348a | |||
3db37e1d52 | |||
b9c4a582ac | |||
a085d9fbd7 | |||
7f2356ba4b | |||
7a7f668879 | |||
69a05aa5de | |||
c0c64a2d5d | |||
3c3b4eb2af | |||
1019e0bcfd | |||
faf2c007aa | |||
43678ac801 | |||
f22d5aa46d | |||
d599c1e740 | |||
0cc4519fc0 | |||
0265c85ba3 | |||
7ec0670748 | |||
d06517265f | |||
a88e486468 | |||
c22ff4c556 | |||
d4c4e30fe4 | |||
1ec3338d2e | |||
bffcfd92da | |||
f44517f4af | |||
5087a5af21 | |||
3a6331693a | |||
56a28923ca | |||
fb8ed718ce | |||
45e5d89353 | |||
d3fe02fb3e | |||
7d2126e2b2 | |||
1f4ae2f63a | |||
b378066ebf | |||
2210d1053a | |||
bb6cd258f3 | |||
b5adb02eb8 | |||
8b702bcfa0 | |||
3e11011229 | |||
e5ba475147 | |||
3cd537ecf2 | |||
d7c7bc5d8e | |||
9191b9e736 | |||
ce340aba7a | |||
62cc14c93b | |||
a284e8c5eb | |||
834c6df9eb | |||
c74fe3c6e2 | |||
ca542f8677 | |||
d6e23b803b | |||
8b327fbc82 | |||
8432c2c9ea | |||
94fd5485b6 | |||
cd4680b422 | |||
7e4a800352 | |||
00bc097abb | |||
5c3a56b283 | |||
48ea805999 | |||
6b753b05d6 |
5
.github/ISSUE_TEMPLATE/01_bug-report.md
vendored
5
.github/ISSUE_TEMPLATE/01_bug-report.md
vendored
@ -7,6 +7,11 @@ assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
Thanks for reporting!
|
||||
First, in order to avoid duplicate Issues, please search to see if the problem you found has already been reported.
|
||||
-->
|
||||
|
||||
## 💡 Summary
|
||||
|
||||
<!-- Tell us what the bug is -->
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"extension": ["ts","js","cjs","mjs"],
|
||||
"require": "ts-node/register",
|
||||
"require": ["ts-node/register", "tsconfig-paths/register"],
|
||||
"slow": 1000,
|
||||
"timeout": 30000,
|
||||
"exit": true
|
||||
|
@ -1 +1 @@
|
||||
see [releases](https://github.com/syuilo/misskey/releases)
|
||||
see [releases](https://github.com/misskey-dev/misskey/releases)
|
||||
|
@ -8,7 +8,7 @@
|
||||
- 温度感高めで見てほしいものは責付いてください。
|
||||
|
||||
## Issues
|
||||
Feature suggestions and bug reports are filed in https://github.com/syuilo/misskey/issues .
|
||||
Feature suggestions and bug reports are filed in https://github.com/misskey-dev/misskey/issues .
|
||||
|
||||
* Please search existing issues to avoid duplication. If your issue is already filed, please add your reaction or comment to the existing one.
|
||||
* If you have multiple independent issues, please submit them separately.
|
||||
|
@ -22,7 +22,7 @@ RUN apk add --no-cache \
|
||||
vips-dev \
|
||||
vips
|
||||
|
||||
COPY package.json yarn.lock ./
|
||||
COPY package.json yarn.lock .yarnrc ./
|
||||
RUN yarn install
|
||||
COPY . ./
|
||||
RUN yarn build
|
||||
|
11
README.md
11
README.md
@ -4,8 +4,8 @@
|
||||
|
||||
<div align="center">
|
||||
|
||||
[](https://circleci.com/gh/syuilo/misskey)
|
||||
[](https://david-dm.org/syuilo/misskey)
|
||||
[](https://circleci.com/gh/misskey-dev/misskey)
|
||||
[](https://david-dm.org/misskey-dev/misskey)
|
||||
[](http://makeapullrequest.com)
|
||||
[](https://github.com/humanetech-community/awesome-humane-tech)
|
||||
|
||||
@ -22,11 +22,16 @@ Why don't you take a short break from the hustle and bustle of the city, and div
|
||||
|
||||
---
|
||||
|
||||
Do you have a question? Or are you experiencing trouble?
|
||||
Visit [our forum](https://forum.misskey.io/)!
|
||||
|
||||
---
|
||||
|
||||

|
||||
|
||||
:sparkles: Features
|
||||
----------------------------------------------------------------
|
||||
<a href="https://xn--931a.moe/"><img src="https://github.com/syuilo/misskey/blob/develop/assets/ai-orig.png?raw=true" align="right" height="320px"/></a>
|
||||
<a href="https://xn--931a.moe/"><img src="https://github.com/misskey-dev/misskey/blob/develop/assets/ai-orig.png?raw=true" align="right" height="320px"/></a>
|
||||
|
||||
<h3>Posting</h3>
|
||||
<p>
|
||||
|
@ -12,13 +12,13 @@ This guide describes how to install and setup Misskey with Docker.
|
||||
----------------------------------------------------------------
|
||||
1. Clone Misskey repository's master branch.
|
||||
|
||||
`git clone -b master git://github.com/syuilo/misskey.git`
|
||||
`git clone -b master git://github.com/misskey-dev/misskey.git`
|
||||
|
||||
2. Move to misskey directory.
|
||||
|
||||
`cd misskey`
|
||||
|
||||
3. Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest) tag.
|
||||
3. Checkout to the [latest release](https://github.com/misskey-dev/misskey/releases/latest) tag.
|
||||
|
||||
`git checkout master`
|
||||
|
||||
|
@ -13,13 +13,13 @@ Ce guide explique comment installer et configurer Misskey avec Docker.
|
||||
----------------------------------------------------------------
|
||||
1. Clone le dépôt de Misskey sur la branche master.
|
||||
|
||||
`git clone -b master git://github.com/syuilo/misskey.git`
|
||||
`git clone -b master git://github.com/misskey-dev/misskey.git`
|
||||
|
||||
2. Naviguez dans le dossier du dépôt.
|
||||
|
||||
`cd misskey`
|
||||
|
||||
3. Checkout sur le tag de la [dernière version](https://github.com/syuilo/misskey/releases/latest).
|
||||
3. Checkout sur le tag de la [dernière version](https://github.com/misskey-dev/misskey/releases/latest).
|
||||
|
||||
`git checkout master`
|
||||
|
||||
|
@ -12,13 +12,13 @@ Dockerを使ったMisskey構築方法
|
||||
----------------------------------------------------------------
|
||||
1. masterブランチからMisskeyレポジトリをクローン
|
||||
|
||||
`git clone -b master git://github.com/syuilo/misskey.git`
|
||||
`git clone -b master git://github.com/misskey-dev/misskey.git`
|
||||
|
||||
2. misskeyディレクトリに移動
|
||||
|
||||
`cd misskey`
|
||||
|
||||
3. [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認
|
||||
3. [最新のリリース](https://github.com/misskey-dev/misskey/releases/latest)を確認
|
||||
|
||||
`git checkout master`
|
||||
|
||||
|
@ -12,13 +12,13 @@ Docker 部署指南
|
||||
----------------------------------------------------------------
|
||||
1. 克隆 Misskey 项目的 master 分支。
|
||||
|
||||
`git clone -b master git://github.com/syuilo/misskey.git`
|
||||
`git clone -b master git://github.com/misskey-dev/misskey.git`
|
||||
|
||||
2. 进入 misskey 文件夹。
|
||||
|
||||
`cd misskey`
|
||||
|
||||
3. 检查 [最新发布版](https://github.com/syuilo/misskey/releases/latest) 标签。
|
||||
3. 检查 [最新发布版](https://github.com/misskey-dev/misskey/releases/latest) 标签。
|
||||
|
||||
`git checkout master`
|
||||
|
||||
|
@ -40,13 +40,13 @@ Please install and setup these softwares:
|
||||
|
||||
2. Clone the misskey repo from master branch.
|
||||
|
||||
`git clone -b master git://github.com/syuilo/misskey.git`
|
||||
`git clone -b master git://github.com/misskey-dev/misskey.git`
|
||||
|
||||
3. Navigate to misskey directory
|
||||
|
||||
`cd misskey`
|
||||
|
||||
4. Checkout to the [latest release](https://github.com/syuilo/misskey/releases/latest)
|
||||
4. Checkout to the [latest release](https://github.com/misskey-dev/misskey/releases/latest)
|
||||
|
||||
`git checkout master`
|
||||
|
||||
|
@ -41,13 +41,13 @@ Installez les paquets suivants :
|
||||
|
||||
2. Clonez la branche master du dépôt misskey.
|
||||
|
||||
`git clone -b master git://github.com/syuilo/misskey.git`
|
||||
`git clone -b master git://github.com/misskey-dev/misskey.git`
|
||||
|
||||
3. Accédez au dossier misskey.
|
||||
|
||||
`cd misskey`
|
||||
|
||||
4. Checkout sur le tag de la [version la plus récente](https://github.com/syuilo/misskey/releases/latest)
|
||||
4. Checkout sur le tag de la [version la plus récente](https://github.com/misskey-dev/misskey/releases/latest)
|
||||
|
||||
`git checkout master`
|
||||
|
||||
|
@ -42,13 +42,13 @@ adduser --disabled-password --disabled-login misskey
|
||||
|
||||
2. masterブランチからMisskeyレポジトリをクローン
|
||||
|
||||
`git clone -b master git://github.com/syuilo/misskey.git`
|
||||
`git clone -b master git://github.com/misskey-dev/misskey.git`
|
||||
|
||||
3. misskeyディレクトリに移動
|
||||
|
||||
`cd misskey`
|
||||
|
||||
4. [最新のリリース](https://github.com/syuilo/misskey/releases/latest)を確認
|
||||
4. [最新のリリース](https://github.com/misskey-dev/misskey/releases/latest)を確認
|
||||
|
||||
`git checkout master`
|
||||
|
||||
|
@ -40,13 +40,13 @@ adduser --disabled-password --disabled-login misskey
|
||||
|
||||
2. 克隆 Misskey 项目的 master 分支。
|
||||
|
||||
`git clone -b master git://github.com/syuilo/misskey.git`
|
||||
`git clone -b master git://github.com/misskey-dev/misskey.git`
|
||||
|
||||
3. 进入 misskey 文件夹。
|
||||
|
||||
`cd misskey`
|
||||
|
||||
4. 检查 [最新发布版](https://github.com/syuilo/misskey/releases/latest) 标签。
|
||||
4. 检查 [最新发布版](https://github.com/misskey-dev/misskey/releases/latest) 标签。
|
||||
|
||||
`git checkout master`
|
||||
|
||||
|
12
gulpfile.ts
12
gulpfile.ts
@ -4,7 +4,6 @@
|
||||
|
||||
import * as fs from 'fs';
|
||||
import * as gulp from 'gulp';
|
||||
import * as ts from 'gulp-typescript';
|
||||
import * as rimraf from 'rimraf';
|
||||
import * as replace from 'gulp-replace';
|
||||
const terser = require('gulp-terser');
|
||||
@ -13,16 +12,6 @@ const cssnano = require('gulp-cssnano');
|
||||
const locales: { [x: string]: any } = require('./locales');
|
||||
const meta = require('./package.json');
|
||||
|
||||
gulp.task('build:ts', () => {
|
||||
const tsProject = ts.createProject('./src/tsconfig.json');
|
||||
|
||||
return tsProject
|
||||
.src()
|
||||
.pipe(tsProject())
|
||||
.on('error', () => {})
|
||||
.pipe(gulp.dest('./built/'));
|
||||
});
|
||||
|
||||
gulp.task('build:copy:views', () =>
|
||||
gulp.src('./src/server/web/views/**/*').pipe(gulp.dest('./built/server/web/views'))
|
||||
);
|
||||
@ -78,7 +67,6 @@ gulp.task('cleanall', gulp.parallel('clean', cb =>
|
||||
));
|
||||
|
||||
gulp.task('build', gulp.parallel(
|
||||
'build:ts',
|
||||
'build:copy',
|
||||
));
|
||||
|
||||
|
@ -1,6 +1,6 @@
|
||||
# **DO NOT edit locale files** except `ja-JP.yml`.
|
||||
|
||||
When you add text to the ja-JP file (of syuilo/misskey), it will automatically be applied to other language files.
|
||||
When you add text to the ja-JP file (of misskey-dev/misskey), it will automatically be applied to other language files.
|
||||
Translations added in ja-JP file should contain the original Japanese strings.
|
||||
|
||||
Please see [Contribution guide](../CONTRIBUTING.md) for more information.
|
||||
|
@ -687,6 +687,7 @@ textColor: "文字"
|
||||
saveAs: "名前を付けて保存"
|
||||
advanced: "高度"
|
||||
value: "値"
|
||||
createdAt: "作成日時"
|
||||
updatedAt: "更新日時"
|
||||
saveConfirm: "保存しますか?"
|
||||
deleteConfirm: "削除しますか?"
|
||||
@ -710,6 +711,23 @@ typingUsers: "{users}が入力中"
|
||||
jumpToSpecifiedDate: "特定の日付にジャンプ"
|
||||
showingPastTimeline: "過去のタイムラインを表示しています"
|
||||
clear: "クリア"
|
||||
markAllAsRead: "全て既読にする"
|
||||
goBack: "戻る"
|
||||
unlikeConfirm: "いいね解除しますか?"
|
||||
fullView: "フルビュー"
|
||||
quitFullView: "フルビュー解除"
|
||||
addDescription: "説明を追加"
|
||||
userPagePinTip: "個々のノートのメニューから「ピン留め」を選択することで、ここにノートを表示しておくことができます。"
|
||||
notSpecifiedMentionWarning: "宛先に含まれていないメンションがあります"
|
||||
info: "情報"
|
||||
userInfo: "ユーザー情報"
|
||||
unknown: "不明"
|
||||
onlineStatus: "オンライン状態"
|
||||
hideOnlineStatus: "オンライン状態を隠す"
|
||||
hideOnlineStatusDescription: "オンライン状態を隠すと、検索などの一部機能において利便性が低下することがあります。"
|
||||
online: "オンライン"
|
||||
active: "アクティブ"
|
||||
offline: "オフライン"
|
||||
|
||||
_email:
|
||||
_follow:
|
||||
@ -878,6 +896,7 @@ _theme:
|
||||
install: "テーマのインストール"
|
||||
manage: "テーマの管理"
|
||||
code: "テーマコード"
|
||||
description: "説明"
|
||||
installed: "{name}をインストールしました"
|
||||
installedThemes: "インストールされたテーマ"
|
||||
builtinThemes: "標準のテーマ"
|
||||
|
@ -4,8 +4,8 @@ export class AddSomeUrls1557761316509 implements MigrationInterface {
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<any> {
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "ToSUrl" character varying(512)`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "repositoryUrl" character varying(512) NOT NULL DEFAULT 'https://github.com/syuilo/misskey'`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "feedbackUrl" character varying(512) DEFAULT 'https://github.com/syuilo/misskey/issues/new'`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "repositoryUrl" character varying(512) NOT NULL DEFAULT 'https://github.com/misskey-dev/misskey'`);
|
||||
await queryRunner.query(`ALTER TABLE "meta" ADD "feedbackUrl" character varying(512) DEFAULT 'https://github.com/misskey-dev/misskey/issues/new'`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<any> {
|
||||
|
16
migration/1618637372000-user-last-active-date.ts
Normal file
16
migration/1618637372000-user-last-active-date.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class userLastActiveDate1618637372000 implements MigrationInterface {
|
||||
name = 'userLastActiveDate1618637372000'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user" ADD "lastActiveDate" TIMESTAMP WITH TIME ZONE DEFAULT NULL`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_seoignmeoprigmkpodgrjmkpormg" ON "user" ("lastActiveDate") `);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`DROP INDEX "IDX_seoignmeoprigmkpodgrjmkpormg"`);
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "lastActiveDate"`);
|
||||
}
|
||||
|
||||
}
|
14
migration/1618639857000-user-hide-online-status.ts
Normal file
14
migration/1618639857000-user-hide-online-status.ts
Normal file
@ -0,0 +1,14 @@
|
||||
import {MigrationInterface, QueryRunner} from "typeorm";
|
||||
|
||||
export class userHideOnlineStatus1618639857000 implements MigrationInterface {
|
||||
name = 'userHideOnlineStatus1618639857000'
|
||||
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user" ADD "hideOnlineStatus" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "hideOnlineStatus"`);
|
||||
}
|
||||
|
||||
}
|
104
package.json
104
package.json
@ -1,11 +1,11 @@
|
||||
{
|
||||
"name": "misskey",
|
||||
"author": "syuilo <syuilotan@yahoo.co.jp>",
|
||||
"version": "12.75.0",
|
||||
"version": "12.77.0",
|
||||
"codename": "indigo",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/syuilo/misskey.git"
|
||||
"url": "https://github.com/misskey-dev/misskey.git"
|
||||
},
|
||||
"main": "./index.js",
|
||||
"private": true,
|
||||
@ -15,11 +15,13 @@
|
||||
"ormconfig": "node ./built/ormconfig.js",
|
||||
"migrate": "ts-node ./node_modules/typeorm/cli.js migration:run",
|
||||
"migrateandstart": "npm run migrate && npm run start",
|
||||
"build": "npm run build-webpack && npm run build-gulp",
|
||||
"build": "npm run build-webpack && npm run build-ts && npm run build-gulp",
|
||||
"build-webpack": "webpack",
|
||||
"build-ts": "tsc -p src/tsconfig.json || echo done. && tsc-alias -p src/tsconfig.json",
|
||||
"build-gulp": "gulp build",
|
||||
"watch": "concurrently \"npm:watch-*\"",
|
||||
"watch-webpack": "webpack --watch",
|
||||
"watch-ts": "tsc -w -p src/tsconfig.json && tsc-alias -w -p src/tsconfig.json",
|
||||
"watch-gulp": "gulp watch",
|
||||
"clean": "gulp clean",
|
||||
"cleanall": "gulp cleanall",
|
||||
@ -35,7 +37,7 @@
|
||||
"lodash": "^4.17.20"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/plugin-transform-runtime": "7.13.10",
|
||||
"@babel/plugin-transform-runtime": "7.13.15",
|
||||
"@elastic/elasticsearch": "7.11.0",
|
||||
"@fortawesome/fontawesome-svg-core": "1.2.35",
|
||||
"@fortawesome/free-brands-svg-icons": "5.15.3",
|
||||
@ -47,7 +49,7 @@
|
||||
"@koa/router": "9.0.1",
|
||||
"@sentry/browser": "5.29.2",
|
||||
"@sentry/tracing": "5.29.2",
|
||||
"@sinonjs/fake-timers": "7.0.2",
|
||||
"@sinonjs/fake-timers": "7.0.5",
|
||||
"@syuilo/aiscript": "0.11.1",
|
||||
"@types/bcryptjs": "2.4.2",
|
||||
"@types/bull": "3.15.0",
|
||||
@ -60,7 +62,7 @@
|
||||
"@types/gulp-replace": "0.0.31",
|
||||
"@types/is-url": "1.2.28",
|
||||
"@types/js-yaml": "4.0.0",
|
||||
"@types/jsdom": "16.2.7",
|
||||
"@types/jsdom": "16.2.10",
|
||||
"@types/jsonld": "1.5.5",
|
||||
"@types/katex": "0.11.0",
|
||||
"@types/koa": "2.13.1",
|
||||
@ -75,10 +77,10 @@
|
||||
"@types/koa__multer": "2.0.2",
|
||||
"@types/koa__router": "8.0.4",
|
||||
"@types/markdown-it": "12.0.1",
|
||||
"@types/matter-js": "0.14.10",
|
||||
"@types/mocha": "8.2.1",
|
||||
"@types/node": "14.14.35",
|
||||
"@types/node-fetch": "2.5.8",
|
||||
"@types/matter-js": "0.14.11",
|
||||
"@types/mocha": "8.2.2",
|
||||
"@types/node": "14.14.41",
|
||||
"@types/node-fetch": "2.5.10",
|
||||
"@types/nodemailer": "6.4.1",
|
||||
"@types/nprogress": "0.2.0",
|
||||
"@types/oauth": "0.9.1",
|
||||
@ -86,6 +88,7 @@
|
||||
"@types/parsimmon": "1.10.6",
|
||||
"@types/portscanner": "2.1.0",
|
||||
"@types/pug": "2.0.4",
|
||||
"@types/punycode": "2.1.0",
|
||||
"@types/qrcode": "1.4.0",
|
||||
"@types/random-seed": "0.3.3",
|
||||
"@types/ratelimiter": "3.4.1",
|
||||
@ -94,7 +97,7 @@
|
||||
"@types/request-stats": "3.0.0",
|
||||
"@types/rimraf": "3.0.0",
|
||||
"@types/seedrandom": "2.4.28",
|
||||
"@types/sharp": "0.27.1",
|
||||
"@types/sharp": "0.28.0",
|
||||
"@types/sinonjs__fake-timers": "6.0.2",
|
||||
"@types/speakeasy": "2.0.5",
|
||||
"@types/throttle-debounce": "2.1.0",
|
||||
@ -102,39 +105,39 @@
|
||||
"@types/tmp": "0.2.0",
|
||||
"@types/uuid": "8.3.0",
|
||||
"@types/web-push": "3.3.0",
|
||||
"@types/webpack": "4.41.26",
|
||||
"@types/webpack-stream": "3.2.11",
|
||||
"@types/webpack": "5.28.0",
|
||||
"@types/webpack-stream": "3.2.12",
|
||||
"@types/websocket": "1.0.2",
|
||||
"@types/ws": "7.4.0",
|
||||
"@typescript-eslint/parser": "4.18.0",
|
||||
"@vue/compiler-sfc": "3.0.7",
|
||||
"@types/ws": "7.4.1",
|
||||
"@typescript-eslint/parser": "4.22.0",
|
||||
"@vue/compiler-sfc": "3.0.11",
|
||||
"abort-controller": "3.0.0",
|
||||
"apexcharts": "3.26.0",
|
||||
"autobind-decorator": "2.4.0",
|
||||
"autosize": "4.0.2",
|
||||
"autwh": "0.1.0",
|
||||
"aws-sdk": "2.867.0",
|
||||
"aws-sdk": "2.887.0",
|
||||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "1.1.3",
|
||||
"broadcast-channel": "3.5.3",
|
||||
"bull": "3.21.1",
|
||||
"bull": "3.22.0",
|
||||
"cafy": "15.2.1",
|
||||
"cbor": "7.0.4",
|
||||
"cbor": "7.0.5",
|
||||
"chalk": "4.1.0",
|
||||
"chart.js": "2.9.4",
|
||||
"cli-highlight": "2.1.10",
|
||||
"commander": "4.1.1",
|
||||
"concurrently": "6.0.0",
|
||||
"cli-highlight": "2.1.11",
|
||||
"commander": "7.2.0",
|
||||
"concurrently": "6.0.2",
|
||||
"content-disposition": "0.5.3",
|
||||
"core-js": "3.9.1",
|
||||
"core-js": "3.10.1",
|
||||
"crc-32": "1.2.0",
|
||||
"css-loader": "5.1.3",
|
||||
"cssnano": "4.1.10",
|
||||
"css-loader": "5.2.1",
|
||||
"cssnano": "5.0.1",
|
||||
"dateformat": "4.5.1",
|
||||
"diskusage": "1.1.3",
|
||||
"escape-regexp": "0.0.1",
|
||||
"eslint": "7.22.0",
|
||||
"eslint-plugin-vue": "7.7.0",
|
||||
"eslint": "7.24.0",
|
||||
"eslint-plugin-vue": "7.9.0",
|
||||
"eventemitter3": "4.0.7",
|
||||
"feed": "4.2.2",
|
||||
"fibers": "5.0.0",
|
||||
@ -148,23 +151,22 @@
|
||||
"gulp-replace": "1.0.0",
|
||||
"gulp-terser": "2.0.1",
|
||||
"gulp-tslint": "8.1.4",
|
||||
"gulp-typescript": "6.0.0-alpha.1",
|
||||
"hard-source-webpack-plugin": "0.13.1",
|
||||
"html-minifier": "4.0.0",
|
||||
"http-proxy-agent": "4.0.1",
|
||||
"http-signature": "1.3.5",
|
||||
"https-proxy-agent": "5.0.0",
|
||||
"idb-keyval": "5.0.4",
|
||||
"idb-keyval": "5.0.5",
|
||||
"insert-text-at-cursor": "0.3.0",
|
||||
"is-root": "2.1.0",
|
||||
"is-svg": "4.3.1",
|
||||
"js-yaml": "4.0.0",
|
||||
"jsdom": "16.5.1",
|
||||
"js-yaml": "4.1.0",
|
||||
"jsdom": "16.5.3",
|
||||
"json5": "2.2.0",
|
||||
"json5-loader": "4.0.1",
|
||||
"jsonld": "4.0.1",
|
||||
"jsrsasign": "8.0.20",
|
||||
"katex": "0.13.0",
|
||||
"katex": "0.13.2",
|
||||
"koa": "2.13.1",
|
||||
"koa-bodyparser": "4.3.0",
|
||||
"koa-favicon": "2.1.0",
|
||||
@ -176,9 +178,10 @@
|
||||
"koa-views": "7.0.1",
|
||||
"langmap": "0.0.16",
|
||||
"lookup-dns-cache": "2.1.0",
|
||||
"markdown-it": "12.0.4",
|
||||
"markdown-it": "12.0.5",
|
||||
"markdown-it-anchor": "7.1.0",
|
||||
"matter-js": "0.16.1",
|
||||
"matter-js": "0.17.1",
|
||||
"mfm-js": "0.15.0",
|
||||
"mocha": "8.3.2",
|
||||
"moji": "0.5.1",
|
||||
"ms": "2.1.3",
|
||||
@ -189,24 +192,23 @@
|
||||
"object-assign-deep": "0.4.0",
|
||||
"os-utils": "0.0.14",
|
||||
"parse5": "6.0.1",
|
||||
"parsimmon": "1.16.0",
|
||||
"pg": "8.5.1",
|
||||
"pg": "8.6.0",
|
||||
"portscanner": "2.2.0",
|
||||
"postcss": "8.2.8",
|
||||
"postcss": "8.2.10",
|
||||
"postcss-loader": "5.2.0",
|
||||
"prismjs": "1.23.0",
|
||||
"probe-image-size": "7.0.1",
|
||||
"probe-image-size": "7.1.0",
|
||||
"promise-limit": "2.7.0",
|
||||
"promise-sequential": "1.1.1",
|
||||
"pug": "3.0.2",
|
||||
"punycode": "2.1.1",
|
||||
"pureimage": "0.2.7",
|
||||
"pureimage": "0.3.2",
|
||||
"qrcode": "1.4.4",
|
||||
"random-seed": "0.3.0",
|
||||
"ratelimiter": "3.4.1",
|
||||
"re2": "1.15.9",
|
||||
"reconnecting-websocket": "4.4.0",
|
||||
"redis": "3.0.2",
|
||||
"redis": "3.1.1",
|
||||
"redis-lock": "0.1.4",
|
||||
"reflect-metadata": "0.1.13",
|
||||
"regenerator-runtime": "0.13.7",
|
||||
@ -219,42 +221,44 @@
|
||||
"sass": "1.32.8",
|
||||
"sass-loader": "11.0.1",
|
||||
"seedrandom": "3.0.5",
|
||||
"sharp": "0.27.2",
|
||||
"sharp": "0.28.1",
|
||||
"speakeasy": "2.0.0",
|
||||
"stringz": "2.1.0",
|
||||
"style-loader": "2.0.0",
|
||||
"summaly": "2.4.0",
|
||||
"syslog-pro": "1.0.0",
|
||||
"systeminformation": "5.6.7",
|
||||
"systeminformation": "5.6.12",
|
||||
"syuilo-password-strength": "0.0.1",
|
||||
"textarea-caret": "3.1.0",
|
||||
"three": "0.117.1",
|
||||
"throttle-debounce": "3.0.1",
|
||||
"tinycolor2": "1.4.2",
|
||||
"tmp": "0.2.1",
|
||||
"ts-loader": "8.0.18",
|
||||
"ts-loader": "8.1.0",
|
||||
"ts-node": "9.1.1",
|
||||
"tsc-alias": "1.2.9",
|
||||
"tsconfig-paths": "3.9.0",
|
||||
"tslint": "6.1.3",
|
||||
"tslint-sonarts": "1.9.0",
|
||||
"typeorm": "0.2.31",
|
||||
"typescript": "4.2.3",
|
||||
"typeorm": "0.2.32",
|
||||
"typescript": "4.2.4",
|
||||
"ulid": "2.3.0",
|
||||
"url-loader": "4.1.1",
|
||||
"uuid": "8.3.2",
|
||||
"v-debounce": "0.1.2",
|
||||
"vanilla-tilt": "1.7.0",
|
||||
"vue": "3.0.7",
|
||||
"vue": "3.0.11",
|
||||
"vue-color": "2.8.1",
|
||||
"vue-json-pretty": "1.7.1",
|
||||
"vue-loader": "16.1.2",
|
||||
"vue-prism-editor": "2.0.0-alpha.2",
|
||||
"vue-router": "4.0.5",
|
||||
"vue-style-loader": "4.1.3",
|
||||
"vue-svg-loader": "0.17.0-beta.2",
|
||||
"vuedraggable": "4.0.1",
|
||||
"web-push": "3.4.4",
|
||||
"webpack": "5.26.3",
|
||||
"webpack-cli": "4.5.0",
|
||||
"websocket": "1.0.33",
|
||||
"webpack": "5.33.2",
|
||||
"webpack-cli": "4.6.0",
|
||||
"websocket": "1.0.34",
|
||||
"ws": "7.4.4",
|
||||
"xev": "2.0.1"
|
||||
},
|
||||
|
3
src/@types/meta.json.d.ts
vendored
3
src/@types/meta.json.d.ts
vendored
@ -1,3 +0,0 @@
|
||||
declare module '*/meta.json' {
|
||||
const version: string;
|
||||
}
|
@ -1,5 +1,7 @@
|
||||
import * as program from 'commander';
|
||||
import config from './config';
|
||||
import { Command } from 'commander';
|
||||
import config from '@/config';
|
||||
|
||||
const program = new Command();
|
||||
|
||||
program
|
||||
.version(config.version)
|
||||
|
@ -6,13 +6,13 @@ import * as isRoot from 'is-root';
|
||||
import { getConnection } from 'typeorm';
|
||||
|
||||
import Logger from '../services/logger';
|
||||
import loadConfig from '../config/load';
|
||||
import { Config } from '../config/types';
|
||||
import loadConfig from '@/config/load';
|
||||
import { Config } from '@/config/types';
|
||||
import { lessThan } from '../prelude/array';
|
||||
import { program } from '../argv';
|
||||
import { showMachineInfo } from '../misc/show-machine-info';
|
||||
import { showMachineInfo } from '@/misc/show-machine-info';
|
||||
import { initDb } from '../db/postgre';
|
||||
import * as meta from '../meta.json';
|
||||
const meta = require('../meta.json');
|
||||
|
||||
const logger = new Logger('core', 'cyan');
|
||||
const bootLogger = logger.createSubLogger('boot', 'magenta', false);
|
||||
|
@ -1,7 +1,7 @@
|
||||
import { reactive } from 'vue';
|
||||
import { apiUrl } from '@/config';
|
||||
import { waiting } from '@/os';
|
||||
import { unisonReload } from '@/scripts/unison-reload';
|
||||
import { apiUrl } from '@client/config';
|
||||
import { waiting } from '@client/os';
|
||||
import { unisonReload } from '@client/scripts/unison-reload';
|
||||
|
||||
// TODO: 他のタブと永続化されたstateを同期
|
||||
|
||||
|
@ -8,31 +8,27 @@
|
||||
</template>
|
||||
</I18n>
|
||||
</template>
|
||||
<div class="dpvffvvy">
|
||||
<div class="dpvffvvy _monolithic_">
|
||||
<div class="_section">
|
||||
<div class="_content">
|
||||
<MkTextarea v-model:value="comment">
|
||||
<span>{{ $ts.details }}</span>
|
||||
<template #desc>{{ $ts.fillAbuseReportDescription }}</template>
|
||||
</MkTextarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="_section">
|
||||
<div class="_content">
|
||||
<MkButton @click="send" primary full :disabled="comment.length === 0">{{ $ts.send }}</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</XWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { faExclamationCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import XWindow from '@/components/ui/window.vue';
|
||||
import MkTextarea from '@/components/ui/textarea.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import * as os from '@/os';
|
||||
import XWindow from '@client/components/ui/window.vue';
|
||||
import MkTextarea from '@client/components/ui/textarea.vue';
|
||||
import MkButton from '@client/components/ui/button.vue';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -80,6 +76,6 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.dpvffvvy {
|
||||
--section-padding: 16px;
|
||||
--root-margin: 16px;
|
||||
}
|
||||
</style>
|
||||
|
@ -36,7 +36,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as tinycolor from 'tinycolor2';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
data() {
|
||||
|
@ -29,12 +29,12 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { emojilist } from '../../misc/emojilist';
|
||||
import contains from '@/scripts/contains';
|
||||
import { twemojiSvgBase } from '../../misc/twemoji-base';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import { acct } from '@/filters/user';
|
||||
import * as os from '@/os';
|
||||
import { emojilist } from '@/misc/emojilist';
|
||||
import contains from '@client/scripts/contains';
|
||||
import { twemojiSvgBase } from '@/misc/twemoji-base';
|
||||
import { getStaticImageUrl } from '@client/scripts/get-static-image-url';
|
||||
import { acct } from '@client/filters/user';
|
||||
import * as os from '@client/os';
|
||||
|
||||
type EmojiDef = {
|
||||
emoji: string;
|
||||
|
@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<div v-for="user in us" :key="user.id" style="display:inline-block;width:32px;height:32px;margin-right:8px;">
|
||||
<MkAvatar :user="user" style="width:32px;height:32px;"/>
|
||||
<MkAvatar :user="user" style="width:32px;height:32px;" :show-indicator="true"/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -21,7 +21,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faSpinner, faPlus, faMinus, } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -123,7 +123,7 @@ export default defineComponent({
|
||||
|
||||
> footer {
|
||||
padding: 12px 16px;
|
||||
border-top: solid 1px var(--divider);
|
||||
border-top: solid 0.5px var(--divider);
|
||||
|
||||
> span {
|
||||
opacity: 0.7;
|
||||
|
@ -18,17 +18,20 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
noGap: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
focus() {
|
||||
this.$slots.default[0].elm.focus();
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
const getDateText = (time: string) => {
|
||||
getDateText(time: string) {
|
||||
const date = new Date(time).getDate();
|
||||
const month = new Date(time).getMonth() + 1;
|
||||
return this.$t('monthAndDay', {
|
||||
@ -36,15 +39,19 @@ export default defineComponent({
|
||||
day: date.toString()
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.items.length === 0) return;
|
||||
|
||||
return h(this.$store.state.animation ? TransitionGroup : 'div', this.$store.state.animation ? {
|
||||
class: 'sqadhkmv _list_',
|
||||
class: 'sqadhkmv' + (this.noGap ? ' noGap _block' : ''),
|
||||
name: 'list',
|
||||
tag: 'div',
|
||||
'data-direction': this.direction,
|
||||
'data-reversed': this.reversed ? 'true' : 'false',
|
||||
} : {
|
||||
class: 'sqadhkmv _list_',
|
||||
class: 'sqadhkmv' + (this.noGap ? ' noGap _block' : ''),
|
||||
}, this.items.map((item, i) => {
|
||||
const el = this.$slots.default({
|
||||
item: item
|
||||
@ -70,10 +77,10 @@ export default defineComponent({
|
||||
class: 'icon',
|
||||
icon: faAngleUp,
|
||||
}),
|
||||
getDateText(item.createdAt)
|
||||
this.getDateText(item.createdAt)
|
||||
]),
|
||||
h('span', [
|
||||
getDateText(this.items[i + 1].createdAt),
|
||||
this.getDateText(this.items[i + 1].createdAt),
|
||||
h(FontAwesomeIcon, {
|
||||
class: 'icon',
|
||||
icon: faAngleDown,
|
||||
@ -117,11 +124,7 @@ export default defineComponent({
|
||||
transform: translateY(-64px);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<style lang="scss">
|
||||
.sqadhkmv {
|
||||
> .separator {
|
||||
text-align: center;
|
||||
|
||||
@ -154,5 +157,26 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.noGap {
|
||||
> * {
|
||||
margin: 0 !important;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
|
||||
&:not(:last-child) {
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
._inContainer_ .sqadhkmv > * {
|
||||
margin: 0 !important;
|
||||
border: none;
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
</style>
|
||||
|
@ -40,10 +40,10 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { faSpinner, faInfoCircle, faExclamationTriangle, faCheck } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faTimesCircle, faQuestionCircle } from '@fortawesome/free-regular-svg-icons';
|
||||
import MkModal from '@/components/ui/modal.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkInput from '@/components/ui/input.vue';
|
||||
import MkSelect from '@/components/ui/select.vue';
|
||||
import MkModal from '@client/components/ui/modal.vue';
|
||||
import MkButton from '@client/components/ui/button.vue';
|
||||
import MkInput from '@client/components/ui/input.vue';
|
||||
import MkSelect from '@client/components/ui/select.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -26,8 +26,8 @@ import {
|
||||
faFileArchive,
|
||||
faFilm
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import ImgWithBlurhash from './img-with-blurhash.vue';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
import ImgWithBlurhash from '@client/components/img-with-blurhash.vue';
|
||||
import { ColdDeviceStorage } from '@client/store';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -20,8 +20,8 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XDrive from './drive.vue';
|
||||
import XModalWindow from '@/components/ui/modal-window.vue';
|
||||
import number from '@/filters/number';
|
||||
import XModalWindow from '@client/components/ui/modal-window.vue';
|
||||
import number from '@client/filters/number';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XDrive from './drive.vue';
|
||||
import XWindow from '@/components/ui/window.vue';
|
||||
import XWindow from '@client/components/ui/window.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -34,10 +34,10 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
|
||||
import { faDownload, faLink, faICursor, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import MkDriveFileThumbnail from './drive-file-thumbnail.vue';
|
||||
import bytes from '@/filters/bytes';
|
||||
import * as os from '@/os';
|
||||
import bytes from '@client/filters/bytes';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -330,8 +330,8 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> .thumbnail {
|
||||
width: 128px;
|
||||
height: 128px;
|
||||
width: 110px;
|
||||
height: 110px;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faFolder, faFolderOpen, faTrashAlt, faWindowRestore } from '@fortawesome/free-regular-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
import { faICursor } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default defineComponent({
|
||||
|
@ -15,7 +15,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faCloud } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -11,7 +11,7 @@
|
||||
<span class="folder current" v-if="folder != null">{{ folder.name }}</span>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="main _section" :class="{ uploading: uploadings.length > 0, fetching }"
|
||||
<div class="main" :class="{ uploading: uploadings.length > 0, fetching }"
|
||||
ref="main"
|
||||
@dragover.prevent.stop="onDragover"
|
||||
@dragenter="onDragenter"
|
||||
@ -52,7 +52,7 @@ import XNavFolder from './drive.nav-folder.vue';
|
||||
import XFolder from './drive.folder.vue';
|
||||
import XFile from './drive.file.vue';
|
||||
import MkButton from './ui/button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
import { faTrashAlt } from '@fortawesome/free-regular-svg-icons';
|
||||
|
||||
export default defineComponent({
|
||||
@ -704,6 +704,7 @@ export default defineComponent({
|
||||
> .main {
|
||||
flex: 1;
|
||||
overflow: auto;
|
||||
padding: var(--margin);
|
||||
|
||||
&, * {
|
||||
user-select: none;
|
||||
@ -735,7 +736,7 @@ export default defineComponent({
|
||||
> .folder,
|
||||
> .file {
|
||||
flex-grow: 1;
|
||||
width: 144px;
|
||||
width: 128px;
|
||||
margin: 4px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
@ -743,7 +744,7 @@ export default defineComponent({
|
||||
> .padding {
|
||||
flex-grow: 1;
|
||||
pointer-events: none;
|
||||
width: 144px + 8px;
|
||||
width: 128px + 8px;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -6,8 +6,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import MkModal from '@/components/ui/modal.vue';
|
||||
import MkEmojiPicker from '@/components/emoji-picker.vue';
|
||||
import MkModal from '@client/components/ui/modal.vue';
|
||||
import MkEmojiPicker from '@client/components/emoji-picker.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -123,7 +123,7 @@ export default defineComponent({
|
||||
> .index {
|
||||
min-height: var(--height);
|
||||
position: relative;
|
||||
border-bottom: solid 1px var(--divider);
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
|
||||
> .arrow {
|
||||
position: absolute;
|
||||
@ -181,7 +181,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
&.result {
|
||||
border-bottom: solid 1px var(--divider);
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
|
@ -13,8 +13,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import MkWindow from '@/components/ui/window.vue';
|
||||
import MkEmojiPicker from '@/components/emoji-picker.vue';
|
||||
import MkWindow from '@client/components/ui/window.vue';
|
||||
import MkEmojiPicker from '@client/components/emoji-picker.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -119,7 +119,7 @@ export default defineComponent({
|
||||
> .index {
|
||||
min-height: var(--height);
|
||||
position: relative;
|
||||
border-bottom: solid 1px var(--divider);
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
|
||||
> .arrow {
|
||||
position: absolute;
|
||||
@ -177,7 +177,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
&.result {
|
||||
border-bottom: solid 1px var(--divider);
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
|
@ -18,7 +18,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { faChevronUp, faChevronDown } from '@fortawesome/free-solid-svg-icons';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import { getStaticImageUrl } from '@client/scripts/get-static-image-url';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -74,15 +74,15 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, markRaw } from 'vue';
|
||||
import { emojilist } from '../../misc/emojilist';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import { emojilist } from '@/misc/emojilist';
|
||||
import { getStaticImageUrl } from '@client/scripts/get-static-image-url';
|
||||
import { faAsterisk, faLeaf, faUtensils, faFutbol, faCity, faDice, faGlobe, faClock, faUser, faChevronDown, faShapes, faBicycle, faHashtag } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faHeart, faFlag, faLaugh } from '@fortawesome/free-regular-svg-icons';
|
||||
import Particle from '@/components/particle.vue';
|
||||
import * as os from '@/os';
|
||||
import { isDeviceTouch } from '@/scripts/is-device-touch';
|
||||
import { isMobile } from '@/scripts/is-mobile';
|
||||
import { emojiCategories } from '@/instance';
|
||||
import Particle from '@client/components/particle.vue';
|
||||
import * as os from '@client/os';
|
||||
import { isDeviceTouch } from '@client/scripts/is-device-touch';
|
||||
import { isMobile } from '@client/scripts/is-mobile';
|
||||
import { emojiCategories } from '@client/instance';
|
||||
import XSection from './emoji-picker.section.vue';
|
||||
|
||||
export default defineComponent({
|
||||
@ -402,7 +402,7 @@ export default defineComponent({
|
||||
> .tab {
|
||||
flex: 1;
|
||||
height: 38px;
|
||||
border-top: solid 1px var(--divider);
|
||||
border-top: solid 0.5px var(--divider);
|
||||
|
||||
&.active {
|
||||
border-top: solid 1px var(--accent);
|
||||
@ -425,7 +425,7 @@ export default defineComponent({
|
||||
> div {
|
||||
&:not(.index) {
|
||||
padding: 4px 0 8px 0;
|
||||
border-top: solid 1px var(--divider);
|
||||
border-top: solid 0.5px var(--divider);
|
||||
}
|
||||
|
||||
> header {
|
||||
@ -492,7 +492,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
&.result {
|
||||
border-bottom: solid 1px var(--divider);
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -7,7 +7,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faFileImage } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -30,7 +30,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faSpinner, faPlus, faMinus, faHourglassHalf } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -48,7 +48,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import XModalWindow from '@/components/ui/modal-window.vue';
|
||||
import XModalWindow from '@client/components/ui/modal-window.vue';
|
||||
import FormBase from './form/base.vue';
|
||||
import FormInput from './form/input.vue';
|
||||
import FormTextarea from './form/textarea.vue';
|
||||
|
@ -20,12 +20,19 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.rbusrurv {
|
||||
line-height: 1.4em;
|
||||
// 他のCSSからも参照されるので消さないように
|
||||
--formXPadding: 32px;
|
||||
--formYPadding: 32px;
|
||||
|
||||
font-size: 95%;
|
||||
line-height: 1.3em;
|
||||
background: var(--bg);
|
||||
padding: 32px;
|
||||
padding: var(--formYPadding) var(--formXPadding);
|
||||
max-width: 750px;
|
||||
margin: 0 auto;
|
||||
|
||||
&:not(.wide).max-width_400px {
|
||||
padding: 32px 0;
|
||||
--formXPadding: 0px;
|
||||
|
||||
> ::v-deep(*) {
|
||||
._formPanel {
|
||||
@ -36,16 +43,16 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
._form_group {
|
||||
> * {
|
||||
&:not(:first-child) {
|
||||
> *:not(._formNoConcat) {
|
||||
&:not(:last-child):not(._formNoConcatPrev) {
|
||||
&._formPanel, ._formPanel {
|
||||
border-top: none;
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
&:not(:first-child):not(._formNoConcatNext) {
|
||||
&._formPanel, ._formPanel {
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
border-top: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,32 +1,48 @@
|
||||
._formPanel {
|
||||
background: var(--panel);
|
||||
border-radius: var(--radius);
|
||||
transition: background 0.2s ease;
|
||||
|
||||
&._formClickable {
|
||||
&:hover {
|
||||
//background: var(--panelHighlight);
|
||||
}
|
||||
|
||||
&:active {
|
||||
background: var(--panelHighlight);
|
||||
transition: background 0s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
._formLabel,
|
||||
._formCaption {
|
||||
font-size: 80%;
|
||||
color: var(--fgTransparentWeak);
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
._formLabel {
|
||||
font-size: 80%;
|
||||
padding: 0 16px 8px 16px;
|
||||
opacity: 0.8;
|
||||
position: sticky;
|
||||
top: var(--stickyTop, 0px);
|
||||
z-index: 2;
|
||||
margin: -8px calc(var(--formXPadding) * -1) 0 calc(var(--formXPadding) * -1);
|
||||
padding: 8px calc(16px + var(--formXPadding)) 8px calc(16px + var(--formXPadding));
|
||||
background: var(--X17);
|
||||
-webkit-backdrop-filter: blur(10px);
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
._themeChanging_ ._formLabel {
|
||||
transition: none !important;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
._formCaption {
|
||||
font-size: 80%;
|
||||
padding: 8px 16px 0 16px;
|
||||
opacity: 0.8;
|
||||
|
||||
&:empty {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
._formItem {
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="vrtktovg _formItem" v-size="{ max: [500] }">
|
||||
<div class="vrtktovg _formItem _formNoConcat" v-size="{ max: [500] }" v-sticky-container>
|
||||
<div class="_formLabel"><slot name="label"></slot></div>
|
||||
<div class="main _form_group">
|
||||
<div class="main _form_group" ref="child">
|
||||
<slot></slot>
|
||||
</div>
|
||||
<div class="_formCaption"><slot name="caption"></slot></div>
|
||||
@ -9,33 +9,69 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent, onMounted, ref } from 'vue';
|
||||
|
||||
export default defineComponent({
|
||||
setup(props, context) {
|
||||
const child = ref<HTMLElement | null>(null);
|
||||
|
||||
const scanChild = () => {
|
||||
if (child.value == null) return;
|
||||
const els = Array.from(child.value.children);
|
||||
for (let i = 0; i < els.length; i++) {
|
||||
const el = els[i];
|
||||
if (el.classList.contains('_formNoConcat')) {
|
||||
if (els[i - 1]) els[i - 1].classList.add('_formNoConcatPrev');
|
||||
if (els[i + 1]) els[i + 1].classList.add('_formNoConcatNext');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
scanChild();
|
||||
|
||||
const observer = new MutationObserver(records => {
|
||||
scanChild();
|
||||
});
|
||||
|
||||
observer.observe(child.value, {
|
||||
childList: true,
|
||||
subtree: false,
|
||||
attributes: false,
|
||||
characterData: false,
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
child
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.vrtktovg {
|
||||
> .main {
|
||||
> ::v-deep(*) {
|
||||
> ::v-deep(*):not(._formNoConcat) {
|
||||
&:not(._formNoConcatNext) {
|
||||
margin: 0;
|
||||
|
||||
&:not(:first-child) {
|
||||
&._formPanel, ._formPanel {
|
||||
border-top: none;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:last-child) {
|
||||
&:not(:last-child):not(._formNoConcatPrev) {
|
||||
&._formPanel, ._formPanel {
|
||||
border-bottom: solid 0.5px var(--divider);
|
||||
border-bottom-left-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(:first-child):not(._formNoConcatNext) {
|
||||
&._formPanel, ._formPanel {
|
||||
border-top: none;
|
||||
border-top-left-radius: 0;
|
||||
border-top-right-radius: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
49
src/client/components/form/info.vue
Normal file
49
src/client/components/form/info.vue
Normal file
@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="fzenkabp _formItem">
|
||||
<div class="_formPanel" :class="{ warn }">
|
||||
<i v-if="warn"><Fa :icon="faExclamationTriangle"/></i>
|
||||
<i v-else><Fa :icon="faInfoCircle"/></i>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faInfoCircle, faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
warn: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
faInfoCircle, faExclamationTriangle
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fzenkabp {
|
||||
> div {
|
||||
padding: 14px 16px;
|
||||
font-size: 90%;
|
||||
background: var(--infoBg);
|
||||
color: var(--infoFg);
|
||||
|
||||
&.warn {
|
||||
background: var(--infoWarnBg);
|
||||
color: var(--infoWarnFg);
|
||||
}
|
||||
|
||||
> i {
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -215,7 +215,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> .input {
|
||||
$height: 52px;
|
||||
$height: 48px;
|
||||
position: relative;
|
||||
|
||||
> input {
|
||||
|
@ -22,9 +22,17 @@ export default defineComponent({
|
||||
align-items: center;
|
||||
padding: 14px 16px;
|
||||
|
||||
> .key {
|
||||
margin-right: 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
> .value {
|
||||
margin-left: auto;
|
||||
opacity: 0.7;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -66,6 +66,7 @@ export default defineComponent({
|
||||
|
||||
&.active {
|
||||
color: var(--accent);
|
||||
background: var(--panelHighlight);
|
||||
}
|
||||
|
||||
> .icon {
|
||||
|
102
src/client/components/form/object-view.vue
Normal file
102
src/client/components/form/object-view.vue
Normal file
@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<FormGroup class="_formItem">
|
||||
<template #label><slot></slot></template>
|
||||
<div class="drooglns _formItem" :class="{ tall }">
|
||||
<div class="input _formPanel">
|
||||
<textarea class="_monospace"
|
||||
v-model="v"
|
||||
readonly
|
||||
:spellcheck="false"
|
||||
></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<template #caption><slot name="desc"></slot></template>
|
||||
</FormGroup>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, ref, toRefs, watch } from 'vue';
|
||||
import * as JSON5 from 'json5';
|
||||
import './form.scss';
|
||||
import FormGroup from './group.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormGroup,
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
required: false
|
||||
},
|
||||
tall: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
pre: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
manualSave: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
setup(props, context) {
|
||||
const { value } = toRefs(props);
|
||||
const v = ref('');
|
||||
|
||||
watch(() => value, newValue => {
|
||||
v.value = JSON5.stringify(newValue.value, null, '\t');
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
|
||||
return {
|
||||
v,
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.drooglns {
|
||||
position: relative;
|
||||
|
||||
> .input {
|
||||
position: relative;
|
||||
|
||||
> textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-width: 100%;
|
||||
max-width: 100%;
|
||||
min-height: 130px;
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
box-sizing: border-box;
|
||||
font: inherit;
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
color: var(--fg);
|
||||
tab-size: 2;
|
||||
white-space: pre;
|
||||
}
|
||||
}
|
||||
|
||||
&.tall {
|
||||
> .input {
|
||||
> textarea {
|
||||
min-height: 200px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -16,7 +16,7 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import FormButton from './button.vue';
|
||||
import FormGroup from './group.vue';
|
||||
import paging from '@/scripts/paging';
|
||||
import paging from '@client/scripts/paging';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent, h } from 'vue';
|
||||
import MkRadio from '@/components/ui/radio.vue';
|
||||
import MkRadio from '@client/components/ui/radio.vue';
|
||||
import './form.scss';
|
||||
|
||||
export default defineComponent({
|
||||
@ -69,8 +69,8 @@ export default defineComponent({
|
||||
display: inline-block;
|
||||
vertical-align: bottom;
|
||||
position: relative;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin-right: 8px;
|
||||
background: none;
|
||||
border: 2px solid var(--inputBorder);
|
||||
|
@ -69,7 +69,7 @@ export default defineComponent({
|
||||
position: relative;
|
||||
|
||||
> .main {
|
||||
padding: 24px 16px;
|
||||
padding: 22px 16px;
|
||||
|
||||
> input {
|
||||
display: block;
|
||||
|
@ -97,7 +97,7 @@ export default defineComponent({
|
||||
font: inherit;
|
||||
font-weight: normal;
|
||||
font-size: 1em;
|
||||
height: 52px;
|
||||
height: 48px;
|
||||
background: none;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
|
92
src/client/components/form/suspense.vue
Normal file
92
src/client/components/form/suspense.vue
Normal file
@ -0,0 +1,92 @@
|
||||
<template>
|
||||
<transition name="fade" mode="out-in">
|
||||
<div class="_formItem" v-if="pending">
|
||||
<div class="_formPanel">
|
||||
<MkLoading/>
|
||||
</div>
|
||||
</div>
|
||||
<FormGroup v-else-if="resolved" class="_formItem">
|
||||
<slot :result="result"></slot>
|
||||
</FormGroup>
|
||||
<div class="_formItem" v-else>
|
||||
<div class="_formPanel">
|
||||
error!
|
||||
<button @click="retry">retry</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType, ref, watch } from 'vue';
|
||||
import './form.scss';
|
||||
import FormGroup from './group.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
FormGroup,
|
||||
},
|
||||
|
||||
props: {
|
||||
p: {
|
||||
type: Function as PropType<() => Promise<any>>,
|
||||
required: true,
|
||||
}
|
||||
},
|
||||
|
||||
setup(props, context) {
|
||||
const pending = ref(true);
|
||||
const resolved = ref(false);
|
||||
const rejected = ref(false);
|
||||
const result = ref(null);
|
||||
|
||||
const process = () => {
|
||||
if (props.p == null) {
|
||||
return;
|
||||
}
|
||||
const promise = props.p();
|
||||
pending.value = true;
|
||||
resolved.value = false;
|
||||
rejected.value = false;
|
||||
promise.then((_result) => {
|
||||
pending.value = false;
|
||||
resolved.value = true;
|
||||
result.value = _result;
|
||||
});
|
||||
promise.catch(() => {
|
||||
pending.value = false;
|
||||
rejected.value = true;
|
||||
});
|
||||
};
|
||||
|
||||
watch(() => props.p, () => {
|
||||
process();
|
||||
}, {
|
||||
immediate: true
|
||||
});
|
||||
|
||||
const retry = () => {
|
||||
process();
|
||||
};
|
||||
|
||||
return {
|
||||
pending,
|
||||
resolved,
|
||||
rejected,
|
||||
result,
|
||||
retry,
|
||||
};
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.125s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
@ -57,7 +57,7 @@ export default defineComponent({
|
||||
> .main {
|
||||
position: relative;
|
||||
display: flex;
|
||||
padding: 16px;
|
||||
padding: 14px 16px;
|
||||
cursor: pointer;
|
||||
|
||||
> * {
|
||||
|
@ -6,7 +6,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as katex from 'katex';import * as os from '@/os';
|
||||
import * as katex from 'katex';import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -3,7 +3,7 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, defineAsyncComponent } from 'vue';import * as os from '@/os';
|
||||
import { defineComponent, defineAsyncComponent } from 'vue';import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -7,12 +7,12 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExpandAlt, faColumns, faExternalLinkAlt, faLink, faWindowMaximize } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import { router } from '@/router';
|
||||
import { url } from '@/config';
|
||||
import { popout } from '@/scripts/popout';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
import * as os from '@client/os';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { router } from '@client/router';
|
||||
import { url } from '@client/config';
|
||||
import { popout } from '@client/scripts/popout';
|
||||
import { ColdDeviceStorage } from '@client/store';
|
||||
|
||||
export default defineComponent({
|
||||
inject: {
|
||||
@ -93,6 +93,10 @@ export default defineComponent({
|
||||
os.pageWindow(this.to);
|
||||
},
|
||||
|
||||
modalWindow() {
|
||||
os.modalPageWindow(this.to);
|
||||
},
|
||||
|
||||
popout() {
|
||||
popout(this.to);
|
||||
},
|
||||
@ -111,6 +115,8 @@ export default defineComponent({
|
||||
if (this.behavior) {
|
||||
if (this.behavior === 'window') {
|
||||
return this.window();
|
||||
} else if (this.behavior === 'modalWindow') {
|
||||
return this.modalWindow();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<span class="mk-acct" v-once>
|
||||
<span class="mk-acct">
|
||||
<span class="name">@{{ user.username }}</span>
|
||||
<span class="host" v-if="user.host || detail || $store.state.showFullAcct">@{{ user.host || host }}</span>
|
||||
</span>
|
||||
@ -7,11 +7,20 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { toUnicode } from 'punycode';
|
||||
import { host } from '@/config';
|
||||
import { toUnicode } from 'punycode/';
|
||||
import { host } from '@client/config';
|
||||
|
||||
export default defineComponent({
|
||||
props: ['user', 'detail'],
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
detail: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
host: toUnicode(host),
|
||||
|
@ -1,19 +1,25 @@
|
||||
<template>
|
||||
<span class="eiwwqkts _noSelect" :class="{ cat }" :title="acct(user)" v-if="disableLink" v-user-preview="disablePreview ? undefined : user.id" @click="onClick">
|
||||
<img class="inner" :src="url" decoding="async"/>
|
||||
<MkUserOnlineIndicator v-if="showIndicator" class="indicator" :user="user"/>
|
||||
</span>
|
||||
<MkA class="eiwwqkts _noSelect" :class="{ cat }" :to="userPage(user)" :title="acct(user)" :target="target" v-else v-user-preview="disablePreview ? undefined : user.id">
|
||||
<img class="inner" :src="url" decoding="async"/>
|
||||
<MkUserOnlineIndicator v-if="showIndicator" class="indicator" :user="user"/>
|
||||
</MkA>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
|
||||
import { acct, userPage } from '@/filters/user';
|
||||
import { getStaticImageUrl } from '@client/scripts/get-static-image-url';
|
||||
import { extractAvgColorFromBlurhash } from '@client/scripts/extract-avg-color-from-blurhash';
|
||||
import { acct, userPage } from '@client/filters/user';
|
||||
import MkUserOnlineIndicator from '@client/components/user-online-indicator.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkUserOnlineIndicator
|
||||
},
|
||||
props: {
|
||||
user: {
|
||||
type: Object,
|
||||
@ -30,6 +36,10 @@ export default defineComponent({
|
||||
disablePreview: {
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
showIndicator: {
|
||||
required: false,
|
||||
default: false
|
||||
}
|
||||
},
|
||||
emits: ['click'],
|
||||
@ -93,7 +103,7 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
.inner {
|
||||
> .inner {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
@ -106,5 +116,14 @@ export default defineComponent({
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
> .indicator {
|
||||
position: absolute;
|
||||
z-index: 1;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 20%;
|
||||
height: 20%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -7,8 +7,8 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import { twemojiSvgBase } from '@/../misc/twemoji-base';
|
||||
import { getStaticImageUrl } from '@client/scripts/get-static-image-url';
|
||||
import { twemojiSvgBase } from '@client/../misc/twemoji-base';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -11,7 +11,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkButton from '@client/components/ui/button.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -1,12 +1,11 @@
|
||||
<template>
|
||||
<div class="yxspomdl" :class="{ inline }">
|
||||
<div class="yxspomdl" :class="{ inline, colored }">
|
||||
<div class="ring"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import * as os from '@/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -14,6 +13,11 @@ export default defineComponent({
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
colored: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: true
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -32,6 +36,11 @@ export default defineComponent({
|
||||
.yxspomdl {
|
||||
padding: 32px;
|
||||
text-align: center;
|
||||
cursor: wait;
|
||||
|
||||
&.colored {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
&.inline {
|
||||
display: inline;
|
||||
@ -41,15 +50,23 @@ export default defineComponent({
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
> .ring {
|
||||
&:before,
|
||||
&:after {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
> .ring {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
opacity: 0.7;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
> .ring:after {
|
||||
&:before,
|
||||
&:after {
|
||||
content: " ";
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
@ -57,8 +74,19 @@ export default defineComponent({
|
||||
height: 48px;
|
||||
border-radius: 50%;
|
||||
border: solid 4px;
|
||||
}
|
||||
|
||||
&:before {
|
||||
border-color: currentColor;
|
||||
opacity: 0.3;
|
||||
}
|
||||
|
||||
&:after {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
border-color: currentColor transparent transparent transparent;
|
||||
animation: ring 0.5s linear infinite;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -4,7 +4,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import MfmCore from '@/components/mfm';
|
||||
import MfmCore from '@client/components/mfm';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -22,10 +22,10 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { toUnicode as decodePunycode } from 'punycode';
|
||||
import { url as local } from '@/config';
|
||||
import { isDeviceTouch } from '@/scripts/is-device-touch';
|
||||
import * as os from '@/os';
|
||||
import { toUnicode as decodePunycode } from 'punycode/';
|
||||
import { url as local } from '@client/config';
|
||||
import { isDeviceTouch } from '@client/scripts/is-device-touch';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -72,7 +72,7 @@ export default defineComponent({
|
||||
if (!document.body.contains(this.$el)) return;
|
||||
if (this.close) return;
|
||||
|
||||
const { dispose } = await os.popup(import('@/components/url-preview-popup.vue'), {
|
||||
const { dispose } = await os.popup(import('@client/components/url-preview-popup.vue'), {
|
||||
url: this.url,
|
||||
source: this.$el
|
||||
});
|
||||
|
@ -8,7 +8,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faSearch } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: ['q'],
|
||||
|
@ -14,9 +14,9 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import bytes from '@/filters/bytes';
|
||||
import number from '@/filters/number';
|
||||
import MkModal from '@/components/ui/modal.vue';
|
||||
import bytes from '@client/filters/bytes';
|
||||
import number from '@client/filters/number';
|
||||
import MkModal from '@client/components/ui/modal.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -71,6 +71,7 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.xubzgfgb {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
@ -82,6 +83,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> canvas {
|
||||
position: absolute;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
|
@ -126,7 +126,7 @@ import { defineComponent, markRaw } from 'vue';
|
||||
import { faChartBar, faUser, faPencilAlt, faSync } from '@fortawesome/free-solid-svg-icons';
|
||||
import Chart from 'chart.js';
|
||||
import MkSelect from './ui/select.vue';
|
||||
import number from '@/filters/number';
|
||||
import number from '@client/filters/number';
|
||||
|
||||
const sum = (...arr) => arr.reduce((r, a) => r.map((b, i) => a[i] + b));
|
||||
const negate = arr => arr.map(x => -x);
|
||||
@ -137,7 +137,7 @@ const alpha = (hex, a) => {
|
||||
const b = parseInt(result[3], 16);
|
||||
return `rgba(${r}, ${g}, ${b}, ${a})`;
|
||||
};
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -7,7 +7,7 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { instanceName } from '@/config';
|
||||
import { instanceName } from '@client/config';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -36,9 +36,9 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faQuestionCircle, faInfoCircle, faCircle } from '@fortawesome/free-solid-svg-icons';
|
||||
import MkModal from '@/components/ui/modal.vue';
|
||||
import { sidebarDef } from '@/sidebar';
|
||||
import { instanceName } from '@/config';
|
||||
import MkModal from '@client/components/ui/modal.vue';
|
||||
import { sidebarDef } from '@client/sidebar';
|
||||
import { instanceName } from '@client/config';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -146,7 +146,7 @@ export default defineComponent({
|
||||
> .sub {
|
||||
margin-top: 8px;
|
||||
padding-top: 8px;
|
||||
border-top: solid 1px var(--divider);
|
||||
border-top: solid 0.5px var(--divider);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -12,9 +12,9 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExternalLinkSquareAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { url as local } from '@/config';
|
||||
import { isDeviceTouch } from '@/scripts/is-device-touch';
|
||||
import * as os from '@/os';
|
||||
import { url as local } from '@client/config';
|
||||
import { isDeviceTouch } from '@client/scripts/is-device-touch';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -46,7 +46,7 @@ export default defineComponent({
|
||||
if (!document.body.contains(this.$el)) return;
|
||||
if (this.close) return;
|
||||
|
||||
const { dispose } = await os.popup(import('@/components/url-preview-popup.vue'), {
|
||||
const { dispose } = await os.popup(import('@client/components/url-preview-popup.vue'), {
|
||||
url: this.url,
|
||||
source: this.$el
|
||||
});
|
||||
|
@ -28,8 +28,8 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import { ColdDeviceStorage } from '@/store';
|
||||
import * as os from '@client/os';
|
||||
import { ColdDeviceStorage } from '@client/store';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -24,11 +24,11 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExclamationTriangle, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
|
||||
import { getStaticImageUrl } from '@/scripts/get-static-image-url';
|
||||
import { extractAvgColorFromBlurhash } from '@/scripts/extract-avg-color-from-blurhash';
|
||||
import { getStaticImageUrl } from '@client/scripts/get-static-image-url';
|
||||
import { extractAvgColorFromBlurhash } from '@client/scripts/extract-avg-color-from-blurhash';
|
||||
import ImageViewer from './image-viewer.vue';
|
||||
import ImgWithBlurhash from './img-with-blurhash.vue';
|
||||
import * as os from '@/os';
|
||||
import ImgWithBlurhash from '@client/components/img-with-blurhash.vue';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -123,7 +123,7 @@ export default defineComponent({
|
||||
|
||||
.gqnyydlz {
|
||||
position: relative;
|
||||
border: solid 1px var(--divider);
|
||||
border: solid 0.5px var(--divider);
|
||||
|
||||
> i {
|
||||
display: block;
|
||||
|
@ -17,7 +17,7 @@ import { defineComponent } from 'vue';
|
||||
import XBanner from './media-banner.vue';
|
||||
import XImage from './media-image.vue';
|
||||
import XVideo from './media-video.vue';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -25,7 +25,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExclamationTriangle, faEyeSlash } from '@fortawesome/free-solid-svg-icons';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -16,10 +16,10 @@
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { toUnicode } from 'punycode';
|
||||
import { host as localHost } from '@/config';
|
||||
import { toUnicode } from 'punycode/';
|
||||
import { host as localHost } from '@client/config';
|
||||
import { wellKnownServices } from '../../well-known-services';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -1,16 +1,15 @@
|
||||
import { VNode, defineComponent, h } from 'vue';
|
||||
import { MfmForest } from '@/../mfm/prelude';
|
||||
import { parse, parsePlain } from '@/../mfm/parse';
|
||||
import MkUrl from '@/components/global/url.vue';
|
||||
import MkLink from '@/components/link.vue';
|
||||
import MkMention from '@/components/mention.vue';
|
||||
import MkEmoji from '@/components/global/emoji.vue';
|
||||
import { concat } from '@/../prelude/array';
|
||||
import MkFormula from '@/components/formula.vue';
|
||||
import MkCode from '@/components/code.vue';
|
||||
import MkGoogle from '@/components/google.vue';
|
||||
import MkA from '@/components/global/a.vue';
|
||||
import { host } from '@/config';
|
||||
import * as mfm from 'mfm-js';
|
||||
import MkUrl from '@client/components/global/url.vue';
|
||||
import MkLink from '@client/components/link.vue';
|
||||
import MkMention from '@client/components/mention.vue';
|
||||
import MkEmoji from '@client/components/global/emoji.vue';
|
||||
import { concat } from '@client/../prelude/array';
|
||||
import MkFormula from '@client/components/formula.vue';
|
||||
import MkCode from '@client/components/code.vue';
|
||||
import MkGoogle from '@client/components/google.vue';
|
||||
import MkA from '@client/components/global/a.vue';
|
||||
import { host } from '@client/config';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -46,23 +45,26 @@ export default defineComponent({
|
||||
render() {
|
||||
if (this.text == null || this.text == '') return;
|
||||
|
||||
const ast = (this.plain ? parsePlain : parse)(this.text);
|
||||
const ast = (this.plain ? mfm.parsePlain : mfm.parse)(this.text);
|
||||
|
||||
const validTime = (t: string | null | undefined) => {
|
||||
if (t == null) return null;
|
||||
return t.match(/^[0-9.]+s$/) ? t : null;
|
||||
};
|
||||
|
||||
const genEl = (ast: MfmForest) => concat(ast.map((token): VNode[] => {
|
||||
switch (token.node.type) {
|
||||
const genEl = (ast: mfm.MfmNode[]) => concat(ast.map((token): VNode[] => {
|
||||
switch (token.type) {
|
||||
case 'text': {
|
||||
const text = token.node.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
||||
const text = token.props.text.replace(/(\r\n|\n|\r)/g, '\n');
|
||||
|
||||
if (!this.plain) {
|
||||
const x = text.split('\n')
|
||||
.map(t => t == '' ? [h('br')] : [t, h('br')]);
|
||||
x[x.length - 1].pop();
|
||||
return x;
|
||||
const res = [];
|
||||
for (const t of text.split('\n')) {
|
||||
res.push(h('br'));
|
||||
res.push(t);
|
||||
}
|
||||
res.shift();
|
||||
return res;
|
||||
} else {
|
||||
return [text.replace(/\n/g, ' ')];
|
||||
}
|
||||
@ -83,38 +85,38 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
case 'fn': {
|
||||
// TODO: CSSを文字列で組み立てていくと token.node.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
|
||||
// TODO: CSSを文字列で組み立てていくと token.props.args.~~~ 経由でCSSインジェクションできるのでよしなにやる
|
||||
let style;
|
||||
switch (token.node.props.name) {
|
||||
switch (token.props.name) {
|
||||
case 'tada': {
|
||||
style = `font-size: 150%;` + (this.$store.state.animatedMfm ? 'animation: tada 1s linear infinite both;' : '');
|
||||
break;
|
||||
}
|
||||
case 'jelly': {
|
||||
const speed = validTime(token.node.props.args.speed) || '1s';
|
||||
const speed = validTime(token.props.args.speed) || '1s';
|
||||
style = (this.$store.state.animatedMfm ? `animation: mfm-rubberBand ${speed} linear infinite both;` : '');
|
||||
break;
|
||||
}
|
||||
case 'twitch': {
|
||||
const speed = validTime(token.node.props.args.speed) || '0.5s';
|
||||
const speed = validTime(token.props.args.speed) || '0.5s';
|
||||
style = this.$store.state.animatedMfm ? `animation: mfm-twitch ${speed} ease infinite;` : '';
|
||||
break;
|
||||
}
|
||||
case 'shake': {
|
||||
const speed = validTime(token.node.props.args.speed) || '0.5s';
|
||||
const speed = validTime(token.props.args.speed) || '0.5s';
|
||||
style = this.$store.state.animatedMfm ? `animation: mfm-shake ${speed} ease infinite;` : '';
|
||||
break;
|
||||
}
|
||||
case 'spin': {
|
||||
const direction =
|
||||
token.node.props.args.left ? 'reverse' :
|
||||
token.node.props.args.alternate ? 'alternate' :
|
||||
token.props.args.left ? 'reverse' :
|
||||
token.props.args.alternate ? 'alternate' :
|
||||
'normal';
|
||||
const anime =
|
||||
token.node.props.args.x ? 'mfm-spinX' :
|
||||
token.node.props.args.y ? 'mfm-spinY' :
|
||||
token.props.args.x ? 'mfm-spinX' :
|
||||
token.props.args.y ? 'mfm-spinY' :
|
||||
'mfm-spin';
|
||||
const speed = validTime(token.node.props.args.speed) || '1.5s';
|
||||
const speed = validTime(token.props.args.speed) || '1.5s';
|
||||
style = this.$store.state.animatedMfm ? `animation: ${anime} ${speed} linear infinite; animation-direction: ${direction};` : '';
|
||||
break;
|
||||
}
|
||||
@ -128,8 +130,8 @@ export default defineComponent({
|
||||
}
|
||||
case 'flip': {
|
||||
const transform =
|
||||
(token.node.props.args.h && token.node.props.args.v) ? 'scale(-1, -1)' :
|
||||
token.node.props.args.v ? 'scaleY(-1)' :
|
||||
(token.props.args.h && token.props.args.v) ? 'scale(-1, -1)' :
|
||||
token.props.args.v ? 'scaleY(-1)' :
|
||||
'scaleX(-1)';
|
||||
style = `transform: ${transform};`;
|
||||
break;
|
||||
@ -148,12 +150,12 @@ export default defineComponent({
|
||||
}
|
||||
case 'font': {
|
||||
const family =
|
||||
token.node.props.args.serif ? 'serif' :
|
||||
token.node.props.args.monospace ? 'monospace' :
|
||||
token.node.props.args.cursive ? 'cursive' :
|
||||
token.node.props.args.fantasy ? 'fantasy' :
|
||||
token.node.props.args.emoji ? 'emoji' :
|
||||
token.node.props.args.math ? 'math' :
|
||||
token.props.args.serif ? 'serif' :
|
||||
token.props.args.monospace ? 'monospace' :
|
||||
token.props.args.cursive ? 'cursive' :
|
||||
token.props.args.fantasy ? 'fantasy' :
|
||||
token.props.args.emoji ? 'emoji' :
|
||||
token.props.args.math ? 'math' :
|
||||
null;
|
||||
if (family) style = `font-family: ${family};`;
|
||||
break;
|
||||
@ -165,7 +167,7 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
if (style == null) {
|
||||
return h('span', {}, ['[', token.node.props.name, ...genEl(token.children), ']']);
|
||||
return h('span', {}, ['[', token.props.name, ...genEl(token.children), ']']);
|
||||
} else {
|
||||
return h('span', {
|
||||
style: 'display: inline-block;' + style,
|
||||
@ -188,7 +190,7 @@ export default defineComponent({
|
||||
case 'url': {
|
||||
return [h(MkUrl, {
|
||||
key: Math.random(),
|
||||
url: token.node.props.url,
|
||||
url: token.props.url,
|
||||
rel: 'nofollow noopener',
|
||||
})];
|
||||
}
|
||||
@ -196,7 +198,7 @@ export default defineComponent({
|
||||
case 'link': {
|
||||
return [h(MkLink, {
|
||||
key: Math.random(),
|
||||
url: token.node.props.url,
|
||||
url: token.props.url,
|
||||
rel: 'nofollow noopener',
|
||||
}, genEl(token.children))];
|
||||
}
|
||||
@ -204,32 +206,31 @@ export default defineComponent({
|
||||
case 'mention': {
|
||||
return [h(MkMention, {
|
||||
key: Math.random(),
|
||||
host: (token.node.props.host == null && this.author && this.author.host != null ? this.author.host : token.node.props.host) || host,
|
||||
username: token.node.props.username
|
||||
host: (token.props.host == null && this.author && this.author.host != null ? this.author.host : token.props.host) || host,
|
||||
username: token.props.username
|
||||
})];
|
||||
}
|
||||
|
||||
case 'hashtag': {
|
||||
return [h(MkA, {
|
||||
key: Math.random(),
|
||||
to: this.isNote ? `/tags/${encodeURIComponent(token.node.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.node.props.hashtag)}`,
|
||||
to: this.isNote ? `/tags/${encodeURIComponent(token.props.hashtag)}` : `/explore/tags/${encodeURIComponent(token.props.hashtag)}`,
|
||||
style: 'color:var(--hashtag);'
|
||||
}, `#${token.node.props.hashtag}`)];
|
||||
}, `#${token.props.hashtag}`)];
|
||||
}
|
||||
|
||||
case 'blockCode': {
|
||||
return [h(MkCode, {
|
||||
key: Math.random(),
|
||||
code: token.node.props.code,
|
||||
lang: token.node.props.lang,
|
||||
code: token.props.code,
|
||||
lang: token.props.lang,
|
||||
})];
|
||||
}
|
||||
|
||||
case 'inlineCode': {
|
||||
return [h(MkCode, {
|
||||
key: Math.random(),
|
||||
code: token.node.props.code,
|
||||
lang: token.node.props.lang,
|
||||
code: token.props.code,
|
||||
inline: true
|
||||
})];
|
||||
}
|
||||
@ -246,10 +247,19 @@ export default defineComponent({
|
||||
}
|
||||
}
|
||||
|
||||
case 'emoji': {
|
||||
case 'emojiCode': {
|
||||
return [h(MkEmoji, {
|
||||
key: Math.random(),
|
||||
emoji: token.node.props.name ? `:${token.node.props.name}:` : token.node.props.emoji,
|
||||
emoji: `:${token.props.name}:`,
|
||||
customEmojis: this.customEmojis,
|
||||
normal: this.plain
|
||||
})];
|
||||
}
|
||||
|
||||
case 'unicodeEmoji': {
|
||||
return [h(MkEmoji, {
|
||||
key: Math.random(),
|
||||
emoji: token.props.emoji,
|
||||
customEmojis: this.customEmojis,
|
||||
normal: this.plain
|
||||
})];
|
||||
@ -258,7 +268,7 @@ export default defineComponent({
|
||||
case 'mathInline': {
|
||||
return [h(MkFormula, {
|
||||
key: Math.random(),
|
||||
formula: token.node.props.formula,
|
||||
formula: token.props.formula,
|
||||
block: false
|
||||
})];
|
||||
}
|
||||
@ -266,7 +276,7 @@ export default defineComponent({
|
||||
case 'mathBlock': {
|
||||
return [h(MkFormula, {
|
||||
key: Math.random(),
|
||||
formula: token.node.props.formula,
|
||||
formula: token.props.formula,
|
||||
block: true
|
||||
})];
|
||||
}
|
||||
@ -274,12 +284,12 @@ export default defineComponent({
|
||||
case 'search': {
|
||||
return [h(MkGoogle, {
|
||||
key: Math.random(),
|
||||
q: token.node.props.query
|
||||
q: token.props.query
|
||||
})];
|
||||
}
|
||||
|
||||
default: {
|
||||
console.error('unrecognized ast type:', token.node.type);
|
||||
console.error('unrecognized ast type:', token.type);
|
||||
|
||||
return [];
|
||||
}
|
||||
|
@ -32,7 +32,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { v4 as uuid } from 'uuid';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
213
src/client/components/modal-page-window.vue
Normal file
213
src/client/components/modal-page-window.vue
Normal file
@ -0,0 +1,213 @@
|
||||
<template>
|
||||
<MkModal ref="modal" @click="$emit('click')" @closed="$emit('closed')">
|
||||
<div class="hrmcaedk _popup _narrow_" :style="{ width: `${width}px`, height: (height ? `min(${height}px, 100%)` : '100%') }">
|
||||
<div class="header">
|
||||
<button class="_button" @click="back()" v-if="history.length > 0"><Fa :icon="faChevronLeft"/></button>
|
||||
<button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button>
|
||||
<span class="title">
|
||||
<XHeader :info="pageInfo" :with-back="false"/>
|
||||
</span>
|
||||
<button class="_button" @click="$refs.modal.close()"><Fa :icon="faTimes"/></button>
|
||||
</div>
|
||||
<div class="body _flat_">
|
||||
<keep-alive>
|
||||
<component :is="component" v-bind="props" :ref="changePage"/>
|
||||
</keep-alive>
|
||||
</div>
|
||||
</div>
|
||||
</MkModal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft, faColumns, faTimes } from '@fortawesome/free-solid-svg-icons';
|
||||
import MkModal from '@client/components/ui/modal.vue';
|
||||
import XHeader from '@client/ui/_common_/header.vue';
|
||||
import { popout } from '@client/scripts/popout';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { resolve } from '@client/router';
|
||||
import { url } from '@client/config';
|
||||
import * as symbols from '@client/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
MkModal,
|
||||
XHeader,
|
||||
},
|
||||
|
||||
inject: {
|
||||
sideViewHook: {
|
||||
default: null
|
||||
}
|
||||
},
|
||||
|
||||
provide() {
|
||||
return {
|
||||
navHook: (path) => {
|
||||
this.navigate(path);
|
||||
}
|
||||
};
|
||||
},
|
||||
|
||||
props: {
|
||||
initialPath: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
initialComponent: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
initialProps: {
|
||||
type: Object,
|
||||
required: false,
|
||||
default: () => {},
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['closed'],
|
||||
|
||||
data() {
|
||||
return {
|
||||
width: 860,
|
||||
height: 660,
|
||||
pageInfo: null,
|
||||
path: this.initialPath,
|
||||
component: this.initialComponent,
|
||||
props: this.initialProps,
|
||||
history: [],
|
||||
faChevronLeft, faTimes,
|
||||
};
|
||||
},
|
||||
|
||||
computed: {
|
||||
url(): string {
|
||||
return url + this.path;
|
||||
},
|
||||
|
||||
contextmenu() {
|
||||
return [{
|
||||
type: 'label',
|
||||
text: this.path,
|
||||
}, {
|
||||
icon: faExpandAlt,
|
||||
text: this.$ts.showInPage,
|
||||
action: this.expand
|
||||
}, this.sideViewHook ? {
|
||||
icon: faColumns,
|
||||
text: this.$ts.openInSideView,
|
||||
action: () => {
|
||||
this.sideViewHook(this.path);
|
||||
this.$refs.window.close();
|
||||
}
|
||||
} : undefined, {
|
||||
icon: faExternalLinkAlt,
|
||||
text: this.$ts.popout,
|
||||
action: this.popout
|
||||
}, null, {
|
||||
icon: faExternalLinkAlt,
|
||||
text: this.$ts.openInNewTab,
|
||||
action: () => {
|
||||
window.open(this.url, '_blank');
|
||||
this.$refs.window.close();
|
||||
}
|
||||
}, {
|
||||
icon: faLink,
|
||||
text: this.$ts.copyLink,
|
||||
action: () => {
|
||||
copyToClipboard(this.url);
|
||||
}
|
||||
}];
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
changePage(page) {
|
||||
if (page == null) return;
|
||||
if (page[symbols.PAGE_INFO]) {
|
||||
this.pageInfo = page[symbols.PAGE_INFO];
|
||||
}
|
||||
},
|
||||
|
||||
navigate(path, record = true) {
|
||||
if (record) this.history.push(this.path);
|
||||
this.path = path;
|
||||
const { component, props } = resolve(path);
|
||||
this.component = component;
|
||||
this.props = props;
|
||||
},
|
||||
|
||||
back() {
|
||||
this.navigate(this.history.pop(), false);
|
||||
},
|
||||
|
||||
expand() {
|
||||
this.$router.push(this.path);
|
||||
this.$refs.window.close();
|
||||
},
|
||||
|
||||
popout() {
|
||||
popout(this.path, this.$el);
|
||||
this.$refs.window.close();
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.hrmcaedk {
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
contain: content;
|
||||
|
||||
--root-margin: 24px;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
--root-margin: 16px;
|
||||
}
|
||||
|
||||
> .header {
|
||||
$height: 52px;
|
||||
$height-narrow: 42px;
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
box-shadow: 0px 1px var(--divider);
|
||||
|
||||
> button {
|
||||
height: $height;
|
||||
width: $height;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
height: $height-narrow;
|
||||
width: $height-narrow;
|
||||
}
|
||||
}
|
||||
|
||||
> .title {
|
||||
flex: 1;
|
||||
line-height: $height;
|
||||
padding-left: 32px;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
pointer-events: none;
|
||||
|
||||
@media (max-width: 500px) {
|
||||
line-height: $height-narrow;
|
||||
padding-left: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
> button + .title {
|
||||
padding-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
> .body {
|
||||
overflow: auto;
|
||||
background: var(--bg);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="note _panel"
|
||||
class="note _block"
|
||||
v-if="!muted"
|
||||
v-show="!isDeleted"
|
||||
:tabindex="!isDeleted ? '-1' : null"
|
||||
@ -35,7 +35,7 @@
|
||||
</div>
|
||||
<article class="article" @contextmenu.stop="onContextmenu">
|
||||
<header class="header">
|
||||
<MkAvatar class="avatar" :user="appearNote.user"/>
|
||||
<MkAvatar class="avatar" :user="appearNote.user" :show-indicator="true"/>
|
||||
<div class="body">
|
||||
<div class="top">
|
||||
<MkA class="name" :to="userPage(appearNote.user)" v-user-preview="appearNote.user.id">
|
||||
@ -120,11 +120,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineAsyncComponent, defineComponent, markRaw, ref } from 'vue';
|
||||
import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug, faExclamationCircle, faPaperclip } from '@fortawesome/free-solid-svg-icons';
|
||||
import { defineAsyncComponent, defineComponent, markRaw } from 'vue';
|
||||
import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug, faExclamationCircle, faPaperclip, faShareAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
|
||||
import { parse } from '../../mfm/parse';
|
||||
import { sum, unique } from '../../prelude/array';
|
||||
import * as mfm from 'mfm-js';
|
||||
import { sum } from '../../prelude/array';
|
||||
import XSub from './note.sub.vue';
|
||||
import XNoteHeader from './note-header.vue';
|
||||
import XNotePreview from './note-preview.vue';
|
||||
@ -132,15 +132,16 @@ import XReactionsViewer from './reactions-viewer.vue';
|
||||
import XMediaList from './media-list.vue';
|
||||
import XCwButton from './cw-button.vue';
|
||||
import XPoll from './poll.vue';
|
||||
import { pleaseLogin } from '@/scripts/please-login';
|
||||
import { focusPrev, focusNext } from '@/scripts/focus';
|
||||
import { url } from '@/config';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import { checkWordMute } from '@/scripts/check-word-mute';
|
||||
import { userPage } from '@/filters/user';
|
||||
import * as os from '@/os';
|
||||
import { noteActions, noteViewInterruptors } from '@/store';
|
||||
import { reactionPicker } from '@/scripts/reaction-picker';
|
||||
import { pleaseLogin } from '@client/scripts/please-login';
|
||||
import { focusPrev, focusNext } from '@client/scripts/focus';
|
||||
import { url } from '@client/config';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { checkWordMute } from '@client/scripts/check-word-mute';
|
||||
import { userPage } from '@client/filters/user';
|
||||
import * as os from '@client/os';
|
||||
import { noteActions, noteViewInterruptors } from '@client/store';
|
||||
import { reactionPicker } from '@client/scripts/reaction-picker';
|
||||
import { extractUrlFromMfm } from '@/misc/extract-url-from-mfm';
|
||||
|
||||
function markRawAll(...xs) {
|
||||
for (const x of xs) {
|
||||
@ -160,8 +161,8 @@ export default defineComponent({
|
||||
XMediaList,
|
||||
XCwButton,
|
||||
XPoll,
|
||||
MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
|
||||
MkInstanceTicker: defineAsyncComponent(() => import('@/components/instance-ticker.vue')),
|
||||
MkUrlPreview: defineAsyncComponent(() => import('@client/components/url-preview.vue')),
|
||||
MkInstanceTicker: defineAsyncComponent(() => import('@client/components/instance-ticker.vue')),
|
||||
},
|
||||
|
||||
inject: {
|
||||
@ -252,21 +253,7 @@ export default defineComponent({
|
||||
|
||||
urls(): string[] {
|
||||
if (this.appearNote.text) {
|
||||
const ast = parse(this.appearNote.text);
|
||||
// TODO: 再帰的にURL要素がないか調べる
|
||||
const urls = unique(ast
|
||||
.filter(t => ((t.node.type == 'url' || t.node.type == 'link') && t.node.props.url && !t.node.props.silent))
|
||||
.map(t => t.node.props.url));
|
||||
|
||||
// unique without hash
|
||||
// [ http://a/#1, http://a/#2, http://b/#3 ] => [ http://a/#1, http://b/#3 ]
|
||||
const removeHash = x => x.replace(/#[^#]*$/, '');
|
||||
|
||||
return urls.reduce((array, url) => {
|
||||
const removed = removeHash(url);
|
||||
if (!array.map(x => removeHash(x)).includes(removed)) array.push(url);
|
||||
return array;
|
||||
}, []);
|
||||
return extractUrlFromMfm(mfm.parse(this.appearNote.text));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -638,6 +625,11 @@ export default defineComponent({
|
||||
window.open(this.appearNote.url || this.appearNote.uri, '_blank');
|
||||
}
|
||||
} : undefined,
|
||||
{
|
||||
icon: faShareAlt,
|
||||
text: this.$ts.share,
|
||||
action: this.share
|
||||
},
|
||||
null,
|
||||
statePromise.then(state => state.isFavorited ? {
|
||||
icon: faStar,
|
||||
@ -687,7 +679,7 @@ export default defineComponent({
|
||||
text: this.$ts.reportAbuse,
|
||||
action: () => {
|
||||
const u = `${url}/notes/${this.appearNote.id}`;
|
||||
os.popup(import('@/components/abuse-report-window.vue'), {
|
||||
os.popup(import('@client/components/abuse-report-window.vue'), {
|
||||
user: this.appearNote.user,
|
||||
initialComment: `Note: ${u}\n-----\n`
|
||||
}, {}, 'closed');
|
||||
@ -863,6 +855,14 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
share() {
|
||||
navigator.share({
|
||||
title: this.$t('noteOf', { user: this.appearNote.user.name }),
|
||||
text: this.appearNote.text,
|
||||
url: `${url}/notes/${this.appearNote.id}`
|
||||
});
|
||||
},
|
||||
|
||||
focus() {
|
||||
this.$el.focus();
|
||||
},
|
||||
@ -1020,7 +1020,7 @@ export default defineComponent({
|
||||
margin: 0 0.5em;
|
||||
padding: 4px 6px;
|
||||
font-size: 80%;
|
||||
border: solid 1px var(--divider);
|
||||
border: solid 0.5px var(--divider);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
@ -1123,7 +1123,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> .reply {
|
||||
border-top: solid 1px var(--divider);
|
||||
border-top: solid 0.5px var(--divider);
|
||||
}
|
||||
|
||||
&.max-width_500px {
|
||||
|
@ -28,7 +28,7 @@ import { faHome, faUnlock, faEnvelope, faMobileAlt, faBookmark, faBiohazard } fr
|
||||
import { faBookmark as farBookmark } from '@fortawesome/free-regular-svg-icons';
|
||||
import notePage from '../filters/note';
|
||||
import { userPage } from '../filters/user';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -78,7 +78,7 @@ export default defineComponent({
|
||||
margin: 0 .5em 0 0;
|
||||
padding: 1px 6px;
|
||||
font-size: 80%;
|
||||
border: solid 1px var(--divider);
|
||||
border: solid 0.5px var(--divider);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
|
@ -21,7 +21,7 @@ import { defineComponent } from 'vue';
|
||||
import XNoteHeader from './note-header.vue';
|
||||
import XSubNoteContent from './sub-note-content.vue';
|
||||
import XCwButton from './cw-button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -24,7 +24,7 @@ import { defineComponent } from 'vue';
|
||||
import XNoteHeader from './note-header.vue';
|
||||
import XSubNoteContent from './sub-note-content.vue';
|
||||
import XCwButton from './cw-button.vue';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'XSub',
|
||||
@ -139,7 +139,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> .reply {
|
||||
border-left: solid 1px var(--divider);
|
||||
border-left: solid 0.5px var(--divider);
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
class="tkcbzcuz _panel"
|
||||
class="tkcbzcuz"
|
||||
v-if="!muted"
|
||||
v-show="!isDeleted"
|
||||
:tabindex="!isDeleted ? '-1' : null"
|
||||
@ -90,7 +90,7 @@
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
||||
<div v-else class="_panel muted" @click="muted = false">
|
||||
<div v-else class="muted" @click="muted = false">
|
||||
<I18n :src="$ts.userSaysSomething" tag="small">
|
||||
<template #name>
|
||||
<MkA class="name" :to="userPage(appearNote.user)" v-user-preview="appearNote.userId">
|
||||
@ -102,11 +102,11 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { computed, defineAsyncComponent, defineComponent, markRaw, ref } from 'vue';
|
||||
import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug, faExclamationCircle, faPaperclip } from '@fortawesome/free-solid-svg-icons';
|
||||
import { defineAsyncComponent, defineComponent, markRaw } from 'vue';
|
||||
import { faSatelliteDish, faBolt, faTimes, faBullhorn, faStar, faLink, faExternalLinkSquareAlt, faPlus, faMinus, faRetweet, faReply, faReplyAll, faEllipsisH, faHome, faUnlock, faEnvelope, faThumbtack, faBan, faQuoteRight, faInfoCircle, faBiohazard, faPlug, faExclamationCircle, faPaperclip, faShareAlt } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faCopy, faTrashAlt, faEdit, faEye, faEyeSlash } from '@fortawesome/free-regular-svg-icons';
|
||||
import { parse } from '../../mfm/parse';
|
||||
import { sum, unique } from '../../prelude/array';
|
||||
import * as mfm from 'mfm-js';
|
||||
import { sum } from '../../prelude/array';
|
||||
import XSub from './note.sub.vue';
|
||||
import XNoteHeader from './note-header.vue';
|
||||
import XNotePreview from './note-preview.vue';
|
||||
@ -114,15 +114,16 @@ import XReactionsViewer from './reactions-viewer.vue';
|
||||
import XMediaList from './media-list.vue';
|
||||
import XCwButton from './cw-button.vue';
|
||||
import XPoll from './poll.vue';
|
||||
import { pleaseLogin } from '@/scripts/please-login';
|
||||
import { focusPrev, focusNext } from '@/scripts/focus';
|
||||
import { url } from '@/config';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import { checkWordMute } from '@/scripts/check-word-mute';
|
||||
import { userPage } from '@/filters/user';
|
||||
import * as os from '@/os';
|
||||
import { noteActions, noteViewInterruptors } from '@/store';
|
||||
import { reactionPicker } from '@/scripts/reaction-picker';
|
||||
import { pleaseLogin } from '@client/scripts/please-login';
|
||||
import { focusPrev, focusNext } from '@client/scripts/focus';
|
||||
import { url } from '@client/config';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { checkWordMute } from '@client/scripts/check-word-mute';
|
||||
import { userPage } from '@client/filters/user';
|
||||
import * as os from '@client/os';
|
||||
import { noteActions, noteViewInterruptors } from '@client/store';
|
||||
import { reactionPicker } from '@client/scripts/reaction-picker';
|
||||
import { extractUrlFromMfm } from '@/misc/extract-url-from-mfm';
|
||||
|
||||
function markRawAll(...xs) {
|
||||
for (const x of xs) {
|
||||
@ -141,8 +142,8 @@ export default defineComponent({
|
||||
XMediaList,
|
||||
XCwButton,
|
||||
XPoll,
|
||||
MkUrlPreview: defineAsyncComponent(() => import('@/components/url-preview.vue')),
|
||||
MkInstanceTicker: defineAsyncComponent(() => import('@/components/instance-ticker.vue')),
|
||||
MkUrlPreview: defineAsyncComponent(() => import('@client/components/url-preview.vue')),
|
||||
MkInstanceTicker: defineAsyncComponent(() => import('@client/components/instance-ticker.vue')),
|
||||
},
|
||||
|
||||
inject: {
|
||||
@ -238,21 +239,7 @@ export default defineComponent({
|
||||
|
||||
urls(): string[] {
|
||||
if (this.appearNote.text) {
|
||||
const ast = parse(this.appearNote.text);
|
||||
// TODO: 再帰的にURL要素がないか調べる
|
||||
const urls = unique(ast
|
||||
.filter(t => ((t.node.type == 'url' || t.node.type == 'link') && t.node.props.url && !t.node.props.silent))
|
||||
.map(t => t.node.props.url));
|
||||
|
||||
// unique without hash
|
||||
// [ http://a/#1, http://a/#2, http://b/#3 ] => [ http://a/#1, http://b/#3 ]
|
||||
const removeHash = x => x.replace(/#[^#]*$/, '');
|
||||
|
||||
return urls.reduce((array, url) => {
|
||||
const removed = removeHash(url);
|
||||
if (!array.map(x => removeHash(x)).includes(removed)) array.push(url);
|
||||
return array;
|
||||
}, []);
|
||||
return extractUrlFromMfm(mfm.parse(this.appearNote.text));
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@ -613,6 +600,11 @@ export default defineComponent({
|
||||
window.open(this.appearNote.url || this.appearNote.uri, '_blank');
|
||||
}
|
||||
} : undefined,
|
||||
{
|
||||
icon: faShareAlt,
|
||||
text: this.$ts.share,
|
||||
action: this.share
|
||||
},
|
||||
null,
|
||||
statePromise.then(state => state.isFavorited ? {
|
||||
icon: faStar,
|
||||
@ -662,7 +654,7 @@ export default defineComponent({
|
||||
text: this.$ts.reportAbuse,
|
||||
action: () => {
|
||||
const u = `${url}/notes/${this.appearNote.id}`;
|
||||
os.popup(import('@/components/abuse-report-window.vue'), {
|
||||
os.popup(import('@client/components/abuse-report-window.vue'), {
|
||||
user: this.appearNote.user,
|
||||
initialComment: `Note: ${u}\n-----\n`
|
||||
}, {}, 'closed');
|
||||
@ -838,6 +830,14 @@ export default defineComponent({
|
||||
});
|
||||
},
|
||||
|
||||
share() {
|
||||
navigator.share({
|
||||
title: this.$t('noteOf', { user: this.appearNote.user.name }),
|
||||
text: this.appearNote.text,
|
||||
url: `${url}/notes/${this.appearNote.id}`
|
||||
});
|
||||
},
|
||||
|
||||
focus() {
|
||||
this.$el.focus();
|
||||
},
|
||||
@ -863,7 +863,7 @@ export default defineComponent({
|
||||
.tkcbzcuz {
|
||||
position: relative;
|
||||
transition: box-shadow 0.1s ease;
|
||||
overflow: hidden;
|
||||
overflow: clip;
|
||||
contain: content;
|
||||
|
||||
// これらの指定はパフォーマンス向上には有効だが、ノートの高さは一定でないため、
|
||||
@ -994,11 +994,12 @@ export default defineComponent({
|
||||
> .avatar {
|
||||
flex-shrink: 0;
|
||||
display: block;
|
||||
//position: sticky;
|
||||
//top: 72px;
|
||||
margin: 0 14px 8px 0;
|
||||
width: 58px;
|
||||
height: 58px;
|
||||
position: sticky;
|
||||
top: calc(22px + var(--stickyTop, 0px));
|
||||
left: 0;
|
||||
}
|
||||
|
||||
> .main {
|
||||
@ -1119,7 +1120,7 @@ export default defineComponent({
|
||||
}
|
||||
|
||||
> .reply {
|
||||
border-top: solid 1px var(--divider);
|
||||
border-top: solid 0.5px var(--divider);
|
||||
}
|
||||
|
||||
&.max-width_500px {
|
||||
@ -1142,6 +1143,7 @@ export default defineComponent({
|
||||
margin: 0 10px 8px 0;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
top: calc(14px + var(--stickyTop, 0px));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,15 @@
|
||||
<template>
|
||||
<div class="_list_">
|
||||
<div class="_fullinfo" v-if="empty">
|
||||
<transition name="fade" mode="out-in">
|
||||
<MkLoading v-if="fetching"/>
|
||||
|
||||
<MkError v-else-if="error" @retry="init()"/>
|
||||
|
||||
<div class="_fullinfo" v-else-if="empty">
|
||||
<img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
|
||||
<div>{{ $ts.noNotes }}</div>
|
||||
</div>
|
||||
|
||||
<MkError v-if="error" @retry="init()"/>
|
||||
|
||||
<div v-else>
|
||||
<div v-show="more && reversed" style="margin-bottom: var(--margin);">
|
||||
<MkButton style="margin: 0 auto;" @click="fetchMoreFeature" :disabled="moreFetching" :style="{ cursor: moreFetching ? 'wait' : 'pointer' }">
|
||||
<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
|
||||
@ -14,8 +17,8 @@
|
||||
</MkButton>
|
||||
</div>
|
||||
|
||||
<XList ref="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed">
|
||||
<XNote :note="note" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/>
|
||||
<XList ref="notes" :items="notes" v-slot="{ item: note }" :direction="reversed ? 'up' : 'down'" :reversed="reversed" :no-gap="noGap">
|
||||
<XNote :note="note" class="_block" @update:note="updated(note, $event)" :key="note._featuredId_ || note._prId_ || note.id"/>
|
||||
</XList>
|
||||
|
||||
<div v-show="more && !reversed" style="margin-top: var(--margin);">
|
||||
@ -24,15 +27,16 @@
|
||||
<template v-if="moreFetching"><MkLoading inline/></template>
|
||||
</MkButton>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import paging from '@/scripts/paging';
|
||||
import paging from '@client/scripts/paging';
|
||||
import XNote from './note.vue';
|
||||
import XList from './date-separated-list.vue';
|
||||
import MkButton from '@/components/ui/button.vue';
|
||||
import MkButton from '@client/components/ui/button.vue';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -55,11 +59,15 @@ export default defineComponent({
|
||||
pagination: {
|
||||
required: true
|
||||
},
|
||||
|
||||
prop: {
|
||||
type: String,
|
||||
required: false
|
||||
}
|
||||
},
|
||||
noGap: {
|
||||
type: Boolean,
|
||||
required: false,
|
||||
default: false
|
||||
},
|
||||
},
|
||||
|
||||
emits: ['before', 'after'],
|
||||
@ -90,3 +98,14 @@ export default defineComponent({
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.125s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
|
@ -9,6 +9,7 @@
|
||||
@closed="$emit('closed')"
|
||||
>
|
||||
<template #header>{{ $ts.notificationSetting }}</template>
|
||||
<div class="_monolithic_">
|
||||
<div v-if="showGlobalToggle" class="_section">
|
||||
<MkSwitch v-model:value="useGlobalSetting">
|
||||
{{ $ts.useGlobalSetting }}
|
||||
@ -21,12 +22,13 @@
|
||||
<MkButton inline @click="enableAll">{{ $ts.enableAll }}</MkButton>
|
||||
<MkSwitch v-for="type in notificationTypes" :key="type" v-model:value="typesMap[type]">{{ $t(`_notification._types.${type}`) }}</MkSwitch>
|
||||
</div>
|
||||
</div>
|
||||
</XModalWindow>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import XModalWindow from '@/components/ui/modal-window.vue';
|
||||
import XModalWindow from '@client/components/ui/modal-window.vue';
|
||||
import MkSwitch from './ui/switch.vue';
|
||||
import MkInfo from './ui/info.vue';
|
||||
import MkButton from './ui/button.vue';
|
||||
|
@ -61,13 +61,13 @@
|
||||
import { defineComponent } from 'vue';
|
||||
import { faIdCardAlt, faPlus, faQuoteLeft, faQuoteRight, faRetweet, faReply, faAt, faCheck, faPollH } from '@fortawesome/free-solid-svg-icons';
|
||||
import { faClock } from '@fortawesome/free-regular-svg-icons';
|
||||
import { getNoteSummary } from '../../misc/get-note-summary';
|
||||
import { getNoteSummary } from '@/misc/get-note-summary';
|
||||
import XReactionIcon from './reaction-icon.vue';
|
||||
import MkFollowButton from './follow-button.vue';
|
||||
import notePage from '../filters/note';
|
||||
import { userPage } from '../filters/user';
|
||||
import { i18n } from '@/i18n';
|
||||
import * as os from '@/os';
|
||||
import { i18n } from '@client/i18n';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
@ -1,6 +1,13 @@
|
||||
<template>
|
||||
<div class="mfcuwfyp _noGap_">
|
||||
<XList class="notifications" :items="items" v-slot="{ item: notification }">
|
||||
<transition name="fade" mode="out-in">
|
||||
<MkLoading v-if="fetching"/>
|
||||
|
||||
<MkError v-else-if="error" @retry="init()"/>
|
||||
|
||||
<p class="mfcuwfyp" v-else-if="empty">{{ $ts.noNotifications }}</p>
|
||||
|
||||
<div v-else class="_magnetParent">
|
||||
<XList class="notifications _magnetChild" :items="items" v-slot="{ item: notification }" :no-gap="true">
|
||||
<XNote v-if="['reply', 'quote', 'mention'].includes(notification.type)" :note="notification.note" @update:note="noteUpdated(notification.note, $event)" :key="notification.id"/>
|
||||
<XNotification v-else :notification="notification" :with-time="true" :full="true" class="_panel notification" :key="notification.id"/>
|
||||
</XList>
|
||||
@ -9,21 +16,18 @@
|
||||
<template v-if="!moreFetching">{{ $ts.loadMore }}</template>
|
||||
<template v-if="moreFetching"><MkLoading inline/></template>
|
||||
</button>
|
||||
|
||||
<p class="empty" v-if="empty">{{ $ts.noNotifications }}</p>
|
||||
|
||||
<MkError v-if="error" @retry="init()"/>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { defineComponent, PropType } from 'vue';
|
||||
import paging from '@/scripts/paging';
|
||||
import paging from '@client/scripts/paging';
|
||||
import XNotification from './notification.vue';
|
||||
import XList from './date-separated-list.vue';
|
||||
import XNote from './note.vue';
|
||||
import { notificationTypes } from '../../types';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -120,17 +124,19 @@ export default defineComponent({
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.125s ease;
|
||||
}
|
||||
.fade-enter-from,
|
||||
.fade-leave-to {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.mfcuwfyp {
|
||||
> .empty {
|
||||
margin: 0;
|
||||
padding: 16px;
|
||||
text-align: center;
|
||||
color: var(--fg);
|
||||
}
|
||||
|
||||
> .placeholder {
|
||||
padding: 32px;
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj _panel" tabindex="-1">
|
||||
<MkA :to="`/@${page.user.username}/pages/${page.name}`" class="vhpxefrj _block _isolated" tabindex="-1">
|
||||
<div class="thumbnail" v-if="page.eyeCatchingImage" :style="`background-image: url('${page.eyeCatchingImage.thumbnailUrl}')`"></div>
|
||||
<article>
|
||||
<header>
|
||||
@ -17,7 +17,7 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { userName } from '../filters/user';
|
||||
import * as os from '@/os';
|
||||
import * as os from '@client/os';
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
@ -35,7 +35,6 @@ export default defineComponent({
|
||||
<style lang="scss" scoped>
|
||||
.vhpxefrj {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
|
@ -14,7 +14,7 @@
|
||||
<button class="_button" @click="back()" v-if="history.length > 0"><Fa :icon="faChevronLeft"/></button>
|
||||
<button class="_button" style="pointer-events: none;" v-else><!-- マージンのバランスを取るためのダミー --></button>
|
||||
</template>
|
||||
<div class="yrolvcoq" style="min-height: 100%; background: var(--bg);">
|
||||
<div class="yrolvcoq _flat_">
|
||||
<component :is="component" v-bind="props" :ref="changePage"/>
|
||||
</div>
|
||||
</XWindow>
|
||||
@ -23,12 +23,13 @@
|
||||
<script lang="ts">
|
||||
import { defineComponent } from 'vue';
|
||||
import { faExternalLinkAlt, faExpandAlt, faLink, faChevronLeft, faColumns } from '@fortawesome/free-solid-svg-icons';
|
||||
import XWindow from '@/components/ui/window.vue';
|
||||
import XHeader from '@/ui/_common_/header.vue';
|
||||
import { popout } from '@/scripts/popout';
|
||||
import copyToClipboard from '@/scripts/copy-to-clipboard';
|
||||
import { resolve } from '@/router';
|
||||
import { url } from '@/config';
|
||||
import XWindow from '@client/components/ui/window.vue';
|
||||
import XHeader from '@client/ui/_common_/header.vue';
|
||||
import { popout } from '@client/scripts/popout';
|
||||
import copyToClipboard from '@client/scripts/copy-to-clipboard';
|
||||
import { resolve } from '@client/router';
|
||||
import { url } from '@client/config';
|
||||
import * as symbols from '@client/symbols';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
@ -123,8 +124,8 @@ export default defineComponent({
|
||||
methods: {
|
||||
changePage(page) {
|
||||
if (page == null) return;
|
||||
if (page.INFO) {
|
||||
this.pageInfo = page.INFO;
|
||||
if (page[symbols.PAGE_INFO]) {
|
||||
this.pageInfo = page[symbols.PAGE_INFO];
|
||||
}
|
||||
},
|
||||
|
||||
@ -155,6 +156,7 @@ export default defineComponent({
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.yrolvcoq {
|
||||
--section-padding: 16px;
|
||||
min-height: 100%;
|
||||
background: var(--bg);
|
||||
}
|
||||
</style>
|
||||
|
@ -19,8 +19,8 @@ import XCounter from './page.counter.vue';
|
||||
import XRadioButton from './page.radio-button.vue';
|
||||
import XCanvas from './page.canvas.vue';
|
||||
import XNote from './page.note.vue';
|
||||
import { Hpml } from '@/scripts/hpml/evaluator';
|
||||
import { Block } from '@/scripts/hpml/block';
|
||||
import { Hpml } from '@client/scripts/hpml/evaluator';
|
||||
import { Block } from '@client/scripts/hpml/block';
|
||||
|
||||
export default defineComponent({
|
||||
components: {
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user