Compare commits

...

255 commits

Author SHA1 Message Date
60415eb92c Revert "New error images!"
This reverts commit 0601a89cb8.
2022-08-10 07:09:06 +00:00
2cfb27ca6a Revert "solid borders on qrns fix"
This reverts commit d8f15171ba.
2022-08-10 06:10:24 +00:00
fedc5ffb7a Revert "Obliteration of Ai-chan 🐱🔫"
This reverts commit 7095abf728.
2022-08-10 06:10:03 +00:00
6f7d29185f Merge branch 'main' of https://codeberg.org/thatonecalculator/calckey 2022-08-10 06:09:29 +00:00
68b8c15463 Merge pull request 'develop' (#9022) from develop into main
Reviewed-on: https://codeberg.org/thatonecalculator/calckey/pulls/9022
2022-08-10 05:15:44 +02:00
42eacf2291 docs: 📝 npm -> yarn 2022-08-09 20:06:49 -07:00
53c125245c docs: 📝 Remove duplicate line 2022-08-09 20:02:29 -07:00
587c677b29 feat: 🔖 12.118.1-calc release! 2022-08-09 19:57:18 -07:00
20c32d6b0e Add back migration 2022-08-09 19:56:03 -07:00
103221b367 Merge branch 'develop' of codeberg.org:thatonecalculator/calckey into develop 2022-08-09 19:55:24 -07:00
b8db50567c revert: timeline for non-logged in users 2022-08-09 19:52:07 -07:00
739d8a7b24 Revert "feature(client): Timeline page for non-login users"
This reverts commit 49beee532b.
2022-08-09 19:49:23 -07:00
c5cea77bcd fix: 🐛 Remove header tabs if guest not enabled and not logged in 2022-08-09 16:27:43 -07:00
dc00651c61 fix: 🐛 timeline secured 2022-08-09 16:22:55 -07:00
7e9c16501e fix: 🐛 Allow timeline if logged in, loll 2022-08-09 16:15:58 -07:00
3fd553b82b fix: 🐛 No computed on declared var 2022-08-09 16:13:53 -07:00
4f0030ecc8 fix: 🔒 Remove timeline source if not logged in and guest tl isn't enabled 2022-08-09 16:12:20 -07:00
0ef9dd3519 chore: 🔖 Bump to .3 2022-08-09 15:53:16 -07:00
69a2a29023 chore: ⚗️ More debugging 2022-08-09 15:52:56 -07:00
92af0d39fe fix: ✏️ Same typo as before... 2022-08-09 15:46:07 -07:00
8f441fd0f4 chore: 🔊 Test logging 2022-08-09 15:44:44 -07:00
50417674b2 fix: ✏️ Async typo 2022-08-09 15:40:15 -07:00
4370ef7b1b fix: ⚗️ Attempt to fix routing 2022-08-09 15:38:30 -07:00
d984b40b4c Merge branch 'develop' of codeberg.org:thatonecalculator/calckey into develop 2022-08-09 13:11:11 -07:00
b49dd35907 fix: caption in admin settings
Move splash below theme

Splash below theme
2022-08-09 13:09:54 -07:00
4fcf5b1081 feat: togglable guest timeline
default false

rc 9

no async

welcome explore button to `/explore`

fix: 🔥 Remove meta implementation in routing for now
2022-08-09 13:09:54 -07:00
73a57858e0 feat: custom css/assets
typo

fix gulpfile
2022-08-09 13:09:54 -07:00
fd088d0c85 Seperate splash screen settings in admin panel 2022-08-09 13:09:54 -07:00
e7f2c7df03 fix: recommended timeline
I doubt itll work, but...

🙏

hail mairy

certified typeorm moment

im stuff

debug log

not a fan of js/ts

istg

missing parenthesis

postgres can kiss my ass

didnt need `::string[]` i think

hide caption button

Remove debug log

Clean up

no longer beta!

fix streaming
2022-08-09 13:09:54 -07:00
da83fd1ef2 fix: 🔥 Remove meta implementation in routing for now 2022-08-09 13:00:14 -07:00
47c19181f8 welcome explore button to /explore 2022-08-09 11:23:23 -07:00
19a2c32836 no async 2022-08-09 11:19:03 -07:00
c845d19695 rc 9 2022-08-09 11:14:40 -07:00
5c164119f1 default false 2022-08-09 11:14:14 -07:00
d9b8450efb Togglable guest timeline 2022-08-09 11:12:56 -07:00
ea01259b96 Splash below theme 2022-08-09 10:35:47 -07:00
072be6870f Move splash below theme 2022-08-09 10:35:46 -07:00
9addbd1e2b fix caption in admin settings 2022-08-09 02:40:51 -07:00
1514757198 fix gulpfile 2022-08-09 02:37:12 -07:00
b2829fa7bf typo 2022-08-09 02:34:00 -07:00
3a1daaeaed Custom 2022-08-09 02:33:07 -07:00
9ec3f97505 Seperate splash screen settings in admin panel 2022-08-09 02:09:09 -07:00
0cdd5c2241 fix streaming 2022-08-09 01:52:32 -07:00
d50ad16ac8 no longer beta! 2022-08-09 01:35:18 -07:00
8d9596d209 Clean up 2022-08-09 01:32:50 -07:00
e7fa99de86 Remove debug log 2022-08-09 01:30:38 -07:00
42de210a6c hide caption button 2022-08-09 01:30:17 -07:00
5e612a96f5 didnt need ::string[] i think 2022-08-09 01:28:03 -07:00
90ecb119db postgres can kiss my ass 2022-08-09 01:26:07 -07:00
30bedee17b missing parenthesis 2022-08-09 01:21:27 -07:00
5289de0cf0 istg 2022-08-09 01:19:56 -07:00
82813ffe7b not a fan of js/ts 2022-08-09 01:13:08 -07:00
6aaf8cbd7d debug log 2022-08-09 01:09:44 -07:00
1ae895d098 im stuff 2022-08-09 01:07:46 -07:00
f9d9bd9f50 certified typeorm moment 2022-08-09 01:04:20 -07:00
7a4ddeba16 hail mairy 2022-08-09 01:02:11 -07:00
f08acce023 🙏 2022-08-09 00:57:15 -07:00
b475ea415d I doubt itll work, but... 2022-08-09 00:53:57 -07:00
7fd1f05bb3 debug log 2022-08-09 00:50:11 -07:00
cc7ddc840d cl 2022-08-09 00:45:53 -07:00
16c274aaab add tes as dep, even if unused 2022-08-09 00:40:29 -07:00
b3f109cf52 disable ocr for now 2022-08-09 00:39:27 -07:00
916e9db51a attempt to fix 2022-08-09 00:37:48 -07:00
8914bffe85 debug 2022-08-08 18:24:03 -07:00
eabc23374b .10 2022-08-08 18:01:03 -07:00
7c53328001 im stuff 2022-08-08 18:00:51 -07:00
58ff242241 actually fix this time? 2022-08-08 17:51:18 -07:00
70484a011c fixxx 2022-08-08 17:43:18 -07:00
dde7cecf43 code ing 2022-08-08 17:39:50 -07:00
30e20cb683 why dont it work 😭 2022-08-08 17:27:19 -07:00
62c5225754 .5 2022-08-08 17:24:29 -07:00
a993acd26c try smth 2022-08-08 17:24:21 -07:00
109b111871 typo 2022-08-08 17:17:26 -07:00
ec474acded ck 2022-08-08 17:14:55 -07:00
b7f00ea40a im stuff 2022-08-08 17:14:10 -07:00
a21698b1b2 4.2 2022-08-08 17:02:01 -07:00
2f539272c3 this.inputValue 2022-08-08 17:00:13 -07:00
c1179b35ef try fix 2022-08-08 15:58:31 -07:00
50eb7b693f try to thing 2022-08-08 15:53:16 -07:00
a83adcc5a8 Merge branch 'develop' of codeberg.org:thatonecalculator/calckey into ocr 2022-08-08 15:40:22 -07:00
98721d0dcd Start work on OCR 2022-08-08 15:40:06 -07:00
dccd29fcd2 yarn 3.2.2 2022-08-09 00:38:33 +02:00
795ed6ff85 api docs 2022-08-08 15:12:21 -07:00
54b741e838 Move title svg location 2022-08-08 15:10:31 -07:00
5ffff41a01 .3 2022-08-08 14:58:38 -07:00
7095abf728 Obliteration of Ai-chan 🐱🔫 2022-08-08 14:58:27 -07:00
4cfda11f8c henki credit in app 2022-08-08 14:14:33 -07:00
ff6402b58a .2 2022-08-08 14:10:39 -07:00
0601a89cb8 New error images! 2022-08-08 14:07:09 -07:00
7ad306831d fix: copy visibility for renotes 2022-08-08 00:18:29 -07:00
ef2ec5035b Fix bullboard 2022-08-07 23:22:49 -07:00
47c376d5c5 ack 2022-08-07 23:22:07 -07:00
1ac790a1ff add bullboard 2022-08-07 23:21:28 -07:00
49e383e704 folder ready instructions 2022-08-07 23:20:57 -07:00
648c58c514 dont need yarn update env anymore 2022-08-07 23:19:16 -07:00
0764d42f40 Upgrade to yarn 3.2.2! 2022-08-07 23:17:48 -07:00
30591e529a Readme 2022-08-07 23:07:22 -07:00
313d5a9542 Merge branch 'develop' of codeberg.org:thatonecalculator/calckey into develop 2022-08-07 22:45:26 -07:00
294e091604 Merge remote-tracking branch 'misskey/develop' into develop 2022-08-07 22:45:03 -07:00
44ad92f127 Mention dockerfile 2022-08-08 06:29:55 +02:00
1c94ba16ea Merge pull request 'Update dockerfile to work correctly' (#9020) from hanna/calckey:develop into develop
Reviewed-on: https://codeberg.org/thatonecalculator/calckey/pulls/9020
2022-08-08 06:27:36 +02:00
Hanna
7927c7cce4 Update 'Dockerfile' 2022-08-08 05:32:59 +02:00
syuilo
55220a31e8 12.118.1 2022-08-08 11:38:45 +09:00
a5541d1283 118.0-calc rl 2022-08-06 22:07:10 -07:00
7452f648b6 Merge remote-tracking branch 'misskey/develop' into develop 2022-08-06 22:06:55 -07:00
syuilo
cabbe8c308 fix(client): cannot show some setting pages
Fix #9043
2022-08-07 11:16:22 +09:00
syuilo
395e18e584 12.118.0 2022-08-07 00:39:01 +09:00
syuilo
1c3715a43a
New translations ja-JP.yml (Chinese Traditional) (#9041) 2022-08-07 00:35:28 +09:00
syuilo
e3aa39e050 refactor 2022-08-06 20:04:23 +09:00
syuilo
c4830dcf3a perf(client): use shallowRef as possible 2022-08-06 19:20:53 +09:00
syuilo
cb35ace047 12.118.0-beta.5 2022-08-06 18:17:01 +09:00
syuilo
d8767fa87b Merge branch 'develop' of https://github.com/misskey-dev/misskey into develop 2022-08-06 18:16:46 +09:00
syuilo
5ec10f9ff7
New Crowdin updates (#9022)
* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Swedish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Arabic)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Japanese, Kansai)

* New translations ja-JP.yml (Thai)

* New translations ja-JP.yml (Bengali)

* New translations ja-JP.yml (Indonesian)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Chinese Simplified)

* New translations ja-JP.yml (Russian)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Romanian)

* New translations ja-JP.yml (Ukrainian)

* New translations ja-JP.yml (Czech)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Chinese Traditional)

* New translations ja-JP.yml (Portuguese)

* New translations ja-JP.yml (French)

* New translations ja-JP.yml (Polish)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (Italian)

* New translations ja-JP.yml (Korean)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (German)

* New translations ja-JP.yml (English)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Spanish)

* New translations ja-JP.yml (Slovak)

* New translations ja-JP.yml (Vietnamese)

* New translations ja-JP.yml (Chinese Simplified)
2022-08-06 18:16:37 +09:00
syuilo
f22c32af05 chore(client): tweak scroll behavior in routing 2022-08-06 18:16:21 +09:00
syuilo
b31f09692a enhance(client): improve clock widget 2022-08-06 18:15:13 +09:00
syuilo
3a9da78901 enhance(client): improve clock widget 2022-08-06 16:39:09 +09:00
syuilo
dea5e6207e enhance(client): improve clock widget 2022-08-06 14:02:03 +09:00
74bd9fd01c docs 2022-08-05 14:11:18 -07:00
31e9279c70 More Calckey branding 2022-08-05 14:07:40 -07:00
syuilo
2cd70b80a2 enhance(client): improve clock widgets 2022-08-05 23:51:15 +09:00
9ab37dd087 cl 2022-08-04 18:16:30 -07:00
497675c49e import order 2022-08-04 18:13:56 -07:00
4cae839bfa thumbs up or star trggers pleroma like 2022-08-04 18:12:57 -07:00
e4e20288c9 change to caption 2022-08-04 14:38:24 -07:00
f4753a9342 rtl settings improve 2022-08-04 14:33:49 -07:00
8be0bad287 RITL to wip 2022-08-04 14:30:27 -07:00
c59f1a424b .18 2022-08-04 14:28:24 -07:00
c1509ebcad idk 2022-08-04 14:28:16 -07:00
26e82b73dc im stuff 2022-08-04 14:25:54 -07:00
b376d3e98e stuff? 2022-08-04 14:16:22 -07:00
1151991b70 t 2022-08-04 14:13:12 -07:00
a3aaf78e78 update preferences backup for calckey 2022-08-04 14:01:34 -07:00
f1a704b276 re-arrange settings 2022-08-04 13:59:24 -07:00
9510b66b6a cl 2022-08-04 13:53:57 -07:00
7395644883 make emoji picker case insensitive
fix FoundKeyGang/FoundKey#50
2022-08-04 13:51:39 -07:00
939a5abb8c fk 2022-08-04 13:51:03 -07:00
84bb48d0e8 refactor: welcome.setup.vue to composition api 2022-08-04 13:50:39 -07:00
66cb028713 space 2022-08-04 13:50:05 -07:00
1fffcff4bc fix textarea not updating properly
fixes FoundKeyGang/FoundKey#54
2022-08-04 13:38:33 -07:00
db6907a6fa client: fix lint "quotes" 2022-08-04 13:37:06 -07:00
e7d942554a calckey 2022-08-04 13:36:43 -07:00
74cf3d887c backend: fix lint "no-throw-literal" 2022-08-04 13:36:37 -07:00
dd28859348 fix spelling error 2022-08-04 13:34:18 -07:00
725ee609dd b5 2022-08-04 13:32:10 -07:00
0e7361d4b8 Merge remote-tracking branch 'misskey/develop' into develop 2022-08-04 13:31:54 -07:00
d29b4001f0 Undo more syuilo fuckup 2022-08-04 13:29:09 -07:00
syuilo
bdaa35d11f feat(client): improve widget 2022-08-04 22:20:00 +09:00
b934c738a6
Collapse long notes (#8990)
* Revert "Revert option to collapse long notes (#8561)"

This reverts commit 71150f21cd and reinstates
the option to collapse long notes again after they were expanded.

* fix(client): wrongly collapsed posts

* fix: don't use ref

* tweak style

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
2022-08-01 17:55:24 +09:00
syuilo
f3164c9cf2 fix: use new for throw error
Co-Authored-By: Acid Chicken (硫酸鶏) <root@acid-chicken.com>
2022-08-01 17:44:53 +09:00
syuilo
78f061b9db update deps 2022-07-31 22:01:01 +09:00
tamaina
14c2f9e47e
feature: Client Preferences Registry (#8511)
* Fix settings page

* nanka iroiro

* clean up

* clean up

* feature: Client Preferences Registry on the account

* add changelog

* インデックスに戻ってもタイトルが残ってしまうのを修正

* fix createdAt -> updatedAt

* remove console.log

* 適用→このデバイスに適用

* add wallpaper

* ローカルのjsonファイルを保存・読み込みできるように

* clean up

* use apiWithDialog

* Update packages/client/src/pages/settings/preferences-registry.vue

Co-authored-by: Andreas Nedbal <github-bf215181b5140522137b3d4f6b73544a@desu.email>

* Update packages/client/src/pages/settings/preferences-registry.vue

Co-authored-by: Andreas Nedbal <github-bf215181b5140522137b3d4f6b73544a@desu.email>

* Update packages/client/src/pages/settings/preferences-registry.vue

Co-authored-by: Andreas Nedbal <github-bf215181b5140522137b3d4f6b73544a@desu.email>

* fix lint

* ✌️

* change router

* nanka iroiro

* tweak

Co-authored-by: syuilo <Syuilotan@yahoo.co.jp>
Co-authored-by: Andreas Nedbal <github-bf215181b5140522137b3d4f6b73544a@desu.email>
2022-07-31 21:55:20 +09:00
syuilo
b5bf4e75a6 use es2021 for node 2022-07-31 14:20:10 +09:00
414d4c2031 typo 2022-07-29 15:23:53 -07:00
025a8e23c4 revers 2022-07-29 15:07:07 -07:00
3ccf2ea098 test 2022-07-29 09:27:39 -07:00
5b9e2ec817 try new query 2022-07-29 09:17:10 -07:00
2d14777573 try to fix streaming 2022-07-29 08:57:53 -07:00
669ee2c600 im stuff 2022-07-29 01:11:25 -07:00
bcc219566d remove console log 2022-07-29 00:44:17 -07:00
261e00fcde console log 2022-07-29 00:40:27 -07:00
8d1144cda0 stuff 2022-07-29 00:35:49 -07:00
1372a246b5 brackets 2022-07-29 00:33:51 -07:00
b51a8750a3 () 2022-07-29 00:30:42 -07:00
b53bbc960f im stuff 2022-07-29 00:28:21 -07:00
5112f6425e try @Johann150's solution :) 2022-07-29 00:25:22 -07:00
13648d03a1 im stuff 2022-07-29 00:20:40 -07:00
40c197ed60 no quotes 2022-07-29 00:13:29 -07:00
152c8c9cae testtt 2022-07-29 00:09:49 -07:00
8380487afe testtt 2022-07-29 00:00:39 -07:00
3fae29e4d5 FROM clause 2022-07-28 23:57:18 -07:00
15ad79c3de aaa 2022-07-28 23:53:42 -07:00
e639496be2 fix!!!! 2022-07-28 23:46:36 -07:00
291649b6e7 proper error 2022-07-28 23:01:13 -07:00
9d53e8322a bruh 2022-07-28 22:57:54 -07:00
e8ccd62a27 help i cant write queer ees :( 2022-07-28 22:54:59 -07:00
e447e72c77 ack 2022-07-28 22:46:51 -07:00
664ea1dcf4 test :( 2022-07-28 22:42:40 -07:00
73ac045aa8 typo!! 2022-07-28 22:31:49 -07:00
2c7ece6bc6 I think i fixed it! 2022-07-28 22:29:54 -07:00
1af22f0bc2 test 2022-07-28 22:17:59 -07:00
4ac0540d01 Calckey 2022-07-28 21:59:49 -07:00
60dde2ef08 signs post icon for recommended 2022-07-28 21:58:25 -07:00
a0529277e3 typo in file name 2022-07-28 21:50:00 -07:00
c96c0ac7d3 typo 2022-07-28 21:49:36 -07:00
60e92cb6a9 typo 2022-07-28 21:45:39 -07:00
74a62309de typo 2022-07-28 21:39:48 -07:00
a761801a27 typo 2022-07-28 21:36:38 -07:00
dac68a30ff fix duplicate keys 2022-07-28 21:34:54 -07:00
0bfbf23ea5 japanese locale 2022-07-28 21:32:34 -07:00
260f87d715 Merge branches 'develop' and 'develop' of codeberg.org:thatonecalculator/calckey into develop 2022-07-28 21:28:53 -07:00
41dd258789 .15 2022-07-28 21:28:32 -07:00
b3a50187b1 Recommended timeline! 2022-07-28 21:28:13 -07:00
fc915b89a2 Patrons 2022-07-29 03:25:02 +02:00
494d672920 .14 2022-07-28 09:26:27 -07:00
1df313ec17 Merge branch 'develop' of codeberg.org:thatonecalculator/calckey into develop 2022-07-28 09:25:19 -07:00
d8f15171ba solid borders on qrns fix 2022-07-28 09:25:11 -07:00
66813e2975 Less cluttered notification summary 2022-07-28 09:24:43 -07:00
eda97e870e Formatting 2022-07-28 07:03:37 +02:00
fccab12c82 im stufff 2022-07-27 21:36:02 -07:00
84c3664628 A 2022-07-27 21:33:23 -07:00
d37e83046c fix againnn 2022-07-27 21:31:05 -07:00
4ac6073a77 fix migration 2022-07-27 21:25:37 -07:00
1001c18e17 Fix migration 2022-07-27 21:22:03 -07:00
ee9ba3ef81 Merge branch 'develop' of codeberg.org:thatonecalculator/calckey into develop 2022-07-27 21:01:31 -07:00
7e22f5fea3 adjust padding on icons only 2022-07-27 21:00:41 -07:00
61b2c2e973 Update 'CALCKEY.md' 2022-07-28 05:58:00 +02:00
b96019ff9c padding 2022-07-27 20:43:51 -07:00
a82c2fd926 foundkey changes 2022-07-27 19:47:00 -07:00
b60d8f147c fix lints 2022-07-27 19:46:56 -07:00
Chloe Kudryavtsev
29cdb93104 backend: improve mutes and blocks
Mutes and blocks now also apply recursively to replies and renotes.
Furthermore, any mentioned user being muted or blocked will also apply.
2022-07-27 19:46:26 -07:00
f73d6c5bb2 formatting 2022-07-27 19:37:52 -07:00
027c09e6fe 🚚 2022-07-27 19:37:21 -07:00
3ada9db8f7 Formatting 2022-07-27 19:36:58 -07:00
1eb1239684 emojos 2022-07-27 19:36:04 -07:00
9d8c2b67cc rose pine code blocks 2022-07-27 19:32:21 -07:00
dc09283b5d formatting 2022-07-27 19:31:33 -07:00
ef78dbb7ff planned 2022-07-27 19:29:49 -07:00
6f5ebb3649 changelog 2022-07-27 19:28:19 -07:00
5b67390baf loader stroke 6px 2022-07-27 19:27:29 -07:00
5464e8a728 Toggleable showing updates 2022-07-27 19:24:51 -07:00
aa6ca4d6e9 speed up to 1.2s 2022-07-27 12:34:23 -07:00
979c7cc839 LOADING ANIMATION IS FINALLY FIXED 2022-07-27 12:32:14 -07:00
80860ec2a7 WIP 2022-07-27 12:29:25 -07:00
db787696b7 40 2022-07-27 12:28:36 -07:00
76ce3f80de 🙏 2022-07-27 12:27:09 -07:00
e1d6e910f7 fix fr on god no cap 2022-07-27 12:04:26 -07:00
160cb38afa hopefully fix fo the last time 2022-07-27 11:57:09 -07:00
f277aaf677 fix again 2022-07-27 11:52:10 -07:00
6e7efb8c43 fix 2022-07-27 11:46:52 -07:00
ac570286a2 accessibility <3 2022-07-27 11:32:55 -07:00
86014698be README 2022-07-27 11:31:56 -07:00
86346ff311 .11 2022-07-27 11:25:01 -07:00
12c030af02 more readable icons 2022-07-27 11:21:25 -07:00
b36bb813c1 Spinner 2022-07-27 11:19:57 -07:00
74e45c24af Changelog 2022-07-27 10:36:49 -07:00
be7e65f082 planned 2022-07-27 10:28:13 -07:00
1129a2ec8c Custom splash icons! 2022-07-27 10:25:30 -07:00
75a75f8508 Codename aqua! 2022-07-27 10:08:53 -07:00
eb843e7cff 🎨 2022-07-27 10:07:52 -07:00
7ae95770aa Add endpoint 2022-07-27 09:58:18 -07:00
4a7166625f clean up 2022-07-27 09:47:29 -07:00
4d22797624 Custom MOTD! 2022-07-27 09:46:35 -07:00
dc66eea6ac Merge branch 'develop' of codeberg.org:thatonecalculator/calckey into develop 2022-07-27 09:31:54 -07:00
9e1e64e58d Update 'CALCKEY.md' 2022-07-27 08:55:58 +02:00
6fac9bc1d6 🎨 2022-07-26 21:35:03 -07:00
67ff22be70 🎨 2022-07-26 21:33:52 -07:00
e1c96a88e3 🎨 2022-07-26 21:32:03 -07:00
768de4f29a More rpine 2022-07-26 21:18:36 -07:00
82e5b80641 cl 2022-07-26 21:03:37 -07:00
4c1b2c79c5 cl 2022-07-26 21:03:22 -07:00
805e389d06 accesibility 2022-07-26 21:02:43 -07:00
a88fe74b82 semi for children 2022-07-26 14:45:36 -07:00
2990539c7d Merge branch 'main' of https://codeberg.org/thatonecalculator/calckey 2022-07-26 21:42:01 +00:00
0c30dd46de merge 2022-07-26 21:40:30 +00:00
233edc9495 hide federation from public local instance info if not mod 2022-07-26 04:47:25 +00:00
125 changed files with 2991 additions and 641 deletions

View file

Before

Width:  |  Height:  |  Size: 8 KiB

After

Width:  |  Height:  |  Size: 8 KiB

View file

@ -3,19 +3,25 @@
## Planned
- MFM button
- Make more of the post clickable like every other SNS
- Better Messaging UI
- Classic mode make instance icon bring up new context menu
- Like/star button
- Option to publicize instance blocks
- Better intro/onboarding
- Fully revamp non-logged-in screen
- Remote follow button
- Personal notes for all accounts
- Non-nyaify cat mode
- Timeline filters
- "Bubble" timeline
- Filter notifications by user
- Remove NSFW/AI stuff
- Build flag to remove NSFW/AI stuff
- [Rat mode?](https://stop.voring.me/notes/933fx97bmd)
## Work in progress
- OCR image captioning
- Admin custom CSS
- Improve accesibility score
<details><summary>Current Misskey score is 57/100</summary>
@ -23,41 +29,43 @@
</details>
## Work in progress
- Less cluttered notification summary
- Better timeline top bar
- Admin custom CSS
## Implemented
- Yarn 3
- Fix Dockerfile @hanna
- Saner defaults
- Recommended instances timeline
- Star as default reaction
- Rosé Pine by default (+ non-themable elements made Rosé Pine)
- Better sidebar/navbar
- MOTD (customizable by admins!)
- Custom randomized splash icons
- [Profile background as banner](https://codeberg.org/Freeplay/Misskey-Tweaks/src/branch/main/snippets/profile-background.styl)
- Better timeline top bar
- Mark as read from notifications widget
- Less cluttered notification summary
- Better welcome screen (not logged in)
- Ability to turn off "Connection lost" message
- Raw instance info only for moderators
- New spinner animation
- Spinner instead of "Loading..."
- SearchX instead of Google
- Spacing on group items
- MOTD
- Reply limit bug fixed (somewhat)
- Quotes have solid border
- Reply limit bug fixed
- Custom assets
- Make showing the update popup optional
- [Make showing ads optional](https://github.com/misskey-dev/misskey/pull/8996)
- [OAuth bearer token authentication](https://github.com/misskey-dev/misskey/pull/9021)
- [Styled Repair Tools](https://github.com/misskey-dev/misskey/pull/8956)
- [Option to make enter send message](https://github.com/misskey-dev/misskey/pull/8954)
- [Make showing ads optional](https://github.com/misskey-dev/misskey/pull/8996)
- [Autocomplete in messaging](https://github.com/misskey-dev/misskey/pull/8955)
- [Star is like](https://github.com/JakeMBauer/Misskey-Extras/blob/master/patches/star-is-like.patch)
- [Star is generic like/favorite](https://github.com/JakeMBauer/Misskey-Extras/blob/master/patches/star-is-like.patch)
- 👍 also triggers generic like/favorite
- [Add additional background for acrylic popups if backdrop-filter is unsupported](https://github.com/misskey-dev/misskey/pull/8671)
- [Timeline page for non-login users](https://github.com/misskey-dev/misskey/pull/8927)
- [Add parameters to MFM rotate](https://github.com/misskey-dev/misskey/pull/8549)
- Many changes from [Foundkey](https://akkoma.dev/FoundKeyGang/Foundkey)
- 0ece67b04c3f0365057624c1068808276ccab981: refactor pages/auth.form.vue to composition API
- 0ece67b04c3f0365057624c1068808276ccab981: refactor pages/auth.form.vue to composition API
- 0ece67b04c3f0365057624c1068808276ccab981: refactor pages/auth.form.vue to composition API
- 4bc9610d8bf5af736b5e89e4782395705de45d7d: remove unnecessary joins
- 9ee609d70082f7a6dc119a5d83c0e7c5e1208676: enhance privacy of notes
@ -70,3 +78,10 @@
- b630cd7eacd695bb705e6748c87f38425ec4ed45: refactor: add NoteReactions.packMany
- 3fe351df6d4e21f7748c46adfa6ca165abd030c0: fix: catch errors from packing with detail
- 63591da33e233b2ed0ab331ae6bb3c9eff5020ae: refactor: colours in queue chart
- 0f6d94f1e7e1f58cfbf8d07e5f835f8de626842e: backend: improve mutes and blocks
- e2bf2715a6462ed377b033956d65260157f042ea: fix spelling error
- 09a7eabda137e77f81ab31f65d69329670693c8d: backend: fix lint "no-throw-literal"
- 4fbe2e065e75ed3e5b4dfdfd4be3baa03cc447c3: client: fix lint "quotes"
- 585e4f5c42cfafb6cdf7eb601ab435d6a4d85a96: fix textarea not updating properly
- 30d8bc9259cb6b72ed76d67b21dbb4cdceca8327: refactor: welcome.setup.vue to composition api
- 751921e24f37ed707fe44a40d88eebb1299efa35: make emoji picker case insensitive

View file

@ -9,10 +9,17 @@
You should also include the user name that made the change.
-->
## 12.x.x (unreleased)
## 12.118.1 (2022/08/08)
### Bugfixes
- Client: can not show some setting pages @syuilo
## 12.118.0 (2022/08/07)
### Improvements
- Client: 設定のバックアップ/リストア機能
- Client: Add vi-VN language support
- Client: Add unix time widget @syuilo
### Bugfixes
- Server: リモートユーザーを正しくブロックできるように修正する @xianonn

View file

@ -1,33 +1,26 @@
FROM node:16.15.1-bullseye AS builder
FROM node:alpine
ENV YARN_CHECKSUM_BEHAVIOR=update
ARG NODE_ENV=production
WORKDIR /misskey
# Copy Files
COPY . ./
RUN apt-get update
RUN apt-get install -y build-essential
RUN git submodule update --init
# Install Dependencies
RUN apk update
RUN apk add git ffmpeg tini alpine-sdk
# Configure corepack and yarn
RUN corepack enable
RUN yarn set version berry
RUN yarn plugin import workspace-tools
# Install Dependencies
RUN yarn install
RUN yarn build
# Remove git files
RUN rm -rf .git
FROM node:16.15.1-bullseye-slim AS runner
WORKDIR /misskey
RUN apt-get update
RUN apt-get install -y ffmpeg tini
COPY --from=builder /misskey/node_modules ./node_modules
COPY --from=builder /misskey/built ./built
COPY --from=builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
COPY --from=builder /misskey/packages/backend/built ./packages/backend/built
COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node_modules
COPY . ./
ENV NODE_ENV=production
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["npm", "run", "migrateandstart"]
ENTRYPOINT [ "/sbin/tini", "--" ]
CMD [ "yarn", "run", "migrateandstart" ]

View file

@ -1,6 +1,6 @@
<div align="center">
<a href="https://stop.voring.me/">
<img src="./assets/title_float.svg" alt="Calckey logo" style="border-radius:50%" width="400"/>
<img src="./.github/title_float.svg" alt="Calckey logo" style="border-radius:50%" width="400"/>
</a>
**🌎 **[Calckey](https://stop.voring.me/)** is an open source, decentralized social media platform that's free forever! 🚀**
@ -11,33 +11,30 @@
<img src="https://pool.jortage.com/voringme/misskey/e7cd2a17-8b23-4e1e-b5cf-709480c623e2.png" align="right" height="320px"/>
## ✨ Features
- **ActivityPub support**\
Not on Calckey? No problem! Not only can Calckey/Misskey instances talk to each other, but you can make friends with people on other networks like Mastodon and Pixelfed!
- **Reactions**\
You can add emoji reactions to any post! No longer are you bound by a like button, show everyone exactly how you feel with the tap of a button.
- **Drive**\
With Calckey's built in drive, you get cloud storage right in your social media, where you can upload any files, make folders, and find media from posts you've made!
- **Rich Web UI**\
Calckey has a rich and easy to use Web UI!
It is highly customizable, from changing the layout and adding widgets to making custom themes.
Furthermore, plugins can be created using AiScript, an original programming language.
- And much more...
# ✨ About Calckey
- Calckey is based off of Misskey, a powerful microblogging server on ActivityPub with features such as emoji reactions, a customizable web ui, rich chatting, and much more!
- Calckey adds many quality of life changes and bug fixes for users and instance admins alike.
- Read **[this document](./CALCKEY.md)** all for current and future differences.
- Notable differences:
- Recommended Instances timeline
- Improved notifications
- Many more user and admin settings
</div>
<div style="clear: both;"></div>
## 📝 Documentation
# 📝 Documentation
Misskey documentation can be found at [Misskey Hub](https://misskey-hub.net/).
- Misskey documentation can be found on [Misskey Hub](https://misskey-hub.net/)
- API reference can be found on any Calckey instance's [API doc page](https://stop.voring.me/api-doc)
## 🤔 What's different about Calckey?
Read [this](./CALCKEY.md) for current and future differences.
# 🚚 Migrating from Misskey to Calckey
## 🛻 Migrating from Misskey to Calckey
You need at least 🐢 NodeJS v16.15.0 (v18.4.0 recommended!) and *exactly* 🧶 Yarn v3.2.2!
You need at least 🐢 NodeJS v16.10.0 (>v18.0.0 \<v18.6.0 reccomended!) and *exactly* 🧶 Yarn v3.2.1!
## 📩 Install dependencies
```sh
# nvm install 18.4.0 && nvm alias default 18.4.0 && nvm use 18.4.0
@ -45,13 +42,40 @@ corepack enable
yarn set version berry
```
## 👀 Get folder ready
```sh
git clone https://codeberg.org/thatonecalculator/calckey.git
cd calckey/
# git checkout main # if you want only stable versions
cp ../misskey/.config/default.yml ./.config/default.yml # or wherever misskey folder is
cp ../misskey/.config/default.yml ./.config/default.yml # replace `../misskey/` with misskey path, replace `default.yml` with `docker.yml` if you use docker
# cp -r ../misskey/files . # if you don't use object storage
YARN_CHECKSUM_BEHAVIOR=update yarn install
NODE_ENV=production npm run build && npm run migrate
```
## 💅 Customize
- To add custom CSS for all users, edit `/custom/instance.css`.
- To add static assets (such as images for the splash screen), place them in the `/custom/` folder. They'll then be avaliable on `https://yourinstance.tld/static-assets/filename.png`.
## 🚀 Build and launch!
### `git pull` and run these steps to update Calckey in the future!
```sh
# git pull
yarn install
NODE_ENV=production yarn run build && yarn run migrate
# Edit service to point to calckey folder and restart!
```
### 🐳 Docker
```sh
# git pull
sudo docker-compose build
sudo docker-compose stop && sudo docker-compose up -d
```
# 💸 Patrons
None yet! You can support of the development of this fork here, every little bit counts: https://liberapay.com/ThatOneCalculator/

7
custom/instance.css Normal file
View file

@ -0,0 +1,7 @@
/*
* !!! WARNING !!!
* Editing this file may cause your instance to break for EVERYONE.
* Please know what you're doing and test it out with regular user custom CSS.
* With that said, GLHF!
* This may eventuallly be replaced with a function in the admin panel.
*/

View file

@ -15,6 +15,10 @@ gulp.task('copy:backend:views', () =>
gulp.src('./packages/backend/src/server/web/views/**/*').pipe(gulp.dest('./packages/backend/built/server/web/views'))
);
gulp.task('copy:backend:custom', () =>
gulp.src('./custom/*').pipe(gulp.dest('./packages/backend/assets/'))
);
gulp.task('copy:client:fonts', () =>
gulp.src('./packages/client/node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/_client_dist_/fonts/'))
);
@ -35,6 +39,7 @@ gulp.task('copy:client:locales', cb => {
cb();
});
gulp.task('build:backend:script', () => {
return gulp.src(['./packages/backend/src/server/web/boot.js', './packages/backend/src/server/web/bios.js', './packages/backend/src/server/web/cli.js'])
.pipe(replace('LANGS', JSON.stringify(Object.keys(locales))))
@ -53,7 +58,7 @@ gulp.task('build:backend:style', () => {
});
gulp.task('build', gulp.parallel(
'copy:client:locales', 'copy:backend:views', 'build:backend:script', 'build:backend:style', 'copy:client:fonts', 'copy:client:fontawesome'
'copy:client:locales', 'copy:backend:views', 'copy:backend:custom', 'build:backend:script', 'build:backend:style', 'copy:client:fonts', 'copy:client:fontawesome'
));
gulp.task('default', gulp.task('build'));

View file

@ -52,6 +52,7 @@ searchUser: "ابحث عن مستخدمين"
reply: "رد"
loadMore: "عرض المزيد"
showMore: "عرض المزيد"
showLess: "اغلق"
youGotNewFollower: "يتابعك"
receiveFollowRequest: "تلقيت طلب متابعة"
followRequestAccepted: "قُبل طلب المتابعة"

View file

@ -52,6 +52,7 @@ searchUser: "ব্যবহারকারী খুঁজুন..."
reply: "জবাব"
loadMore: "আরও দেখুন"
showMore: "আরও দেখুন"
showLess: "বন্ধ"
youGotNewFollower: "আপনাকে অনুসরণ করছে"
receiveFollowRequest: "অনুসরণ করার জন্য অনুরোধ পাওয়া গেছে"
followRequestAccepted: "অনুসরণ করার অনুরোধ গৃহীত হয়েছে"

View file

@ -52,6 +52,7 @@ searchUser: "Vyhledat uživatele"
reply: "Odpovědět"
loadMore: "Zobrazit více"
showMore: "Zobrazit více"
showLess: "Zavřít"
youGotNewFollower: "Máte nového následovníka"
receiveFollowRequest: "Žádost o sledování přijata"
followRequestAccepted: "Žádost o sledování přijata"

View file

@ -52,6 +52,7 @@ searchUser: "Nach einem Benutzer suchen"
reply: "Antworten"
loadMore: "Mehr laden"
showMore: "Mehr anzeigen"
showLess: "Schließen"
youGotNewFollower: "ist dir gefolgt"
receiveFollowRequest: "Follow-Anfrage erhalten"
followRequestAccepted: "Follow-Anfrage akzeptiert"
@ -561,6 +562,7 @@ author: "Autor"
leaveConfirm: "Es gibt unspeicherte Änderungen. Möchtest du diese verwerfen?"
manage: "Verwaltung"
plugins: "Plugins"
preferencesBackups: "Einstellungsbackups"
deck: "Deck"
undeck: "Deck verlassen"
useBlurEffectForModal: "Weichzeichnungseffekt für Modals verwenden"
@ -862,7 +864,7 @@ requireAdminForView: "Melde dich mit einem Administratorkonto an, um dies einzus
isSystemAccount: "Ein Benutzerkonto, dass durch das System erstellt und automatisch kontrolliert wird."
typeToConfirm: "Bitte gib zur Bestätigung {x} ein"
deleteAccount: "Benutzerkonto löschen"
document: "Dokument"
document: "Dokumentation"
numberOfPageCache: "Seitencachegröße"
numberOfPageCacheDescription: "Das Erhöhen dieses Caches führt zu einer angenehmerern Benutzererfahrung, erhöht aber Serverlast und Arbeitsspeicherauslastung."
logoutConfirm: "Wirklich abmelden?"
@ -941,6 +943,24 @@ _plugin:
install: "Plugins installieren"
installWarn: "Installiere bitte nur vertrauenswürdige Plugins."
manage: "Plugins verwalten"
_preferencesBackups:
list: "Erstellte Backups"
saveNew: "Neu erstellen"
loadFile: "Von Datei laden"
apply: "Auf dieses Gerät anwenden"
save: "Speichern"
inputName: "Gib einen Namen für dieses Backup ein"
cannotSave: "Speichern fehlgeschlagen"
nameAlreadyExists: "Es existiert bereits ein Backup unter dem Namen \"{name}\". Bitte gib einen anderen Namen ein."
applyConfirm: "Wirklich das Backup \"{name}\" auf dieses Gerät anwenden? Bestehende Einstellungen darauf werden überschrieben."
saveConfirm: "Als {name} speichern?"
deleteConfirm: "Das Backup {name} löschen?"
renameConfirm: "Soll dieses Backup von \"{old}\" zu \"{new}\" umbenannt werden?"
noBackups: "Keine Backups existieren. Backups können über \"Neu erstellen\" erstelllt werden."
createdAt: "Erstellt am: {date} {time}"
updatedAt: "Aktualisiert am: {date} {time}"
cannotLoad: "Laden fehlgeschlagen"
invalidFile: "Ungültiges Dateiformat."
_registry:
scope: "Scope"
key: "Schlüssel"
@ -1024,6 +1044,8 @@ _mfm:
sparkleDescription: "Verleiht Inhalt einen glitzernden Partikeleffekt."
rotate: "Drehen"
rotateDescription: "Dreht den Inhalt um einen angegebenen Winkel."
plain: "Schlicht"
plainDescription: "Deaktiviert jegliche MFM-Syntax, die sich innerhalb dieses MFM-Effekts befindet."
_instanceTicker:
none: "Nie anzeigen"
remote: "Für Benutzer fremder Instanzen anzeigen"
@ -1257,6 +1279,7 @@ _widgets:
activity: "Aktivität"
photos: "Fotos"
digitalClock: "Digitaluhr"
unixClock: "UNIX-Uhr"
federation: "Föderation"
instanceCloud: "Instanzwolke"
postForm: "Notizfenster"

View file

@ -52,6 +52,7 @@ searchUser: "Search for a user"
reply: "Reply"
loadMore: "Load more"
showMore: "Show more"
showLess: "Close"
youGotNewFollower: "followed you"
receiveFollowRequest: "Follow request received"
followRequestAccepted: "Follow request accepted"
@ -322,6 +323,7 @@ connectService: "Connect"
disconnectService: "Disconnect"
enableLocalTimeline: "Enable local timeline"
enableGlobalTimeline: "Enable global timeline"
enableRecommendedTimeline: "Enable recommended timeline"
disablingTimelinesInfo: "Adminstrators and Moderators will always have access to all timelines, even if they are not enabled."
registration: "Register"
enableRegistration: "Enable new user registration"
@ -561,6 +563,7 @@ author: "Author"
leaveConfirm: "There are unsaved changes. Do you want to discard them?"
manage: "Management"
plugins: "Plugins"
preferencesBackups: "Preference backups"
deck: "Deck"
undeck: "Leave Deck"
useBlurEffectForModal: "Use blur effect for modals"
@ -789,6 +792,7 @@ previewNoteText: "Show preview"
customCss: "Custom CSS"
customCssWarn: "This setting should only be used if you know what it does. Entering improper values may cause the client to stop functioning normally."
global: "Global"
recommended: "Recommended"
squareAvatars: "Display squared avatars"
sent: "Sent"
received: "Received"
@ -861,7 +865,6 @@ recentNHours: "Last {n} hours"
recentNDays: "Last {n} days"
noEmailServerWarning: "Email server not configured."
thereIsUnresolvedAbuseReportWarning: "There are unsolved reports."
recommended: "Recommended"
check: "Check"
driveCapOverrideLabel: "Change the drive capacity for this user"
driveCapOverrideCaption: "Reset the capacity to default by inputting a value of 0 or lower."
@ -869,7 +872,7 @@ requireAdminForView: "You must log in with an administrator account to view this
isSystemAccount: "An account created and automatically operated by the system."
typeToConfirm: "Please enter {x} to confirm"
deleteAccount: "Delete account"
document: "Document"
document: "Documentation"
numberOfPageCache: "Number of cached pages"
numberOfPageCacheDescription: "Increasing this number will improve convenience for users but cause more server load as well as more memory to be used."
logoutConfirm: "Really log out?"
@ -901,6 +904,15 @@ move: "Move"
showAds: "Show ads"
enterSendsMessage: "Press Return in Messaging to send message (off is Ctrl + Return)"
adminCustomCssWarn: "This setting should only be used if you know what it does. Entering improper values may cause EVERYONE'S clients to stop functioning normally. Please ensure your CSS works properly by testing it in your user settings."
customMOTD: "Custom MOTD (splash screen messages)"
customMOTDDescription: "Custom messages for the MOTD (splash screen) separated by line breaks to be shown randomly every time a user loads/reloads the page."
customSplashIcons: "Custom splash screen icons (urls)"
customSplashIconsDescription: "URLs for custom splash screen icons separated by line breaks to be shown randomly every time a user loads/reloads the page. Please make sure the images are on a static URL, preferably all resized to 192x192."
showUpdates: "Show a popup when Calckey updates"
recommendedInstances: "Recommended instances"
recommendedInstancesDescription: "Recommended instances seperated by line breaks to appear in the recommended timeline. Do NOT add `https://`, ONLY the domain."
caption: "Auto Caption"
splash: "Splash Screen"
_sensitiveMediaDetection:
description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server."
@ -952,6 +964,24 @@ _plugin:
install: "Install plugins"
installWarn: "Please do not install untrustworthy plugins."
manage: "Manage plugins"
_preferencesBackups:
list: "Created backups"
saveNew: "Save new backup"
loadFile: "Load from file"
apply: "Apply to this device"
save: "Save changes"
inputName: "Please enter a name for this backup"
cannotSave: "Saving failed"
nameAlreadyExists: "A backup called \"{name}\" already exists. Please enter a different name."
applyConfirm: "Do you really want to apply the \"{name}\" backup to this device? Existing settings of this device will be overwritten."
saveConfirm: "Save backup as {name}?"
deleteConfirm: "Delete the {name} backup?"
renameConfirm: "Rename this backup from \"{old}\" to \"{new}\"?"
noBackups: "No backups exist. You may backup your client settings on this server by using \"Create new backup\"."
createdAt: "Created at: {date} {time}"
updatedAt: "Updated at: {date} {time}"
cannotLoad: "Loading failed"
invalidFile: "Invalid file format"
_registry:
scope: "Scope"
key: "Key"
@ -1035,6 +1065,8 @@ _mfm:
sparkleDescription: "Gives content a sparkling particle effect."
rotate: "Rotate"
rotateDescription: "Turns content by a specified angle."
plain: "Plain"
plainDescription: "Deactivates the effects of all MFM contained within this MFM effect."
_instanceTicker:
none: "Never show"
remote: "Show for remote users"
@ -1269,6 +1301,7 @@ _widgets:
activity: "Activity"
photos: "Photos"
digitalClock: "Digital clock"
unixClock: "UNIX clock"
federation: "Federation"
instanceCloud: "Instance cloud"
postForm: "Posting form"
@ -1377,6 +1410,7 @@ _instanceCharts:
_timelines:
home: "Home"
local: "Local"
recommended: "Recommended"
social: "Social"
global: "Global"
_pages:

View file

@ -1,16 +1,16 @@
---
_lang_: "Español"
headlineMisskey: "Red conectada por notas"
introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto.\nEscribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos 👍\nExplora un nuevo mundo 🚀"
introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto.\nEscribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos 👍\n¡Exploremos juntos un nuevo mundo! 🚀"
monthAndDay: "{day}/{month}"
search: "Buscar"
notifications: "Notificaciones"
username: "Nombre de usuario"
password: "Contraseña"
forgotPassword: "Olvidé mi Contraseña"
fetchingAsApObject: "Buscando en el fediverso"
fetchingAsApObject: "Recuperando desde el Fediverso..."
ok: "OK"
gotIt: "Entendido"
gotIt: "¡Lo tengo!"
cancel: "Cancelar"
enterUsername: "Introduce el nombre de usuario"
renotedBy: "Renotado por {user}"
@ -22,36 +22,37 @@ basicSettings: "Configuración Básica"
otherSettings: "Configuración avanzada"
openInWindow: "Abrir en una ventana"
profile: "Perfil"
timeline: "Linea de tiempo"
noAccountDescription: "Este usuario no tiene una descripción"
timeline: "Línea de tiempo"
noAccountDescription: "Este usuario no ha escrito su biografía aún"
login: "Iniciar sesión"
loggingIn: "Iniciando sesión"
logout: "Cerrar sesión"
signup: "Registrarse"
uploading: "Cargando"
uploading: "Cargando..."
save: "Guardar"
users: "Usuarios"
addUser: "Agregar usuario"
favorite: "Favorito"
favorite: "Añadir a favoritos"
favorites: "Favoritos"
unfavorite: "Quitar de favoritos"
favorited: "Añadido a favoritos"
favorited: "Añadido a favoritos."
alreadyFavorited: "Ya había sido añadido a favoritos"
cantFavorite: "No fue añadido a favoritos"
pin: "Fijar"
cantFavorite: "No se puede añadir a favoritos."
pin: "Fijar al perfil"
unpin: "Desfijar"
copyContent: "Copiar contenido"
copyLink: "Copiar enlace"
delete: "Borrar"
deleteAndEdit: "Borrar y editar"
deleteAndEditConfirm: "¿Quieres borrar y editar este nota? Las reacciones, renotes, respuestas y todo desaparecerán."
deleteAndEditConfirm: "¿Estás seguro de que quieres borrar esta nota y editarla? Perderás todas las reacciones, renotas y respuestas."
addToList: "Agregar a lista"
sendMessage: "Énviar mensaje"
sendMessage: "Enviar un mensaje"
copyUsername: "Copiar nombre de usuario"
searchUser: "Búsqueda de usuarios"
searchUser: "Buscar un usuario"
reply: "Responder"
loadMore: "Ver más"
showMore: "Ver más"
showLess: "Cerrar"
youGotNewFollower: "te ha seguido"
receiveFollowRequest: "Recibiste una solicitud de seguimiento"
followRequestAccepted: "La solicitud de seguimiento fue aceptada"
@ -87,11 +88,11 @@ enterListName: "Ingrese nombre de lista"
privacy: "Privacidad"
makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento"
defaultNoteVisibility: "Visibilidad por defecto"
follow: "Sigue"
followRequest: "Solicitud de seguimiento"
follow: "Seguir"
followRequest: "Enviar solicitud de seguimiento"
followRequests: "Solicitudes de seguimiento"
unfollow: "Dejar de seguir"
followRequestPending: "Solicitudes de seguimiento pendientes"
followRequestPending: "Solicitudes de seguimiento pendiente"
enterEmoji: "Ingresar emojis"
renote: "Renotar"
unrenote: "Quitar renota"
@ -100,7 +101,7 @@ cantRenote: "No se puede renotar este post"
cantReRenote: "No se puede renotar una renota"
quote: "Citar"
pinnedNote: "Nota fijada"
pinned: "Fijar"
pinned: "Fijar al perfil"
you: "Tú"
clickToShow: "Click para ver"
sensitive: "Marcado como sensible"
@ -203,6 +204,7 @@ done: "Terminado"
processing: "Procesando"
preview: "Vista previa"
default: "Predeterminado"
defaultValueIs: "Predeterminado"
noCustomEmojis: "No hay emojis personalizados"
noJobs: "No hay trabajos"
federating: "Federando"
@ -381,6 +383,7 @@ administrator: "Administrador"
token: "Token"
twoStepAuthentication: "Autenticación de dos factores"
moderator: "Moderador"
moderation: "Moderación"
nUsersMentioned: "{n} usuarios mencionados"
securityKey: "Clave de seguridad"
securityKeyName: "Nombre de la Clave"
@ -559,6 +562,7 @@ author: "Autor"
leaveConfirm: "Hay modificaciones sin guardar. ¿Desea descartarlas?"
manage: "Administrar"
plugins: "Plugins"
preferencesBackups: "Respaldo de preferencias"
deck: "Deck"
undeck: "Quitar deck"
useBlurEffectForModal: "Usar efecto borroso en modales"
@ -854,6 +858,9 @@ noEmailServerWarning: "No se ha configurado un servidor de correo electrónico."
thereIsUnresolvedAbuseReportWarning: "Hay reportes sin resolver"
recommended: "Recomendado"
check: "Verificar"
driveCapOverrideLabel: "Cambiar la capacidad de la unidad para este usuario"
driveCapOverrideCaption: "Restablecer la capacidad a su predeterminado ingresando un valor de 0 o menos"
requireAdminForView: "Necesitas iniciar sesión como administrador para ver esto."
isSystemAccount: "Cuenta creada y operada automáticamente por el sistema"
typeToConfirm: "Ingrese {x} para confirmar"
deleteAccount: "Borrar cuenta"
@ -861,11 +868,39 @@ document: "Documento"
numberOfPageCache: "Cantidad de páginas cacheadas"
numberOfPageCacheDescription: "Al aumentar el número mejora la conveniencia pero tambien puede aumentar la carga y la memoria a usarse"
logoutConfirm: "¿Cerrar sesión?"
lastActiveDate: "Utilizado por última vez el"
statusbar: "Barra de estado"
pleaseSelect: "Selecciona una opción"
reverse: "Echar de un capirotazo"
colored: "Color"
refreshInterval: "Intervalo de actualización"
label: "Etiqueta"
type: "Tipo"
speed: "Velocidad"
slow: "Lento"
fast: "Rápido"
sensitiveMediaDetection: "Detección de contenido NSFW"
localOnly: "Solo local"
remoteOnly: "Sólo remoto"
failedToUpload: "La subida falló"
cannotUploadBecauseInappropriate: "Este archivo no se puede subir debido a que algunas partes han sido detectadas comoNSFW."
cannotUploadBecauseNoFreeSpace: "La subida falló debido a falta de espacio libre en la unidad del usuario."
beta: "Beta"
enableAutoSensitive: "Marcar automáticamente contenido NSFW"
enableAutoSensitiveDescription: "Permite la detección y marcado automático de contenido NSFW usando 'Machine Learning' cuando sea posible. Incluso si esta opción está desactivada, puede ser activado para toda la instancia."
activeEmailValidationDescription: "Habilita la validación estricta de direcciones de correo electrónico, lo cual incluye la revisión de direcciones desechables y si se puede comunicar con éstas. Cuando está deshabilitado, sólo el formato de la dirección es validado."
navbar: "Barra de navegación"
shuffle: "Aleatorio"
account: "Cuentas"
move: "Mover"
_sensitiveMediaDetection:
description: "Reduce el esfuerzo de la moderación el el servidor a través del reconocimiento automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar ligeramente la carga en el servidor."
sensitivity: "Sensibilidad de detección"
sensitivityDescription: "Reducir la sensibilidad puede acarrear a varios falsos positivos, mientras que incrementarla puede reducir las detecciones (falsos negativos)."
setSensitiveFlagAutomatically: "Marcar como NSFW"
setSensitiveFlagAutomaticallyDescription: "Los resultados de la detección interna pueden ser retenidos incluso si la opción está desactivada."
analyzeVideos: "Habilitar el análisis de videos"
analyzeVideosDescription: "Analizar videos en adición a las imágenes. Esto puede incrementar ligeramente la carga del servidor."
_emailUnavailable:
used: "Ya fue usado"
format: "Formato no válido."
@ -908,6 +943,24 @@ _plugin:
install: "Instalar plugins"
installWarn: "Por favor no instale plugins que no son de confianza"
manage: "Gestionar plugins"
_preferencesBackups:
list: "Respaldos creados"
saveNew: "Guardar nuevo respaldo"
loadFile: "Cargar desde archivo"
apply: "Aplicar a este dispositivo"
save: "Guardar cambios"
inputName: "Por favor, ingresa un nombre para este respaldo"
cannotSave: "Fallo al guardar"
nameAlreadyExists: "Un respaldo llamado \"{name}\" ya existe. Por favor ingresa un nombre diferente"
applyConfirm: "¿Realmente quieres aplicar los cambios desde el archivo \"{name}\" a este dispositivo? Las configuraciones existentes serán sobreescritas. "
saveConfirm: "¿Guardar respaldo como \"{name}\"?"
deleteConfirm: "¿Borrar el respaldo \"{name}\"?"
renameConfirm: "¿Renombrar este respaldo de \"{old}\" a \"{new}\"?"
noBackups: "No existen respaldos. Deberás respaldar las configuraciones del cliente en este servidor usando \"Crear nuevo respaldo\""
createdAt: "Creado: {date} {time}"
updatedAt: "Actualizado: {date} {time}"
cannotLoad: "La carga falló"
invalidFile: "Formato de archivo inválido"
_registry:
scope: "Alcance"
key: "Clave"
@ -991,6 +1044,8 @@ _mfm:
sparkleDescription: "Aplica un efecto de partículas parpadeantes"
rotate: "Rotar"
rotateDescription: "Rota el contenido a un ángulo especificado."
plain: "Plano"
plainDescription: "Desactiva los efectos de todo el contenido MFM con este efecto MFM."
_instanceTicker:
none: "No mostrar"
remote: "Mostrar a usuarios remotos"
@ -1220,9 +1275,11 @@ _widgets:
trends: "Tendencias"
clock: "Reloj"
rss: "Lector RSS"
rssTicker: "Ticker-RSS"
activity: "Actividad"
photos: "Fotos"
digitalClock: "Reloj digital"
unixClock: "Reloj UNIX"
federation: "Federación"
instanceCloud: "Nube de palabras de la instancia"
postForm: "Formulario"
@ -1663,6 +1720,7 @@ _deck:
alwaysShowMainColumn: "Siempre mostrar la columna principal"
columnAlign: "Alinear columnas"
addColumn: "Agregar columna"
configureColumn: "Ajustes de columna"
swapLeft: "Mover a la izquierda"
swapRight: "Mover a la derecha"
swapUp: "Mover arriba"
@ -1670,6 +1728,11 @@ _deck:
stackLeft: "Apilar a la izquierda"
popRight: "Sacar a la derecha"
profile: "Perfil"
newProfile: "Nuevo perfil"
deleteProfile: "Eliminar perfil"
introduction: "¡Crea la interfaz perfecta para tí organizando las columnas libremente!"
introduction2: "Presiona en la + de la derecha de la pantalla para añadir nuevas columnas donde quieras."
widgetsIntroduction: "Por favor selecciona \"Editar Widgets\" en el menú columna y agrega un widget."
_columns:
main: "Principal"
widgets: "Widgets"

View file

@ -52,6 +52,7 @@ searchUser: "Chercher un·e utilisateur·rice"
reply: "Répondre"
loadMore: "Afficher plus …"
showMore: "Afficher plus …"
showLess: "Fermer"
youGotNewFollower: "Vous suit"
receiveFollowRequest: "Demande dabonnement reçue"
followRequestAccepted: "La demande dabonnement a été acceptée"

View file

@ -52,6 +52,7 @@ searchUser: "Cari pengguna"
reply: "Balas"
loadMore: "Selebihnya"
showMore: "Selebihnya"
showLess: "Tutup"
youGotNewFollower: "Mengikuti kamu"
receiveFollowRequest: "Ingin mengikuti kamu"
followRequestAccepted: "Permintaan mengikuti telah disetujui"

View file

@ -52,6 +52,7 @@ searchUser: "Cerca utente"
reply: "Rispondi"
loadMore: "Mostra di più"
showMore: "Mostra di più"
showLess: "Chiudi"
youGotNewFollower: "Ha iniziato a seguirti"
receiveFollowRequest: "Hai ricevuto una richiesta di follow."
followRequestAccepted: "Richiesta di follow accettata"

View file

@ -52,6 +52,7 @@ searchUser: "ユーザーを検索"
reply: "返信"
loadMore: "もっと見る"
showMore: "もっと見る"
showLess: "閉じる"
youGotNewFollower: "フォローされました"
receiveFollowRequest: "フォローリクエストされました"
followRequestAccepted: "フォローが承認されました"
@ -322,6 +323,7 @@ connectService: "接続する"
disconnectService: "切断する"
enableLocalTimeline: "ローカルタイムラインを有効にする"
enableGlobalTimeline: "グローバルタイムラインを有効にする"
enableRecommendedTimeline: "推奨されるタイムラインを有効にする"
disablingTimelinesInfo: "これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。"
registration: "登録"
enableRegistration: "誰でも新規登録できるようにする"
@ -561,6 +563,7 @@ author: "作者"
leaveConfirm: "未保存の変更があります。破棄しますか?"
manage: "管理"
plugins: "プラグイン"
preferencesBackups: "設定のバックアップ"
deck: "デッキ"
undeck: "デッキ解除"
useBlurEffectForModal: "モーダルにぼかし効果を使用"
@ -901,6 +904,15 @@ shuffle: "シャッフル"
account: "アカウント"
move: "移動"
adminCustomCssWarn: "この設定は、それが何をするものであるかを知っている場合のみ使用してください。不適切な値を入力すると、クライアントが正常に動作しなくなる可能性があります。ユーザー設定でCSSをテストし、正しく動作することを確認してください。"
customMOTD: "カスタムMOTDスプラッシュスクリーンメッセージ"
customMOTDDescription: "ユーザがページをロード/リロードするたびにランダムに表示される、改行で区切られたMOTDスプラッシュスクリーン用のカスタムメッセージ"
customSplashIcons: "カスタムスプラッシュスクリーンアイコン"
customSplashIconsDescription: "ユーザがページをロード/リロードするたびにランダムに表示される、改行で区切られたカスタムスプラッシュスクリーンアイコンの URL。画像は静的なURLで、できればすべて192x192にリサイズしてください。"
showUpdates: "Calckeyの更新時にポップアップを表示する"
recommendedInstances: "推奨インスタンス"
recommendedInstancesDescription: "推奨タイムラインに表示するために改行で区切られた推奨インスタンス。`https//`を追加しないでください。ドメインのみを追加してください。"
caption: "自動キャプション"
splash: "スプラッシュスクリーン"
_sensitiveMediaDetection:
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"
@ -962,6 +974,25 @@ _plugin:
installWarn: "信頼できないプラグインはインストールしないでください。"
manage: "プラグインの管理"
_preferencesBackups:
list: "作成したバックアップ"
saveNew: "新規保存"
loadFile: "ファイルを読み込み"
apply: "このデバイスに適用"
save: "上書き保存"
inputName: "バックアップ名を入力"
cannotSave: "保存できません"
nameAlreadyExists: "バックアップ名「{name}」は既に存在します。違う名前を指定してください。"
applyConfirm: "バックアップ「{name}」を現在のデバイスに適用しますか?現在のデバイス設定は失われます。"
saveConfirm: "{name}に上書き保存しますか?"
deleteConfirm: "{name}を削除しますか?"
renameConfirm: "「{old}」を「{new}」に変更しますか?"
noBackups: "バックアップはありません。「新規保存」で現在のクライアント設定をサーバーに保存できます。"
createdAt: "作成日時: {date} {time}"
updatedAt: "更新日時: {date} {time}"
cannotLoad: "読み込みできません"
invalidFile: "ファイル形式が違います。"
_registry:
scope: "スコープ"
key: "キー"
@ -1302,6 +1333,7 @@ _widgets:
activity: "アクティビティ"
photos: "フォト"
digitalClock: "デジタル時計"
unixClock: "UNIX時計"
federation: "連合"
instanceCloud: "インスタンスクラウド"
postForm: "投稿フォーム"
@ -1419,6 +1451,7 @@ _instanceCharts:
_timelines:
home: "ホーム"
local: "ローカル"
recommended: "一押し"
social: "ソーシャル"
global: "グローバル"

View file

@ -52,6 +52,7 @@ searchUser: "ユーザーを検索"
reply: "返事"
loadMore: "まだまだあるで!"
showMore: "まだまだあるで!"
showLess: "閉じる"
youGotNewFollower: "フォローされたで"
receiveFollowRequest: "フォローリクエストされたで"
followRequestAccepted: "フォローが承認されたで"
@ -203,6 +204,7 @@ done: "でけた"
processing: "処理しとる"
preview: "プレビュー"
default: "デフォルト"
defaultValueIs: "デフォルト"
noCustomEmojis: "絵文字はあらへん"
noJobs: "ジョブはあらへん"
federating: "連合しとる"
@ -317,6 +319,8 @@ monthX: "{month}月"
yearX: "{year}年"
pages: "ページ"
integration: "連携"
connectService: "つなげるで"
disconnectService: "切るで"
enableLocalTimeline: "ローカルタイムラインを使えるようにする"
enableGlobalTimeline: "グローバルタイムラインを使えるようにする"
disablingTimelinesInfo: "ここらへんのタイムラインを使えんようにしてしもても、管理者とモデレーターは使えるままになってるで、そうやなかったら不便やからな。"
@ -328,10 +332,13 @@ driveCapacityPerRemoteAccount: "リモートユーザーひとりあたりのド
inMb: "メガバイト単位"
iconUrl: "アイコン画像のURL"
bannerUrl: "バナー画像のURL"
backgroundImageUrl: "背景画像のURL"
basicInfo: "基本情報"
pinnedUsers: "ピン留めしたユーザー"
pinnedUsersDescription: "「みつける」ページとかにピン留めしたいユーザーをここに書けばええんやで。他ん人との名前は改行で区切ればええんやで。"
pinnedPages: "ピン留めページ"
pinnedPagesDescription: "インスタンスのいっちゃん上にピン留めしたいページのパスを改行で区切って記述してな"
pinnedClipId: "ピン留めするクリップのID"
pinnedNotes: "ピン留めされとるノート"
hcaptcha: "hCaptchaキャプチャ"
enableHcaptcha: "hCaptchaキャプチャをつけとく"
@ -376,6 +383,7 @@ administrator: "管理者"
token: "トークン"
twoStepAuthentication: "二段階認証"
moderator: "モデレーター"
moderation: "モデレーション"
nUsersMentioned: "{n}人が投稿"
securityKey: "セキュリティキー"
securityKeyName: "キーの名前"
@ -435,13 +443,17 @@ strongPassword: "ええ感じのパスワード"
passwordMatched: "よし!一致や!"
passwordNotMatched: "一致しとらんで?"
signinWith: "{x}でログイン"
signinFailed: "ログインできんかったで。もっかいユーザー名とパスワードを確認してみてな。"
tapSecurityKey: "セキュリティキーにタッチしてな"
or: "それか"
language: "言語"
uiLanguage: "UIの表示言語"
groupInvited: "グループに招待されとるで"
aboutX: "{x}について"
useOsNativeEmojis: "OSネイティブの絵文字を使う"
disableDrawer: "メニューをドロワーで表示せぇへん"
youHaveNoGroups: "グループがあらへんねぇ。"
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループ作ってからやってな"
noHistory: "履歴はあらへんねぇ。"
signinHistory: "ログイン履歴"
disableAnimatedMfm: "動きがやかましいMFMを止める"
@ -450,6 +462,7 @@ category: "カテゴリ"
tags: "タグ"
docSource: "このドキュメントのソース"
createAccount: "アカウントを作成"
existingAccount: "既存のアカウント"
regenerate: "再生成"
fontSize: "フォントサイズ"
noFollowRequests: "フォロー申請はあらへんで"
@ -473,10 +486,15 @@ useObjectStorage: "オブジェクトストレージを使う"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "参照に使うにURLやで。CDNやProxyを使用してるんならそのURL、S3: 'https://<bucket>.s3.amazonaws.com'、GCSとかなら: 'https://storage.googleapis.com/<bucket>'。"
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "使ってるサービスのbucket名を選んでな"
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "このprefixのディレクトリ下に格納されるで"
objectStorageEndpoint: "Endpoint"
objectStorageEndpointDesc: "S3のときは空、それ以外は各サービスのendpointを指定してなー。'<host>'ってやるか'<host>:<port>'みたいに指定するんやで。"
objectStorageRegion: "Region"
objectStorageRegionDesc: "'xx-east-1'みたいなregionを指定したってやー。使ってるサービスにregionの概念がないときは、空か'us-east-1'にするんやで。"
objectStorageUseSSL: "SSLを使う"
objectStorageUseSSLDesc: "API接続にhttpsを使わん場合はオフにするんやで"
objectStorageUseProxy: "Proxyを使う"
objectStorageUseProxyDesc: "API接続にproxy使わんのやったら切ってくれへん"
objectStorageSetPublicRead: "アップロードした時に'public-read'を設定してや"
@ -517,29 +535,52 @@ removeAllFollowing: "フォローを全解除"
removeAllFollowingDescription: "{host}からのフォローをすべて解除するで。そのインスタンスが消えて無くなった時とかには便利な機能やで。"
userSuspended: "このユーザーは...凍結されとる。"
userSilenced: "このユーザーは...サイレンスされとる。"
yourAccountSuspendedTitle: "あんたのアカウント凍結されとるで"
yourAccountSuspendedDescription: "あんたのアカウントは、サーバーの利用規約に違反したとかの理由で、凍結されとるで。細かいことは管理者までお問い合わせたってなー。絶対に新しいアカウント作ったらあかんで。絶対やで。"
menu: "メニュー"
divider: "分割線"
addItem: "項目を追加"
relays: "リレー"
addRelay: "リレーの追加"
inboxUrl: "inboxのURL"
addedRelays: "追加済みのリレー"
serviceworkerInfo: "プッシュ通知をするんなら有効にせなあかんで。"
deletedNote: "消された投稿"
invisibleNote: "非公開の投稿"
enableInfiniteScroll: "自動でもっと見る"
visibility: "公開範囲"
poll: "アンケート"
useCw: "内容を隠す"
enablePlayer: "プレイヤーを開く"
disablePlayer: "プレイヤーを閉じる"
expandTweet: "ツイートを展開する"
themeEditor: "テーマエディター"
description: "説明"
describeFile: "キャプションを付ける"
enterFileDescription: "キャプションを入力"
author: "作者"
leaveConfirm: "未保存の変更があるで!ほかしてええか?"
manage: "管理"
plugins: "プラグイン"
deck: "デッキ"
undeck: "デッキ解除"
useBlurEffectForModal: "モーダルにぼかし効果を使用"
useFullReactionPicker: "フル機能にリアクションピッカーを使用"
width: "幅"
height: "高さ"
large: "大"
medium: "中"
small: "小"
generateAccessToken: "アクセストークンの発行"
permission: "権限"
enableAll: "全部使えるようにする"
disableAll: "全部使えへんようにする"
tokenRequested: "アカウントへのアクセス許可"
pluginTokenRequestedDescription: "このプラグインはここで設定した権限を使えるようになるで。"
notificationType: "通知の種類"
edit: "編集"
useStarForReactionFallback: "リアクションがようわからん場合、★を使う"
emailServer: "メールサーバー"
enableEmail: "メール配信を受け取る"
emailConfigInfo: "メールアドレスの確認とかパスワードリセットの時に使うで"
email: "メール"
@ -551,8 +592,12 @@ smtpUser: "ユーザー名"
smtpPass: "パスワード"
emptyToDisableSmtpAuth: "ユーザー名とパスワードになんも入れんかったら、SMTP認証を無効化するで"
smtpSecure: "SMTP 接続に暗黙的なSSL/TLSを使用する"
smtpSecureInfo: "STARTTLS使っとる時はオフにするで。"
testEmail: "配信テスト"
wordMute: "ワードミュート"
regexpError: "正規表現エラー"
regexpErrorDescription: "{tab}ワードミュートの{line}行目の正規表現にエラーが出てきたで:"
instanceMute: "インスタンスミュート"
userSaysSomething: "{name}が何か言ったようやで"
makeActive: "使うで"
display: "表示"
@ -567,13 +612,24 @@ create: "作成"
notificationSetting: "通知設定"
notificationSettingDesc: "表示する通知の種類えらんでや。"
useGlobalSetting: "グローバル設定を使ってや"
useGlobalSettingDesc: "オンにすると、アカウントの通知設定が使われるで。オフにすると、別々に設定できるようになるで。"
other: "その他"
regenerateLoginToken: "ログイントークンを再生成"
regenerateLoginTokenDescription: "ログインに使われる内部トークンをもっかい作るで。いつもならこれをやる必要はないで。もっかい作ると、全部のデバイスでログアウトされるで気ぃつけてなー。"
setMultipleBySeparatingWithSpace: "スペースで区切って複数設定できるで。"
fileIdOrUrl: "ファイルIDかURL"
behavior: "動作"
sample: "サンプル"
abuseReports: "通報"
reportAbuse: "通報"
reportAbuseOf: "{name}を通報する"
fillAbuseReportDescription: "細かい通報理由を書いてなー。対象ートがある時はそのURLも書いといてなー。"
abuseReported: "無事内容が送信されたみたいやで。おおきに〜。"
reporter: "通報者"
reporteeOrigin: "通報先"
reporterOrigin: "通報元"
forwardReport: "リモートインスタンスに通報を転送するで"
forwardReportIsAnonymous: "リモートインスタンスからはあんたの情報は見れへんくって、匿名のシステムアカウントとして表示されるで。"
send: "送信"
abuseMarkAsResolved: "対応したで"
openInNewTab: "新しいタブで開く"
@ -587,22 +643,57 @@ system: "システム"
switchUi: "UI切り替え"
desktop: "デスクトップ"
clip: "クリップ"
createNew: "新しく作るで"
optional: "任意"
createNewClip: "新しいクリップを作るで"
unclip: "クリップ解除するで"
confirmToUnclipAlreadyClippedNote: "このノートはすでにクリップ「{name}」に含まれとるで。ノートをこのクリップから除外したる?"
public: "パブリック"
i18nInfo: "Misskeyは有志によっていろんな言語に翻訳されとるで。{link}で翻訳に協力したってやー。"
manageAccessTokens: "アクセストークンの管理"
accountInfo: "アカウント情報"
notesCount: "ノートの数やで"
repliesCount: "返信した数やで"
renotesCount: "Renoteした数やで"
repliedCount: "返信された数やで"
renotedCount: "Renoteされた数やで"
followingCount: "フォロー数やで"
followersCount: "フォロワー数やで"
sentReactionsCount: "リアクションした数やで"
receivedReactionsCount: "リアクションされた数"
pollVotesCount: "アンケートに投票した数"
pollVotedCount: "アンケートに投票された数"
yes: "はい"
no: "いいえ"
driveFilesCount: "ドライブのファイル数"
driveUsage: "ドライブ使用量やで"
noCrawle: "クローラーによるインデックスを拒否するで"
noCrawleDescription: "検索エンジンにあんたのユーザーページ、ート、Pagesとかのコンテンツを登録(インデックス)せぇへんように頼むで。"
lockedAccountInfo: "フォローを承認制にしとっても、ノートの公開範囲を「フォロワー」にせぇへん限り、誰でもあんたのノートを見れるで。"
alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にするで"
loadRawImages: "添付画像のサムネイルをオリジナル画質にするで"
disableShowingAnimatedImages: "アニメーション画像を再生しやへんで"
verificationEmailSent: "無事確認のメールを送れたで。メールに書いてあるリンクにアクセスして、設定を完了してなー。"
notSet: "未設定"
emailVerified: "メールアドレスは確認されたで"
noteFavoritesCount: "お気に入りノートの数やで"
pageLikesCount: "Pageにええやんと思った数"
pageLikedCount: "Pageにええやんと思ってくれた数"
contact: "連絡先"
useSystemFont: "システムのデフォルトのフォントを使うで"
clips: "クリップ"
experimentalFeatures: "実験的機能やで"
developer: "開発者やで"
makeExplorable: "アカウントを見つけやすくするで"
makeExplorableDescription: "オフにすると、「みつける」にアカウントが載らんくなるで。"
showGapBetweenNotesInTimeline: "タイムラインのノートを放して表示するで"
duplicate: "複製"
left: "左"
center: "中央"
wide: "広い"
narrow: "狭い"
reloadToApplySetting: "設定はページリロード後に反映されるで。今リロードしとくか?"
needReloadToApply: "反映には再起動せなあかんで"
showTitlebar: "タイトルバーを見せる"
clearCache: "キャッシュをほかす"
onlineUsersCount: "{n}人が起きとるで"
@ -621,6 +712,7 @@ createdAt: "作成した日"
updatedAt: "更新日時"
saveConfirm: "保存するで?"
deleteConfirm: "ホンマに削除するで?"
invalidValue: "有効な値じゃないみたいやで。"
registry: "レジストリ"
closeAccount: "アカウントを閉鎖する"
currentVersion: "現在のバージョン"
@ -634,6 +726,7 @@ editCode: "コードを編集"
apply: "適用"
receiveAnnouncementFromInstance: "インスタンスからのお知らせを受け取る"
emailNotification: "メール通知"
publish: "公開"
inChannelSearch: "チャンネル内検索"
useReactionPickerForContextMenu: "右クリックでリアクションピッカーを開くようにする"
typingUsers: "{users}が今書きよるで"
@ -642,23 +735,121 @@ showingPastTimeline: "過去のタイムラインを表示してるで"
clear: "クリア"
markAllAsRead: "もうみな読んでもうたわ"
goBack: "戻る"
unlikeConfirm: "いいね解除するんか?"
fullView: "フルビュー"
quitFullView: "フルビュー解除"
addDescription: "説明を追加するで"
userPagePinTip: "個々のノートのメニューから「ピン留め」を選んどくと、ここにノートを表示しておけるで。"
notSpecifiedMentionWarning: "宛先に含まれてへんメンションがあるで"
info: "情報"
userInfo: "ユーザー情報やで"
unknown: "不明"
onlineStatus: "オンライン状態"
hideOnlineStatus: "オンライン状態を隠すで"
hideOnlineStatusDescription: "オンライン状態を隠すと、検索とかの一部の機能で使いにくくなるかもしれんよ。"
online: "オンライン"
active: "アクティブ"
offline: "オフライン"
notRecommended: "あんま推奨しやんで"
botProtection: "Botプロテクション"
instanceBlocking: "インスタンスブロック"
selectAccount: "アカウントを選んでなー"
switchAccount: "アカウントを変えるで"
enabled: "有効"
disabled: "無効"
quickAction: "クイックアクション"
user: "ユーザー"
administration: "管理"
accounts: "アカウント"
switch: "切り替え"
noMaintainerInformationWarning: "管理者情報が設定されてへんで"
noBotProtectionWarning: "Botプロテクションが設定されてへんで。"
configure: "設定する"
postToGallery: "ギャラリーへ投稿"
gallery: "ギャラリー"
recentPosts: "最近の投稿"
popularPosts: "人気の投稿"
shareWithNote: "ノートで共有"
ads: "広告"
expiration: "期限"
memo: "メモ"
priority: "優先度"
high: "高い"
middle: "中"
low: "低い"
emailNotConfiguredWarning: "メアドの設定がされてへんで。"
ratio: "比率"
previewNoteText: "本文を下見するで"
customCss: "カスタムCSS"
customCssWarn: "この設定は必ず知識のある人がやらなあかんで。あんま良くない設定をしたるとクライアントがちゃんと使えへんくなってくで。"
global: "グローバル"
squareAvatars: "アイコンを四角形で表示するで"
sent: "送信"
received: "受信"
searchResult: "検索結果やで"
hashtags: "ハッシュタグ"
troubleshooting: "トラブルシューティング"
useBlurEffect: "UIにぼかし効果を使うで"
learnMore: "詳しく"
misskeyUpdated: "Misskeyが更新されたで\nモデレーターの人らに感謝せなあかんで"
whatIsNew: "更新情報を見るで"
translate: "翻訳"
translatedFrom: "{x}から翻訳するで"
accountDeletionInProgress: "アカウント削除しとるで待っとってなー"
usernameInfo: "サーバー上であんたのアカウントをあんたやと分かるようにするための名前やで。アルファベット(a~z, A~Z)、数字(0~9)、それとアンダーバー(_)が使って考えてな。この名前は後から変更することはできへんからちゃんと考えるんやで。"
aiChanMode: "藍モードやで"
keepCw: "CWを維持するで"
pubSub: "Pub/Subのアカウント"
lastCommunication: "直近の通信"
resolved: "解決したで"
unresolved: "まだ解決してないで"
breakFollow: "フォロワーを解除するで"
itsOn: "オンになっとるよ"
hide: "隠す"
searchByGoogle: "探す"
indefinitely: "無期限"
file: "ファイル"
requireAdminForView: "これを見るには管理者アカウントでログインしとらなあかんで。"
isSystemAccount: "システムが自動で作成・管理しとるアカウントやで。"
typeToConfirm: "この操作をやるんなら {x} と入力してなー"
deleteAccount: "アカウント削除するで"
document: "ドキュメント"
numberOfPageCache: "ページキャッシュ数やで"
numberOfPageCacheDescription: "増やすと使いやすくなる、負荷とメモリ使用量が増えてくで。一長一短やな。"
logoutConfirm: "ログアウトしまっか?"
lastActiveDate: "最後に使った日時"
statusbar: "ステータスバー"
pleaseSelect: "選択したってやー"
reverse: "反転"
colored: "色付き"
refreshInterval: "更新間隔"
label: "ラベル"
type: "タイプ"
speed: "速度"
slow: "遅い"
fast: "速い"
sensitiveMediaDetection: "センシティブなメディアの検出"
localOnly: "ローカルのみ"
remoteOnly: "リモートのみ"
failedToUpload: "アップロードに失敗したで"
cannotUploadBecauseInappropriate: "不適切な内容を含むかもしれへんって判定されたでアップロードできまへん。"
cannotUploadBecauseNoFreeSpace: "ドライブの空き容量が無いでアップロードできまへん。"
beta: "ベータ"
enableAutoSensitive: "自動NSFW判定"
enableAutoSensitiveDescription: "使える時は、機械学習を使って自動でメディアにNSFWフラグを設定するで。この機能をオフにしても、インスタンスによっては自動で設定されることがあるで。"
activeEmailValidationDescription: "ユーザーのメールアドレスのバリデーションを、捨てアドかどうかや実際に通信可能かどうかとかを判定して積極的に行うで。オフにすると単に文字列として正しいかどうかだけチェックするで。"
navbar: "ナビゲーションバー"
shuffle: "シャッフルするで"
account: "アカウント"
move: "移動するで"
_sensitiveMediaDetection:
description: "機械学習を使って自動でセンシティブなメディアを検出して、モデレーションに役立てることができるで。サーバーの負荷が少し増えてまうなあ。"
sensitivity: "検出感度やで"
sensitivityDescription: "感度を低くすると、誤検知(偽陽性)が減るで。感度を高くすると、検知漏れ(偽陰性)が減るで。"
setSensitiveFlagAutomatically: "NSFWフラグを設定するで"
setSensitiveFlagAutomaticallyDescription: "この設定をオフにしても内部的に判定結果は保持されるで。"
_ffVisibility:
public: "公開"
_ad:
back: "戻る"
_gallery:

View file

@ -52,6 +52,7 @@ searchUser: "사용자 검색"
reply: "답글"
loadMore: "더 보기"
showMore: "더 보기"
showLess: "닫기"
youGotNewFollower: "새로운 팔로워가 있습니다"
receiveFollowRequest: "새로운 팔로우 요청이 있습니다"
followRequestAccepted: "팔로우가 수락되었습니다"
@ -889,6 +890,7 @@ activeEmailValidationDescription: "유저가 입력한 메일 주소가 일회
navbar: "네비게이션 바"
shuffle: "셔플"
account: "계정"
move: "이동"
_sensitiveMediaDetection:
description: "기계학습을 통해 자동으로 민감한 미디어를 탐지하여, 모더레이션에 참고할 수 있도록 합니다. 서버의 부하를 약간 증가시킵니다."
sensitivity: "탐지 민감도"
@ -1022,6 +1024,8 @@ _mfm:
sparkleDescription: "반짝이는 파티클 효과를 추가합니다."
rotate: "회전"
rotateDescription: "지정한 각도로 회전시킵니다."
plain: "평문"
plainDescription: "안에 있는 MFM 구문을 모두 무시하고 평문으로 표시합니다."
_instanceTicker:
none: "보이지 않음"
remote: "리모트 유저에게만 보이기"

View file

@ -52,6 +52,7 @@ searchUser: "Wyszukiwanie użytkowników"
reply: "Odpowiedz"
loadMore: "Załaduj więcej"
showMore: "Załaduj więcej"
showLess: "Zamknij"
youGotNewFollower: "Zaobserwował(a) Cię"
receiveFollowRequest: "Otrzymano prośbę o możliwość obserwacji"
followRequestAccepted: "Zaakceptowano prośbę o możliwość obserwacji"

View file

@ -52,6 +52,7 @@ searchUser: "Pesquisar utilizador"
reply: "Responder"
loadMore: "Carregar mais"
showMore: "Ver mais"
showLess: "Fechar"
youGotNewFollower: "Você tem um novo seguidor"
receiveFollowRequest: "Pedido de seguimento recebido"
followRequestAccepted: "Pedido de seguir aceito"

View file

@ -52,6 +52,7 @@ searchUser: "Caută un utilizator"
reply: "Răspunde"
loadMore: "Incarcă mai mult"
showMore: "Arată mai mult"
showLess: "Închide"
youGotNewFollower: "te-a urmărit"
receiveFollowRequest: "Cerere de urmărire primită"
followRequestAccepted: "Cerere de urmărire acceptată"

View file

@ -52,6 +52,7 @@ searchUser: "Поиск людей"
reply: "Ответить"
loadMore: "Показать еще"
showMore: "Показать еще"
showLess: "Закрыть"
youGotNewFollower: "Новый подписчик"
receiveFollowRequest: "Получен запрос на подписку"
followRequestAccepted: "Запрос на подписку принят"

View file

@ -52,6 +52,7 @@ searchUser: "Hľadať používateľov"
reply: "Odpovedať"
loadMore: "Zobraziť viac"
showMore: "Zobraziť viac"
showLess: "Zavrieť"
youGotNewFollower: "Máte nového sledujúceho"
receiveFollowRequest: "Žiadosť o sledovanie prijatá"
followRequestAccepted: "Žiadosť o sledovanie akceptovaná"
@ -561,6 +562,7 @@ author: "Autor"
leaveConfirm: "Máte neuložené zmeny. Chcete ich zahodiť?"
manage: "Administrácia"
plugins: "Pluginy"
preferencesBackups: "Zálohy nastavení"
deck: "Deck"
useBlurEffectForModal: "Použiť efekt rozmazania na okná"
useFullReactionPicker: "Použiť plnú veľkosť výberu reakcií"
@ -936,6 +938,24 @@ _plugin:
install: "Inštalova pluginy"
installWarn: "Prosím neinštalujte nedôveryhodné pluginy."
manage: "Spravovanie pluginov"
_preferencesBackups:
list: "Vytvorené zálohy"
saveNew: "Uložiť novú"
loadFile: "Nahrať súbor"
apply: "Použiť na toto zariadenie"
save: "Uložiť"
inputName: "Názov zálohy"
cannotSave: "Nedá sa uložiť"
nameAlreadyExists: "Záloha s názvom \"{name}\" už existuje. Zadajte iný názov."
applyConfirm: "Chcete použiť zálohu '{name}' na aktuálne zariadenie? Aktuálne nastavenia zariadenia sa stratia."
saveConfirm: "Chcete prepísať {name}?"
deleteConfirm: "Naozaj chcete odstrániť \"{name}\"?"
renameConfirm: "Chcete zmeniť \"{old}\" na \"{new}\"?"
noBackups: "Nie je k dispozícii žiadna záloha. \"Uložiť novú\" umožňuje uložiť aktuálnu konfiguráciu zariadenia na server."
createdAt: "Dátum vytvorenia: {date} {time}"
updatedAt: "Dátum úpravy: {date} {time}"
cannotLoad: "Nedá sa nahrať"
invalidFile: "Neplatný formát súboru"
_registry:
scope: "Oblasť"
key: "Kľúč"
@ -1019,6 +1039,8 @@ _mfm:
sparkleDescription: "Obsahu dodá trblietajúci efekt."
rotate: "Otáčať"
rotateDescription: "Otočí obsah o určitý uhol."
plain: "Obyčajné"
plainDescription: "Bez akejkoľvej syntaxe"
_instanceTicker:
none: "Nikdy nezobrazovať"
remote: "Zobraziť pre vzdialených používateľov"
@ -1252,6 +1274,7 @@ _widgets:
activity: "Aktivita"
photos: "Fotky"
digitalClock: "Digitálne hodiny"
unixClock: "UNIX čas"
federation: "Federácia"
instanceCloud: "Cloud serverov"
postForm: "Napísať poznámku"

View file

@ -203,6 +203,7 @@ done: "Klar"
processing: "Bearbetar..."
preview: "Förhandsvisning"
default: "Standard"
defaultValueIs: "Standard: {value}"
noCustomEmojis: "Det finns ingen emoji"
noJobs: "Det finns inga jobb"
federating: "Federerar"

View file

@ -52,6 +52,7 @@ searchUser: "ค้นหาผู้ใช้งาน"
reply: "ตอบกลับ"
loadMore: "โหลดเพิ่มเติม"
showMore: "แสดงเพิ่มเติม"
showLess: "ปิด"
youGotNewFollower: "ได้ติดตามคุณ"
receiveFollowRequest: "คำขอผู้ติดตามที่ได้รับ"
followRequestAccepted: "ผู้ติดตามได้ตอบรับคำขอร้องของคุณแล้ว"
@ -835,10 +836,83 @@ themeColor: "อินสแตนซ์ Ticker Color"
size: "ขนาด"
numberOfColumn: "จำนวนคอลัมน์"
searchByGoogle: "ค้นหา"
instanceDefaultLightTheme: "ธีมสว่างค่าเริ่มต้นสำหรับอินสแตนซ์"
instanceDefaultDarkTheme: "ธีมมืดค่าเริ่มต้นอินสแตนซ์"
instanceDefaultThemeDescription: "ป้อนรหัสธีมในรูปแบบออบเจ็กต์"
mutePeriod: "ระยะเวลาปิดเสียง"
indefinitely: "ตลอดไป"
tenMinutes: "10 นาที"
oneHour: "1 ชั่วโมง"
oneDay: "1 วัน"
oneWeek: "1 สัปดาห์"
reflectMayTakeTime: "อาจจำเป็นต้องใช้เวลาสักระยะหนึ่งจึงจะเห็นแสดงผลได้นะ"
failedToFetchAccountInformation: "ไม่สามารถเรียกดึงข้อมูลบัญชีได้"
rateLimitExceeded: "เกินขีดจำกัดอัตรา"
cropImage: "ครอบตัดรูปภาพ"
cropImageAsk: "คุณต้องการครอบตัดรูปภาพนี้อย่างงั้นหรือ?"
file: "ไฟล์"
recentNHours: "ล่าสุด {n} ชั่วโมงที่แล้ว"
recentNDays: "ล่าสุด {n} วันที่แล้ว"
noEmailServerWarning: "ไม่ได้กำหนดค่าเซิร์ฟเวอร์อีเมลนี้"
thereIsUnresolvedAbuseReportWarning: "มีรายงานที่ยังไม่ได้แก้ไข"
recommended: "แนะนำ"
check: "ตรวจสอบ"
driveCapOverrideLabel: "เปลี่ยนความจุของไดรฟ์สำหรับผู้ใช้รายนี้"
driveCapOverrideCaption: "รีเซ็ตความจุเป็นค่าเริ่มต้นโดยการป้อนค่าเป็น 0 หรือ ต่ำกว่า"
requireAdminForView: "คุณจำเป็นต้องเข้าสู่ระบบด้วยบัญชีผู้ดูแลระบบเพื่อเข้าดูสิ่งนี้"
isSystemAccount: "บัญชีที่ถูกสร้างมานั้น และถูกดำเนินการโดยอัตโนมัติด้วยระบบ"
typeToConfirm: "โปรดป้อน {x} เพื่อยืนยัน"
deleteAccount: "ลบบัญชี"
document: "เอกสาร"
numberOfPageCache: "จำนวนหน้าเพจที่แคช"
numberOfPageCacheDescription: "การเพิ่มจำนวนนี้จะช่วยเพิ่มความสะดวกให้กับผู้ใช้งาน แต่จะทำให้เซิร์ฟเวอร์โหลดมากขึ้นและต้องใช้หน่วยความจำมากขึ้นอีกด้วย"
logoutConfirm: "คุณแน่ใจว่าต้องการออกจากระบบ?"
lastActiveDate: "ใช้งานล่าสุดที่"
statusbar: "ไอคอนบนแถบสถานะ"
pleaseSelect: "ตัวเลือก"
reverse: "ย้อนกลับ"
colored: "สี"
refreshInterval: "รอบการอัพเดต"
label: "ป้ายชื่อ"
type: "รูปแบบ"
speed: "ความเร็ว"
slow: "ช้า"
fast: "เร็ว"
sensitiveMediaDetection: "การตรวจจับของสื่อ NSFW"
localOnly: "เฉพาะท้องถิ่น"
remoteOnly: "รีโมทเท่านั้น"
failedToUpload: "การอัปโหลดล้มเหลว"
cannotUploadBecauseInappropriate: "ไม่สามารถอัปโหลดไฟล์นี้ได้เนื่องจากระบบตรวจพบบางส่วนของไฟล์ว่านี้อาจจะเป็น NSFW"
cannotUploadBecauseNoFreeSpace: "การอัปโหลดนั้นล้มเหลวเนื่องจากไม่มีความจุของไดรฟ์"
beta: "เบต้า"
enableAutoSensitive: "ทำเครื่องหมาย NSFW อัตโนมัติ"
enableAutoSensitiveDescription: "อนุญาตให้ตรวจหาและทำเครื่องหมายสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่องหากเป็นไปได้ แม้ว่าตัวเลือกนี้จะถูกปิดใช้งาน แต่ก็สามารถเปิดใช้งานได้ทั้งอินสแตนซ์นี้"
activeEmailValidationDescription: "เปิดใช้งานการตรวจสอบที่อยู่อีเมลให้มีความเข้มงวดยิ่งขึ้น ซึ่งอาจจะรวมไปถึงการตรวจสอบที่อยู่อีเมล์ที่ใช้แล้วทิ้งและโดยให้พิจารณาว่าสามารถสื่อสารด้วยได้หรือไม่ เมื่อไม่เลือกระบบจะตรวจสอบเฉพาะรูปแบบของอีเมลเท่านั้น"
navbar: "แถบนำทาง"
shuffle: "สลับ"
account: "บัญชีผู้ใช้"
move: "ย้าย"
_sensitiveMediaDetection:
description: "ลดความพยายามในการดูแลเซิร์ฟเวอร์ผ่านการจดจำสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่อง การทำสิ่งนี้อาจจะเพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย"
sensitivity: "การตรวจจับความไว"
sensitivityDescription: "การลดความไวนั้นจะนำไปสู่การตรวจจับที่ผิดพลาดน้อยลง (ผลบวกที่ผิดพลาด) แต่ในขณะที่การเพิ่มนั้นจะนำไปสู่การตรวจหาที่พลาดน้อยลง (ผลลบเท็จ)"
setSensitiveFlagAutomatically: "ทำเครื่องหมายว่าเป็น NSFW"
setSensitiveFlagAutomaticallyDescription: "ผลลัพธ์ของการตรวจจับภายในนั้นจะยังคงอยู่ ถึงแม้ว่าจะปิดตัวเลือกนี้"
analyzeVideos: "เปิดใช้งานวิเคราะห์ของวิดีโอ"
analyzeVideosDescription: "การวิเคราะห์วิดีโอนอกเหนือจากรูปภาพนั้น การทำสิ่งนี้จะทำให้เพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย"
_emailUnavailable:
used: "ที่อยู่อีเมลนี้ได้ถูกใช้ไปแล้ว"
format: "รูปแบบของที่อยู่อีเมลนี้ไม่ถูกต้อง"
disposable: "ที่อยู่อีเมลที่ใช้แล้วทิ้งนั้นไม่สามารถใช้ได้"
mx: "เซิร์ฟเวอร์อีเมลนี้ไม่ถูกต้อง"
smtp: "เซิร์ฟเวอร์อีเมลนี้ไม่มีการตอบสนอง"
_ffVisibility:
public: "เผยแพร่"
followers: "ปรากฏให้แก่ผู้ติดตามเท่านั้น"
private: "ส่วนตัว"
_signup:
almostThere: "เกือบจะมี"
emailAddressInfo: "โปรดกรอกอีเมลของคุณ มันจะไม่เปิดเผยต่อสาธารณะ"
_ad:
back: "ย้อนกลับ"
_email:
@ -846,9 +920,22 @@ _email:
title: "ได้ติดตามคุณ"
_mfm:
mention: "กล่าวถึง"
centerDescription: "แสดงผลเนื้อหาเป็นศูนย์กลาง"
inlineCode: "โค้ด (อินไลน์)"
inlineCodeDescription: "แสดงผลการเน้นไวยากรณ์แบบอินไลน์สำหรับโค้ด (โปรแกรม)"
blockCode: "โค้ด (บล็อก)"
blockCodeDescription: "แสดงผลการเน้นไวยากรณ์สำหรับโค้ดหลายบรรทัด (โปรแกรม) ในบล็อก"
inlineMath: "คณิต (อินไลน์)"
inlineMathDescription: "แสดงผลสูตรคณิต (KaTeX) ในบรรทัด"
blockMath: "คณิต (บล็อก)"
blockMathDescription: "แสดงผลสูตรคณิตหลายบรรทัด (KaTeX) ในบล็อก"
quote: "อ้างคำพูด"
quoteDescription: "แสดงผลเนื้อหาเป็นใบเสนอราคา"
emoji: "กำหนดอีโมจิเอง"
emojiDescription: "โดยล้อมรอบชื่ออีโมจิที่กำหนดเองด้วยเครื่องหมายทวิภาค จะสามารถแสดงผลอีโมจิที่กำหนดเองได้"
search: "ค้นหา"
searchDescription: "แสดงผลกล่องค้นหาพร้อมกับข้อความที่ป้อนไว้ล่วงหน้า"
flip: "พลิก"
_theme:
description: "รายละเอียด"
keys:

View file

@ -52,6 +52,7 @@ searchUser: "Пошук користувачів"
reply: "Відповісти"
loadMore: "Показати більше"
showMore: "Показати більше"
showLess: "Закрити"
youGotNewFollower: "Новий підписник"
receiveFollowRequest: "Отримано запит на підписку"
followRequestAccepted: "Підписка прийнята"

View file

@ -52,6 +52,7 @@ searchUser: "Tìm kiếm người dùng"
reply: "Trả lời"
loadMore: "Tải thêm"
showMore: "Xem thêm"
showLess: "Đóng"
youGotNewFollower: "đã theo dõi bạn"
receiveFollowRequest: "Đã yêu cầu theo dõi"
followRequestAccepted: "Đã chấp nhận yêu cầu theo dõi"
@ -561,6 +562,7 @@ author: "Tác giả"
leaveConfirm: "Có những thay đổi chưa được lưu. Bạn có muốn bỏ chúng không?"
manage: "Quản lý"
plugins: "Plugin"
preferencesBackups: "Sao lưu thiết lập"
deck: "Deck"
undeck: "Bỏ Deck"
useBlurEffectForModal: "Sử dụng hiệu ứng mờ cho các hộp thoại"
@ -890,6 +892,7 @@ activeEmailValidationDescription: "Cho phép xác minh địa chỉ email chặt
navbar: "Thanh điều hướng"
shuffle: "Xáo trộn"
account: "Tài khoản của bạn"
move: "Di chuyển"
_sensitiveMediaDetection:
description: "Giảm nỗ lực kiểm duyệt máy chủ thông qua việc tự động nhận dạng media NSFW thông qua học máy. Điều này sẽ làm tăng một chút áp lực trên máy chủ."
sensitivity: "Phát hiện nhạy cảm"
@ -940,6 +943,24 @@ _plugin:
install: "Cài đặt tiện ích"
installWarn: "Vui lòng không cài đặt những tiện ích đáng ngờ."
manage: "Quản lý plugin"
_preferencesBackups:
list: "Tạo sao lưu"
saveNew: "Lưu bản sao lưu"
loadFile: "Nhập tập tin"
apply: "Áp dụng lên thiết bị này"
save: "Lưu thay đổi"
inputName: "Nhập tên bản sao lưu"
cannotSave: "Không thể lưu"
nameAlreadyExists: "Bản sao lưu \"{name}\" đã tồn tại. Xin nhập tên khác."
applyConfirm: "Bạn có chắc muốn áp dụng bản sao lưu \"{name}\" cho thiết bị này? Thiết lập hiện tại sẽ bị ghi đè."
saveConfirm: "Lưu bản sao lưu {name}?"
deleteConfirm: "Xóa bản sao lưu {name}?"
renameConfirm: "Đổi tên bản sao lưu \"{old}\" thành \"{new}\"?"
noBackups: "Chưa có bản sao lưu. Bạn có thể sao lưu thiết lập trên máy chủ này bằng cách sử dụng \"Tạo sao lưu\"."
createdAt: "Tạo vào: {time} {date}"
updatedAt: "Cập nhật: {time} {date}"
cannotLoad: "Tải thất bại"
invalidFile: "Sai định dạng tập tin"
_registry:
scope: "Phạm vi"
key: "Mã"
@ -1023,6 +1044,8 @@ _mfm:
sparkleDescription: "Làm cho nội dung hiệu ứng hạt lấp lánh."
rotate: "Xoay"
rotateDescription: "Xoay nội dung theo một góc cụ thể."
plain: "Đơn giản"
plainDescription: "Vô hiệu hóa mọi hiệu ứng MFM chứa trong hiệu ứng MFM này."
_instanceTicker:
none: "Không hiển thị"
remote: "Hiện cho người dùng từ máy chủ khác"
@ -1256,6 +1279,7 @@ _widgets:
activity: "Hoạt động"
photos: "Kho ảnh"
digitalClock: "Đồng hồ số"
unixClock: "Đồng hồ UNIX"
federation: "Liên hợp"
instanceCloud: "Instance cloud"
postForm: "Mẫu đăng"

View file

@ -52,6 +52,7 @@ searchUser: "搜索用户"
reply: "回复"
loadMore: "查看更多"
showMore: "查看更多"
showLess: "关闭"
youGotNewFollower: "你有新的关注者"
receiveFollowRequest: "您收到了关注请求"
followRequestAccepted: "您的关注请求被通过了"
@ -483,13 +484,13 @@ showFeaturedNotesInTimeline: "在时间线上显示热门推荐"
objectStorage: "对象存储"
useObjectStorage: "使用对象存储"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "URL前缀用于构造URL到对象媒体的引用如果您使用的是CDN或反向代理请指定其URL否则请根据您使用的服务指定可公开访问的地址。例如“https://<bucket>.s3.amazonaws.com”用于AWS S3“https://storage.googleapis.com/<bucket>”用于GCS"
objectStorageBaseUrlDesc: "用于引用的URL。如果您正在使用CDN或反向代理请指定其URL例如S3“https://<bucket>.s3.amazonaws.com”GCS“https://storage.googleapis.com/<bucket>”"
objectStorageBucket: "存储桶"
objectStorageBucketDesc: "请指定使用的对象存储服务的存储桶名称。"
objectStoragePrefix: "前缀"
objectStoragePrefixDesc: "文件将存储在此前缀的目录下。"
objectStorageEndpoint: "端点"
objectStorageEndpointDesc: "如果你希望使用AWS S3请留空。否则请根据你使用的服务来进行设置指定端点形式为“<host>”或“<host>:<port>”。"
objectStorageEndpointDesc: "如果你使用AWS S3请留空。否则请根据你使用的服务商的说明来进行设置,指定端点形式为“<host>”或“<host>:<port>”。"
objectStorageRegion: "可用区"
objectStorageRegionDesc: "指定一个可用区例如“xx-east-1”。 如果您的对象存储服务没有可用区概念请将其留空或填写“us-east-1”。"
objectStorageUseSSL: "使用SSL"
@ -561,6 +562,7 @@ author: "作者"
leaveConfirm: "存在未保存的更改。要放弃更改吗?"
manage: "管理"
plugins: "插件"
preferencesBackups: "备份设置"
deck: "Deck"
undeck: "取消Deck"
useBlurEffectForModal: "对话框使用模糊效果"
@ -846,6 +848,7 @@ oneDay: "1天"
oneWeek: "1周"
reflectMayTakeTime: "可能需要一些时间才能体现出效果。"
failedToFetchAccountInformation: "获取账户信息失败"
rateLimitExceeded: "已超過速率限制"
cropImage: "剪裁图像"
cropImageAsk: "是否要裁剪图像?"
file: "文件"
@ -855,6 +858,7 @@ noEmailServerWarning: "电子邮件服务器未设置。"
thereIsUnresolvedAbuseReportWarning: "有未解决的报告"
recommended: "推荐"
check: "检查"
driveCapOverrideLabel: "變更此用戶的雲端硬碟容量上限"
driveCapOverrideCaption: "设定为 0 以下则会解除此限制。"
requireAdminForView: "需要使用管理员账户登录才能查看。"
isSystemAccount: "该账号由系统自动创建和管理。"
@ -883,9 +887,12 @@ cannotUploadBecauseInappropriate: "因为可能含有不适宜的内容,无法
cannotUploadBecauseNoFreeSpace: "因为已无可用空间,无法上传。"
beta: "测试"
enableAutoSensitive: "自动 NSFW 识别"
enableAutoSensitiveDescription: "如果可用,请使用机器学习在媒体上自动设置 NSFW 标志。即使关闭此功能,也可能会根据实例自动设置。"
activeEmailValidationDescription: "积极地验证用户的电子邮件地址,判断它是一次性的电子邮件地址,还是可以实际通信的地址。关闭时,则只检查字符串是否正确。"
navbar: "导航栏"
shuffle: "随机"
account: "账户"
move: "移动"
_sensitiveMediaDetection:
description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。"
sensitivity: "检测敏感度"
@ -936,6 +943,24 @@ _plugin:
install: "安装插件"
installWarn: "请不要安装不可信的插件。"
manage: "管理插件..."
_preferencesBackups:
list: "已创建的备份"
saveNew: "另存为"
loadFile: "导入文件"
apply: "应用于本设备"
save: "覆盖存档"
inputName: "请输入备份的名称"
cannotSave: "无法保存"
nameAlreadyExists: "备份名称\"{name}\"已经存在,请指定其他名称。"
applyConfirm: "您是否要将备份\"{name}\"应用到当前设备上?当前设备现有配置将被丢弃。"
saveConfirm: "您确定要覆盖保存 {name} 吗?"
deleteConfirm: "您确定要删除 {name} 吗?"
renameConfirm: "您确定要把“{old}”改为“{new}”吗?"
noBackups: "当前没有备份,“另存为”允许您在服务器上保存当前客户端的配置。"
createdAt: "创建日期:{date} {time}"
updatedAt: "更新日期:{date} {time}"
cannotLoad: "无法加载"
invalidFile: "无效的的文件格式。"
_registry:
scope: "范围"
key: "主要"
@ -1019,6 +1044,8 @@ _mfm:
sparkleDescription: "添加发光粒子效果。"
rotate: "旋转"
rotateDescription: "旋转指定的角度。"
plain: "简洁"
plainDescription: "禁用所有内部语法。"
_instanceTicker:
none: "不显示"
remote: "仅远程用户"
@ -1248,9 +1275,11 @@ _widgets:
trends: "趋势"
clock: "时钟"
rss: "RSS阅读器"
rssTicker: "RSS Ticker"
activity: "活动"
photos: "照片"
digitalClock: "数字时钟"
unixClock: "UNIX时钟"
federation: "联邦宇宙"
instanceCloud: "实例云"
postForm: "投稿窗口"
@ -1701,6 +1730,9 @@ _deck:
profile: "配置文件"
newProfile: "新建配置文件"
deleteProfile: "删除配置文件"
introduction: "将各列进行组合以创建您自己的界面!"
introduction2: "您可以随时通过屏幕右侧的 + 来添加列"
widgetsIntroduction: "从列菜单中,选择“小工具编辑”来添加小工具"
_columns:
main: "主列"
widgets: "小工具"

View file

@ -52,6 +52,7 @@ searchUser: "搜尋使用者"
reply: "回覆"
loadMore: "載入更多"
showMore: "載入更多"
showLess: "關閉"
youGotNewFollower: "您有新的追隨者"
receiveFollowRequest: "您有新的追隨請求"
followRequestAccepted: "追隨請求已接受"
@ -561,6 +562,7 @@ author: "作者"
leaveConfirm: "有未保存的更改。要放棄嗎?"
manage: "管理"
plugins: "外掛"
preferencesBackups: "備份設定檔"
deck: "多欄模式"
undeck: "取消多欄模式"
useBlurEffectForModal: "在模態框使用模糊效果"
@ -888,7 +890,9 @@ enableAutoSensitive: "自動NSFW判定"
enableAutoSensitiveDescription: "如果可用,請利用機器學習在媒體上自動設置 NSFW 旗標。 即使關閉此功能,依實例而定也可能會自動設置。"
activeEmailValidationDescription: "積極地驗證用戶的電子郵件地址,判斷它是否為免洗地址,或者它是否可以通信。 若關閉,則只會檢查字元是否正確。"
navbar: "導覽列"
shuffle: "隨機"
account: "帳戶"
move: "移動 "
_sensitiveMediaDetection:
description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。"
sensitivity: "檢測敏感度"
@ -939,6 +943,24 @@ _plugin:
install: "安裝外掛組件"
installWarn: "請不要安裝來源不明的外掛組件。"
manage: "管理外掛"
_preferencesBackups:
list: "已備份的設定檔"
saveNew: "另存新檔"
loadFile: "讀取檔案"
apply: "套用在此裝置"
save: "覆蓋存檔"
inputName: "輸入備份檔名稱"
cannotSave: "無法儲存"
nameAlreadyExists: "備份檔名稱「{name}」已經存在。請指定不同的名稱。"
applyConfirm: "將備份檔「{name}」套用在現在的裝置嗎?現在的裝置設定將會消失。"
saveConfirm: "要覆蓋存檔{name}嗎?"
deleteConfirm: "要刪除{name}嗎?"
renameConfirm: "要將「{old}」變更為「{new}」嗎?"
noBackups: "沒有備份檔。您可以用「另存新檔」將現在的客戶端設定儲存在伺服器上。"
createdAt: "建立日期:{date} {time}"
updatedAt: "更新日期:{date} {time}"
cannotLoad: "無法讀取"
invalidFile: "檔案形式錯誤。"
_registry:
scope: "範圍"
key: "機碼"
@ -1022,6 +1044,8 @@ _mfm:
sparkleDescription: "添加閃閃發光的粒子效果。"
rotate: "旋轉"
rotateDescription: "以指定的角度旋轉。"
plain: "簡潔"
plainDescription: "停用全部的內部語法。"
_instanceTicker:
none: "隱藏"
remote: "向遠端使用者顯示"
@ -1255,6 +1279,7 @@ _widgets:
activity: "動態"
photos: "照片"
digitalClock: "電子時鐘"
unixClock: "UNIX時間"
federation: "聯邦宇宙"
instanceCloud: "實例雲"
postForm: "發佈窗口"

View file

@ -1,12 +1,12 @@
{
"name": "misskey",
"version": "12.118.0-calc.8.b4",
"codename": "indigo",
"version": "12.118.1-calc",
"codename": "aqua",
"repository": {
"type": "git",
"url": "https://codeberg.org/thatonecalculator/calckey.git"
},
"packageManager": "yarn@3.2.1",
"packageManager": "yarn@3.2.2",
"workspaces": [
"packages/client",
"packages/backend",
@ -39,6 +39,8 @@
"lodash": "^4.17.21"
},
"dependencies": {
"@bull-board/api": "^4.2.2",
"@bull-board/ui": "^4.2.2",
"@tensorflow/tfjs": "^3.18.0",
"eslint": "^8.20.0",
"execa": "5.1.1",
@ -54,7 +56,7 @@
"devDependencies": {
"@types/gulp": "4.0.9",
"@types/gulp-rename": "2.0.1",
"@typescript-eslint/parser": "5.30.7",
"@typescript-eslint/parser": "5.31.0",
"cross-env": "7.0.3",
"cypress": "10.3.1",
"start-server-and-test": "1.14.0",

View file

@ -0,0 +1,8 @@
export class CustomMOTD1658939464003 {
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "customMOTD" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "customMOTD"`);
}
}

View file

@ -0,0 +1,8 @@
export class CustomSplashIcons1658941974648 {
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "customSplashIcons" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "customSplashIcons"`);
}
}

View file

@ -0,0 +1,15 @@
export class FixCalckey1658981842728 {
name = 'FixCalckey1658981842728'
async up(queryRunner) {
await queryRunner.query(`UPDATE "meta" SET "useStarForReactionFallback" = TRUE;`);
await queryRunner.query(`UPDATE "meta" SET "repositoryUrl" = 'https://codeberg/thatonecalculator/calckey'`);
await queryRunner.query(`UPDATE "meta" SET "feedbackUrl" = 'https://codeberg/thatonecalculator/calckey/issues'`);
}
async down(queryRunner) {
await queryRunner.query(`UPDATE "meta" SET "useStarForReactionFallback" = FALSE;`);
await queryRunner.query(`UPDATE "meta" SET "repositoryUrl" = 'https://codeberg/thatonecalculator/calckey'`);
await queryRunner.query(`UPDATE "meta" SET "feedbackUrl" = 'https://codeberg/thatonecalculator/calckey/issues'`);
}
}

View file

@ -0,0 +1,11 @@
export class RecommendedTimeline1659042130648 {
name = 'RecommendedTimeline1659042130648'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "disableRecommendedTimeline" boolean NOT NULL DEFAULT true`);
await queryRunner.query(`ALTER TABLE "meta" ADD "recommendedInstances" character varying(256) array NOT NULL DEFAULT '{}'::varchar[]`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "disableRecommendedTimeline"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "recommendedInstances"`);
}
}

View file

@ -0,0 +1,9 @@
export class GuestTimeline1660068273737 {
name = 'GuestTimeline1660068273737'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "enableGuestTimeline" boolean NOT NULL DEFAULT false`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableGuestTimeline"`);
}
}

View file

@ -17,9 +17,7 @@
"@tensorflow/tfjs-node": "3.19.0"
},
"dependencies": {
"@bull-board/api": "4.0.0",
"@bull-board/koa": "4.0.0",
"@bull-board/ui": "4.0.0",
"@bull-board/koa": "4.1.1",
"@discordapp/twemoji": "14.0.2",
"@elastic/elasticsearch": "7.17.0",
"@koa/cors": "3.3.0",
@ -32,10 +30,10 @@
"archiver": "5.3.1",
"autobind-decorator": "2.4.0",
"autwh": "0.1.0",
"aws-sdk": "2.1165.0",
"aws-sdk": "2.1185.0",
"bcryptjs": "2.4.3",
"blurhash": "1.1.5",
"bull": "4.8.4",
"bull": "4.8.5",
"cacheable-lookup": "6.0.4",
"cbor": "8.1.0",
"chalk": "5.0.1",
@ -48,9 +46,9 @@
"deep-email-validator": "0.1.21",
"escape-regexp": "0.0.1",
"feed": "4.2.2",
"file-type": "17.1.3",
"file-type": "17.1.4",
"fluent-ffmpeg": "2.1.2",
"got": "12.2.0",
"got": "12.3.0",
"hpagent": "0.1.2",
"ioredis": "4.28.5",
"ip-cidr": "3.0.10",
@ -76,7 +74,7 @@
"mocha": "10.0.0",
"multer": "1.4.4",
"nested-property": "4.0.0",
"node-fetch": "3.2.9",
"node-fetch": "3.2.10",
"nodemailer": "6.7.7",
"nsfwjs": "2.4.1",
"oauth": "^0.9.15",
@ -125,9 +123,9 @@
"xev": "3.0.2"
},
"devDependencies": {
"@redocly/openapi-core": "1.0.0-beta.104",
"@redocly/openapi-core": "1.0.0-beta.105",
"@types/bcryptjs": "2.4.2",
"@types/bull": "3.15.8",
"@types/bull": "3.15.9",
"@types/cbor": "6.0.0",
"@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.20",
@ -147,7 +145,7 @@
"@types/koa__multer": "2.0.4",
"@types/koa__router": "8.0.11",
"@types/mocha": "9.1.1",
"@types/node": "18.6.1",
"@types/node": "18.6.3",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.4",
"@types/oauth": "0.9.1",
@ -169,8 +167,8 @@
"@types/web-push": "3.3.2",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "5.30.7",
"@typescript-eslint/parser": "5.30.7",
"@typescript-eslint/eslint-plugin": "5.31.0",
"@typescript-eslint/parser": "5.31.0",
"cross-env": "7.0.3",
"eslint": "8.20.0",
"eslint-plugin-import": "2.26.0",

View file

@ -18,7 +18,7 @@ const ev = new Xev();
* Init process
*/
export default async function() {
process.title = `Misskey (${cluster.isPrimary ? 'master' : 'worker'})`;
process.title = `Calckey (${cluster.isPrimary ? 'master' : 'worker'})`;
if (cluster.isPrimary || envOption.disableClustering) {
await masterMain();

View file

@ -112,13 +112,12 @@ function loadConfigBoot(): Config {
try {
config = loadConfig();
} catch (exception) {
if (typeof exception === 'string') {
configLogger.error(exception);
process.exit(1);
}
if (exception.code === 'ENOENT') {
configLogger.error('Configuration file not found', null, true);
process.exit(1);
} else if (e instanceof Error) {
configLogger.error(e.message);
process.exit(1);
}
throw exception;
}

View file

@ -45,7 +45,7 @@ export default function load() {
mixin.apiUrl = `${mixin.scheme}://${mixin.host}/api`;
mixin.authUrl = `${mixin.scheme}://${mixin.host}/auth`;
mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`;
mixin.userAgent = `Misskey/${meta.version} (${config.url})`;
mixin.userAgent = `Calckey/${meta.version} (${config.url})`;
mixin.clientEntry = clientManifest['src/init.ts'];
if (!config.redis.prefix) config.redis.prefix = mixin.host;
@ -57,6 +57,6 @@ function tryCreateUrl(url: string) {
try {
return new URL(url);
} catch (e) {
throw `url="${url}" is not a valid URL.`;
throw new Error(`url="${url}" is not a valid URL.`);
}
}

View file

@ -12,7 +12,7 @@ export function createConnection() {
});
}
export const subsdcriber = createConnection();
subsdcriber.subscribe(config.host);
export const subscriber = createConnection();
subscriber.subscribe(config.host);
export const redisClient = createConnection();

View file

@ -1,6 +1,6 @@
import { Antennas } from '@/models/index.js';
import { Antenna } from '@/models/entities/antenna.js';
import { subsdcriber } from '../db/redis.js';
import { subscriber } from '@/db/redis.js';
let antennasFetched = false;
let antennas: Antenna[] = [];
@ -14,7 +14,7 @@ export async function getAntennas() {
return antennas;
}
subsdcriber.on('message', async (_, data) => {
subscriber.on('message', async (_, data) => {
const obj = JSON.parse(data);
if (obj.channel === 'internal') {

View file

@ -5,23 +5,23 @@ import config from '@/config/index.js';
export async function verifyRecaptcha(secret: string, response: string) {
const result = await getCaptchaResponse('https://www.recaptcha.net/recaptcha/api/siteverify', secret, response).catch(e => {
throw `recaptcha-request-failed: ${e}`;
throw new Error(`recaptcha-request-failed: ${e.message}`);
});
if (result.success !== true) {
const errorCodes = result['error-codes'] ? result['error-codes']?.join(', ') : '';
throw `recaptcha-failed: ${errorCodes}`;
throw new Error(`recaptcha-failed: ${errorCodes}`);
}
}
export async function verifyHcaptcha(secret: string, response: string) {
const result = await getCaptchaResponse('https://hcaptcha.com/siteverify', secret, response).catch(e => {
throw `hcaptcha-request-failed: ${e}`;
throw new Error(`hcaptcha-request-failed: ${e.message}`);
});
if (result.success !== true) {
const errorCodes = result['error-codes'] ? result['error-codes']?.join(', ') : '';
throw `hcaptcha-failed: ${errorCodes}`;
throw new Error(`hcaptcha-failed: ${errorCodes}`);
}
}
@ -46,11 +46,11 @@ async function getCaptchaResponse(url: string, secret: string, response: string)
//timeout: 10 * 1000,
agent: getAgentByUrl,
}).catch(e => {
throw `${e.message || e}`;
throw new Error(`${e.message || e}`);
});
if (!res.ok) {
throw `${res.status}`;
throw new Error(`${res.status}`);
}
return await res.json() as CaptchaResponse;

View file

@ -1,15 +1,7 @@
export function isUserRelated(note: any, userIds: Set<string>): boolean {
if (userIds.has(note.userId)) {
return true;
}
if (note.reply != null && userIds.has(note.reply.userId)) {
return true;
}
if (note.renote != null && userIds.has(note.renote.userId)) {
return true;
}
export function isUserRelated(note: any, ids: Set<string>): boolean {
if (ids.has(note.userId)) return true; // note author is muted
if (note.mentions && note.mentions.some((user: string) => ids.has(user))) return true; // any of mentioned users are muted
if (note.reply && isUserRelated(note.reply, ids)) return true; // also check reply target
if (note.renote && isUserRelated(note.renote, ids)) return true; // also check renote target
return false;
}

View file

@ -1,6 +1,6 @@
import { Webhooks } from '@/models/index.js';
import { Webhook } from '@/models/entities/webhook.js';
import { subsdcriber } from '../db/redis.js';
import { subscriber } from '@/db/redis.js';
let webhooksFetched = false;
let webhooks: Webhook[] = [];
@ -16,7 +16,7 @@ export async function getActiveWebhooks() {
return webhooks;
}
subsdcriber.on('message', async (_, data) => {
subscriber.on('message', async (_, data) => {
const obj = JSON.parse(data);
if (obj.channel === 'internal') {

View file

@ -47,6 +47,11 @@ export class Meta {
})
public disableLocalTimeline: boolean;
@Column('boolean', {
default: true,
})
public disableRecommendedTimeline: boolean;
@Column('boolean', {
default: false,
})
@ -67,6 +72,21 @@ export class Meta {
})
public pinnedUsers: string[];
@Column('varchar', {
length: 256, array: true, default: '{}',
})
public recommendedInstances: string[];
@Column('varchar', {
length: 256, array: true, default: '{}',
})
public customMOTD: string[];
@Column('varchar', {
length: 256, array: true, default: '{}',
})
public customSplashIcons: string[];
@Column('varchar', {
length: 256, array: true, default: '{}',
})

View file

@ -16,7 +16,7 @@ export const packedFederationInstanceSchema = {
host: {
type: 'string',
optional: false, nullable: false,
example: 'misskey.example.com',
example: 'calckey.example.com',
},
usersCount: {
type: 'number',

View file

@ -4,7 +4,7 @@ export const packedHashtagSchema = {
tag: {
type: 'string',
optional: false, nullable: false,
example: 'misskey',
example: 'calckey',
},
mentionedUsersCount: {
type: 'number',

View file

@ -13,7 +13,7 @@ export function dateUTC(time: number[]): Date {
: time.length === 7 ? Date.UTC(time[0], time[1], time[2], time[3], time[4], time[5], time[6])
: null;
if (!d) throw 'wrong number of arguments';
if (!d) throw new Error('wrong number of arguments');
return new Date(d);
}

View file

@ -55,7 +55,7 @@ export async function importBlocking(job: Bull.Job<DbUserImportJobData>, done: a
}
if (target == null) {
throw `cannot resolve user: @${username}@${host}`;
throw new Error(`cannot resolve user: @${username}@${host}`);
}
// skip myself

View file

@ -55,7 +55,7 @@ export async function importFollowing(job: Bull.Job<DbUserImportJobData>, done:
}
if (target == null) {
throw `cannot resolve user: @${username}@${host}`;
throw new Error(`cannot resolve user: @${username}@${host}`);
}
// skip myself

View file

@ -56,7 +56,7 @@ export async function importMuting(job: Bull.Job<DbUserImportJobData>, done: any
}
if (target == null) {
throw `cannot resolve user: @${username}@${host}`;
throw new Error(`cannot resolve user: @${username}@${host}`);
}
// skip myself

View file

@ -93,7 +93,7 @@ export default async (job: Bull.Job<DeliverJobData>) => {
}
// 5xx etc.
throw `${res.statusCode} ${res.statusMessage}`;
throw new Error(`${res.statusCode} ${res.statusMessage}`);
} else {
// DNS error, socket error, timeout ...
throw res;

View file

@ -67,7 +67,7 @@ export default async (job: Bull.Job<InboxJobData>): Promise<string> => {
if (e.isClientError) {
return `skip: Ignored deleted actors on both ends ${activity.actor} - ${e.statusCode}`;
}
throw `Error in actor ${activity.actor} - ${e.statusCode || e}`;
throw new Error(`Error in actor ${activity.actor} - ${e.statusCode || e}`);
}
}
}

View file

@ -16,10 +16,10 @@ export default async (job: Bull.Job<WebhookDeliverJobData>) => {
url: job.data.to,
method: 'POST',
headers: {
'User-Agent': 'Misskey-Hooks',
'X-Misskey-Host': config.host,
'X-Misskey-Hook-Id': job.data.webhookId,
'X-Misskey-Hook-Secret': job.data.secret,
'User-Agent': 'Calckey-Hooks',
'X-Calckey-Host': config.host,
'X-Calckey-Hook-Id': job.data.webhookId,
'X-Calckey-Hook-Secret': job.data.secret,
},
body: JSON.stringify({
hookId: job.data.webhookId,
@ -50,7 +50,7 @@ export default async (job: Bull.Job<WebhookDeliverJobData>) => {
}
// 5xx etc.
throw `${res.statusCode} ${res.statusMessage}`;
throw new Error(`${res.statusCode} ${res.statusMessage}`);
} else {
// DNS error, socket error, timeout ...
throw res;

View file

@ -85,7 +85,7 @@ export class LdSignature {
private getLoader() {
return async (url: string): Promise<any> => {
if (!url.match('^https?\:\/\/')) throw `Invalid URL ${url}`;
if (!url.match('^https?\:\/\/')) throw new Error(`Invalid URL ${url}`);
if (this.preLoad) {
if (url in CONTEXTS) {
@ -118,7 +118,7 @@ export class LdSignature {
agent: u => u.protocol === 'http:' ? httpAgent : httpsAgent,
}).then(res => {
if (!res.ok) {
throw `${res.status} ${res.statusText}`;
throw new Error(`${res.status} ${res.statusText}`);
} else {
return res.json();
}

View file

@ -189,7 +189,7 @@ export async function createNote(value: string | IObject, resolver?: Resolver, s
quote = results.filter((x): x is { status: 'ok', res: Note | null } => x.status === 'ok').map(x => x.res).find(x => x);
if (!quote) {
if (results.some(x => x.status === 'temperror')) {
throw 'quote resolve failed';
throw new Error('quote resolve failed');
}
}
}
@ -276,7 +276,7 @@ export async function resolveNote(value: string | IObject, resolver?: Resolver):
// ブロックしてたら中断
const meta = await fetchMeta();
if (meta.blockedHosts.includes(extractDbHost(uri))) throw { statusCode: 451 };
if (meta.blockedHosts.includes(extractDbHost(uri))) throw new StatusError('host blocked', 451, `host ${extractDbHost(uri)} is blocked`);
const unlock = await getApLock(uri);

View file

@ -1,8 +1,8 @@
import { IsNull } from 'typeorm';
import config from '@/config/index.js';
import { NoteReaction } from '@/models/entities/note-reaction.js';
import { Note } from '@/models/entities/note.js';
import { Emojis } from '@/models/index.js';
import { IsNull } from 'typeorm';
import renderEmoji from './emoji.js';
export const renderLike = async (noteReaction: NoteReaction, note: Note) => {
@ -13,7 +13,7 @@ export const renderLike = async (noteReaction: NoteReaction, note: Note) => {
id: `${config.url}/likes/${noteReaction.id}`,
actor: `${config.url}/users/${noteReaction.userId}`,
object: note.uri ? note.uri : `${config.url}/notes/${noteReaction.noteId}`,
... (reaction !== '\u2b50' ? {
... (!['\u2b50', '\u1f44d'].includes(reaction) ? {
content: reaction,
_misskey_reaction: reaction,
} : {}),

View file

@ -235,6 +235,7 @@ import * as ep___notes_featured from './endpoints/notes/featured.js';
import * as ep___notes_globalTimeline from './endpoints/notes/global-timeline.js';
import * as ep___notes_hybridTimeline from './endpoints/notes/hybrid-timeline.js';
import * as ep___notes_localTimeline from './endpoints/notes/local-timeline.js';
import * as ep___notes_recommendedTimeline from './endpoints/notes/recommended-timeline.js';
import * as ep___notes_mentions from './endpoints/notes/mentions.js';
import * as ep___notes_polls_recommendation from './endpoints/notes/polls/recommendation.js';
import * as ep___notes_polls_vote from './endpoints/notes/polls/vote.js';
@ -267,7 +268,10 @@ import * as ep___pages_show from './endpoints/pages/show.js';
import * as ep___pages_unlike from './endpoints/pages/unlike.js';
import * as ep___pages_update from './endpoints/pages/update.js';
import * as ep___ping from './endpoints/ping.js';
import * as ep___recommendedInstances from './endpoints/recommended-instances.js';
import * as ep___pinnedUsers from './endpoints/pinned-users.js';
import * as ep___customMOTD from './endpoints/custom-motd.js';
import * as ep___customSplashIcons from './endpoints/custom-splash-icons.js';
import * as ep___promo_read from './endpoints/promo/read.js';
import * as ep___requestResetPassword from './endpoints/request-reset-password.js';
import * as ep___resetDb from './endpoints/reset-db.js';
@ -552,6 +556,7 @@ const eps = [
['notes/global-timeline', ep___notes_globalTimeline],
['notes/hybrid-timeline', ep___notes_hybridTimeline],
['notes/local-timeline', ep___notes_localTimeline],
['notes/recommended-timeline', ep___notes_recommendedTimeline],
['notes/mentions', ep___notes_mentions],
['notes/polls/recommendation', ep___notes_polls_recommendation],
['notes/polls/vote', ep___notes_polls_vote],
@ -585,6 +590,9 @@ const eps = [
['pages/update', ep___pages_update],
['ping', ep___ping],
['pinned-users', ep___pinnedUsers],
['recommended-instances', ep___recommendedInstances],
['custom-motd', ep___customMOTD],
['custom-motd', ep___customSplashIcons],
['promo/read', ep___promo_read],
['request-reset-password', ep___requestResetPassword],
['reset-db', ep___resetDb],

View file

@ -163,6 +163,14 @@ export const meta = {
type: 'boolean',
optional: true, nullable: false,
},
recommendedInstances: {
type: 'array',
optional: true, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
pinnedUsers: {
type: 'array',
optional: true, nullable: false,
@ -171,6 +179,22 @@ export const meta = {
optional: false, nullable: false,
},
},
customMOTD: {
type: 'array',
optional: true, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
customSplashIcons: {
type: 'array',
optional: true, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
hiddenTags: {
type: 'array',
optional: true, nullable: false,
@ -372,6 +396,7 @@ export default define(meta, paramDef, async (ps, me) => {
feedbackUrl: instance.feedbackUrl,
disableRegistration: instance.disableRegistration,
disableLocalTimeline: instance.disableLocalTimeline,
disableRecommendedTimeline: instance.disableRecommendedTimeline,
disableGlobalTimeline: instance.disableGlobalTimeline,
driveCapacityPerLocalUserMb: instance.localDriveCapacityMb,
driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb,
@ -401,7 +426,10 @@ export default define(meta, paramDef, async (ps, me) => {
pinnedClipId: instance.pinnedClipId,
cacheRemoteFiles: instance.cacheRemoteFiles,
useStarForReactionFallback: instance.useStarForReactionFallback,
recommendedInstances: instance.recommendedInstances,
pinnedUsers: instance.pinnedUsers,
customMOTD: instance.customMOTD,
customSplashIcons: instance.customSplashIcons,
hiddenTags: instance.hiddenTags,
blockedHosts: instance.blockedHosts,
allowedHosts: instance.allowedHosts,

View file

@ -27,6 +27,7 @@ export const meta = {
format: 'id',
},
inbox: {
description: 'URL of the inbox, must be a https scheme URL',
type: 'string',
optional: false, nullable: false,
format: 'url',
@ -56,7 +57,7 @@ export const paramDef = {
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, user) => {
try {
if (new URL(ps.inbox).protocol !== 'https:') throw 'https only';
if (new URL(ps.inbox).protocol !== 'https:') throw new Error('https only');
} catch {
throw new ApiError(meta.errors.invalidUrl);
}

View file

@ -68,7 +68,7 @@ async function unFollowAll(follower: User) {
});
if (followee == null) {
throw `Cant find followee ${following.followeeId}`;
throw new Error(`Cant find followee ${following.followeeId}`);
}
await deleteFollowing(follower, followee, true);

View file

@ -16,11 +16,21 @@ export const paramDef = {
properties: {
disableRegistration: { type: 'boolean', nullable: true },
disableLocalTimeline: { type: 'boolean', nullable: true },
disableRecommendedTimeline: { type: 'boolean', nullable: true },
disableGlobalTimeline: { type: 'boolean', nullable: true },
useStarForReactionFallback: { type: 'boolean', nullable: true },
recommendedInstances: { type: 'array', nullable: true, items: {
type: 'string',
} },
pinnedUsers: { type: 'array', nullable: true, items: {
type: 'string',
} },
customMOTD: { type: 'array', nullable: true, items: {
type: 'string',
} },
customSplashIcons: { type: 'array', nullable: true, items: {
type: 'string',
} },
hiddenTags: { type: 'array', nullable: true, items: {
type: 'string',
} },
@ -123,6 +133,10 @@ export default define(meta, paramDef, async (ps, me) => {
set.disableLocalTimeline = ps.disableLocalTimeline;
}
if (typeof ps.disableRecommendedTimeline === 'boolean') {
set.disableRecommendedTimeline = ps.disableRecommendedTimeline;
}
if (typeof ps.disableGlobalTimeline === 'boolean') {
set.disableGlobalTimeline = ps.disableGlobalTimeline;
}
@ -135,6 +149,18 @@ export default define(meta, paramDef, async (ps, me) => {
set.pinnedUsers = ps.pinnedUsers.filter(Boolean);
}
if (Array.isArray(ps.customMOTD)) {
set.customMOTD = ps.customMOTD.filter(Boolean);
}
if (Array.isArray(ps.customSplashIcons)) {
set.customSplashIcons = ps.customSplashIcons.filter(Boolean);
}
if (Array.isArray(ps.recommendedInstances)) {
set.recommendedInstances = ps.recommendedInstances.filter(Boolean);
}
if (Array.isArray(ps.hiddenTags)) {
set.hiddenTags = ps.hiddenTags.filter(Boolean);
}

View file

@ -0,0 +1,32 @@
// import { IsNull } from 'typeorm';
import { fetchMeta } from '@/misc/fetch-meta.js';
import define from '../define.js';
export const meta = {
tags: ['meta'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async () => {
const meta = await fetchMeta();
const motd = await Promise.all(meta.customMOTD.map(x => x));
return motd;
});

View file

@ -0,0 +1,32 @@
// import { IsNull } from 'typeorm';
import { fetchMeta } from '@/misc/fetch-meta.js';
import define from '../define.js';
export const meta = {
tags: ['meta'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async () => {
const meta = await fetchMeta();
const icons = await Promise.all(meta.customSplashIcons.map(x => x));
return icons;
});

View file

@ -36,7 +36,7 @@ export const meta = {
type: 'string',
optional: false, nullable: false,
format: 'url',
example: 'https://misskey.example.com',
example: 'https://calckey.example.com',
},
description: {
type: 'string',
@ -57,12 +57,12 @@ export const meta = {
repositoryUrl: {
type: 'string',
optional: false, nullable: false,
default: 'https://github.com/misskey-dev/misskey',
default: 'https://codeberg.org/thatonecalculator/calckey',
},
feedbackUrl: {
type: 'string',
optional: false, nullable: false,
default: 'https://github.com/misskey-dev/misskey/issues/new',
default: 'https://codeberg.org/thatonecalculator/calckey/issues',
},
defaultDarkTheme: {
type: 'string',
@ -80,6 +80,10 @@ export const meta = {
type: 'boolean',
optional: false, nullable: false,
},
disableRecommendedTimeline: {
type: 'boolean',
optional: false, nullable: false,
},
disableGlobalTimeline: {
type: 'boolean',
optional: false, nullable: false,
@ -248,6 +252,10 @@ export const meta = {
type: 'boolean',
optional: false, nullable: false,
},
recommendedTimeLine: {
type: 'boolean',
optional: false, nullable: false,
},
globalTimeLine: {
type: 'boolean',
optional: false, nullable: false,
@ -356,6 +364,7 @@ export default define(meta, paramDef, async (ps, me) => {
disableRegistration: instance.disableRegistration,
disableLocalTimeline: instance.disableLocalTimeline,
disableRecommendedTimeline: instance.disableRecommendedTimeline,
disableGlobalTimeline: instance.disableGlobalTimeline,
driveCapacityPerLocalUserMb: instance.localDriveCapacityMb,
driveCapacityPerRemoteUserMb: instance.remoteDriveCapacityMb,
@ -412,6 +421,7 @@ export default define(meta, paramDef, async (ps, me) => {
response.features = {
registration: !instance.disableRegistration,
localTimeLine: !instance.disableLocalTimeline,
recommendedTimeline: !instance.disableRecommendedTimeline,
globalTimeLine: !instance.disableGlobalTimeline,
emailRequiredForSignup: instance.emailRequiredForSignup,
elasticsearch: config.elasticsearch ? true : false,

View file

@ -55,7 +55,7 @@ export default define(meta, paramDef, async (ps, user) => {
.andWhere('note.id IN (SELECT id FROM note_replies(:noteId, :depth, :limit))', { noteId: ps.noteId, depth: ps.depth, limit: ps.limit })
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('user.avatar', 'avatar')
.leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('user.banner', 'banner');
generateVisibilityQuery(query, user);
if (user) {

View file

@ -0,0 +1,121 @@
import { Brackets } from 'typeorm';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { Notes } from '@/models/index.js';
import { activeUsersChart } from '@/services/chart/index.js';
import define from '../../define.js';
import { ApiError } from '../../error.js';
import { generateMutedUserQuery } from '../../common/generate-muted-user-query.js';
import { makePaginationQuery } from '../../common/make-pagination-query.js';
import { generateVisibilityQuery } from '../../common/generate-visibility-query.js';
import { generateRepliesQuery } from '../../common/generate-replies-query.js';
import { generateMutedNoteQuery } from '../../common/generate-muted-note-query.js';
import { generateChannelQuery } from '../../common/generate-channel-query.js';
import { generateBlockedUserQuery } from '../../common/generate-block-query.js';
export const meta = {
tags: ['notes'],
requireCredentialPrivateMode: true,
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'object',
optional: false, nullable: false,
ref: 'Note',
},
},
errors: {
rtlDisabled: {
message: 'Recommended timeline has been disabled.',
code: 'RTL_DISABLED',
id: '45a6eb02-7695-4393-b023-dd3be9aaaefe',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
withFiles: {
type: 'boolean',
default: false,
description: 'Only show notes that have attached files.',
},
fileType: { type: 'array', items: {
type: 'string',
} },
excludeNsfw: { type: 'boolean', default: false },
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
sinceId: { type: 'string', format: 'misskey:id' },
untilId: { type: 'string', format: 'misskey:id' },
sinceDate: { type: 'integer' },
untilDate: { type: 'integer' },
},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, user) => {
const m = await fetchMeta();
if (m.disableRecommendedTimeline) {
if (user == null || (!user.isAdmin && !user.isModerator)) {
throw new ApiError(meta.errors.rtlDisabled);
}
}
//#region Construct query
const query = makePaginationQuery(Notes.createQueryBuilder('note'),
ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate)
.andWhere(`(note.userHost = ANY ('{"${m.recommendedInstances.join('","')}"}'))`)
.andWhere('(note.visibility = \'public\')')
.innerJoinAndSelect('note.user', 'user')
.leftJoinAndSelect('user.avatar', 'avatar')
.leftJoinAndSelect('user.banner', 'banner')
.leftJoinAndSelect('note.reply', 'reply')
.leftJoinAndSelect('note.renote', 'renote')
.leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('replyUser.avatar', 'replyUserAvatar')
.leftJoinAndSelect('replyUser.banner', 'replyUserBanner')
.leftJoinAndSelect('renote.user', 'renoteUser')
.leftJoinAndSelect('renoteUser.avatar', 'renoteUserAvatar')
.leftJoinAndSelect('renoteUser.banner', 'renoteUserBanner');
generateChannelQuery(query, user);
generateRepliesQuery(query, user);
generateVisibilityQuery(query, user);
if (user) generateMutedUserQuery(query, user);
if (user) generateMutedNoteQuery(query, user);
if (user) generateBlockedUserQuery(query, user);
if (ps.withFiles) {
query.andWhere('note.fileIds != \'{}\'');
}
if (ps.fileType != null) {
query.andWhere('note.fileIds != \'{}\'');
query.andWhere(new Brackets(qb => {
for (const type of ps.fileType!) {
const i = ps.fileType!.indexOf(type);
qb.orWhere(`:type${i} = ANY(note.attachedFileTypes)`, { [`type${i}`]: type });
}
}));
if (ps.excludeNsfw) {
query.andWhere('note.cw IS NULL');
query.andWhere('0 = (SELECT COUNT(*) FROM drive_file df WHERE df.id = ANY(note."fileIds") AND df."isSensitive" = TRUE)');
}
}
//#endregion
const timeline = await query.take(ps.limit).getMany();
process.nextTick(() => {
if (user) {
activeUsersChart.read(user);
}
});
return await Notes.packMany(timeline, user);
});

View file

@ -87,14 +87,14 @@ export default define(meta, paramDef, async (ps, me) => {
try {
if (ps.tag) {
if (!safeForSql(ps.tag)) throw 'Injection';
if (!safeForSql(ps.tag)) throw new Error('Injection');
query.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`);
} else {
query.andWhere(new Brackets(qb => {
for (const tags of ps.query!) {
qb.orWhere(new Brackets(qb => {
for (const tag of tags) {
if (!safeForSql(tag)) throw 'Injection';
if (!safeForSql(tag)) throw new Error('Injection');
qb.andWhere(`'{"${normalizeForSearch(tag)}"}' <@ note.tags`);
}
}));
@ -102,7 +102,7 @@ export default define(meta, paramDef, async (ps, me) => {
}));
}
} catch (e) {
if (e === 'Injection') return [];
if (e.message === 'Injection') return [];
throw e;
}

View file

@ -0,0 +1,32 @@
// import { IsNull } from 'typeorm';
import { fetchMeta } from '@/misc/fetch-meta.js';
import define from '../define.js';
export const meta = {
tags: ['meta'],
requireCredential: false,
requireCredentialPrivateMode: true,
res: {
type: 'array',
optional: false, nullable: false,
items: {
type: 'string',
optional: false, nullable: false,
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {},
required: [],
} as const;
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async () => {
const meta = await fetchMeta();
const instances = await Promise.all(meta.recommendedInstances.map(x => x));
return instances;
});

View file

@ -22,7 +22,7 @@ export const paramDef = {
// eslint-disable-next-line import/no-default-export
export default define(meta, paramDef, async (ps, user) => {
if (process.env.NODE_ENV !== 'test') throw 'NODE_ENV is not a test';
if (process.env.NODE_ENV !== 'test') throw new Error('NODE_ENV is not a test');
await resetDb();

View file

@ -15,7 +15,7 @@ export function genOpenapiSpec() {
externalDocs: {
description: 'Repository',
url: 'https://github.com/misskey-dev/misskey',
url: 'https://codeberg.org/thatonecalculator/calckey',
},
servers: [{
@ -95,7 +95,7 @@ export function genOpenapiSpec() {
description: desc,
externalDocs: {
description: 'Source code',
url: `https://github.com/misskey-dev/misskey/blob/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`,
url: `https://codeberg.org/thatonecalculator/calckey/src/branch/develop/packages/backend/src/server/api/endpoints/${endpoint.name}.ts`,
},
tags: endpoint.meta.tags || undefined,
security,

View file

@ -2,6 +2,7 @@ import main from './main.js';
import homeTimeline from './home-timeline.js';
import localTimeline from './local-timeline.js';
import hybridTimeline from './hybrid-timeline.js';
import recommendedTimeline from './recommended-timeline.js';
import globalTimeline from './global-timeline.js';
import serverStats from './server-stats.js';
import queueStats from './queue-stats.js';
@ -18,6 +19,7 @@ export default {
main,
homeTimeline,
localTimeline,
recommendedTimeline,
hybridTimeline,
globalTimeline,
serverStats,

View file

@ -0,0 +1,67 @@
import Channel from '../channel.js';
import { fetchMeta } from '@/misc/fetch-meta.js';
import { checkWordMute } from '@/misc/check-word-mute.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js';
import { Packed } from '@/misc/schema.js';
export default class extends Channel {
public readonly chName = 'recommendedTimeline';
public static shouldShare = true;
public static requireCredential = true;
constructor(id: string, connection: Channel['connection']) {
super(id, connection);
this.onNote = this.withPackedNote(this.onNote.bind(this));
}
public async init(params: any) {
const meta = await fetchMeta();
if (meta.disableRecommendedTimeline && !this.user!.isAdmin && !this.user!.isModerator) return;
// Subscribe events
this.subscriber.on('notesStream', this.onNote);
}
private async onNote(note: Packed<'Note'>) {
// チャンネルの投稿ではなく、自分自身の投稿 または
// チャンネルの投稿ではなく、その投稿のユーザーをフォローしている または
// チャンネルの投稿ではなく、全体公開のローカルの投稿 または
// フォローしているチャンネルの投稿 の場合だけ
const meta = await fetchMeta();
if (!(
((note.user.host != null && meta.recommendedInstances.includes(note.user.host)) && note.visibility === 'public')
)) return;
// Ignore notes from instances the user has muted
if (isInstanceMuted(note, new Set<string>(this.userProfile?.mutedInstances ?? []))) return;
// 関係ない返信は除外
if (note.reply && !this.user!.showTimelineReplies) {
const reply = note.reply;
// 「チャンネル接続主への返信」でもなければ、「チャンネル接続主が行った返信」でもなければ、「投稿者の投稿者自身への返信」でもない場合
if (reply.userId !== this.user!.id && note.userId !== this.user!.id && reply.userId !== note.userId) return;
}
// 流れてきたNoteがミュートしているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.muting)) return;
// 流れてきたNoteがブロックされているユーザーが関わるものだったら無視する
if (isUserRelated(note, this.blocking)) return;
// 流れてきたNoteがミュートすべきNoteだったら無視する
// TODO: 将来的には、単にMutedNoteテーブルにレコードがあるかどうかで判定したい(以下の理由により難しそうではある)
// 現状では、ワードミュートにおけるMutedNoteレコードの追加処理はストリーミングに流す処理と並列で行われるため、
// レコードが追加されるNoteでも追加されるより先にここのストリーミングの処理に到達することが起こる。
// そのためレコードが存在するかのチェックでは不十分なので、改めてcheckWordMuteを呼んでいる
if (this.userProfile && await checkWordMute(note, this.user, this.userProfile.mutedWords)) return;
this.connection.cacheNote(note);
this.send('note', note);
}
public dispose() {
// Unsubscribe events
this.subscriber.off('notesStream', this.onNote);
}
}

View file

@ -1,12 +1,12 @@
import * as http from 'node:http';
import { EventEmitter } from 'events';
import { ParsedUrlQuery } from 'querystring';
import * as websocket from 'websocket';
import MainStreamConnection from './stream/index.js';
import { ParsedUrlQuery } from 'querystring';
import authenticate from './authenticate.js';
import { EventEmitter } from 'events';
import { subsdcriber as redisClient } from '../../db/redis.js';
import { subscriber as redisClient } from '@/db/redis.js';
import { Users } from '@/models/index.js';
import MainStreamConnection from './stream/index.js';
import authenticate from './authenticate.js';
export const initializeStreamingServer = (server: http.Server) => {
// Init websocket server

View file

@ -67,6 +67,7 @@ const nodeinfo2 = async () => {
feedbackUrl: meta.feedbackUrl,
disableRegistration: meta.disableRegistration,
disableLocalTimeline: meta.disableLocalTimeline,
disableRecommendedTimeline: meta.disableRecommendedTimeline,
disableGlobalTimeline: meta.disableGlobalTimeline,
emailRequiredForSignup: meta.emailRequiredForSignup,
enableHcaptcha: meta.enableHcaptcha,

View file

@ -268,22 +268,6 @@ router.get('/@:user.json', async ctx => {
}
});
// MOTD
const motd = [
'If you\'re on mobile, you can tap install/add to homescreen to get the app!',
'You can click the time a note was posted to get a full view of the note.',
'Wanna find people to follow? Head over to the Explore tab!',
'Want more ways to post? You can make blogs in Pages and galleries in Gallery tab.',
'You can add cool stuff to notes like CWs, polls, multiple videos/gifs, and audio!',
'Use #hashtags to tag notes and reach more people, especially for #art.',
'If your note gets popular, it might show up on the Featured tap for up to 3 days!',
'Use the 4 buttons at the top (or the top drop-down on mobile) to switch timelines.',
'The Fediverse is made up of more than just Calckey.',
'Avatars and banners can be GIFs.',
'When writing a note, type $ to see a list of cool text effects (mfm).',
'Be gay, do crime.',
];
//#region SSR (for crawlers)
// User
router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => {
@ -311,9 +295,8 @@ router.get(['/@:user', '/@:user/:sub'], async (ctx, next) => {
icon: meta.iconUrl,
themeColor: meta.themeColor,
privateMode: meta.privateMode,
randomMOTD: motd[Math.floor(Math.random() * motd.length)],
});
ctx.set('Cache-Control', 'public, max-age=3');
ctx.set('Cache-Control', 'public, max-age=15');
} else {
// リモートユーザーなので
// モデレータがAPI経由で参照可能にするために404にはしない
@ -355,7 +338,7 @@ router.get('/notes/:note', async (ctx, next) => {
avatarUrl: await Users.getAvatarUrl(await Users.findOneByOrFail({ id: note.userId })),
// TODO: Let locale changeable by instance setting
summary: getNoteSummary(_note),
instanceName: meta.name || 'Misskey',
instanceName: meta.name || 'Calckey',
icon: meta.iconUrl,
themeColor: meta.themeColor,
});
@ -543,16 +526,25 @@ router.get('/streaming', async ctx => {
// Render base html for all requests
router.get('(.*)', async ctx => {
const meta = await fetchMeta();
let motd = ['Loading...'];
if (meta.customMOTD.length > 0) {
motd = meta.customMOTD;
}
let iconUrl = meta.iconUrl;
if (meta.customSplashIcons.length > 0) {
iconUrl = meta.customSplashIcons[Math.floor(Math.random() * meta.customSplashIcons.length)];
}
await ctx.render('base', {
img: meta.bannerUrl,
title: meta.name || 'Calckey',
instanceName: meta.name || 'Calckey',
desc: meta.description,
icon: meta.iconUrl,
icon: iconUrl,
themeColor: meta.themeColor,
privateMode: meta.privateMode,
randomMOTD: motd[Math.floor(Math.random() * motd.length)],
});
ctx.set('Cache-Control', 'public, max-age=15');
ctx.set('Cache-Control', 'public, max-age=3');
});
// Register router

View file

@ -36,6 +36,7 @@ html
link(rel='prefetch' href='/static-assets/badges/not-found.jpg')
link(rel='prefetch' href='/static-assets/badges/error.jpg')
link(rel='stylesheet' href='/assets/fontawesome/css/all.css')
link(rel='stylesheet' href='/static-assets/instance.css')
link(rel='modulepreload' href=`/assets/${clientEntry.file}`)
each href in clientEntry.css

View file

@ -51,7 +51,7 @@ html
.then(registrations => {
return Promise.all(registrations.map(registration => registration.unregister()));
})
.catch(e => { throw Error(e) });
.catch(e => { throw new Error(e) });
}
message(successText);

View file

@ -94,14 +94,14 @@ async function fetchNodeinfo(instance: Instance): Promise<NodeInfo> {
const wellknown = await getJson('https://' + instance.host + '/.well-known/nodeinfo')
.catch(e => {
if (e.statusCode === 404) {
throw 'No nodeinfo provided';
throw new Error('No nodeinfo provided');
} else {
throw e.statusCode || e.message;
throw new Error(e.statusCode || e.message);
}
}) as Record<string, unknown>;
if (wellknown.links == null || !Array.isArray(wellknown.links)) {
throw 'No wellknown links';
throw new Error('No wellknown links');
}
const links = wellknown.links as any[];
@ -112,19 +112,19 @@ async function fetchNodeinfo(instance: Instance): Promise<NodeInfo> {
const link = lnik2_1 || lnik2_0 || lnik1_0;
if (link == null) {
throw 'No nodeinfo link provided';
throw new Error('No nodeinfo link provided');
}
const info = await getJson(link.href)
.catch(e => {
throw e.statusCode || e.message;
throw new Error(e.statusCode || e.message);
});
logger.succ(`Successfuly fetched nodeinfo of ${instance.host}`);
return info as NodeInfo;
} catch (e) {
logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e}`);
logger.error(`Failed to fetch nodeinfo of ${instance.host}: ${e.message}`);
throw e;
}

View file

@ -47,7 +47,7 @@ export async function removeRelay(inbox: string) {
});
if (relay == null) {
throw 'relay not found';
throw new Error('relay not found');
}
const relayActor = await getRelayActor();

View file

@ -1,14 +1,14 @@
import { CacheableLocalUser, CacheableUser, ILocalUser, User } from '@/models/entities/user.js';
import { Users } from '@/models/index.js';
import { Cache } from '@/misc/cache.js';
import { subsdcriber } from '@/db/redis.js';
import { subscriber } from '@/db/redis.js';
export const userByIdCache = new Cache<CacheableUser>(Infinity);
export const localUserByNativeTokenCache = new Cache<CacheableLocalUser | null>(Infinity);
export const localUserByIdCache = new Cache<CacheableLocalUser>(Infinity);
export const uriPersonCache = new Cache<CacheableUser | null>(Infinity);
subsdcriber.on('message', async (_, data) => {
subscriber.on('message', async (_, data) => {
const obj = JSON.parse(data);
if (obj.channel === 'internal') {

View file

@ -223,6 +223,78 @@ describe('Streaming', () => {
});
});
describe('Recommended Timeline', () => {
it('自分の投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'recommendedTimeline', // ayano:Local
() => api('notes/create', { text: 'foo' }, ayano), // ayano posts
msg => msg.type === 'note' && msg.body.text === 'foo'
);
assert.strictEqual(fired, true);
});
it('フォローしていないローカルユーザーの投稿が流れる', async () => {
const fired = await waitFire(
ayano, 'recommendedTimeline', // ayano:Local
() => api('notes/create', { text: 'foo' }, chitose), // chitose posts
msg => msg.type === 'note' && msg.body.userId === chitose.id // wait chitose
);
assert.strictEqual(fired, true);
});
it('リモートユーザーの投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'recommendedTimeline', // ayano:Local
() => api('notes/create', { text: 'foo' }, chinatsu), // chinatsu posts
msg => msg.type === 'note' && msg.body.userId === chinatsu.id // wait chinatsu
);
assert.strictEqual(fired, false);
});
it('フォローしてたとしてもリモートユーザーの投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'recommendedTimeline', // ayano:Local
() => api('notes/create', { text: 'foo' }, akari), // akari posts
msg => msg.type === 'note' && msg.body.userId === akari.id // wait akari
);
assert.strictEqual(fired, false);
});
it('ホーム指定の投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'recommendedTimeline', // ayano:Local
() => api('notes/create', { text: 'foo', visibility: 'home' }, kyoko), // kyoko home posts
msg => msg.type === 'note' && msg.body.userId === kyoko.id // wait kyoko
);
assert.strictEqual(fired, false);
});
it('フォローしているローカルユーザーのダイレクト投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'recommendedTimeline', // ayano:Local
() => api('notes/create', { text: 'foo', visibility: 'specified', visibleUserIds: [ayano.id] }, kyoko), // kyoko DM => ayano
msg => msg.type === 'note' && msg.body.userId === kyoko.id // wait kyoko
);
assert.strictEqual(fired, false);
});
it('フォローしていないローカルユーザーのフォロワー宛て投稿は流れない', async () => {
const fired = await waitFire(
ayano, 'recommendedTimeline', // ayano:Local
() => api('notes/create', { text: 'foo', visibility: 'followers' }, chitose),
msg => msg.type === 'note' && msg.body.userId === chitose.id // wait chitose
);
assert.strictEqual(fired, false);
});
});
describe('Hybrid Timeline', () => {
it('自分の投稿が流れる', async () => {
const fired = await waitFire(

View file

@ -9,7 +9,7 @@
"noFallthroughCasesInSwitch": true,
"declaration": false,
"sourceMap": true,
"target": "es2017",
"target": "es2021",
"module": "es2020",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,

View file

@ -9,7 +9,7 @@
"noFallthroughCasesInSwitch": true,
"declaration": false,
"sourceMap": false,
"target": "es2017",
"target": "es2021",
"module": "es2020",
"moduleResolution": "node",
"allowSyntheticDefaultImports": true,

View file

@ -8,7 +8,7 @@
},
"dependencies": {
"@discordapp/twemoji": "14.0.2",
"@fortawesome/fontawesome-free": "6.1.1",
"@fortawesome/fontawesome-free": "6.1.2",
"@rollup/plugin-alias": "3.1.9",
"@rollup/plugin-json": "4.1.0",
"@rollup/pluginutils": "^4.2.1",
@ -47,8 +47,9 @@
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"syuilo-password-strength": "0.0.1",
"tesseract.js": "^2.1.5",
"textarea-caret": "3.1.0",
"three": "0.142.0",
"three": "0.143.0",
"throttle-debounce": "5.0.0",
"tinycolor2": "1.4.2",
"tsc-alias": "1.7.0",
@ -57,7 +58,7 @@
"typescript": "4.7.4",
"uuid": "8.3.2",
"vanilla-tilt": "1.7.2",
"vite": "3.0.3",
"vite": "3.0.4",
"vue": "3.2.37",
"vue-prism-editor": "2.0.0-alpha.2",
"vuedraggable": "4.0.1"
@ -74,14 +75,14 @@
"@types/throttle-debounce": "5.0.0",
"@types/tinycolor2": "1.4.3",
"@types/uuid": "8.3.4",
"@typescript-eslint/eslint-plugin": "5.30.7",
"@typescript-eslint/parser": "5.30.7",
"@typescript-eslint/eslint-plugin": "5.31.0",
"@typescript-eslint/parser": "5.31.0",
"cross-env": "7.0.3",
"cypress": "10.3.1",
"eslint": "8.20.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-vue": "9.3.0",
"rollup": "2.77.0",
"rollup": "2.77.2",
"start-server-and-test": "1.14.0"
}
}

View file

@ -1,12 +1,30 @@
<template>
<svg class="mbcofsoe" viewBox="0 0 10 10" preserveAspectRatio="none">
<circle v-for="(angle, i) in graduations"
:key="i"
:cx="5 + (Math.sin(angle) * (5 - graduationsPadding))"
:cy="5 - (Math.cos(angle) * (5 - graduationsPadding))"
:r="i % 5 == 0 ? 0.125 : 0.05"
:fill="i % 5 == 0 ? majorGraduationColor : minorGraduationColor"
/>
<template v-if="props.graduations === 'dots'">
<circle
v-for="(angle, i) in graduationsMajor"
:cx="5 + (Math.sin(angle) * (5 - graduationsPadding))"
:cy="5 - (Math.cos(angle) * (5 - graduationsPadding))"
:r="0.125"
:fill="(props.twentyfour ? h : h % 12) === i ? nowColor : majorGraduationColor"
:opacity="!props.fadeGraduations || (props.twentyfour ? h : h % 12) === i ? 1 : Math.max(0, 1 - (angleDiff(hAngle, angle) / Math.PI) - numbersOpacityFactor)"
/>
</template>
<template v-else-if="props.graduations === 'numbers'">
<text
v-for="(angle, i) in texts"
:x="5 + (Math.sin(angle) * (5 - textsPadding))"
:y="5 - (Math.cos(angle) * (5 - textsPadding))"
text-anchor="middle"
dominant-baseline="middle"
:font-size="(props.twentyfour ? h : h % 12) === i ? 1 : 0.7"
:font-weight="(props.twentyfour ? h : h % 12) === i ? 'bold' : 'normal'"
:fill="(props.twentyfour ? h : h % 12) === i ? nowColor : 'currentColor'"
:opacity="!props.fadeGraduations || (props.twentyfour ? h : h % 12) === i ? 1 : Math.max(0, 1 - (angleDiff(hAngle, angle) / Math.PI) - numbersOpacityFactor)"
>
{{ i === 0 ? (props.twentyfour ? '24' : '12') : i }}
</text>
</template>
<line
:x1="5 - (Math.sin(sAngle) * (sHandLengthRatio * handsTailLength))"
@ -41,63 +59,116 @@
</template>
<script lang="ts" setup>
import { ref, computed, onMounted, onBeforeUnmount } from 'vue';
import { ref, computed, onMounted, onBeforeUnmount, shallowRef } from 'vue';
import tinycolor from 'tinycolor2';
import { globalEvents } from '@/events.js';
withDefaults(defineProps<{
thickness: number;
// https://stackoverflow.com/questions/1878907/how-can-i-find-the-difference-between-two-angles
const angleDiff = (a: number, b: number) => {
const x = Math.abs(a - b);
return Math.abs((x + Math.PI) % (Math.PI * 2) - Math.PI);
};
const graduationsPadding = 0.5;
const textsPadding = 0.6;
const handsPadding = 1;
const handsTailLength = 0.7;
const hHandLengthRatio = 0.75;
const mHandLengthRatio = 1;
const sHandLengthRatio = 1;
const numbersOpacityFactor = 0.35;
const props = withDefaults(defineProps<{
thickness?: number;
offset?: number;
twentyfour?: boolean;
graduations?: 'none' | 'dots' | 'numbers';
fadeGraduations?: boolean;
}>(), {
numbers: false,
thickness: 0.1,
offset: 0 - new Date().getTimezoneOffset(),
twentyfour: false,
graduations: 'dots',
fadeGraduations: true,
});
const now = ref(new Date());
const enabled = ref(true);
const graduationsPadding = ref(0.5);
const handsPadding = ref(1);
const handsTailLength = ref(0.7);
const hHandLengthRatio = ref(0.75);
const mHandLengthRatio = ref(1);
const sHandLengthRatio = ref(1);
const computedStyle = getComputedStyle(document.documentElement);
const dark = computed(() => tinycolor(computedStyle.getPropertyValue('--bg')).isDark());
const majorGraduationColor = computed(() => dark.value ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)');
const minorGraduationColor = computed(() => dark.value ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)');
const sHandColor = computed(() => dark.value ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)');
const mHandColor = computed(() => tinycolor(computedStyle.getPropertyValue('--fg')).toHexString());
const hHandColor = computed(() => tinycolor(computedStyle.getPropertyValue('--accent')).toHexString());
const s = computed(() => now.value.getSeconds());
const m = computed(() => now.value.getMinutes());
const h = computed(() => now.value.getHours());
const hAngle = computed(() => Math.PI * (h.value % 12 + (m.value + s.value / 60) / 60) / 6);
const mAngle = computed(() => Math.PI * (m.value + s.value / 60) / 30);
const sAngle = computed(() => Math.PI * s.value / 30);
const graduations = computed(() => {
const graduationsMajor = computed(() => {
const angles: number[] = [];
for (let i = 0; i < 60; i++) {
const angle = Math.PI * i / 30;
const times = props.twentyfour ? 24 : 12;
for (let i = 0; i < times; i++) {
const angle = Math.PI * i / (times / 2);
angles.push(angle);
}
return angles;
});
const texts = computed(() => {
const angles: number[] = [];
const times = props.twentyfour ? 24 : 12;
for (let i = 0; i < times; i++) {
const angle = Math.PI * i / (times / 2);
angles.push(angle);
}
return angles;
});
let enabled = true;
let majorGraduationColor = $ref<string>();
//let minorGraduationColor = $ref<string>();
let sHandColor = $ref<string>();
let mHandColor = $ref<string>();
let hHandColor = $ref<string>();
let nowColor = $ref<string>();
let h = $ref<number>(0);
let m = $ref<number>(0);
let s = $ref<number>(0);
let hAngle = $ref<number>(0);
let mAngle = $ref<number>(0);
let sAngle = $ref<number>(0);
function tick() {
now.value = new Date();
const now = new Date();
now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + props.offset));
s = now.getSeconds();
m = now.getMinutes();
h = now.getHours();
hAngle = Math.PI * (h % (props.twentyfour ? 24 : 12) + (m + s / 60) / 60) / (props.twentyfour ? 12 : 6);
mAngle = Math.PI * (m + s / 60) / 30;
sAngle = Math.PI * s / 30;
}
tick();
function calcColors() {
const computedStyle = getComputedStyle(document.documentElement);
const dark = tinycolor(computedStyle.getPropertyValue('--bg')).isDark();
const accent = tinycolor(computedStyle.getPropertyValue('--accent')).toHexString();
majorGraduationColor = dark ? 'rgba(255, 255, 255, 0.3)' : 'rgba(0, 0, 0, 0.3)';
//minorGraduationColor = dark ? 'rgba(255, 255, 255, 0.2)' : 'rgba(0, 0, 0, 0.2)';
sHandColor = dark ? 'rgba(255, 255, 255, 0.5)' : 'rgba(0, 0, 0, 0.3)';
mHandColor = tinycolor(computedStyle.getPropertyValue('--fg')).toHexString();
hHandColor = accent;
nowColor = accent;
}
calcColors();
onMounted(() => {
const update = () => {
if (enabled.value) {
if (enabled) {
tick();
window.setTimeout(update, 1000);
}
};
update();
globalEvents.on('themeChanged', calcColors);
});
onBeforeUnmount(() => {
enabled.value = false;
enabled = false;
globalEvents.off('themeChanged', calcColors);
});
</script>

View file

@ -0,0 +1,77 @@
<template>
<span class="zjobosdg">
<span v-text="hh"></span>
<span class="colon" :class="{ showColon }">:</span>
<span v-text="mm"></span>
<span v-if="showS" class="colon" :class="{ showColon }">:</span>
<span v-if="showS" v-text="ss"></span>
<span v-if="showMs" class="colon" :class="{ showColon }">:</span>
<span v-if="showMs" v-text="ms"></span>
</span>
</template>
<script lang="ts" setup>
import { onUnmounted, ref, watch } from 'vue';
const props = withDefaults(defineProps<{
showS?: boolean;
showMs?: boolean;
offset?: number;
}>(), {
showS: true,
showMs: false,
offset: 0 - new Date().getTimezoneOffset(),
});
let intervalId;
const hh = ref('');
const mm = ref('');
const ss = ref('');
const ms = ref('');
const showColon = ref(false);
let prevSec: number | null = null;
watch(showColon, (v) => {
if (v) {
window.setTimeout(() => {
showColon.value = false;
}, 30);
}
});
const tick = () => {
const now = new Date();
now.setMinutes(now.getMinutes() + (new Date().getTimezoneOffset() + props.offset));
hh.value = now.getHours().toString().padStart(2, '0');
mm.value = now.getMinutes().toString().padStart(2, '0');
ss.value = now.getSeconds().toString().padStart(2, '0');
ms.value = Math.floor(now.getMilliseconds() / 10).toString().padStart(2, '0');
if (now.getSeconds() !== prevSec) showColon.value = true;
prevSec = now.getSeconds();
};
tick();
watch(() => props.showMs, () => {
if (intervalId) window.clearInterval(intervalId);
intervalId = window.setInterval(tick, props.showMs ? 10 : 1000);
}, { immediate: true });
onUnmounted(() => {
window.clearInterval(intervalId);
});
</script>
<style lang="scss" scoped>
.zjobosdg {
> .colon {
opacity: 0;
transition: opacity 1s ease;
&.showColon {
opacity: 1;
transition: opacity 0s;
}
}
}
</style>

View file

@ -135,7 +135,7 @@ watch(q, () => {
return;
}
const newQ = q.value.replace(/:/g, '');
const newQ = q.value.replace(/:/g, '').toLowerCase();
const searchCustom = () => {
const max = 8;

View file

@ -137,7 +137,7 @@ export default defineComponent({
v.value = newValue;
});
watch(v, newValue => {
watch($$(v), () => {
if (!props.manualSave) {
if (props.debounce) {
debouncedUpdated();

View file

@ -31,7 +31,7 @@ export default defineComponent({
},
},
methods: {
userName
userName,
}
});
</script>

View file

@ -1,15 +1,8 @@
<template>
<div :class="[$style.root, { [$style.inline]: inline, [$style.colored]: colored, [$style.mini]: mini }]">
<div :class="$style.container">
<svg :class="[$style.spinner, $style.bg]" viewBox="0 0 168 168" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(1.125,0,0,1.125,12,12)">
<circle cx="64" cy="64" r="64" style="fill:none;stroke:currentColor;stroke-width:21.33px;"/>
</g>
</svg>
<svg :class="[$style.spinner, $style.fg]" viewBox="0 0 168 168" xmlns="http://www.w3.org/2000/svg">
<g transform="matrix(1.125,0,0,1.125,12,12)">
<path d="M128,64C128,28.654 99.346,0 64,0C99.346,0 128,28.654 128,64Z" style="fill:none;stroke:currentColor;stroke-width:21.33px;"/>
</g>
<div :class="$style.container" aria-hidden="true">
<svg :class="[$style.spinner]" viewBox="0 0 50 50" xmlns="http://www.w3.org/2000/svg">
<circle :class="[$style.path]" cx="25" cy="25" r="20" fill="none" stroke-width="6px" style="fill:none;stroke:currentColor;stroke-width:6px;"></circle>
</svg>
</div>
</div>
@ -30,7 +23,9 @@ const props = withDefaults(defineProps<{
</script>
<style lang="scss" module>
@keyframes spinner {
/* Credit to https://codepen.io/supah/pen/BjYLdW */
@keyframes spin {
0% {
transform: rotate(0deg);
}
@ -39,6 +34,21 @@ const props = withDefaults(defineProps<{
}
}
@keyframes dash {
0% {
stroke-dasharray: 1, 150;
stroke-dashoffset: 0;
}
50% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -35;
}
100% {
stroke-dasharray: 90, 150;
stroke-dashoffset: -124;
}
}
.root {
padding: 32px;
text-align: center;
@ -73,20 +83,16 @@ const props = withDefaults(defineProps<{
position: absolute;
top: 0;
left: 0;
z-index: 999;
width: var(--size);
height: var(--size);
fill-rule: evenodd;
clip-rule: evenodd;
animation: spin 2s linear infinite;
}
.path {
stroke: var(--accent);
stroke-linecap: round;
stroke-linejoin: round;
stroke-miterlimit: 1.5;
animation: dash 1.2s ease-in-out infinite;
}
.bg {
opacity: 0.275;
}
.fg {
animation: spinner 0.5s linear infinite;
}
</style>

View file

@ -20,7 +20,7 @@ const props = withDefaults(defineProps<{
const _time = typeof props.time === 'string' ? new Date(props.time) : props.time;
const absolute = _time.toLocaleString();
let now = $ref(new Date());
let now = $shallowRef(new Date());
const relative = $computed(() => {
const ago = (now.getTime() - _time.getTime()) / 1000/*ms*/;
return (

View file

@ -1,6 +1,6 @@
<template>
<div class="hpaizdrt" :style="bg">
<img v-if="instance.faviconUrl" class="icon" :src="instance.faviconUrl"/>
<img v-if="instance.faviconUrl" class="icon" :src="instance.faviconUrl" aria-hidden="true"/>
<span class="name">{{ instance.name }}</span>
</div>
</template>

View file

@ -1,35 +1,39 @@
<template>
<MkModal ref="modal" @click="done(true)" @closed="$emit('closed')">
<div class="container">
<div class="fullwidth top-caption">
<div class="mk-dialog">
<header>
<Mfm v-if="title" class="title" :text="title"/>
<span class="text-count" :class="{ over: remainingLength < 0 }">{{ remainingLength }}</span>
</header>
<textarea v-model="inputValue" autofocus :placeholder="input.placeholder" @keydown="onInputKeydown"></textarea>
<div v-if="(showOkButton || showCancelButton)" class="buttons">
<MkButton inline primary :disabled="remainingLength < 0" @click="ok">{{ $ts.ok }}</MkButton>
<MkButton inline @click="cancel" >{{ $ts.cancel }}</MkButton>
</div>
<MkModal ref="modal" @click="done(true)" @closed="$emit('closed')">
<div class="container">
<div class="fullwidth top-caption">
<div class="mk-dialog">
<header>
<Mfm v-if="title" class="title" :text="title"/>
<span class="text-count" :class="{ over: remainingLength < 0 }">{{ remainingLength }}</span>
<br/>
<span id="recognized-text"></span>
</header>
<textarea id="captioninput" v-model="inputValue" autofocus :placeholder="input.placeholder" @keydown="onInputKeydown"></textarea>
<div v-if="(showOkButton || showCaptionButton || showCancelButton)" class="buttons">
<MkButton inline primary :disabled="remainingLength < 0" @click="ok">{{ $ts.ok }}</MkButton>
<!-- <MkButton inline @click="caption" >{{ $ts.caption }}</MkButton> -->
<MkButton inline @click="cancel" >{{ $ts.cancel }}</MkButton>
</div>
</div>
<div class="hdrwpsaf fullwidth">
<header>{{ image.name }}</header>
<img :src="image.url" :alt="image.comment" :title="image.comment" @click="$refs.modal.close()"/>
<footer>
<span>{{ image.type }}</span>
<span>{{ bytes(image.size) }}</span>
<span v-if="image.properties && image.properties.width">{{ number(image.properties.width) }}px × {{ number(image.properties.height) }}px</span>
</footer>
</div>
</div>
</MkModal>
<div class="hdrwpsaf fullwidth">
<header>{{ image.name }}</header>
<img id="imgtocaption" :src="image.url" :alt="image.comment" :title="image.comment" @click="$refs.modal.close()"/>
<footer>
<span>{{ image.type }}</span>
<span>{{ bytes(image.size) }}</span>
<span v-if="image.properties && image.properties.width">{{ number(image.properties.width) }}px × {{ number(image.properties.height) }}px</span>
</footer>
</div>
</div>
</MkModal>
</template>
<script lang="ts">
import { defineComponent } from 'vue';
import { length } from 'stringz';
import Tesseract from 'tesseract.js';
import MkModal from '@/components/ui/modal.vue';
import MkButton from '@/components/ui/button.vue';
import bytes from '@/filters/bytes';
@ -48,22 +52,26 @@ export default defineComponent({
},
title: {
type: String,
required: false
required: false,
},
input: {
required: true
required: true,
},
showOkButton: {
type: Boolean,
default: true
default: true,
},
showCaptionButton: {
type: Boolean,
default: false,
},
showCancelButton: {
type: Boolean,
default: true
default: true,
},
cancelableByBgClick: {
type: Boolean,
default: true
default: true,
},
},
@ -130,8 +138,42 @@ export default defineComponent({
this.ok();
}
}
}
}
},
caption() {
const getBase64FromUrl = async (url) => {
const data = await fetch(url);
const blob = await data.blob();
return new Promise((resolve) => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onloadend = () => {
const base64data = reader.result;
resolve(base64data);
};
});
};
const img = document.getElementById('imgtocaption') as HTMLImageElement;
const b64 = getBase64FromUrl(img.src);
console.log(b64);
Tesseract.recognize(
b64,
'eng',
{ logger: m => console.log(m) },
).then(({ data: { text } }) => {
console.log(text);
});
// await worker.load();
// await worker.loadLanguage('eng');
// await worker.initialize('eng');
// const { data: { text } } = await worker.recognize(img);
// console.log(text);
// // document.getElementById('recognized-text').innerText = text;
// // const allowedLength = 512 - this.inputValue.length;
// // this.inputValue += text.slice(0, allowedLength);
// await worker.terminate();
},
},
});
</script>

View file

@ -41,7 +41,7 @@
<Mfm v-if="appearNote.cw != ''" class="text" :text="appearNote.cw" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
<XCwButton v-model="showContent" :note="appearNote"/>
</p>
<div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed }">
<div v-show="appearNote.cw == null || showContent" class="content" :class="{ collapsed, isLong }">
<div class="text">
<MkA v-if="appearNote.replyId" class="reply" :to="`/notes/${appearNote.replyId}`"><i class="fas fa-reply"></i></MkA>
<Mfm v-if="appearNote.text" :text="appearNote.text" :author="appearNote.user" :i="$i" :custom-emojis="appearNote.emojis"/>
@ -60,9 +60,12 @@
<XPoll v-if="appearNote.poll" ref="pollViewer" :note="appearNote" class="poll"/>
<MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" class="url-preview"/>
<div v-if="appearNote.renote" class="renote"><XNoteSimple :note="appearNote.renote"/></div>
<button v-if="collapsed" class="fade _button" @click="collapsed = false">
<button v-if="isLong && collapsed" class="fade _button" @click="collapsed = false">
<span>{{ i18n.ts.showMore }}</span>
</button>
<button v-else-if="isLong && !collapsed" class="showLess _button" @click="collapsed = true">
<span>{{ i18n.ts.showLess }}</span>
</button>
</div>
<MkA v-if="appearNote.channel && !inChannel" class="channel" :to="`/channels/${appearNote.channel.id}`"><i class="fas fa-satellite-dish"></i> {{ appearNote.channel.name }}</MkA>
</div>
@ -161,10 +164,11 @@ const reactButton = ref<HTMLElement>();
let appearNote = $computed(() => isRenote ? note.renote as misskey.entities.Note : note);
const isMyRenote = $i && ($i.id === note.userId);
const showContent = ref(false);
const collapsed = ref(appearNote.cw == null && appearNote.text != null && (
const isLong = (appearNote.cw == null && appearNote.text != null && (
(appearNote.text.split('\n').length > 9) ||
(appearNote.text.length > 500)
));
const collapsed = ref(appearNote.cw == null && isLong);
const isDeleted = ref(false);
const muted = ref(checkWordMute(appearNote, $i, defaultStore.state.mutedWords));
const translation = ref(null);
@ -319,7 +323,7 @@ function readPromo() {
margin: auto;
width: calc(100% - 8px);
height: calc(100% - 8px);
border: dashed 1px var(--focus);
border: solid 1px var(--focus);
border-radius: var(--radius);
box-sizing: border-box;
}
@ -441,6 +445,24 @@ function readPromo() {
}
> .content {
&.isLong {
> .showLess {
width: 100%;
margin-top: 1em;
position: sticky;
bottom: 1em;
> span {
display: inline-block;
background: var(--popup);
padding: 6px 10px;
font-size: 0.8em;
border-radius: 999px;
box-shadow: 0 2px 6px rgb(0 0 0 / 20%);
}
}
}
&.collapsed {
position: relative;
max-height: 9em;
@ -507,7 +529,7 @@ function readPromo() {
> * {
padding: 16px;
border: dashed 1px var(--renote);
border: solid 1px var(--renote);
border-radius: 8px;
}
}

View file

@ -221,43 +221,43 @@ useTooltip(reactionRef, (showing) => {
&.follow, &.followRequestAccepted, &.receiveFollowRequest, &.groupInvited {
padding: 3px;
background: #36aed2;
background: #31748f;
pointer-events: none;
}
&.renote {
padding: 3px;
background: #36d298;
background: #31748f;
pointer-events: none;
}
&.quote {
padding: 3px;
background: #36d298;
background: #31748f;
pointer-events: none;
}
&.reply {
padding: 3px;
background: #007aff;
background: #c4a7e7;
pointer-events: none;
}
&.mention {
padding: 3px;
background: #88a6b7;
background: #908caa;
pointer-events: none;
}
&.pollVote {
padding: 3px;
background: #88a6b7;
background: #908caa;
pointer-events: none;
}
&.pollEnded {
padding: 3px;
background: #88a6b7;
background: #908caa;
pointer-events: none;
}
}

View file

@ -1,6 +1,5 @@
<template>
<button
v-if="canRenote"
<button v-if="canRenote"
ref="buttonRef"
class="eddddedb _button canRenote"
@click="renote()"
@ -13,9 +12,8 @@
</button>
</template>
<script lang="ts" setup>
import { computed, ref } from 'vue';
import * as misskey from 'misskey-js';
<script lang="ts">
import { computed, defineComponent, ref } from 'vue';
import XDetails from '@/components/users-tooltip.vue';
import { pleaseLogin } from '@/scripts/please-login';
import * as os from '@/os';
@ -23,55 +21,72 @@ import { $i } from '@/account';
import { useTooltip } from '@/scripts/use-tooltip';
import { i18n } from '@/i18n';
const props = defineProps<{
note: misskey.entities.Note;
count: number;
}>();
export default defineComponent({
props: {
count: {
type: Number,
required: true,
},
note: {
type: Object,
required: true,
},
},
const buttonRef = ref<HTMLElement>();
setup(props) {
const buttonRef = ref<HTMLElement>();
const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
const canRenote = computed(() => ['public', 'home'].includes(props.note.visibility) || props.note.userId === $i.id);
useTooltip(buttonRef, async (showing) => {
const renotes = await os.api('notes/renotes', {
noteId: props.note.id,
limit: 11,
});
useTooltip(buttonRef, async (showing) => {
const renotes = await os.api('notes/renotes', {
noteId: props.note.id,
limit: 11
});
const users = renotes.map(x => x.user);
const users = renotes.map(x => x.user);
if (users.length < 1) return;
if (users.length < 1) return;
os.popup(XDetails, {
showing,
users,
count: props.count,
targetElement: buttonRef.value,
}, {}, 'closed');
os.popup(XDetails, {
showing,
users,
count: props.count,
targetElement: buttonRef.value
}, {}, 'closed');
});
const renote = (viaKeyboard = false) => {
pleaseLogin();
os.popupMenu([{
text: i18n.ts.renote,
icon: 'fas fa-retweet',
action: () => {
os.api('notes/create', {
renoteId: props.note.id,
visibility: props.note.visibility,
});
}
}, {
text: i18n.ts.quote,
icon: 'fas fa-quote-right',
action: () => {
os.post({
renote: props.note,
});
}
}], buttonRef.value, {
viaKeyboard
});
};
return {
buttonRef,
canRenote,
renote,
};
},
});
const renote = (viaKeyboard = false) => {
pleaseLogin();
os.popupMenu([{
text: i18n.ts.renote,
icon: 'fas fa-retweet',
action: () => {
os.api('notes/create', {
renoteId: props.note.id,
});
},
}, {
text: i18n.ts.quote,
icon: 'fas fa-quote-right',
action: () => {
os.post({
renote: props.note,
});
},
}], buttonRef.value, {
viaKeyboard,
});
};
</script>
<style lang="scss" scoped>

View file

@ -32,6 +32,8 @@ export default defineComponent({
.pxhvhrfw {
display: flex;
font-size: 90%;
border-radius: var(--radius);
padding: 10px 8px;
> button {
flex: 1;

View file

@ -77,6 +77,10 @@ if (props.src === 'antenna') {
endpoint = 'notes/local-timeline';
connection = stream.useChannel('localTimeline');
connection.on('note', prepend);
} else if (props.src === 'recommended') {
endpoint = 'notes/recommended-timeline';
connection = stream.useChannel('recommendedTimeline');
connection.on('note', prepend);
} else if (props.src === 'social') {
endpoint = 'notes/hybrid-timeline';
connection = stream.useChannel('hybridTimeline');

Some files were not shown because too many files have changed in this diff Show more